"""Test the service primitives."""

import logging

import pytest

from pydicom import config as PYD_CONFIG
from pydicom.uid import UID

from pynetdicom import _config
from pynetdicom.pdu import A_ASSOCIATE_RQ, A_ABORT_RQ, P_DATA_TF
from pynetdicom.pdu_primitives import (
    SOPClassExtendedNegotiation,
    SOPClassCommonExtendedNegotiation,
    MaximumLengthNotification,
    ImplementationClassUIDNotification,
    ImplementationVersionNameNotification,
    P_DATA,
    A_RELEASE,
    A_ASSOCIATE,
    A_P_ABORT,
    A_ABORT,
    SCP_SCU_RoleSelectionNegotiation,
    AsynchronousOperationsWindowNegotiation,
    UserIdentityNegotiation,
)
from pynetdicom.presentation import PresentationContext
from pynetdicom.utils import pretty_bytes

if hasattr(PYD_CONFIG, "settings"):
    PYD_CONFIG.settings.reading_validation_mode = 0

LOGGER = logging.getLogger("pynetdicom")
LOGGER.setLevel(logging.CRITICAL)

def print_nice_bytes(bytestream):
    """Nice output for bytestream."""
    str_list = pretty_bytes(
        bytestream, prefix="b'\\x", delimiter="\\x", items_per_line=10
    )
    for string in str_list:
        print(string)

class TestPrimitive_MaximumLengthNotification:
    def test_assignment_and_exceptions(self):
        """Test incorrect setter for maximum_length_received raises"""
        primitive = MaximumLengthNotification()

        # Check default assignment
        assert primitive.maximum_length_received == 16382

        # Check new assignment
        primitive.maximum_length_received = 45
        assert primitive.maximum_length_received == 45

        # Check exceptions
        with pytest.raises(TypeError):
            primitive.maximum_length_received = 45.2

        with pytest.raises(ValueError):
            primitive.maximum_length_received = -1

        with pytest.raises(TypeError):
            primitive.maximum_length_received = "abc"

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        ## Check conversion to item using default value
        primitive = MaximumLengthNotification()
        item = primitive.from_primitive()

        # \x3F\xFE = 16382
        assert item.encode() == b"\x51\x00\x00\x04\x00\x00\x3f\xfe"

        ## Check conversion using 0 (unlimited)
        primitive.maximum_length_received = 0
        item = primitive.from_primitive()

        # \x00\x00 = 0
        assert item.encode() == b"\x51\x00\x00\x04\x00\x00\x00\x00"

    def test_string(self):
        """Check the string output."""
        primitive = MaximumLengthNotification()
        assert "16382 bytes" in primitive.__str__()

