






'''simple docstring'''
import string

import numpy




def 		__SCREAMING_SNAKE_CASE					(  _UpperCamelCase						,		_UpperCamelCase							):


   """simple docstring"""




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


class        _UpperCAmelCase						:
    __lowerCamelCase:   Dict		       =    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)
    __lowerCamelCase:   str		       =    numpy.vectorize(lambda snake_case			: x % 36			)

    __lowerCamelCase:   Union[str, Any]		       =    numpy.vectorize(SCREAMING_SNAKE_CASE__			)




    def __init__(			self  :       Any  ,     a  :       Any			):






       '''simple docstring'''






       lowercase_	:    Dict           =     self.modulus(snake_case__			)  # mod36 calc's on the encrypt key
       self.check_determinant()  # validate the determinant of the encryption key
       lowercase_	:    Tuple           =     encrypt_key.shape[0]




    def 				lowerCAmelCase__				(			self  :       List[Any]  ,     a  :       Tuple			):






       '''simple docstring'''






       return self.key_string.index(snake_case__			)




    def 				lowerCAmelCase__				(			self  :       Optional[int]  ,     a  :       Any			):






       '''simple docstring'''






       return self.key_string[round(snake_case__			)]




    def 				lowerCAmelCase__				(			self  :       Union[str, Any]			):






       '''simple docstring'''






       lowercase_	:    Optional[Any]           =     round(numpy.linalg.det(self.encrypt_key			)			)

       if det < 0:
          lowercase_	:    List[Any]           =     det % len(self.key_string			)

       lowercase_	:    Dict           =     len(self.key_string			)
       if greatest_common_divisor(snake_case__  ,     len(self.key_string			)			) != 1:
          lowercase_	:    Optional[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(snake_case__			)




    def 				lowerCAmelCase__				(			self  :       int  ,     a  :       str			):






       '''simple docstring'''






       lowercase_	:    List[str]           =     [char for char in text.upper() if char in self.key_string]

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

       return "".join(snake_case__			)




    def 				lowerCAmelCase__				(			self  :       int  ,     a  :       Any			):






       '''simple docstring'''






       lowercase_	:    Any           =     self.process_text(text.upper()			)
       lowercase_	:    Tuple           =     ""

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

       return encrypted




    def 				lowerCAmelCase__				(			self  :       Any			):






       '''simple docstring'''






       lowercase_	:    int           =     round(numpy.linalg.det(self.encrypt_key			)			)

       if det < 0:
          lowercase_	:    Optional[Any]           =     det % len(self.key_string			)
       lowercase_	:    Dict           =     None
       for i in range(len(self.key_string			)			):
          if (det * i) % len(self.key_string			) == 1:
             lowercase_	:    Tuple           =     i
             break

       lowercase_	:    List[str]           =     (
           det_inv
           * numpy.linalg.det(self.encrypt_key			)
           * numpy.linalg.inv(self.encrypt_key			)
       )

       return self.to_int(self.modulus(snake_case__			)			)





    def 				lowerCAmelCase__				(			self  :       List[str]  ,     a  :       List[str]			):






       '''simple docstring'''






       lowercase_	:    str           =     self.make_decrypt_key()
       lowercase_	:    int           =     self.process_text(text.upper()			)
       lowercase_	:    Union[str, Any]           =     ""

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

       return decrypted




def 		__SCREAMING_SNAKE_CASE					(  ):


   """simple docstring"""




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

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

   lowercase_	:    Union[str, Any]           =     HillCipher(numpy.array(_UpperCamelCase							)							)

   print("Would you like to encrypt or decrypt some text? (1 or 2)"							)
   lowercase_	:    List[Any]           =     input("\n1. Encrypt\n2. Decrypt\n"							)
   if option == "1":
      lowercase_	:    Optional[int]           =     input("What text would you like to encrypt?: "							)
      print("Your encrypted text is:"							)
      print(hc.encrypt(_UpperCamelCase							)							)
   elif option == "2":
      lowercase_	:    Dict           =     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'''
def 		__SCREAMING_SNAKE_CASE					(  _UpperCamelCase							):


   """simple docstring"""




   if number < 0:
      raise ValueError("number must not be negative"							)
   return number & (number - 1) == 0


if __name__ == "__main__":
 import doctest

 doctest.testmod()




