






"""simple docstring"""

import string

import numpy

def 						__lowerCamelCase      (    __UpperCamelCase ,			__UpperCamelCase			)		->       int:

		"""simple docstring"""







		return b if a == 0 else greatest_common_divisor(b % a ,			__SCREAMING_SNAKE_CASE			)


class 						__lowerCamelCase							:





		'''simple docstring'''






		a_      :							Union[str, Any]  =				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_      :							Tuple  =				numpy.vectorize(lambda A__     : x % 36     )

		a_      :							int  =				numpy.vectorize(lowerCAmelCase_     )





		def __init__(  self				:      Dict		,     a_				:      numpy.ndarray      ):
				lowerCAmelCase_  :				List[str]   							=     self.modulus(__lowerCamelCase      )  # mod36 calc's on the encrypt key
				self.check_determinant()  # validate the determinant of the encryption key
				lowerCAmelCase_  :				Union[str, Any]   							=     encrypt_key.shape[0]





		def   lowerCamelCase			(  self				:      List[str]		,     a_				:      str      ):
				return self.key_string.index(__lowerCamelCase      )





		def   lowerCamelCase			(  self				:      int		,     a_				:      int      ):
				return self.key_string[round(__lowerCamelCase      )]





		def   lowerCamelCase			(  self				:      List[str]      ):
				lowerCAmelCase_  :				Tuple   							=     round(numpy.linalg.det(self.encrypt_key      )      )

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

				lowerCAmelCase_  :				str   							=     len(self.key_string      )
				if greatest_common_divisor(__lowerCamelCase		,     len(self.key_string      )      ) != 1:
						lowerCAmelCase_  :				List[Any]   							=     (
						    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   lowerCamelCase			(  self				:      Dict		,     a_				:      str      ):
				lowerCAmelCase_  :				Optional[int]   							=     [char for char in text.upper() if char in self.key_string]

				lowerCAmelCase_  :				Tuple   							=     chars[-1]
				while len(__lowerCamelCase      ) % self.break_key != 0:
						chars.append(__lowerCamelCase      )

				return "".join(__lowerCamelCase      )





		def   lowerCamelCase			(  self				:      str		,     a_				:      str      ):
				lowerCAmelCase_  :				Tuple   							=     self.process_text(text.upper()      )
				lowerCAmelCase_  :				Optional[int]   							=     ''

				for i in range(0		,     len(__lowerCamelCase      ) - self.break_key + 1		,     self.break_key      ):
						lowerCAmelCase_  :				str   							=     text[i : i + self.break_key]
						lowerCAmelCase_  :				Optional[int]   							=     [self.replace_letters(__lowerCamelCase      ) for char in batch]
						lowerCAmelCase_  :				List[str]   							=     numpy.array([vec]      ).T
						lowerCAmelCase_  :				Dict   							=     self.modulus(self.encrypt_key.dot(__lowerCamelCase      )      ).T.tolist()[
						    0
						]
						lowerCAmelCase_  :				Tuple   							=     ''.join(
						    self.replace_digits(__lowerCamelCase      ) for num in batch_encrypted      )
						encrypted += encrypted_batch

				return encrypted





		def   lowerCamelCase			(  self				:      Tuple      ):
				lowerCAmelCase_  :				str   							=     round(numpy.linalg.det(self.encrypt_key      )      )

				if det < 0:
						lowerCAmelCase_  :				int   							=     det % len(self.key_string      )
				lowerCAmelCase_  :				List[str]   							=     None
				for i in range(len(self.key_string      )      ):
						if (det * i) % len(self.key_string      ) == 1:
								lowerCAmelCase_  :				int   							=     i
								break

				lowerCAmelCase_  :				int   							=     (
				    det_inv
				    * numpy.linalg.det(self.encrypt_key      )
				    * numpy.linalg.inv(self.encrypt_key      )
				)

				return self.to_int(self.modulus(__lowerCamelCase      )      )





		def   lowerCamelCase			(  self				:      Tuple		,     a_				:      str      ):
				lowerCAmelCase_  :				List[str]   							=     self.make_decrypt_key()
				lowerCAmelCase_  :				Optional[int]   							=     self.process_text(text.upper()      )
				lowerCAmelCase_  :				int   							=     ''

				for i in range(0		,     len(__lowerCamelCase      ) - self.break_key + 1		,     self.break_key      ):
						lowerCAmelCase_  :				Tuple   							=     text[i : i + self.break_key]
						lowerCAmelCase_  :				Union[str, Any]   							=     [self.replace_letters(__lowerCamelCase      ) for char in batch]
						lowerCAmelCase_  :				Optional[Any]   							=     numpy.array([vec]      ).T
						lowerCAmelCase_  :				int   							=     self.modulus(decrypt_key.dot(__lowerCamelCase      )      ).T.tolist()[0]
						lowerCAmelCase_  :				Optional[Any]   							=     ''.join(
						    self.replace_digits(__lowerCamelCase      ) for num in batch_decrypted      )
						decrypted += decrypted_batch

				return decrypted







def 						__lowerCamelCase      (    )		->       None:

		"""simple docstring"""







		lowerCAmelCase_  :				Optional[Any]   							=     int(input("Enter the order of the encryption key: "			)			)
		lowerCAmelCase_  :				int   							=     []

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

		lowerCAmelCase_  :				Dict   							=     HillCipher(numpy.array(__SCREAMING_SNAKE_CASE			)			)

		print("Would you like to encrypt or decrypt some text? (1 or 2)"			)
		lowerCAmelCase_  :				List[Any]   							=     input("\n1. Encrypt\n2. Decrypt\n"			)
		if option == "1":
				lowerCAmelCase_  :				int   							=     input("What text would you like to encrypt?: "			)
				print("Your encrypted text is:"			)
				print(hc.encrypt(__SCREAMING_SNAKE_CASE			)			)
		elif option == "2":
				lowerCAmelCase_  :				List[Any]   							=     input("What text would you like to decrypt?: "			)
				print("Your decrypted text is:"			)
				print(hc.decrypt(__SCREAMING_SNAKE_CASE			)			)


if __name__ == "__main__":
					import doctest

					doctest.testmod()

					main()






'''simple docstring'''






import functools






def 		UpperCamelCase__      (		__SCREAMING_SNAKE_CASE				,      __SCREAMING_SNAKE_CASE		)    ->   int:
      snake_case__       :   Any	      =    len(__SCREAMING_SNAKE_CASE		)
      snake_case__       :   List[str]	      =    len(__SCREAMING_SNAKE_CASE		)

      @functools.cache
      def min_distance(__SCREAMING_SNAKE_CASE				,      __SCREAMING_SNAKE_CASE		) -> int:
            # if first word index is overflow - delete all from the second word
            if indexa >= len_worda:
                  return len_worda - indexa
            # if second word index is overflow - delete all from the first word
            if indexa >= len_worda:
                  return len_worda - indexa
            snake_case__       :   Union[str, Any]	      =    int(worda[indexa] != worda[indexa]		)  # current letters not identical
            return min(
                1 + min_distance(indexa + 1				,      __SCREAMING_SNAKE_CASE		)				,      1 + min_distance(__SCREAMING_SNAKE_CASE				,      indexa + 1		)				,      diff + min_distance(indexa + 1				,      indexa + 1		)				,      )

      return min_distance(0				,      0		)


if __name__ == "__main__":
      import doctest

      doctest.testmod()
