






import string

import numpy


def 					snake_case__				(				UpperCAmelCase				:	Optional[Any]     ,      UpperCAmelCase				:	List[str]							):
    return b if a == 0 else greatest_common_divisor(b % a     ,      UpperCAmelCase							)







class 						_UpperCAmelCase	:




      """simple docstring"""



      A    =     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    =     numpy.vectorize(lambda _A		: x % 36		)

      A    =     numpy.vectorize(lowerCAmelCase__		)
      def __init__( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          lowerCAmelCase__ :str   				=					self.modulus(_lowerCamelCase		)  # mod36 calc's on the encrypt key
          self.check_determinant()  # validate the determinant of the encryption key
          lowerCAmelCase__ :List[str]   				=					encrypt_key.shape[0]
      def       snake_case_   ( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          return self.key_string.index(_lowerCamelCase		)
      def       snake_case_   ( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          return self.key_string[round(_lowerCamelCase		)]
      def       snake_case_   ( self		):



          '''simple docstring'''
          lowerCAmelCase__ :List[Any]   				=					round(numpy.linalg.det(self.encrypt_key		)		)

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

          lowerCAmelCase__ :Dict   				=					len(self.key_string		)
          if greatest_common_divisor(_lowerCamelCase     ,						len(self.key_string		)		) != 1:
              lowerCAmelCase__ :Optional[int]   				=					(
                  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       snake_case_   ( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          lowerCAmelCase__ :Optional[int]   				=					[char for char in text.upper() if char in self.key_string]

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

          return "".join(_lowerCamelCase		)
      def       snake_case_   ( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          lowerCAmelCase__ :List[str]   				=					self.process_text(text.upper()		)
          lowerCAmelCase__ :Optional[Any]   				=					""""""

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

          return encrypted
      def       snake_case_   ( self		):



          '''simple docstring'''
          lowerCAmelCase__ :List[str]   				=					round(numpy.linalg.det(self.encrypt_key		)		)

          if det < 0:
              lowerCAmelCase__ :Dict   				=					det % len(self.key_string		)
          lowerCAmelCase__ :int   				=					None
          for i in range(len(self.key_string		)		):
              if (det * i) % len(self.key_string		) == 1:
                  lowerCAmelCase__ :Union[str, Any]   				=					i
                  break

          lowerCAmelCase__ :str   				=					(
              det_inv
              * numpy.linalg.det(self.encrypt_key		)
              * numpy.linalg.inv(self.encrypt_key		)
          )

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



      def       snake_case_   ( self     ,						_lowerCAmelCase		):



          '''simple docstring'''
          lowerCAmelCase__ :Any   				=					self.make_decrypt_key()
          lowerCAmelCase__ :Optional[int]   				=					self.process_text(text.upper()		)
          lowerCAmelCase__ :Any   				=					""""""

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

          return decrypted





def 					snake_case__				(				):
    lowerCAmelCase__ :List[Any]   				=					int(input("Enter the order of the encryption key: "							)							)
    lowerCAmelCase__ :Optional[Any]   				=					[]

    print("Enter each row of the encryption key with space separated integers"							)
    for _ in range(UpperCAmelCase							):
        lowerCAmelCase__ :Union[str, Any]   				=					[int(UpperCAmelCase							) for x in input().split()]
        hill_matrix.append(UpperCAmelCase							)

    lowerCAmelCase__ :Optional[Any]   				=					HillCipher(numpy.array(UpperCAmelCase							)							)

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


if __name__ == "__main__":
 import doctest

 doctest.testmod()

 main()






"""simple docstring"""
_A							=  """ABCDEFGHIJKLMNOPQRSTUVWXYZ"""




def 					a__						(						)	-> None:
      UpperCAmelCase__			:							Optional[Any]							=    input("""Enter message: """    )
      UpperCAmelCase__			:							Optional[Any]							=    input("""Enter key [alphanumeric]: """    )
      UpperCAmelCase__			:							Optional[Any]							=    input("""Encrypt/Decrypt [e/d]: """    )

      if mode.lower().startswith("""e"""    ):
            UpperCAmelCase__			:							Optional[Any]							=    """encrypt"""
            UpperCAmelCase__			:							List[Any]							=    encrypt_message(lowerCAmelCase			,	lowerCAmelCase    )
      elif mode.lower().startswith("""d"""    ):
            UpperCAmelCase__			:							Optional[Any]							=    """decrypt"""
            UpperCAmelCase__			:							Dict							=    decrypt_message(lowerCAmelCase			,	lowerCAmelCase    )

      print(F"""\n{mode.title()}ed message:"""    )
      print(lowerCAmelCase    )




def 					a__						(						lowerCAmelCase			,	lowerCAmelCase    )	-> str:
      return translate_message(lowerCAmelCase			,	lowerCAmelCase			,	"""encrypt"""    )




def 					a__						(						lowerCAmelCase			,	lowerCAmelCase    )	-> str:
      return translate_message(lowerCAmelCase			,	lowerCAmelCase			,	"""decrypt"""    )




def 					a__						(						lowerCAmelCase			,	lowerCAmelCase			,	lowerCAmelCase    )	-> str:
      UpperCAmelCase__			:							Optional[Any]							=    []
      UpperCAmelCase__			:							int							=    0
      UpperCAmelCase__			:							str							=    key.upper()

      for symbol in message:
            UpperCAmelCase__			:							Optional[Any]							=    LETTERS.find(symbol.upper()    )
            if num != -1:
                  if mode == "encrypt":
                        num += LETTERS.find(key[key_index]    )
                  elif mode == "decrypt":
                        num -= LETTERS.find(key[key_index]    )

                  num %= len(lowerCAmelCase    )

                  if symbol.isupper():
                        translated.append(LETTERS[num]    )
                  elif symbol.islower():
                        translated.append(LETTERS[num].lower()    )

                  key_index += 1
                  if key_index == len(lowerCAmelCase    ):
                        UpperCAmelCase__			:							List[str]							=    0
            else:
                  translated.append(lowerCAmelCase    )
      return "".join(lowerCAmelCase    )


if __name__ == "__main__":
    main()

