




import string

import numpy





def 		snake_case_							(_a			:							Optional[Any]					,     _a			:							Optional[int]  ):
    return b if a == 0 else greatest_common_divisor(b % a					,     _lowerCAmelCase  )


class     _a						:
       __a       :  Tuple            =      string.ascii_uppercase + string.digits
       # This cipher takes alphanumerics into account
       # i.e. a total of 36 characters

       # take x and return x % len(key_string)
       __a       :  Dict            =      numpy.vectorize(lambda __a				: x % 36				)

       __a       :  Optional[int]            =      numpy.vectorize(__UpperCAmelCase				)
       def __init__(			self		:			Tuple     ,       lowercase		:			numpy.ndarray       ):



           '''simple docstring'''

           UpperCAmelCase     =	self.modulus(_lowerCamelCase       )  # mod36 calc's on the encrypt key
           self.check_determinant()  # validate the determinant of the encryption key
           UpperCAmelCase     =	encrypt_key.shape[0]
       def 					A   (			self		:			Optional[Any]     ,       lowercase		:			str       ):



           '''simple docstring'''

           return self.key_string.index(_lowerCamelCase       )
       def 					A   (			self		:			List[str]     ,       lowercase		:			int       ):



           '''simple docstring'''

           return self.key_string[round(_lowerCamelCase       )]
       def 					A   (			self		:			List[str]       ):



           '''simple docstring'''

           UpperCAmelCase     =	round(numpy.linalg.det(self.encrypt_key       )       )

           if det < 0:
               UpperCAmelCase     =	det % len(self.key_string       )

           UpperCAmelCase     =	len(self.key_string       )
           if greatest_common_divisor(_lowerCamelCase     ,       len(self.key_string       )       ) != 1:
               UpperCAmelCase     =	(
                   f"determinant modular {req_l} of encryption key({det}) "
                   f"is not co prime w.r.t {req_l}.\nTry another key."
               )
               raise ValueError(_lowerCamelCase       )
       def 					A   (			self		:			Dict     ,       lowercase		:			str       ):



           '''simple docstring'''

           UpperCAmelCase     =	[char for char in text.upper() if char in self.key_string]

           UpperCAmelCase     =	chars[-1]
           while len(_lowerCamelCase       ) % self.break_key != 0:
               chars.append(_lowerCamelCase       )

           return "".join(_lowerCamelCase       )
       def 					A   (			self		:			Optional[int]     ,       lowercase		:			str       ):



           '''simple docstring'''

           UpperCAmelCase     =	self.process_text(text.upper()       )
           UpperCAmelCase     =	''''''

           for i in range(0     ,       len(_lowerCamelCase       ) - self.break_key + 1     ,       self.break_key       ):
               UpperCAmelCase     =	text[i : i + self.break_key]
               UpperCAmelCase     =	[self.replace_letters(_lowerCamelCase       ) for char in batch]
               UpperCAmelCase     =	numpy.array([vec]       ).T
               UpperCAmelCase     =	self.modulus(self.encrypt_key.dot(_lowerCamelCase       )       ).T.tolist()[
                   0
               ]
               UpperCAmelCase     =	''''''.join(
                   self.replace_digits(_lowerCamelCase       ) for num in batch_encrypted       )
               encrypted += encrypted_batch

           return encrypted
       def 					A   (			self		:			str       ):



           '''simple docstring'''

           UpperCAmelCase     =	round(numpy.linalg.det(self.encrypt_key       )       )

           if det < 0:
               UpperCAmelCase     =	det % len(self.key_string       )
           UpperCAmelCase     =	None
           for i in range(len(self.key_string       )       ):
               if (det * i) % len(self.key_string       ) == 1:
                   UpperCAmelCase     =	i
                   break

           UpperCAmelCase     =	(
               det_inv
               * numpy.linalg.det(self.encrypt_key       )
               * numpy.linalg.inv(self.encrypt_key       )
           )

           return self.to_int(self.modulus(_lowerCamelCase       )       )







       def 					A   (			self		:			Union[str, Any]     ,       lowercase		:			str       ):



           '''simple docstring'''

           UpperCAmelCase     =	self.make_decrypt_key()
           UpperCAmelCase     =	self.process_text(text.upper()       )
           UpperCAmelCase     =	''''''

           for i in range(0     ,       len(_lowerCamelCase       ) - self.break_key + 1     ,       self.break_key       ):
               UpperCAmelCase     =	text[i : i + self.break_key]
               UpperCAmelCase     =	[self.replace_letters(_lowerCamelCase       ) for char in batch]
               UpperCAmelCase     =	numpy.array([vec]       ).T
               UpperCAmelCase     =	self.modulus(decrypt_key.dot(_lowerCamelCase       )       ).T.tolist()[0]
               UpperCAmelCase     =	''''''.join(
                   self.replace_digits(_lowerCamelCase       ) for num in batch_decrypted       )
               decrypted += decrypted_batch

           return decrypted






def 		snake_case_							():
    UpperCAmelCase     =	int(input('''Enter the order of the encryption key: '''  )  )
    UpperCAmelCase     =	[]

    print('''Enter each row of the encryption key with space separated integers'''  )
    for _ in range(_lowerCAmelCase  ):
        UpperCAmelCase     =	[int(_lowerCAmelCase  ) for x in input().split()]
        hill_matrix.append(_lowerCAmelCase  )

    UpperCAmelCase     =	HillCipher(numpy.array(_lowerCAmelCase  )  )

    print('''Would you like to encrypt or decrypt some text? (1 or 2)'''  )
    UpperCAmelCase     =	input('''\n1. Encrypt\n2. Decrypt\n'''  )
    if option == "1":
        UpperCAmelCase     =	input('''What text would you like to encrypt?: '''  )
        print('''Your encrypted text is:'''  )
        print(hc.encrypt(_lowerCAmelCase  )  )
    elif option == "2":
        UpperCAmelCase     =	input('''What text would you like to decrypt?: '''  )
        print('''Your decrypted text is:'''  )
        print(hc.decrypt(_lowerCAmelCase  )  )


if __name__ == "__main__":
   import doctest

   doctest.testmod()

   main()










'''simple docstring'''







def 		snake_case_							(_a			:							list[list[int]]					,     _a			:							int					,     _a			:							int					,     _a			:							list[int]  ):

    # 1. Validate that path exists between current and next vertices
    if graph[path[curr_ind - 1]][next_ver] == 0:
        return False

    # 2. Validate that next vertex is not already in path
    return not any(vertex == next_ver for vertex in path  )





def 		snake_case_							(_a			:							list[list[int]]					,     _a			:							list[int]					,     _a			:							int  ):

    # Base Case
    if curr_ind == len(_a  ):
        # return whether path exists between current and starting vertices
        return graph[path[curr_ind - 1]][path[0]] == 1

    # Recursive Step
    for next_ver in range(0					,     len(_a  )  ):
        if valid_connection(_a					,     _a					,     _a					,     _a  ):
            # Insert current vertex  into path as next transition
            UpperCAmelCase     =	next_ver
            # Validate created path
            if util_hamilton_cycle(_a					,     _a					,     curr_ind + 1  ):
                return True
            # Backtrack
            UpperCAmelCase     =	-1
    return False







def 		snake_case_							(_a			:							list[list[int]]					,     _a			:							int = 0  ):
    UpperCAmelCase     =	[-1] * (len(_a  ) + 1)
    # initialize start and end of path with starting index
    UpperCAmelCase     =	UpperCAmelCase     =	start_index
    # evaluate and if we find answer return path either return empty array
    return path if util_hamilton_cycle(_a					,     _a					,     1  ) else []