class TestPrimitive_ImplementationClassUIDNotification:
    def setup_method(self):
        self.default_conformance = _config.ENFORCE_UID_CONFORMANCE

    def teardown_method(self):
        _config.ENFORCE_UID_CONFORMANCE = self.default_conformance

    def test_uid_conformance_false(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = False"""
        _config.ENFORCE_UID_CONFORMANCE = False
        primitive = ImplementationClassUIDNotification()

        primitive.implementation_class_uid = "abc"
        assert primitive.implementation_class_uid == "abc"

        with pytest.raises(ValueError):
            primitive.implementation_class_uid = "abc" * 22

    def test_uid_conformance_true(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = True"""
        _config.ENFORCE_UID_CONFORMANCE = True
        primitive = ImplementationClassUIDNotification()

        with pytest.raises(ValueError):
            primitive.implementation_class_uid = "abc"

    def test_assignment_and_exceptions(self):
        """Check incorrect setter for implementation_class_uid raises"""
        primitive = ImplementationClassUIDNotification()

        ## Check assignment
        reference_uid = UID("1.2.826.0.1.3680043.9.3811.0.9.0")

        # bytes
        primitive.implementation_class_uid = b"1.2.826.0.1.3680043.9.3811.0.9.0"
        assert primitive.implementation_class_uid == reference_uid

        # str
        primitive.implementation_class_uid = "1.2.826.0.1.3680043.9.3811.0.9.0"
        assert primitive.implementation_class_uid == reference_uid

        # UID
        primitive.implementation_class_uid = UID("1.2.826.0.1.3680043.9.3811.0.9.0")
        assert primitive.implementation_class_uid == reference_uid

        ## Check exceptions
        primitive = ImplementationClassUIDNotification()

        # No value set
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        # Non UID, bytes or str
        with pytest.raises(TypeError):
            primitive.implementation_class_uid = 45.2

        with pytest.raises(TypeError):
            primitive.implementation_class_uid = 100

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = ImplementationClassUIDNotification()
        primitive.implementation_class_uid = UID("1.2.826.0.1.3680043.9.3811.0.9.0")
        item = primitive.from_primitive()

        assert item.encode() == (
            b"\x52\x00\x00\x20\x31\x2e\x32\x2e\x38\x32\x36\x2e\x30\x2e\x31"
            b"\x2e\x33\x36\x38\x30\x30\x34\x33\x2e\x39\x2e\x33\x38\x31\x31\x2e"
            b"\x30\x2e\x39\x2e\x30"
        )

    def test_string(self):
        """Check the string output."""
        primitive = ImplementationClassUIDNotification()
        primitive.implementation_class_uid = UID("1.2.826.0.1.3680043.9.3811.0.9.0")
        assert "1.2.826.0.1.3680043.9.3811.0.9.0" in primitive.__str__()

class TestPrimitive_ImplementationVersionNameNotification:
    def test_assignment_and_exceptions(self):
        """Check incorrect setting for implementation_version_name raises"""
        primitive = ImplementationVersionNameNotification()

        ## Check assignment
        reference_name = "PYNETDICOM_090"

        ## Check maximum length allowable
        primitive.implementation_version_name = "1234567890ABCDEF"
        assert primitive.implementation_version_name == "1234567890ABCDEF"

        # bytes
        primitive.implementation_version_name = "PYNETDICOM_090"
        assert primitive.implementation_version_name == reference_name

        # str
        primitive.implementation_version_name = "PYNETDICOM_090"
        assert primitive.implementation_version_name == reference_name

        primitive.implementation_version_name = "P"
        assert primitive.implementation_version_name == "P"

        ## Check exceptions
        primitive = ImplementationVersionNameNotification()

        # No value set
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        # Non UID, bytes or str
        with pytest.raises(TypeError):
            primitive.implementation_version_name = 45.2

        with pytest.raises(TypeError):
            primitive.implementation_version_name = 100

        bad = "ABCD1234ABCD12345"
        msg = (
            f"Invalid 'Implementation Version Name' value '{bad}' - must not "
            "exceed 16 characters"
        )
        with pytest.raises(ValueError, match=msg):
            primitive.implementation_version_name = bad

        primitive.implementation_version_name = ""
        assert primitive.implementation_version_name == ""
        primitive.implementation_version_name = None
        assert primitive.implementation_version_name is None

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = ImplementationVersionNameNotification()
        primitive.implementation_version_name = "PYNETDICOM_090"
        item = primitive.from_primitive()

        assert item.encode() == (
            b"\x55\x00\x00\x0e\x50\x59\x4e\x45\x54\x44\x49\x43\x4f\x4d\x5f"
            b"\x30\x39\x30"
        )

    def test_string(self):
        """Check the string output."""
        primitive = ImplementationVersionNameNotification()
        primitive.implementation_version_name = "PYNETDICOM3_090"
        assert "PYNETDICOM3_090" in primitive.__str__()

class TestPrimitive_AsynchronousOperationsWindowNegotiation:
    def test_assignment_and_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = AsynchronousOperationsWindowNegotiation()

        ## Check default assignment
        assert primitive.maximum_number_operations_invoked == 1
        assert primitive.maximum_number_operations_performed == 1

        ## Check assignment
        primitive.maximum_number_operations_invoked = 10
        assert primitive.maximum_number_operations_invoked == 10

        primitive.maximum_number_operations_performed = 11
        assert primitive.maximum_number_operations_performed == 11

        ## Check exceptions
        with pytest.raises(TypeError):
            primitive.maximum_number_operations_invoked = 45.2

        with pytest.raises(ValueError):
            primitive.maximum_number_operations_invoked = -1

        with pytest.raises(TypeError):
            primitive.maximum_number_operations_invoked = "ABCD1234ABCD12345"

        with pytest.raises(TypeError):
            primitive.maximum_number_operations_performed = 45.2

        with pytest.raises(ValueError):
            primitive.maximum_number_operations_performed = -1

        with pytest.raises(TypeError):
            primitive.maximum_number_operations_performed = "ABCD1234ABCD12345"

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = AsynchronousOperationsWindowNegotiation()
        primitive.maximum_number_operations_invoked = 10
        primitive.maximum_number_operations_performed = 0
        item = primitive.from_primitive()

        assert item.encode() == b"\x53\x00\x00\x04\x00\x0a\x00\x00"

    def test_string(self):
        """Check the string output."""
        primitive = AsynchronousOperationsWindowNegotiation()
        primitive.maximum_number_operations_invoked = 10
        primitive.maximum_number_operations_performed = 0
        assert "invoked: 10" in primitive.__str__()
        assert "performed: 0" in primitive.__str__()

class TestPrimitive_SCP_SCU_RoleSelectionNegotiation:
    def setup_method(self):
        self.default_conformance = _config.ENFORCE_UID_CONFORMANCE

    def teardown_method(self):
        _config.ENFORCE_UID_CONFORMANCE = self.default_conformance

    def test_uid_conformance_false(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = False"""
        _config.ENFORCE_UID_CONFORMANCE = False
        primitive = SCP_SCU_RoleSelectionNegotiation()

        primitive.sop_class_uid = "abc"
        assert primitive.sop_class_uid == "abc"

    def test_uid_conformance_true(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = True"""
        _config.ENFORCE_UID_CONFORMANCE = True
        primitive = SCP_SCU_RoleSelectionNegotiation()

        with pytest.raises(ValueError):
            primitive.sop_class_uid = "abc"

    def test_assignment_and_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = SCP_SCU_RoleSelectionNegotiation()

        ## Check assignment
        # SOP Class UID
        reference_uid = UID("1.2.840.10008.5.1.4.1.1.2")

        primitive.sop_class_uid = None
        assert primitive.sop_class_uid is None
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = "1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = UID("1.2.840.10008.5.1.4.1.1.2")
        assert primitive.sop_class_uid == reference_uid

        # SCP Role
        primitive.scp_role = None
        assert primitive.scp_role is None
        primitive.scp_role = False
        assert primitive.scp_role is False

        # SCU Role
        primitive.scu_role = None
        assert primitive.scu_role is None
        primitive.scu_role = True
        assert primitive.scu_role is True

        ## Check exceptions
        with pytest.raises(TypeError):
            primitive.sop_class_uid = 10

        with pytest.raises(TypeError):
            primitive.sop_class_uid = 45.2

        with pytest.raises(TypeError):
            primitive.scp_role = 1

        with pytest.raises(TypeError):
            primitive.scp_role = "abc"

        with pytest.raises(TypeError):
            primitive.scu_role = 1

        with pytest.raises(TypeError):
            primitive.scu_role = "abc"

        # No value set
        primitive = SCP_SCU_RoleSelectionNegotiation()
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive.scp_role = False
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive = SCP_SCU_RoleSelectionNegotiation()
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        primitive.scu_role = True
        item = primitive.from_primitive()
        assert item.scu_role
        assert not item.scp_role

        primitive = SCP_SCU_RoleSelectionNegotiation()
        primitive.scp_role = True
        primitive.scu_role = True
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = SCP_SCU_RoleSelectionNegotiation()
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        primitive.scp_role = True
        primitive.scu_role = False
        item = primitive.from_primitive()

        assert item.encode() == (
            b"\x54\x00\x00\x1d\x00\x19\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30"
            b"\x30\x30\x38\x2e\x35\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x2e\x32"
            b"\x00\x01"
        )

        primitive = SCP_SCU_RoleSelectionNegotiation()
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        primitive.scp_role = False
        primitive.scu_role = False
        with pytest.raises(ValueError):
            primitive.from_primitive()

class TestPrimitive_SOPClassExtendedNegotiation:
    def setup_method(self):
        self.default_conformance = _config.ENFORCE_UID_CONFORMANCE

    def teardown_method(self):
        _config.ENFORCE_UID_CONFORMANCE = self.default_conformance

    def test_uid_conformance_false(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = False"""
        _config.ENFORCE_UID_CONFORMANCE = False
        primitive = SOPClassExtendedNegotiation()

        primitive.sop_class_uid = "abc"
        assert primitive.sop_class_uid == "abc"

    def test_uid_conformance_true(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = True"""
        _config.ENFORCE_UID_CONFORMANCE = True
        primitive = SOPClassExtendedNegotiation()

        with pytest.raises(ValueError):
            primitive.sop_class_uid = "abc"

    def test_assignment_and_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = SOPClassExtendedNegotiation()

        ## Check assignment
        # SOP Class UID
        reference_uid = UID("1.2.840.10008.5.1.4.1.1.2")

        primitive.sop_class_uid = None
        assert primitive.sop_class_uid is None
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = "1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = UID("1.2.840.10008.5.1.4.1.1.2")
        assert primitive.sop_class_uid == reference_uid

        # Service Class Application Information
        primitive.service_class_application_information = None
        assert primitive.service_class_application_information is None
        primitive.service_class_application_information = b"\x02\x00\x03\x00\x01\x00"
        assert primitive.service_class_application_information == (
            b"\x02\x00\x03\x00\x01\x00"
        )

        ## Check exceptions
        # SOP Class UID
        with pytest.raises(TypeError):
            primitive.sop_class_uid = 10

        with pytest.raises(TypeError):
            primitive.sop_class_uid = 45.2

        # Service Class Application Information
        with pytest.raises(TypeError):
            primitive.service_class_application_information = 10

        with pytest.raises(TypeError):
            primitive.service_class_application_information = 45.2

        # No value set
        primitive = SOPClassExtendedNegotiation()
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive = SOPClassExtendedNegotiation()
        primitive.service_class_application_information = b"\x02\x00\x03\x00\x01\x00"
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = SOPClassExtendedNegotiation()
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        primitive.service_class_application_information = b"\x02\x00\x03\x00\x01\x00"
        item = primitive.from_primitive()

        assert item.encode() == (
            b"\x56\x00\x00\x21\x00\x19\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30"
            b"\x30\x30\x38\x2e\x35\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x2e\x32"
            b"\x02\x00\x03\x00\x01\x00"
        )

class TestPrimitive_SOPClassCommonExtendedNegotiation:
    def setup_method(self):
        self.default_conformance = _config.ENFORCE_UID_CONFORMANCE

    def teardown_method(self):
        _config.ENFORCE_UID_CONFORMANCE = self.default_conformance

    def test_uid_conformance_false(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = False"""
        _config.ENFORCE_UID_CONFORMANCE = False
        primitive = SOPClassCommonExtendedNegotiation()

        primitive.sop_class_uid = None
        assert primitive.sop_class_uid is None
        primitive.sop_class_uid = "abc"
        assert primitive.sop_class_uid == "abc"
        primitive.service_class_uid = None
        assert primitive.service_class_uid is None
        primitive.service_class_uid = "abc"
        assert primitive.service_class_uid == "abc"
        primitive.related_general_sop_class_identification = ["abc"]
        assert primitive.related_general_sop_class_identification == ["abc"]

        with pytest.raises(ValueError):
            primitive.sop_class_uid = "abc" * 22

        with pytest.raises(ValueError):
            primitive.service_class_uid = "abc" * 22

        with pytest.raises(ValueError):
            primitive.related_general_sop_class_identification = ["abc" * 22]

    def test_uid_conformance_true(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = True"""
        _config.ENFORCE_UID_CONFORMANCE = True
        primitive = SOPClassCommonExtendedNegotiation()

        with pytest.raises(ValueError):
            primitive.sop_class_uid = "abc"

        with pytest.raises(ValueError):
            primitive.service_class_uid = "abc"

        with pytest.raises(ValueError):
            primitive.related_general_sop_class_identification = ["abc"]

    def test_assignment_and_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = SOPClassCommonExtendedNegotiation()

        ## Check assignment
        # SOP Class UID
        reference_uid = UID("1.2.840.10008.5.1.4.1.1.2")

        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = "abc"
        assert primitive.sop_class_uid == "abc"
        primitive.sop_class_uid = "1.2.840.10008.5.1.4.1.1.2"
        assert primitive.sop_class_uid == reference_uid
        primitive.sop_class_uid = UID("1.2.840.10008.5.1.4.1.1.2")
        assert primitive.sop_class_uid == reference_uid

        # Service Class UID
        primitive.service_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        assert primitive.service_class_uid == reference_uid
        primitive.service_class_uid = "abc"
        assert primitive.service_class_uid == "abc"
        primitive.service_class_uid = "1.2.840.10008.5.1.4.1.1.2"
        assert primitive.service_class_uid == reference_uid
        primitive.service_class_uid = UID("1.2.840.10008.5.1.4.1.1.2")
        assert primitive.service_class_uid == reference_uid

        # Related General SOP Class Identification
        ref_list = [
            UID("1.2.840.10008.5.1.4.1.1.2"),
            UID("1.2.840.10008.5.1.4.1.1.3"),
            UID("1.2.840.10008.5.1.4.1.1.4"),
        ]

        uid_list = []
        uid_list.append(b"1.2.840.10008.5.1.4.1.1.2")
        uid_list.append("1.2.840.10008.5.1.4.1.1.3")
        uid_list.append(UID("1.2.840.10008.5.1.4.1.1.4"))
        primitive.related_general_sop_class_identification = uid_list
        assert primitive.related_general_sop_class_identification == ref_list
        primitive.related_general_sop_class_identification = ["abc"]
        assert primitive.related_general_sop_class_identification == ["abc"]

        with pytest.raises(TypeError):
            primitive.related_general_sop_class_identification = "test"

        ## Check exceptions
        # SOP Class UID
        with pytest.raises(TypeError):
            primitive.sop_class_uid = 10

        with pytest.raises(TypeError):
            primitive.sop_class_uid = 45.2

        # Service Class UID
        with pytest.raises(TypeError):
            primitive.service_class_uid = 10

        with pytest.raises(TypeError):
            primitive.service_class_uid = 45.2

        # Related General SOP Class Identification
        with pytest.raises(TypeError):
            primitive.related_general_sop_class_identification = [10]

        with pytest.raises(TypeError):
            primitive.related_general_sop_class_identification = [45.2]

        # No value set
        primitive = SOPClassCommonExtendedNegotiation()
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        primitive = SOPClassCommonExtendedNegotiation()
        primitive.service_class_uid = b"1.2.840.10008.5.1.4.1.1.2"
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = SOPClassCommonExtendedNegotiation()
        primitive.sop_class_uid = b"1.2.840.10008.5.1.4.1.1.4"
        primitive.service_class_uid = b"1.2.840.10008.4.2"
        primitive.related_general_sop_class_identification = [
            "1.2.840.10008.5.1.4.1.1.88.22"
        ]
        item = primitive.from_primitive()

        assert item.encode() == (
            b"\x57\x00\x00\x4f\x00\x19\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30"
            b"\x30\x30\x38\x2e\x35\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x2e\x34\x00"
            b"\x11\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e\x34"
            b"\x2e\x32\x00\x1f\x00\x1d\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30"
            b"\x30\x30\x38\x2e\x35\x2e\x31\x2e\x34\x2e\x31\x2e\x31\x2e\x38\x38"
            b"\x2e\x32\x32"
        )

class TestPrimitive_UserIdentityNegotiation:
    def test_assignment_and_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = UserIdentityNegotiation()
        for type_no in [1, 2, 3, 4, 5]:
            primitive.user_identity_type = type_no
            assert primitive.user_identity_type == type_no

        with pytest.raises(ValueError):
            primitive.user_identity_type = 6

        with pytest.raises(TypeError):
            primitive.user_identity_type = "a"

        primitive.positive_response_requested = True
        assert primitive.positive_response_requested
        with pytest.raises(TypeError):
            primitive.positive_response_requested = "test"

        primitive.primary_field = None
        assert primitive.primary_field is None
        primitive.primary_field = b"\x00\x01"
        assert primitive.primary_field == b"\x00\x01"
        with pytest.raises(TypeError):
            primitive.primary_field = ["test"]

        primitive.secondary_field = b"\x00\x21"
        assert primitive.secondary_field == b"\x00\x21"
        primitive.secondary_field = None
        assert primitive.secondary_field is None
        with pytest.raises(TypeError):
            primitive.secondary_field = ["test"]

        primitive.server_response = None
        assert primitive.server_response is None
        primitive.server_response = b"\x00\x31"
        assert primitive.server_response == b"\x00\x31"
        with pytest.raises(TypeError):
            primitive.server_response = ["test"]

        primitive = UserIdentityNegotiation()
        with pytest.raises(ValueError):
            primitive.from_primitive()

        primitive.user_identity_type = None
        assert primitive.user_identity_type is None
        primitive.user_identity_type = 2
        with pytest.raises(ValueError):
            primitive.from_primitive()

    def test_string(self):
        """Check string output."""
        primitive = UserIdentityNegotiation()
        primitive.user_identity_type = 1
        primitive.positive_response_requested = True
        primitive.primary_field = b"\x00\x01"
        primitive.secondary_field = b"\x00\x21"
        assert "requested: True" in primitive.__str__()
        assert "type: 1" in primitive.__str__()
        assert "Primary" in primitive.__str__()
        assert "Secondary" in primitive.__str__()

        primitive.server_response = b"\x00\x31"
        assert "Server response" in primitive.__str__()

    def test_conversion(self):
        """Check converting to PDU item works correctly"""
        primitive = UserIdentityNegotiation()
        # -RQ
        primitive.user_identity_type = 1
        primitive.primary_field = b"test"
        item = primitive.from_primitive()

        primitive.user_identity_type = 2
        primitive.secondary_field = b""
        with pytest.raises(ValueError):
            item = primitive.from_primitive()

        # -AC
        primitive = UserIdentityNegotiation()
        primitive.server_response = b"Test"
        item = primitive.from_primitive()
        assert item.encode() == b"\x59\x00\x00\x06\x00\x04\x54\x65\x73\x74"

class TestPrimitive_A_ASSOCIATE:
    def setup_method(self):
        self.default_conformance = _config.ENFORCE_UID_CONFORMANCE

    def teardown_method(self):
        _config.ENFORCE_UID_CONFORMANCE = self.default_conformance

    def test_uid_conformance_false(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = False"""
        _config.ENFORCE_UID_CONFORMANCE = False
        primitive = A_ASSOCIATE()

        primitive.application_context_name = "abc"
        assert primitive.application_context_name == "abc"

    def test_uid_conformance_true(self):
        """Test UID conformance with ENFORCE_UID_CONFORMANCE = True"""
        _config.ENFORCE_UID_CONFORMANCE = True
        primitive = A_ASSOCIATE()

        with pytest.raises(ValueError):
            primitive.application_context_name = "abc"

    def test_assignment(self):
        """Check assignment works correctly"""
        assoc = A_ASSOCIATE()

        with pytest.raises(AttributeError):
            assoc.mode = "test value"

        with pytest.raises(AttributeError):
            assoc.presentation_requirements = "test value2"

        with pytest.raises(AttributeError):
            assoc.session_requirements = "test value3"

        assoc.application_context_name = None
        assert assoc.application_context_name is None
        assoc.application_context_name = "1.2.840.10008.3.1.1.1"
        assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1")
        assoc.application_context_name = b"1.2.840.10008.3.1.1.1"
        assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1")
        assoc.application_context_name = UID("1.2.840.10008.3.1.1.1")
        assert assoc.application_context_name == UID("1.2.840.10008.3.1.1.1")

        msg = "'Calling AE Title' must be str, not 'NoneType'"
        with pytest.raises(TypeError, match=msg):
            assoc.calling_ae_title = None

        assoc.calling_ae_title = "ABCDEF1234567890"
        assert assoc.calling_ae_title == "ABCDEF1234567890"

        msg = "'Called AE Title' must be str, not 'NoneType'"
        with pytest.raises(TypeError, match=msg):
            assoc.called_ae_title = None

        assert assoc.responding_ae_title == assoc.called_ae_title
        assoc.called_ae_title = "1234567890ABCDEF"
        assert assoc.called_ae_title == "1234567890ABCDEF"
        assert assoc.responding_ae_title == assoc.called_ae_title

        max_length = MaximumLengthNotification()
        max_length.maximum_length_received = 31222
        assoc.user_information.append(max_length)
        assert assoc.user_information[0].maximum_length_received == 31222

        assoc.user_information = ["a", max_length]
        assert assoc.user_information == [max_length]

        assoc.result = None
        assert assoc.result is None
        assoc.result = 0
        assoc.result = 1
        assert assoc.result == 1
        assoc.result = 2
        assert assoc.result == 2

        assoc.result_source = None
        assert assoc.result_source is None
        assoc.result_source = 1
        assert assoc.result_source == 1
        assoc.result_source = 2
        assert assoc.result_source == 2
        assoc.result_source = 3
        assert assoc.result_source == 3

        assoc.diagnostic = None
        assert assoc.diagnostic is None
        assoc.diagnostic = 1
        assert assoc.diagnostic == 1
        assoc.diagnostic = 2
        assert assoc.diagnostic == 2
        assoc.diagnostic = 3
        assert assoc.diagnostic == 3
        assoc.diagnostic = 7
        assert assoc.diagnostic == 7

        assoc.calling_presentation_address = None
        assert assoc.calling_presentation_address is None
        assoc.calling_presentation_address = ("10.40.94.43", 105)
        assert assoc.calling_presentation_address == ("10.40.94.43", 105)

        assoc.called_presentation_address = None
        assert assoc.called_presentation_address is None
        assoc.called_presentation_address = ("10.40.94.44", 106)
        assert assoc.called_presentation_address == ("10.40.94.44", 106)

        pc = PresentationContext()
        pc.context_id = 1
        assoc.presentation_context_definition_list = [pc]
        assert assoc.presentation_context_definition_list == [pc]
        assoc.presentation_context_definition_list = ["a", pc]
        assert assoc.presentation_context_definition_list == [pc]

        assoc.presentation_context_definition_results_list = [pc]
        assert assoc.presentation_context_definition_results_list == [pc]
        assoc.presentation_context_definition_results_list = ["a", pc]
        assert assoc.presentation_context_definition_results_list == [pc]

        assoc = A_ASSOCIATE()
        # No maximum_length_received set
        assert assoc.maximum_length_received is None

        # No MaximumLengthNotification present
        assoc.maximum_length_received = 31223
        assert assoc.user_information[0].maximum_length_received == 31223
        assert assoc.maximum_length_received == 31223

        # MaximumLengthNotification already present
        assoc.maximum_length_received = 31224
        assert assoc.maximum_length_received == 31224

        # No ImplementationClassUIDNotification present
        assoc.implementation_class_uid = "1.1.2.3.4"
        assert assoc.user_information[1].implementation_class_uid == UID("1.1.2.3.4")
        assert assoc.implementation_class_uid == UID("1.1.2.3.4")

        # ImplementationClassUIDNotification already present
        assoc.implementation_class_uid = "1.1.2.3.4"
        assert assoc.implementation_class_uid == UID("1.1.2.3.4")

    def test_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        assoc = A_ASSOCIATE()

        # application_context_name
        with pytest.raises(TypeError):
            assoc.application_context_name = 10

        with pytest.raises(TypeError):
            assoc.application_context_name = 45.2

        # calling_ae_title
        with pytest.raises(TypeError):
            assoc.calling_ae_title = 45.2

        with pytest.raises(TypeError):
            assoc.calling_ae_title = 100

        with pytest.raises(ValueError):
            assoc.calling_ae_title = ""

        with pytest.raises(ValueError):
            assoc.calling_ae_title = "    "

        # called_ae_title
        with pytest.raises(TypeError):
            assoc.called_ae_title = 45.2

        with pytest.raises(TypeError):
            assoc.called_ae_title = 100

        with pytest.raises(ValueError):
            assoc.called_ae_title = ""

        with pytest.raises(ValueError):
            assoc.called_ae_title = "    "

        # user_information
        with pytest.raises(TypeError):
            assoc.user_information = 45.2

        # result
        with pytest.raises(ValueError):
            assoc.result = -1

        with pytest.raises(ValueError):
            assoc.result = 3

        # result_source
        with pytest.raises(ValueError):
            assoc.result_source = 0

        # result_source
        with pytest.raises(ValueError):
            assoc.result_source = 4

        # diagnostic
        with pytest.raises(ValueError):
            assoc.diagnostic = 0

        with pytest.raises(ValueError):
            assoc.diagnostic = 4

        with pytest.raises(ValueError):
            assoc.diagnostic = 5

        with pytest.raises(ValueError):
            assoc.diagnostic = 6

        with pytest.raises(ValueError):
            assoc.diagnostic = 8

        # calling_presentation_addresss
        with pytest.raises(TypeError):
            assoc.calling_presentation_address = ["10.40.94.43", 105]

        with pytest.raises(TypeError):
            assoc.calling_presentation_address = (105, "10.40.94.43")

        # called_presentation_addresss
        with pytest.raises(TypeError):
            assoc.called_presentation_address = ["10.40.94.43", 105]

        with pytest.raises(TypeError):
            assoc.called_presentation_address = (105, "10.40.94.43")

        # presentation_context_definition_list
        with pytest.raises(TypeError):
            assoc.presentation_context_definition_list = 45.2

        # presentation_context_definition_results_list
        with pytest.raises(TypeError):
            assoc.presentation_context_definition_results_list = 45.2

        # implementation_class_uid
        with pytest.raises(ValueError):
            x = assoc.implementation_class_uid

        imp_uid = ImplementationClassUIDNotification()
        assoc.user_information.append(imp_uid)
        with pytest.raises(ValueError):
            x = assoc.implementation_class_uid

    def test_conversion(self):
        """Check conversion to a PDU produces the correct output"""
        assoc = A_ASSOCIATE()
        assoc.application_context_name = "1.2.840.10008.3.1.1.1"
        assoc.calling_ae_title = "ECHOSCU"
        assoc.called_ae_title = "ANY-SCP"
        assoc.maximum_length_received = 16382
        assoc.implementation_class_uid = "1.2.826.0.1.3680043.9.3811.0.9.0"

        imp_ver_name = ImplementationVersionNameNotification()
        imp_ver_name.implementation_version_name = "PYNETDICOM_090"
        assoc.user_information.append(imp_ver_name)

        pc = PresentationContext()
        pc.context_id = 1
        pc.abstract_syntax = "1.2.840.10008.1.1"
        pc.transfer_syntax = ["1.2.840.10008.1.2"]
        assoc.presentation_context_definition_list = [pc]

        pdu = A_ASSOCIATE_RQ()
        pdu.from_primitive(assoc)
        data = pdu.encode()

        assert data == (
            b"\x01\x00\x00\x00\x00\xd1\x00\x01\x00\x00\x41\x4e\x59\x2d\x53\x43"
            b"\x50\x20\x20\x20\x20\x20\x20\x20\x20\x20\x45\x43\x48\x4f\x53\x43"
            b"\x55\x20\x20\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00\x00\x00\x00"
            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x15\x31\x2e"
            b"\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e\x33\x2e\x31\x2e"
            b"\x31\x2e\x31\x20\x00\x00\x2e\x01\x00\x00\x00\x30\x00\x00\x11\x31"
            b"\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e\x31\x2e\x31"
            b"\x40\x00\x00\x11\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30"
            b"\x38\x2e\x31\x2e\x32\x50\x00\x00\x3e\x51\x00\x00\x04\x00\x00\x3f"
            b"\xfe\x52\x00\x00\x20\x31\x2e\x32\x2e\x38\x32\x36\x2e\x30\x2e\x31"
            b"\x2e\x33\x36\x38\x30\x30\x34\x33\x2e\x39\x2e\x33\x38\x31\x31\x2e"
            b"\x30\x2e\x39\x2e\x30\x55\x00\x00\x0e\x50\x59\x4e\x45\x54\x44\x49"
            b"\x43\x4f\x4d\x5f\x30\x39\x30"
        )

    def test_invalid_result_str(self, caplog):
        """Test an invalid result value gets logged and doesn't raise."""
        pdu = A_ASSOCIATE()
        pdu.result = None
        with caplog.at_level(logging.WARNING, logger="pynetdicom"):
            assert pdu.result_str == "(no value available)"
            assert "Invalid A-ASSOCIATE 'Result' None" in caplog.text

    def test_invalid_source_str(self, caplog):
        """Test an invalid source value gets logged and doesn't raise."""
        pdu = A_ASSOCIATE()
        pdu.result_source = None
        with caplog.at_level(logging.WARNING, logger="pynetdicom"):
            assert pdu.source_str == "(no value available)"
            assert "Invalid A-ASSOCIATE 'Result Source' None" in caplog.text

    def test_invalid_reason_str(self, caplog):
        """Test an invalid diagnostic value gets logged and doesn't raise."""
        pdu = A_ASSOCIATE()
        pdu.result = 1
        pdu.result_source = 2
        pdu.diagnostic = 7
        with caplog.at_level(logging.WARNING, logger="pynetdicom"):
            assert pdu.reason_str == "(no value available)"
            assert (
                "Invalid A-ASSOCIATE 'Result Source' 2 and/or 'Diagnostic' 7 values"
            ) in caplog.text

class TestPrimitive_A_RELEASE:
    def test_assignment(self):
        """Check assignment works correctly"""
        assoc = A_RELEASE()
        assert assoc.reason == "normal"

        assoc.result = "affirmative"
        assert assoc.result == "affirmative"

    def test_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        assoc = A_RELEASE()

        with pytest.raises(AttributeError):
            assoc.reason = "something"

        with pytest.raises(ValueError):
            assoc.result = "accepted"

class TestPrimitive_A_ABORT:
    def test_assignment(self):
        """Check assignment works correctly"""
        primitive = A_ABORT()
        primitive.abort_source = 0
        assert primitive.abort_source == 0
        primitive.abort_source = 1
        assert primitive.abort_source == 1
        primitive.abort_source = 2
        assert primitive.abort_source == 2

    def test_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = A_ABORT()

        with pytest.raises(ValueError):
            primitive.abort_source = 3

        with pytest.raises(ValueError):
            primitive.abort_source

    def test_conversion(self):
        """Check conversion to a PDU produces the correct output"""
        primitive = A_ABORT()
        primitive.abort_source = 0

        pdu = A_ABORT_RQ()
        pdu.from_primitive(primitive)
        data = pdu.encode()

        assert data == b"\x07\x00\x00\x00\x00\x04\x00\x00\x00\x00"

class TestPrimitive_A_P_ABORT:
    def test_assignment(self):
        """Check assignment works correctly"""
        primitive = A_P_ABORT()
        primitive.provider_reason = 0
        assert primitive.provider_reason == 0
        primitive.provider_reason = 1
        assert primitive.provider_reason == 1
        primitive.provider_reason = 2
        assert primitive.provider_reason == 2
        primitive.provider_reason = 4
        assert primitive.provider_reason == 4
        primitive.provider_reason = 5
        assert primitive.provider_reason == 5
        primitive.provider_reason = 6
        assert primitive.provider_reason == 6

    def test_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = A_P_ABORT()

        with pytest.raises(ValueError):
            primitive.provider_reason = 3
        with pytest.raises(ValueError):
            primitive.provider_reason

    def test_conversion(self):
        """Check conversion to a PDU produces the correct output"""
        primitive = A_P_ABORT()
        primitive.provider_reason = 4

        pdu = A_ABORT_RQ()
        pdu.from_primitive(primitive)
        data = pdu.encode()

        assert data == b"\x07\x00\x00\x00\x00\x04\x00\x00\x02\x04"

class TestPrimitive_P_DATA:
    def test_assignment(self):
        """Check assignment works correctly"""
        primitive = P_DATA()
        primitive.presentation_data_value_list = [[1, b"\x00"]]
        assert primitive.presentation_data_value_list == [[1, b"\x00"]]

    def test_exceptions(self):
        """Check incorrect types/values for properties raise exceptions"""
        primitive = P_DATA()

        with pytest.raises(TypeError):
            primitive.presentation_data_value_list = [1, b"\x00"]

        with pytest.raises(TypeError):
            primitive.presentation_data_value_list = [1, b"\x00"]

        with pytest.raises(TypeError):
            primitive.presentation_data_value_list = [[b"\x00", 1]]

        with pytest.raises(TypeError):
            primitive.presentation_data_value_list = "test"

    def test_conversion(self):
        """Check conversion to a PDU produces the correct output"""
        primitive = P_DATA()
        pdv = (
            b"\x03\x00\x00\x00\x00"
            b"\x04\x00\x00\x00\x42\x00\x00\x00\x00\x00\x02\x00\x12\x00\x00"
            b"\x00\x31\x2e\x32\x2e\x38\x34\x30\x2e\x31\x30\x30\x30\x38\x2e"
            b"\x31\x2e\x31\x00\x00\x00\x00\x01\x02\x00\x00\x00\x30\x80\x00"
            b"\x00\x20\x01\x02\x00\x00\x00\x01\x00\x00\x00\x00\x08\x02\x00"
            b"\x00\x00\x01\x01\x00\x00\x00\x09\x02\x00\x00\x00\x00\x00"
        )
        primitive.presentation_data_value_list = [[1, pdv]]

        pdu = P_DATA_TF()
        pdu.from_primitive(primitive)
        data = pdu.encode()

        assert data == b"\x04\x00\x00\x00\x00\x54\x00\x00\x00\x50\x01" + pdv

    def test_string(self):
        """Check the string output."""
        primitive = P_DATA()
        primitive.presentation_data_value_list = [[0, b"\x00\x00"]]
        assert "Byte: 00000000" in primitive.__str__()
        primitive.presentation_data_value_list = [[0, b"\x01\x00"]]
        assert "Byte: 00000001" in primitive.__str__()
        primitive.presentation_data_value_list = [[0, b"\x02\x00"]]
        assert "Byte: 00000010" in primitive.__str__()
        primitive.presentation_data_value_list = [[0, b"\x03\x00"]]
        assert "Byte: 00000011" in primitive.__str__()

class TestServiceParameter:
    def test_equality(self):
        """Test equality of ServiceParameter subclasses."""
        prim_a = MaximumLengthNotification()
        prim_b = MaximumLengthNotification()
        assert prim_a == prim_b
        assert not prim_a == "test"
        assert not prim_a != prim_b
        prim_b.maximum_length_received = 12
        assert not prim_a == prim_b
        assert prim_a != prim_b


from itertools import permutations, product
def solution(dice):
    answer = 0
    N = len(dice)
    permu_list = []
    candidate_list = []
    for i in range(1,N+1):
        permu_list.append(list(permutations(dice, i)))

    for element in permu_list:

        for element2 in element:
            product_num = list(product(*element2))
            for element3 in product_num:
                candidate_list.append(int("".join(map(str, element3))))

    candidate_list = set(candidate_list)

    for i in range(1,10000):
        if i not in candidate_list:
            answer = i
            return answer

if __name__ == "__main__":
    print(solution([[1, 6, 2, 5, 3, 4], [9, 9, 1, 0, 7, 8]]), 22)
    print(solution([[0, 1, 5, 3, 9, 2], [2, 1, 0, 4, 8, 7], [6, 3, 4, 7, 6, 5]]), 66)


"""
Demonstrating Concurrent Programming through threads

task1 : Brute-forcing a md5 hash, plaintext "word"
task2 : Brute-forcing a solution to all factors < 100 of an integer, 360
"""

import threading
import time

from hashlib import md5
from itertools import permutations

def task1(hash_: str):
	LETTERS = "abcdefghijklmnopqrstuvwxyz"
	length = 0
	while True:
		length += 1
		for string in permutations(LETTERS, length):
			joined = "".join(string)
			if md5(joined.encode("UTF-8")).hexdigest() == hash_:
				return

def task2(n: int):
	factors = set()
	for i in range(1, 100):
		if not n % i:
			factors.add(i)

if __name__ == "__main__":
	# Arguments
	hashTmp = md5("word".encode("UTF-8"))
	task1Args = (hashTmp.hexdigest(), )
	task2Args = (360, )
	task3Args = (3, 31, 56)

	# Normally, all procedurally
	startNormal = time.time_ns()

	task1(*task1Args)
	task2(*task2Args)

	endNormal = time.time_ns()

	# Concurrently, using threading
	thread1 = threading.Thread(target=task1, args=task1Args, daemon=True)
	thread2 = threading.Thread(target=task2, args=task2Args, daemon=True)

	startThreaded = time.time_ns()

	thread1.start()
	thread2.start()

	endThreaded = time.time_ns()

	# Output
	print(f"""
	Time to run normally:       {endNormal - startNormal} (ns)
	Time to run with threads:   {endThreaded - startThreaded} (ns)

	Difference: {abs((endNormal - startNormal) - (endThreaded - startThreaded))} (ns)
""")


import tensorflow as tf

def add_batch_norm_into_update_ops():
  ops = tf.compat.v1.get_default_graph().get_operations()
  update_ops = [
      op for op in ops
      if 'AssignMovingAvg' in op.name and op.type == "AssignSubVariableOp"
  ]

  for update_op in update_ops:
    tf.compat.v1.add_to_collection(tf.compat.v1.GraphKeys.UPDATE_OPS, update_op)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@author: zparteka
"""
from peak_stats.reader.peaks import Image, Group, Peak
from scipy.spatial import ConvexHull
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

# Histograms global options
Figsize = (30, 20)
LabelFontsize = 35
TitleFontsize = 40
TickFontsize = 30
Facecolor = "g"
Alpha = 0.75
Dpi = 300

# 3D plots global options
Color = "red"

class ImgStats:

    def __init__(self, image: Image):
        self.group_count = image.group_count()
        self.peak_count = image.peak_count()
        self.avg_group_sigma_xyz = self.add_groups_sigma(image)
        self.avg_peaks = self.avg_peaks_per_group(image)
        self.photons = self.photons_in_image(image=image)
        self.photons_per_group = self.average_photons_per_group(image)

    @staticmethod
    def add_groups_sigma(image: Image):
        """Calculate sigma for x y and z"""
        avg_groups_sigma_z = []
        avg_groups_sigma_x = []
        avg_groups_sigma_y = []
        for group in image.groups:
            new_group_stat = GroupStats(group)
            avg_groups_sigma_x.append(new_group_stat.group_avg_sigma_x_pos_full)
            avg_groups_sigma_y.append(new_group_stat.group_avg_sigma_y_pos_full)
            avg_groups_sigma_z.append(new_group_stat.group_avg_sigma_z)
        return [avg_groups_sigma_x, avg_groups_sigma_y, avg_groups_sigma_z]

    @staticmethod
    def add_peak_sigma(image: Image):
        """Return peaks sigma in format[[simgax_peka1,...],[simgay_peak1...],[sigmaz_peak1]]"""
        sigma_x = []
        sigma_y = []
        sigma_z = []
        for group in image.groups:
            group_stats = GroupStats(group=group)
            sigma_x += group_stats.list_peak_sigma_x(group=group)
            sigma_y += group_stats.list_peak_sigma_y(group=group)
            sigma_z += group_stats.list_peak_sigma_z(group=group)
        return [sigma_x, sigma_y, sigma_z]

    @staticmethod
    def peaks_per_group(image: Image):
        peaks_in_spots = []
        for spot in image.groups:
            peaks_in_spots.append(len(spot))
        return peaks_in_spots

    def avg_peaks_per_group(self, image: Image):
        peaks = self.peaks_per_group(image)
        avg = sum(peaks) / len(peaks)
        return avg

    def average_sigma_x(self):
        average_sigma_x = sum(self.avg_group_sigma_xyz[0]) / len(self.avg_group_sigma_xyz[0])
        return average_sigma_x

    def average_sigma_y(self):
        average_sigma_y = sum(self.avg_group_sigma_xyz[1]) / len(self.avg_group_sigma_xyz[1])
        return average_sigma_y

    def average_sigma_z(self):
        average_sigma_z = sum(self.avg_group_sigma_xyz[2]) / len(self.avg_group_sigma_xyz[2])
        return average_sigma_z

    def average_sigma(self):
        average = (sum(self.avg_group_sigma_xyz[2]) / len(self.avg_group_sigma_xyz[2]) + sum(
            self.avg_group_sigma_xyz[1]) / len(self.avg_group_sigma_xyz[1]) + sum(self.avg_group_sigma_xyz[0]) / len(
            self.avg_group_sigma_xyz[0])) / 3
        return average

    def plot_sigma_x_hist(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        plt.hist(self.avg_group_sigma_xyz[0], 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Sigma X', fontsize=LabelFontsize)
        plt.title('Sigma X', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_sigma_X"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_sigma_y_hist(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        plt.hist(self.avg_group_sigma_xyz[1], 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Sigma Y', fontsize=LabelFontsize)
        plt.title('Sigma Y', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_sigma_Y"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_sigma_z_hist(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        plt.hist(self.avg_group_sigma_xyz[2], 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Sigma Z', fontsize=LabelFontsize)
        plt.title('Sigma Z', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_sigma_Z"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_peak_sigma_x(self, x_sigma, save=False, outdir=None, draw_sigma=15):
        plt.figure(figsize=Figsize)
        plt.hist(x_sigma, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Peaks', fontsize=LabelFontsize)
        plt.xlabel('Sigma X', fontsize=LabelFontsize)
        plt.title('Sigma X', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if draw_sigma:
            plt.axvline(x=draw_sigma, color="red")
        if save:
            outfile = outdir + "_sigma_X_peaks_g"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_peak_sigma_y_(self, y_sigma, save=False, outdir=None, draw_sigma=15):
        plt.figure(figsize=Figsize)
        plt.hist(y_sigma, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Peaks', fontsize=LabelFontsize)
        plt.xlabel('Sigma Y', fontsize=LabelFontsize)
        plt.title('Sigma Y', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if draw_sigma:
            plt.axvline(x=draw_sigma, color="red")
        if save:
            outfile = outdir + "_sigma_Y_peaks_g"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_peak_sigma_z(self, sigma_z, save=False, outdir=None, draw_sigma=15):
        plt.figure(figsize=Figsize)
        plt.hist(sigma_z, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Peaks', fontsize=LabelFontsize)
        plt.xlabel('Sigma Z', fontsize=LabelFontsize)
        plt.title('Sigma Z', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if draw_sigma:
            plt.axvline(x=draw_sigma, color="red")
        if save:
            outfile = outdir + "_sigma_Z_peaks_g"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_average_peak_sigma(self, sigma_z, sigma_y, sigma_x, save=False, outdir=None, draw_sigma=15):
        avg = [0] * len(sigma_x)
        for i in range(len(sigma_x)):
            avg[i] += sigma_x[i]
            avg[i] += sigma_y[i]
            avg[i] += sigma_z[i]
            avg[i] /= 3
        plt.figure(figsize=Figsize)
        plt.hist(avg, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Peaks', fontsize=70)
        plt.xlabel('Average sigma', fontsize=70)
        plt.title('Average Peak Sigma', fontsize=100)
        plt.tick_params(axis='both', which='major', labelsize=50)
        plt.grid(True)
        if draw_sigma:
            plt.axvline(x=draw_sigma, color="red")
        if save:
            outfile = outdir + "_average_peaks_g_v2"
            plt.savefig(outfile, dpi=100, format="png")
        else:
            plt.show()
        plt.close()

    def plot_average_sigma(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        data = np.array(self.avg_group_sigma_xyz)
        avg = np.average(data, axis=0)
        plt.hist(avg, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Average Sigma', fontsize=LabelFontsize)
        plt.title('Groups Average Sigma', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_sigma_avg"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_peaks_per_group_histogram(self, image: Image, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        data = self.peaks_per_group(image=image)
        plt.hist(data, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Number of Peaks', fontsize=LabelFontsize)
        plt.title('Peaks per Group histogram', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.minorticks_on()
        plt.grid(True)
        if save:
            outfile = outdir + "_peaks_per_group"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()

    def plot_photons_per_group(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        plt.hist(self.photons_per_group, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Groups', fontsize=LabelFontsize)
        plt.xlabel('Average Number of Photons', fontsize=LabelFontsize)
        plt.title('Average number of photons per peak in group', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_photons_per_group"
            plt.savefig(outfile, dpi=Dpi, formay="png")
        else:
            plt.show()
        plt.close()

    def plot_photons_per_peaks(self, save=False, outdir=None):
        plt.figure(figsize=Figsize)
        plt.hist(self.photons, 50, facecolor=Facecolor, alpha=Alpha)
        plt.ylabel('Number of Peaks', fontsize=LabelFontsize)
        plt.xlabel('Number of Photons', fontsize=LabelFontsize)
        plt.title('Number of photons per peak', fontsize=TitleFontsize)
        plt.tick_params(axis='both', which='major', labelsize=TickFontsize)
        plt.grid(True)
        if save:
            outfile = outdir + "_photons_per_image"
            plt.savefig(outfile, dpi=Dpi, format="png")
        else:
            plt.show()
        plt.close()
        pass

    def save_statistics(self, output, image: Image, sigma_threshold):
        with open(output, 'w') as out:
            peaks = PeakPositions(image=image, sigma_threshold=sigma_threshold)
            groups = GroupPeakStats(image=image)
            peaks.plot_convex_hull(show=False)
            out.write("Group Count:  {}".format(self.group_count) + "\n")
            out.write("Peak Count:   {}".format(self.peak_count) + "\n")
            out.write("Average peaks per group   {}".format(self.peak_count / self.group_count) + "\n")
            out.write("Average sigma X:  {} nm".format(self.average_sigma_x()) + "\n")
            out.write("Average sigma Y:  {} nm".format(self.average_sigma_y()) + "\n")
            out.write("Average sigma Z:  {} nm".format(self.average_sigma_z()) + "\n")
            out.write("Convex hull volume:   {}".format(peaks.hull_volume) + "\n")
            out.write("Convex hull area:    {}".format(peaks.hull_area) + "\n")
            out.write("Average number of photons per peak:  {}".format(sum(self.photons) / len(self.photons)) + "\n")
            out.write("Average number of photons per group peak: {}".format(groups.average_photons()) + "\n")
            out.write("Average sigma X per group peaks: {} nm".format(groups.averege_sigma_x()) + "\n")
            out.write("Average sigma Y per group peaks: {} nm".format(groups.averege_sigma_y()) + "\n")
            out.write("Average sigma Z per group peaks: {} nm".format(groups.averege_sigma_z()))

    def print_statistics(self, image: Image, sigma_threshold=None):
        peaks = PeakPositions(image=image, sigma_threshold=sigma_threshold)
        groups = GroupPeakStats(image=image)
        peaks.plot_convex_hull(show=False)
        print("Group Count:  {}".format(self.group_count))
        print("Peak Count:   {}".format(self.peak_count))
        print("Average peaks per group   {}".format(self.peak_count / self.group_count))
        print("Average sigma X:  {} nm".format(self.average_sigma_x()))
        print("Average sigma Y:  {} nm".format(self.average_sigma_y()))
        print("Average sigma Z:  {} nm".format(self.average_sigma_z()))
        print("Convex hull volume:   {}".format(peaks.hull_volume))
        print("Convex hull area:    {}".format(peaks.hull_area))
        print("Average number of photons per peak:  {}".format(sum(self.photons) / len(self.photons)))
        print("Average number of photons per group peak: {}".format(groups.average_photons()))
        print("Average sigma X per group peaks: {} nm".format(groups.averege_sigma_x()))
        print("Average sigma Y per group peaks: {} nm".format(groups.averege_sigma_y()))
        print("Average sigma Z per group peaks: {} nm".format(groups.averege_sigma_z()))

    @staticmethod
    def photons_in_image(image: Image):
        """Return a list of number of phontons in each peak in the image"""
        photons_per_peak = []
        for spot in image.groups:
            photons_per_peak += GroupStats.photons(group=spot)
        return photons_per_peak

    @staticmethod
    def average_photons_per_group(image: Image):
        """Return a list of average number of photons per spot"""
        photons_per_spot = []
        for spot in image.groups:
            avg_photons = sum(GroupStats.photons(group=spot)) / len(spot)
            photons_per_spot.append(avg_photons)
        return photons_per_spot

class GroupStats:

    def __init__(self, group: Group):
        self.peak_count = len(group.peaks)
        self.group_avg_sigma_x_pos_full = self.calculate_avg_sigma_x(group)
        self.group_avg_sigma_y_pos_full = self.calculate_avg_sigma_y(group)
        self.group_avg_sigma_z = self.calculate_avg_sigma_z(group)

    def list_peak_sigma_x(self, group: Group, pixel_size=133):
        sigma_x = []
        for i in group.peaks:
            sigma_x.append(float(i.data["Sigma X Pos Full"]) * pixel_size)
        return sigma_x

    def list_peak_sigma_y(self, group: Group, pixel_size=133):
        sigma_y = []
        for i in group.peaks:
            sigma_y.append(float(i.data["Sigma Y Pos Full"]) * pixel_size)
        return sigma_y

    def list_peak_sigma_z(self, group: Group):
        sigma_z = []
        for i in group.peaks:
            sigma_z.append(float(i.data["Sigma Z"]))
        return sigma_z

    @staticmethod
    def calculate_avg_sigma_x(group: Group, pixel_size=133):
        sum_sigma = 0
        for i in group.peaks:
            sum_sigma += (float(i.data["Sigma X Pos Full"]) * pixel_size)
        avg_sigma = sum_sigma / len(group.peaks)
        return avg_sigma

    @staticmethod
    def calculate_avg_sigma_y(group: Group, pixel_size=133):
        sum_sigma = 0
        for i in group.peaks:
            sum_sigma += (float(i.data["Sigma Y Pos Full"]) * pixel_size)
        avg_sigma = sum_sigma / len(group.peaks)
        return avg_sigma

    @staticmethod
    def calculate_avg_sigma_z(group: Group):
        sum_sigma = 0
        for i in group.peaks:
            sum_sigma += float(i.data["Sigma Z"])
        avg_sigma = sum_sigma / len(group.peaks)
        return avg_sigma

    @staticmethod
    def photons(group: Group):
        """"return list of number of photons per peak in given spot"""
        photons = []
        for peak in group.peaks:
            photon = peak.data["6 N Photons"]
            photons.append(photon)
        return photons

class PeakPositions:

    def __init__(self, image: Image, sigma_threshold=1000, minimize=False):
        self.peaks_positions = self.image_peak_positions(image, sigma_threshold)
        self.parse_peak_positions_to_nm()
        if minimize:
            self.minimize_xy()
        self.hull_volume = None
        self.hull_area = None

    @staticmethod
    def single_peak_position(peak: Peak, sigma_threshold, pixel_size=133):
        peak_x = peak.data["X Position"]
        peak_y = peak.data["Y Position"]
        peak_z = peak.data["Unwrapped Z"]
        peak_position = [peak_x, peak_y, peak_z]
        if sigma_threshold:
            if peak.data["Sigma X Pos Full"] * pixel_size < sigma_threshold and peak.data[
                "Sigma Y Pos Full"] * pixel_size < sigma_threshold and peak.data["Sigma Z"] < sigma_threshold:
                return peak_position
            else:
                return None
        return peak_position

    def group_peak_positions(self, group: Group, sigma_threshold):
        spot_peaks_positions = []
        for peak in group.peaks:
            peak_positions = self.single_peak_position(peak, sigma_threshold)
            if peak_positions:
                spot_peaks_positions.append(peak_positions)
        return spot_peaks_positions

    def image_peak_positions(self, image: Image, sigma_threshold):
        image_peaks_positions = []
        for spot in image.groups:
            spot_peaks_positions = self.group_peak_positions(spot, sigma_threshold)
            image_peaks_positions += spot_peaks_positions
        return np.array(image_peaks_positions)

    def parse_peak_positions_to_nm(self, pixel_size=133):
        """X/Y positions in ASCII file are in pixels and Z position is in nm. """
        self.peaks_positions[:, 0] *= pixel_size
        self.peaks_positions[:, 1] *= pixel_size

    def minimize_xy(self):
        minimal = np.amin(self.peaks_positions, axis=0)
        self.peaks_positions[:, 0] = self.peaks_positions[:, 0] - minimal[0]
        self.peaks_positions[:, 1] = self.peaks_positions[:, 1] - minimal[1]
        return minimal

    def plot_peak_positions(self, title=None, outpath=None):
        """Draw 3D plot of peak positions."""
        fig = plt.figure(figsize=(10, 7))
        ax = Axes3D(fig)
        ax.scatter(self.peaks_positions[:, 0], self.peaks_positions[:, 1], self.peaks_positions[:, 2], s=20,
                   color=Color)
        ax.tick_params(axis='both', which='major', labelsize=12)
        ax.set_xlabel("X [nm]", fontsize=12, labelpad=5)
        ax.set_ylabel("Y [nm]", fontsize=12, labelpad=5)
        ax.set_zlabel("Z [nm]", fontsize=12)
        if title:
            plt.title(title, fontsize=15)
        if outpath:
            plt.savefig(outpath, dpi=300, format="png")
        else:
            plt.show()

    def plot_convex_hull(self, title=None, outpath=None, show=True):
        """Plot convex hull on peak positions """
        hull = ConvexHull(self.peaks_positions)
        fig = plt.figure(figsize=(10, 7))
        ax = Axes3D(fig)
        ax.scatter(self.peaks_positions[:, 0], self.peaks_positions[:, 1], self.peaks_positions[:, 2], s=20,
                   color=Color)
        ax.tick_params(axis='both', which='major', labelsize=12)
        for s in hull.simplices:
            s = np.append(s, s[0])
            ax.plot(self.peaks_positions[s, 0], self.peaks_positions[s, 1], self.peaks_positions[s, 2],
                    color="grey", linewidth=1)
        if title:
            plt.title(title, fontsize=15)
        ax.tick_params(axis='both', which='major', labelsize=12)
        ax.set_xlabel("X [nm]", fontsize=12, labelpad=5)
        ax.set_ylabel("Y [nm]", fontsize=12, labelpad=5)
        ax.set_zlabel("Z [nm]", fontsize=12)
        if outpath:
            plt.savefig(outpath, dpi=300)
        elif outpath is None and show is True:
            plt.show()
        plt.close()
        print(hull.volume)
        print(hull.area)
        self.hull_volume = hull.volume
        self.hull_area = hull.area

class GroupPeakStats:

    def __init__(self, image: Image):
        self.group_peaks = self.add_group_peaks(image=image)
        self.group_number = len(self.group_peaks)
        self.photons_per_group = self.photons_per_group_peak()
        self.positions = self.group_peaks_positions()
        self.minimize_group_peaks()

    @staticmethod
    def add_group_peaks(image: Image):
        group_peaks = []
        for spot in image.groups:
            group_peaks.append(spot.group_peak)
        return group_peaks

    # -----------------------------PHOTONS--------------------------------------
    def photons_per_group_peak(self):
        """Return a list of number of photons for each group peak."""
        photons = []
        for group_peak in self.group_peaks:
            photons.append(group_peak.photon_number)
        return photons

    def average_photons(self):
        """Return average number of photons per group"""
        return sum(self.photons_per_group) / len(self.photons_per_group)

    # ------------------------------XYZ POSITIONS---------------------------------
    def group_peaks_positions(self):
        """Return a list of group peaks xyz positions."""
        peak_positions = []
        for group_peak in self.group_peaks:
            position = [group_peak.x_position, group_peak.y_position, group_peak.z_position]
            peak_positions.append(position)
        return np.array(peak_positions)

    # -------------------------------SIGMA---------------------------------------

    def groups_sigma_x(self):
        """Return a list with x sigmas for all group peaks"""
        sigmas = []
        for group_peak in self.group_peaks:
            sigmas.append(group_peak.group_sigma_x)
        return sigmas

    def groups_sigma_y(self):
        """Return a list with y sigmas for all group peaks"""
        sigmas = []
        for group_peak in self.group_peaks:
            sigmas.append(group_peak.group_sigma_y)
        return sigmas

    def groups_sigma_z(self):
        """Return a list with z sigmas for all group peaks"""
        sigmas = []
        for group_peak in self.group_peaks:
            sigmas.append(group_peak.group_sigma_z)
        return sigmas

    def averege_sigma_x(self):
        """Return average group X sigma for the image"""
        sigmas = self.groups_sigma_x()
        return sum(sigmas) / len(sigmas)

    def averege_sigma_y(self):
        """Return average group Y sigma for the image"""
        sigmas = self.groups_sigma_y()
        return sum(sigmas) / len(sigmas)

    def averege_sigma_z(self):
        """Return average group Z sigma for the image"""
        sigmas = self.groups_sigma_z()
        return sum(sigmas) / len(sigmas)

    def minimize_group_peaks(self):
        """Move the structure to 0, 0 """
        minimal = np.amin(self.positions, axis=0)
        self.positions[:, 0] = self.positions[:, 0] - minimal[0]
        self.positions[:, 1] = self.positions[:, 1] - minimal[1]
        return minimal

    # -----------------PLOTTING----------------------------------

    def plot_3d_group_peaks(self, title="Group Peaks", outpath=None):
        """Draw 3D plot of group peaks in XYZ space."""
        fig = plt.figure(figsize=(10, 7))
        ax = Axes3D(fig)
        ax.scatter(self.positions[:, 0], self.positions[:, 1], self.positions[:, 2], s=20,
                   color=Color)
        ax.tick_params(axis='both', which='major', labelsize=15)
        ax.set_xlabel("X [nm]", fontsize=20, labelpad=10)
        ax.set_ylabel("Y [nm]", fontsize=20, labelpad=10)
        ax.set_zlabel("Z [nm]", fontsize=20, labelpad=10)
        if title:
            plt.title(title, fontsize=40)
        if outpath:
            plt.savefig(outpath, dpi=100, format="png")
        else:
            plt.show()

    def plot_group_peaks_convex_hull(self, title="Group Peaks Convex Hull", outpath=None, show=True):
        """Draw 3D plot of convex hull od group peaks and return the volume"""
        hull = ConvexHull(self.positions)
        fig = plt.figure(figsize=(10, 7))
        ax = Axes3D(fig)
        ax.scatter(self.positions[:, 0], self.positions[:, 1], self.positions[:, 2], s=20,
                   color=Color)
        ax.tick_params(axis='both', which='major', labelsize=12)
        for s in hull.simplices:
            s = np.append(s, s[0])
            ax.plot(self.positions[s, 0], self.positions[s, 1], self.positions[s, 2],
                    color="grey", linewidth=1)
        if title:
            plt.title(title, fontsize=40)
        ax.tick_params(axis='both', which='major', labelsize=15)
        ax.set_xlabel("X [nm]", fontsize=20, labelpad=10)
        ax.set_ylabel("Y [nm]", fontsize=20, labelpad=10)
        ax.set_zlabel("Z [nm]", fontsize=20, labelpad=10)
        if outpath:
            plt.savefig(outpath, dpi=100)
        elif outpath is None and show is True:
            plt.show()
        plt.close()
        print(hull.volume)
        print(hull.area)


import os
from gmpy2 import invert, powmod as pow, mpz
from hashlib import sha384
import pyaes
from binascii import hexlify

#  y^2 = x^3 – 3x +b (mod p)
#  p = 0xfffffffffffffffffffffffffffffffeffffffffffffffff
#  n = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831
#  SEED = 3045ae6f c8422f64 ed579528 d38120ea e12196d5
#  c = 3099d2bb bfcb2538 542dcd5f b078b6ef 5f3d6fe2 c745de65
#  b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1
#  G = (0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012,
#       0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811)

p = mpz(
    0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff
)
n = mpz(
    0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973
)
b = mpz(
    0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef
)
G = (mpz(
    0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7
),
     mpz(0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f
         ))

infty = (None, None)

def is_on_curve(P):
    if P == infty:
        return True
    (x, y) = P
    return pow(y, 2, p) == (pow(x, 3, p) - 3 * x + b) % p

def add(P, Q):
    if Q == infty:
        return P
    if P == infty:
        return Q
    x_1, y_1 = P
    x_2, y_2 = Q
    if x_1 != x_2:
        m = (y_2 - y_1) * invert(x_2 - x_1, p) % p
        x_3 = (pow(m, 2, p) - x_1 - x_2) % p
        y_3 = (m * (x_1 - x_3) - y_1) % p
        return (x_3, y_3)
    elif y_1 != y_2:
        return infty
    elif y_1 != 0:
        m = (3 * pow(x_1, 2, p) - 3) * invert(2 * y_1, p) % p
        x_3 = (pow(m, 2, p) - 2 * x_1) % p
        y_3 = (m * (x_1 - x_3) - y_1) % p
        return (x_3, y_3)
    else:
        return infty

def mul(P, k):
    if P == infty or k == 0:
        return infty
    Q = infty
    while k > 1:
        if k & 1:
            Q = add(P, Q)
            P = add(P, P)
        else:
            P = add(P, P)
        k = k >> 1
    return add(P, Q)

class RNG():
    def __init__(self, seed=None):
        if seed is None:
            self.key = os.urandom(32)
        else:
            assert len(seed) in [16, 24, 32]
            self.key = seed
        self.aes = pyaes.AESModeOfOperationECB(self.key)
        self.state = [0, 1, 2, 3]
        self.state_len = len(self.state)

    def next(self, prefix=''):
        plain_blocks = [word.to_bytes(16, 'big') for word in self.state]
        #  print(': '.join([prefix, repr(self.state)]))
        output = b''.join(map(self.aes.encrypt, plain_blocks))
        #  print(hexlify(output).decode()[:16])
        self.state = [x + self.state_len for x in self.state]
        return output

def keygen():
    a = mpz(int.from_bytes(os.urandom(n.bit_length() // 8), 'big')) % n
    A = mul(G, a)
    return a, A

def sign(sk, m, rng):
    a = sk
    h = mpz(int.from_bytes(sha384(m).digest(), 'big'))
    r = 0
    while r == 0:
        k = mpz(int.from_bytes(rng.next('K'), 'big')) % n
        x_r, y_r = mul(G, k)
        r = x_r % n
    s = invert(k, n) * (h + a * r) % n
    return (r, s)

def serialize_sig(sig):
    r, s = sig
    byte_len = p.bit_length() // 8
    bites = b''.join(
        [int(r).to_bytes(byte_len, 'big'),
         int(s).to_bytes(byte_len, 'big')])
    assert len(bites) == SERIALIZED_SIZE
    return bites

def unserialize_sig(bites):
    byte_len = p.bit_length() // 8
    r = mpz(int.from_bytes(bites[:byte_len], 'big'))
    s = mpz(int.from_bytes(bites[byte_len:], 'big'))
    return (r, s)

SERIALIZED_SIZE = 96

def verify(pk, m, sig):
    Q = pk
    if Q == infty or not is_on_curve(Q) or mul(Q, n) != infty:
        return False
    r, s = sig
    if not 0 <= r < n or not 0 <= s < n:
        return False
    h = mpz(int.from_bytes(sha384(m).digest(), 'big'))
    print('h:', h)
    print('r:', r)
    print('s:', s)
    w = invert(s, n)
    u_1 = w * h % n
    u_2 = w * r % n
    V = add(mul(G, u_1), mul(Q, u_2))
    if V == infty:
        return False
    return V[0] == r

def test_correctness():
    sk, pk = keygen()
    rng = RNG()
    m = b"foobar"
    sig = sign(sk, m, rng)
    assert verify(pk, m, sig)
    assert sig == unserialize_sig(serialize_sig(sig))

if __name__ == '__main__':
    test_correctness()


# -*- coding: utf-8 -*-
from PIL import Image
import numpy as np

import number.CNN_num_recognition as cnn_num_rec
import number.KNN_num_recognition as knn_num_rec
import number.SVM_num_recognition as svm_num_rec
import number.FCNN_num_recognition as fcnn_num_rec
import number.RF_num_recognition as rf_num_rec

import letter.CNN_letter_recognition as cnn_let_rec
import letter.FCNN_letter_recognition as fcnn_let_rec
import letter.KNN_letter_recognition as knn_let_rec
import letter.SVM_letter_recognition as svm_let_rec
import letter.RF_letter_recognition as rf_let_rec

import tensorflow as tf
import cv2
from keras import models
import time

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse
import uvicorn

app = FastAPI(title="手写字符识别")

@app.get("/")
async def root():
    return {"message": "Hello world, FastAPI"}

@app.get("/typeChange", summary="更改识别类型", tags=["识别类型"],
         description="model_type: 0:CNN 1:FCNN 2:KNN 3:SVM 4:RF \r\n"
                     "multi_char: 0:单字符 1:多字符 \r\n"
                     "num_letter: 0:数字 1:字母")
async def typeChange(model_type: int, multi_char: int, num_letter: int):
    try:
        global modelType
        global multiChar
        global numLetter
        modelType = model_type
        multiChar = multi_char
        numLetter = num_letter

        if modelType == 0:
            modelName = 'CNN'
        elif modelType == 1:
            modelName = 'FCNN'
        elif modelType == 2:
            modelName = 'KNN'
        elif modelType == 3:
            modelName = 'SVM'
        elif modelType == 4:
            modelName = 'RF'

        data = {
            "code": 200,
            "msg": "修改类型成功",
            "model_type": modelType,
            "model_name": modelName,
            "multi_char": multiChar,
            "num_letter": numLetter
        }
        return JSONResponse(data)
    except Exception as e:
        return {"code": 201, "msg": "修改类型失败"}

@app.post("/upload", summary="上传识别", tags=["上传"])
async def upload(file: UploadFile = File(...)):
    # 计算开始时间
    time1 = time.time()
    file_bytes = file.file.read()
    tempfile = 'imgs/temp.png'
    try:
        with open(tempfile, "wb") as f:
            f.write(file_bytes)
        result = recognition(tempfile, numLetter, modelType, multiChar)
        if result == '':
            return {"code": 201, "msg": "上传失败"}
        # 计算结束时间
        time2 = time.time()
        data = {
            "code": 200,
            "msg": "上传成功",
            "result": result,
            "time": str(time2 - time1)
        }
        return JSONResponse(data)
    except Exception as e:
        return {"code": 201, "msg": "上传失败"}

def recognition(path, num_letter, model_type, multi_char):
    # 识别数字
    if num_letter == 0:
        # cnn
        if model_type == 0:
            model = 'model/cnn_num_model.h5'
            # 多字符
            if multi_char == 1:
                print("CNN算法识别多字符数字...")
                result = cnn_num_rec.multi_predict(model, path)
                print("识别结果:", result)
            # 单字符
            else:
                print("CNN算法识别单字符数字...")
                result = cnn_num_rec.single_predict(model, path)
                print("识别结果:", result)

        # fcnn
        elif model_type == 1:
            model = 'model/fcnn_num_model.h5'
            if multi_char == 1:
                print("FCNN算法识别多字符数字...")
                result = fcnn_num_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("FCNN算法识别单字符数字...")
                result = fcnn_num_rec.single_predict(model, path)
                print("识别结果:", result)

        # knn
        elif model_type == 2:
            model = 'model/knn_num_model.pkl'
            if multi_char == 1:
                print("KNN算法识别多字符数字...")
                result = knn_num_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("KNN算法识别单字符数字...")
                result = knn_num_rec.single_predict(model, path)
                print("识别结果:", result)

        # svm
        elif model_type == 3:
            model = 'model/svm_num_model.pkl'
            if multi_char == 1:
                print("SVM算法识别多字符数字...")
                result = svm_num_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("SVM算法识别单字符数字...")
                result = svm_num_rec.single_predict(model, path)
                print("识别结果:", result)

        # rf
        elif model_type == 4:
            model = 'model/rf_num_model.pkl'
            if multi_char == 1:
                print("RandomForest算法识别多字符数字...")
                result = rf_num_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("RandomForest算法识别单字符数字...")
                result = rf_num_rec.single_predict(model, path)
                print("识别结果:", result)

    # 识别字母
    elif num_letter == 1:
        # cnn
        if model_type == 0:
            model = 'model/cnn_letter_model.h5'
            # 多字符
            if multi_char == 1:
                print("CNN算法识别多字符字母...")
                result = cnn_let_rec.multi_predict(model, path)
                print("识别结果:", result)
            # 单字符
            else:
                print("CNN算法识别单字符字母...")
                result = cnn_let_rec.single_predict(model, path)
                print("识别结果:", result)

        # fcnn
        elif model_type == 1:
            model = 'model/fcnn_letter_model.h5'
            if multi_char == 1:
                print("FCNN算法识别多字符字母...")
                result = fcnn_let_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("FCNN算法识别单字符字母...")
                result = fcnn_let_rec.single_predict(model, path)
                print("识别结果:", result)

        # knn
        if model_type == 2:
            model = 'model/knn_letter_model.pkl'
            if multi_char == 1:
                print("KNN算法识别多字符字母...")
                result = knn_let_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("KNN算法识别单字符字母...")
                result = knn_let_rec.single_predict(model, path)
                print("识别结果:", result)

        # svm
        elif model_type == 3:
            model = 'model/svm_letter_model.pkl'
            if multi_char == 1:
                print("SVM算法识别多字符字母...")
                result = svm_let_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("SVM算法识别单字符字母...")
                result = svm_let_rec.single_predict(model, path)
                print("识别结果:", result)

        # rf
        elif model_type == 4:
            model = 'model/rf_letter_model.pkl'
            if multi_char == 1:
                print("RandomForest算法识别多字符字母...")
                result = rf_let_rec.multi_predict(model, path)
                print("识别结果:", result)
            else:
                print("RandomForest算法识别单字符字母...")
                result = rf_let_rec.single_predict(model, path)
                print("识别结果:", result)

    return str(result)

# 连接手机
# if __name__ == '__main__':
#     uvicorn.run(app='main:app', host="192.168.1.113", port=5000, reload=True, debug=True)

if __name__ == '__main__':
    uvicorn.run(app='main:app', host="127.0.0.1", port=5000, reload=True, debug=True)


import networkx as nx

class PruneBranches:
    def __init__(self):
        self.ast: nx.DiGraph = nx.DiGraph()

    def run(self, ast: nx.DiGraph):
        assert isinstance(ast, nx.DiGraph)
        self.ast = ast

        entries = set(filter(self.__is_entry, self.ast.nodes))
        main_entry = max(entries, key=self.__tree_size)
        entries.remove(main_entry)

        self.__clean(entries)
        return self.ast

    def __clean(self, worklist: set):
        while worklist:
            curr = worklist.pop()
            if not self.__is_entry(curr):
                continue

            succs = set(self.ast.successors(curr))
            out_edges_of_curr = [(curr, succ) for succ in succs]

            self.ast.remove_edges_from(out_edges_of_curr)
            self.ast.remove_node(curr)

            worklist |= succs

    def __is_entry(self, n: str):
        return self.ast.in_degree[n] == 0

    def __tree_size(self, n: str):
        return 1 + sum(map(self.__tree_size, self.ast.successors(n)))


# -*- coding: utf-8 -*-
"""
Created on Tue Nov 10 15:55:12 2020

@author: Ihsaan Malek & Olivier Racette
"""
from node import Node
import numpy as np

#Makes Node objects from a series of (y, x) tuples that represent coordinates of the "zero" tile and adds them to the passed movesList
def addMoves(moves_list, zeros, parent, cost):
    zero = parent.zero_coords

    for z in zeros:
        b = np.copy(parent.board)
        b[z[0], z[1]], b[zero[0], zero[1]] = b[zero[0], zero[1]], b[z[0], z[1]]             #swapping: a,b = b,a
        moves_list.append(Node(parent, parent.depth+1, cost, b, z, b[zero[0], zero[1]]))    #for the token that was moved: it was just swapped with 0!

#Method that takes in a node (any node) and generates its immediate children
# Legal moves:
#   direct horizontal / vertical translation (cost 1)
#   wrapping horizontal / vertical translation (cost 2) NOTE: vertical wrapping is only OK when there are more than 2 rows
#   diagonal (direct or wrap) (cost 3)
def buildChildren(node):
    #viewing the board as y by x matrix
    y_max = len(node.board)-1
    x_max = len(node.board[0])-1
    zero = node.zero_coords

    #NOTE that x and y are inverted for all tuples (array[y][x])
    upper_left = (0, 0)
    upper_right = (0, x_max)
    lower_left = (y_max, 0)
    lower_right = (y_max, x_max)

    #building a list of moves that are possible
    all_moves = []

    cost_1_moves = []
    cost_2_moves = []
    cost_3_moves = []

    #cases where the 0 tile is at the diagonals
    if zero == upper_left:
        #can go directly diagonal lower right or wrap diagonal with last value in board
        cost_3_moves.append((zero[0] + 1, zero[1] + 1))
        cost_3_moves.append((zero[0] + 1, x_max))

        #can also wrap around with the tile at the other end, either horizontally or vertically (if there are more than 2 rows)
        cost_2_moves.append((zero[0], x_max))

        if y_max > 1:
            cost_2_moves.append((y_max, zero[1]))

    elif zero == upper_right:
        #can go to immediate diagonal lower right or wrap diagonal lower left
        cost_3_moves.append((zero[0] + 1, zero[1] - 1))
        cost_3_moves.append((zero[0] + 1, 0))

        #wrap around
        cost_2_moves.append((zero[0], 0))

        if y_max > 1:
            cost_2_moves.append((y_max, zero[1]))

    elif zero == lower_left:
        #print("0 at: lower left")
        #can go to immediate diagonal upper right or wrap diagonal upper left
        cost_3_moves.append((zero[0] - 1, zero[1] + 1))
        cost_3_moves.append((zero[0] - 1, x_max))

        #wrap around
        cost_2_moves.append((zero[0], x_max))

        if y_max > 1:
            cost_2_moves.append((0, zero[1]))

    elif zero == lower_right:
        #print("0 at: lower right")
        #can go to immediate diagonal upper left or wrap diagonal upper right
        cost_3_moves.append((zero[0] - 1, zero[1] - 1))
        cost_3_moves.append((zero[0] - 1, 0))

        #wrap around
        cost_2_moves.append((zero[0], 0))

        if y_max > 1:
            cost_2_moves.append((0, zero[1]))

    #cases with simple translation
    if zero[1] < x_max:
        cost_1_moves.append((zero[0], zero[1] + 1))

    if zero[1] > 0:
        cost_1_moves.append((zero[0], zero[1] - 1))

    if zero[0] > 0:
        cost_1_moves.append((zero[0] - 1, zero[1]))

    if zero[0] < y_max:
        cost_1_moves.append((zero[0] + 1, zero[1]))

    #I had to patch this at the last minute because I didn't code the rules properly!! Code could be better but eh it works.
    addMoves(all_moves, cost_1_moves, node, 1)
    addMoves(all_moves, cost_2_moves, node, 2)
    addMoves(all_moves, cost_3_moves, node, 3)

    return all_moves


#Author: Ami
#Date: March 4

#opens/reads file
f = open("input.txt", "r")
lines = f.readlines()
f.close()

#creates/wipes output file
f = open("output.txt", "w")

#scans every line(word) and tests if its over 10 characters long
for word in lines:
	if len(word) < 11:
		f.write(word)

	if len(word) > 10:
		number = len(word)
		left = number - 10
		word = word[: - left]  #cuts off at 10 letters
		f.write(word + "\n")


from hamcrest import assert_that, contains_inanyorder, empty, has_entries, not_

from . import confd
from ..helpers import associations as a, errors as e, fixtures, scenarios as s
from ..helpers.config import MAIN_TENANT, SUB_TENANT

FAKE_ID = 999999999

@fixtures.agent()
@fixtures.user()
def test_associate_errors(agent, user):
    fake_user = confd.users(FAKE_ID).agents(agent['id']).put
    fake_agent = confd.users(user['id']).agents(FAKE_ID).put

    yield s.check_resource_not_found, fake_user, 'User'
    yield s.check_resource_not_found, fake_agent, 'Agent'

@fixtures.user()
def test_dissociate_errors(user):
    fake_user = confd.users(FAKE_ID).agents().delete

    yield s.check_resource_not_found, fake_user, 'User'

@fixtures.agent()
@fixtures.user()
def test_associate_user_agent(agent, user):
    response = confd.users(user['id']).agents(agent['id']).put()
    response.assert_updated()

@fixtures.agent()
@fixtures.user()
def test_associate_using_uuid(agent, user):
    response = confd.users(user['uuid']).agents(agent['id']).put()
    response.assert_updated()

@fixtures.user(wazo_tenant=MAIN_TENANT)
@fixtures.user(wazo_tenant=SUB_TENANT)
@fixtures.agent(wazo_tenant=MAIN_TENANT)
@fixtures.agent(wazo_tenant=SUB_TENANT)
def test_associate_multi_tenant(main_user, sub_user, main_agent, sub_agent):
    response = (
        confd.users(main_user['uuid'])
        .agents(sub_agent['id'])
        .put(wazo_tenant=SUB_TENANT)
    )
    response.assert_match(404, e.not_found('User'))

    response = (
        confd.users(sub_user['uuid'])
        .agents(main_agent['id'])
        .put(wazo_tenant=SUB_TENANT)
    )
    response.assert_match(404, e.not_found('Agent'))

    response = (
        confd.users(main_user['uuid'])
        .agents(sub_agent['id'])
        .put(wazo_tenant=MAIN_TENANT)
    )
    response.assert_match(400, e.different_tenant())

@fixtures.agent()
@fixtures.agent()
@fixtures.user()
def test_associate_when_user_already_associated_to_other_agent(agent1, agent2, user):
    with a.user_agent(user, agent1):
        response = confd.users(user['id']).agents(agent2['id']).put()
        response.assert_match(400, e.resource_associated('User', 'Agent'))

@fixtures.agent()
@fixtures.user()
def test_associate_when_user_already_associated_to_same_agent(agent, user):
    with a.user_agent(user, agent):
        response = confd.users(user['id']).agents(agent['id']).put()
        response.assert_updated()

@fixtures.agent()
@fixtures.user()
def test_dissociate(agent, user):
    with a.user_agent(user, agent, check=False):
        response = confd.users(user['id']).agents().delete()
        response.assert_deleted()

    with a.user_agent(user, agent, check=False):
        response = confd.users(user['uuid']).agents().delete()
        response.assert_deleted()

@fixtures.agent()
@fixtures.user()
def test_dissociate_not_associated(agent, user):
    response = confd.users(user['uuid']).agents().delete()
    response.assert_deleted()

@fixtures.user(wazo_tenant=MAIN_TENANT)
@fixtures.agent(wazo_tenant=MAIN_TENANT)
def test_dissociate_multi_tenant(user, agent):
    with a.user_agent(user, agent, check=False):
        response = confd.users(user['uuid']).agents().delete(wazo_tenant=SUB_TENANT)
        response.assert_match(404, e.not_found('User'))

@fixtures.agent()
@fixtures.user()
def test_get_agent_relation(agent, user):
    with a.user_agent(user, agent):
        response = confd.users(user['id']).get()
        assert_that(
            response.item,
            has_entries(agent=has_entries(id=agent['id'], number=agent['number'])),
        )

@fixtures.agent()
@fixtures.user()
@fixtures.user()
def test_get_users_relation(agent, user1, user2):
    with a.user_agent(user1, agent), a.user_agent(user2, agent):
        response = confd.agents(agent['id']).get()
        assert_that(
            response.item,
            has_entries(
                users=contains_inanyorder(
                    has_entries(
                        uuid=user1['uuid'],
                        firstname=user1['firstname'],
                        lastname=user1['lastname'],
                    ),
                    has_entries(
                        uuid=user2['uuid'],
                        firstname=user2['firstname'],
                        lastname=user2['lastname'],
                    ),
                )
            ),
        )

@fixtures.agent()
@fixtures.user()
def test_delete_user_when_user_and_agent_associated(agent, user):
    with a.user_agent(user, agent, check=False):
        response = confd.agents(agent['id']).get()
        assert_that(response.item['users'], not_(empty()))
        confd.users(user['id']).delete().assert_deleted()
        response = confd.agents(agent['id']).get()
        assert_that(response.item['users'], empty())

@fixtures.agent()
@fixtures.user()
def test_bus_events(agent, user):
    headers = {
        'tenant_uuid': user['tenant_uuid'],
        f'user_uuid:{user["uuid"]}': True,
    }

    url = confd.users(user['id']).agents(agent['id']).put
    yield s.check_event, 'user_agent_associated', headers, url

    url = confd.users(user['id']).agents.delete
    yield s.check_event, 'user_agent_dissociated', headers, url


from collections import defaultdict
import math

def sum_of_goodness(arr):
    goodness_sum = 0

    for index, num in enumerate(arr):
        contribution = num - index
        goodness_sum = (goodness_sum + contribution) % (10**9 + 7)

    return goodness_sum

for _ in range(int(input())):
    n=int(input())
    x=list(map(int,input().split()))
    # a,b=map(int,input().split())
    # s=input()
    # d=defaultdict(lambda:0)
    print(sum_of_goodness(x))


from quantization.lsq_layer import QuantAct, QuantConv2d, QuantLinear, QuantMultiHeadAct, QuantMuitiHeadLinear, QuantMuitiHeadLinear_in
import paddle
# paddle.disable_static()
@paddle.no_grad()
def initialize_quantization(data_loader, model, device, output_dir, sample_iters=5):

    # metric_logger = utils.MetricLogger(delimiter="  ")
    header = 'Initialization:'
    # if utils.is_main_process():
    with (output_dir / "scales.txt").open("w") as f:
        f.write("weight scales:\n")
        for name, m in model.named_sublayers():
            if (isinstance(m, QuantLinear) or isinstance(m, QuantConv2d) or isinstance(m, QuantMuitiHeadLinear) or isinstance(m, QuantMuitiHeadLinear_in)) and m.alpha is not None:
                print(f"initialize the weight scale for module {name}")
                m.initialize_scale(device)
                f.write(name + ': ' + str(m.alpha.numpy()) + '\n')

        # switch to evaluation mode
        model.eval()
        f.write("activation scales:\n")
        n = 0
        for images, target in data_loader:
            n += 1
            if n > sample_iters:
                break
            # images = images.to(device, non_blocking=True)

            # compute output
            # with paddle.cuda.amp.autocast():
            output = model(images)
        for name, m in model.named_sublayers():
            if (isinstance(m, QuantAct) or isinstance(m, QuantMultiHeadAct)) and m.alpha is not None:
                print(f"initialize the activation scale for module {name}")
                m.initialize_scale_offset(device)
                f.write(name + ': ' + str(m.alpha.numpy()) + '\n')
                if m.offset:
                    f.write("offset" + ': ' + str(m.beta.numpy()) + '\n')
    # gather the stats from all processes
    # metric_logger.synchronize_between_processes()

    return

def initialize_muitihead_quantization(model, device):
    for name, m in model.named_sublayers():
        if (isinstance(m, QuantMuitiHeadLinear) or isinstance(m, QuantMuitiHeadLinear_in) or isinstance(m, QuantMultiHeadAct)) and m.alpha is not None:
            m.nbits = paddle.create_parameter(shape=(paddle.ones(m.num_head).to(device) * m.nbits).shape,
                                        dtype=str((paddle.ones(m.num_head).to(device) * m.nbits).numpy().dtype),
                                        default_initializer=paddle.nn.initializer.Assign(paddle.ones(m.num_head).to(device) * m.nbits))
            print(f"Initialize bit-width for {name}, bit:{m.nbits.data}")
# @paddle.no_grad()
# def update_bn(data_loader, model, device):
#     criterion = paddle.nn.CrossEntropyLoss()

#     metric_logger = utils.MetricLogger(delimiter="  ")
#     header = 'Update BN:'

#     # switch to evaluation mode
#     model.train()

#     for images, _ in metric_logger.log_every(data_loader, 10, header):
#         images = images.to(device, non_blocking=True)

#         # compute output
#         output = model(images)

#     # gather the stats from all processes
#     metric_logger.synchronize_between_processes()

#     return {k: meter.global_avg for k, meter in metric_logger.meters.items()}

# @paddle.no_grad()
# def log_quantization_parameters(model, output_dir):
#     with (output_dir / "q_param.txt").open("w") as f:
#         f.write("weight scales:\n")
#         for name, m in model.named_modules():
#             if isinstance(m, QuantLinear) or isinstance(m, QuantConv2d) or isinstance(m, QuantMultiHeadAct) or isinstance(m, QuantAct) or isinstance(m, QuantMuitiHeadLinear) or isinstance(m, QuantMuitiHeadLinear_in):
#                 if m.alpha is not None:
#                     f.write(name + '\n')
#                     f.write('--bitwidth: ' + str(m.nbits.data) + '\n')
#                     if m.nbits.grad is not None:
#                         f.write('--grad: ' + str(m.nbits.grad.data) + '\n')
#                     n = m.nbits.round().to(paddle.long)
#                     f.write('--scales: ' + str(m.alpha.data) + '\n')
#                     f.write('--scale_n: ' + str(m.alpha[n-2]) + '\n')
#                     if m.alpha.grad is not None:
#                         f.write('--grad: ' + str(m.alpha.grad.data) + '\n')
#                 if isinstance(m, QuantAct) and m.offset:
#                     f.write("--offsets: " + str(m.beta.data) + '\n')
#                     f.write("--offset_n: " + str(m.beta[n-2]) + '\n')