




import string

import numpy



def      __lowerCAmelCase			(					UpperCamelCase	,      UpperCamelCase      )     ->	int:
  return b if a == 0 else greatest_common_divisor(b % a	,      UpperCamelCase      )






class  _lowerCAmelCase     :
      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 _lowercase	: x % 36	)

      A__		     =		numpy.vectorize(_lowercase	)

      def __init__(   self      ,				__UpperCAmelCase		):
        lowerCAmelCase__				:  Dict		       =     self.modulus(__UpperCAmelCase		)  # 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 	__magic_name__(   self      ,				__UpperCAmelCase		):
        return self.key_string.index(__UpperCAmelCase		)

      def 	__magic_name__(   self      ,				__UpperCAmelCase		):
        return self.key_string[round(__UpperCAmelCase		)]

      def 	__magic_name__(   self		):
        lowerCAmelCase__				:  Optional[int]		       =     round(numpy.linalg.det(self.encrypt_key		)		)

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

        lowerCAmelCase__				:  int		       =     len(self.key_string		)
        if greatest_common_divisor(__UpperCAmelCase      ,				len(self.key_string		)		) != 1:
          lowerCAmelCase__				:  Tuple		       =     (
              f"""determinant modular {req_l} of encryption key({det}) """
              f"""is not co prime w.r.t {req_l}.\nTry another key."""
          )
          raise ValueError(__UpperCAmelCase		)

      def 	__magic_name__(   self      ,				__UpperCAmelCase		):
        lowerCAmelCase__				:  List[str]		       =     [char for char in text.upper() if char in self.key_string]

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

        return "".join(__UpperCAmelCase		)

      def 	__magic_name__(   self      ,				__UpperCAmelCase		):
        lowerCAmelCase__				:  Dict		       =     self.process_text(text.upper()		)
        lowerCAmelCase__				:  int		       =     ''''''

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

        return encrypted

      def 	__magic_name__(   self		):
        lowerCAmelCase__				:  Dict		       =     round(numpy.linalg.det(self.encrypt_key		)		)

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

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

        return self.to_int(self.modulus(__UpperCAmelCase		)		)







      def 	__magic_name__(   self      ,				__UpperCAmelCase		):
        lowerCAmelCase__				:  Dict		       =     self.make_decrypt_key()
        lowerCAmelCase__				:  Dict		       =     self.process_text(text.upper()		)
        lowerCAmelCase__				:  Any		       =     ''''''

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

        return decrypted



def      __lowerCAmelCase			(					)     ->	None:
  lowerCAmelCase__				:  Dict		       =     int(input('''Enter the order of the encryption key: '''      )      )
  lowerCAmelCase__				:  Union[str, Any]		       =     []

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

  lowerCAmelCase__				:  Any		       =     HillCipher(numpy.array(UpperCamelCase      )      )

  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(UpperCamelCase      )      )
  elif option == "2":
    lowerCAmelCase__				:  Optional[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()







from __future__ import annotations



def      __lowerCAmelCase			(					UpperCamelCase      )     ->	list[int]:  # This function is recursive
  lowerCAmelCase__				:  Tuple		       =     len(UpperCamelCase      )
  # If the array contains only one element, we return it (it's the stop condition of
  # recursion)
  if array_length <= 1:
    return array
    # Else
  lowerCAmelCase__				:  Optional[int]		       =     array[0]
  lowerCAmelCase__				:  Union[str, Any]		       =     False
  lowerCAmelCase__				:  List[str]		       =     1
  lowerCAmelCase__				:  list[int]		       =     []
  while not is_found and i < array_length:
    if array[i] < pivot:
      lowerCAmelCase__				:  List[Any]		       =     True
      lowerCAmelCase__				:  Optional[int]		       =     [element for element in array[i:] if element >= array[i]]
      lowerCAmelCase__				:  Any		       =     longest_subsequence(UpperCamelCase      )
      if len(UpperCamelCase      ) > len(UpperCamelCase      ):
        lowerCAmelCase__				:  Optional[int]		       =     temp_array
    else:
      i += 1

  lowerCAmelCase__				:  Optional[Any]		       =     [element for element in array[1:] if element >= pivot]
  lowerCAmelCase__				:  List[Any]		       =     [pivot, *longest_subsequence(UpperCamelCase      )]
  if len(UpperCamelCase      ) > len(UpperCamelCase      ):
    return temp_array
  else:
    return longest_subseq


if __name__ == "__main__":
     import doctest

     doctest.testmod()

