


import string

import numpy



def       _lowerCAmelCase    (				lowerCAmelCase_				:int		,    lowerCAmelCase_				:int				)->int:
   '''simple docstring'''






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


class      __lowerCAmelCase  :






  """simple docstring"""





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

  _SCREAMING_SNAKE_CASE		     =							numpy.vectorize(a       )



  def __init__(  self							:							Tuple     ,					_lowerCAmelCase							:							numpy.ndarray	) ->			None:




     """simple docstring"""
     snake_case_            =       self.modulus(_lowerCAmelCase	)  # mod36 calc's on the encrypt key
     self.check_determinant()  # validate the determinant of the encryption key
     snake_case_            =       encrypt_key.shape[0]



  def        lowerCAmelCase__	(  self							:							Optional[int]     ,					_lowerCAmelCase							:							str	) ->			int:




     """simple docstring"""
     return self.key_string.index(_lowerCAmelCase	)



  def        lowerCAmelCase__	(  self							:							List[Any]     ,					_lowerCAmelCase							:							int	) ->			str:




     """simple docstring"""
     return self.key_string[round(_lowerCAmelCase	)]



  def        lowerCAmelCase__	(  self							:							Dict	) ->			None:




     """simple docstring"""
     snake_case_            =       round(numpy.linalg.det(self.encrypt_key	)	)

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

     snake_case_            =       len(self.key_string	)
     if greatest_common_divisor(_lowerCAmelCase     ,					len(self.key_string	)	) != 1:
        snake_case_            =       (
            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							:							Tuple     ,					_lowerCAmelCase							:							str	) ->			str:




     """simple docstring"""
     snake_case_            =       [char for char in text.upper() if char in self.key_string]

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

     return "".join(_lowerCAmelCase	)



  def        lowerCAmelCase__	(  self							:							int     ,					_lowerCAmelCase							:							str	) ->			str:




     """simple docstring"""
     snake_case_            =       self.process_text(text.upper()	)
     snake_case_            =       ""

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

     return encrypted



  def        lowerCAmelCase__	(  self							:							Tuple	) ->			numpy.ndarray:




     """simple docstring"""
     snake_case_            =       round(numpy.linalg.det(self.encrypt_key	)	)

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

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

     return self.to_int(self.modulus(_lowerCAmelCase	)	)



  def        lowerCAmelCase__	(  self							:							str     ,					_lowerCAmelCase							:							str	) ->			str:




     """simple docstring"""
     snake_case_            =       self.make_decrypt_key()
     snake_case_            =       self.process_text(text.upper()	)
     snake_case_            =       ""

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

     return decrypted






def       _lowerCAmelCase    (				)->None:
   '''simple docstring'''






   snake_case_            =       int(input("Enter the order of the encryption key: "				)				)
   snake_case_            =       []

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

   snake_case_            =       HillCipher(numpy.array(lowerCAmelCase_				)				)

   print("Would you like to encrypt or decrypt some text? (1 or 2)"				)
   snake_case_            =       input("\n1. Encrypt\n2. Decrypt\n"				)
   if option == "1":
      snake_case_            =       input("What text would you like to encrypt?: "				)
      print("Your encrypted text is:"				)
      print(hc.encrypt(lowerCAmelCase_				)				)
   elif option == "2":
      snake_case_            =       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()








def       _lowerCAmelCase    (				lowerCAmelCase_				:int		,    lowerCAmelCase_				:int				)->str:
   '''simple docstring'''






   if not isinstance(lowerCAmelCase_		,    lowerCAmelCase_				):
      raise ValueError("iterations must be defined as integers"				)
   if not isinstance(lowerCAmelCase_		,    lowerCAmelCase_				) or not number >= 1:
      raise ValueError(
          "starting number must be\n                         and integer and be more than 0"				)
   if not iterations >= 1:
      raise ValueError("Iterations must be done more than 0 times to play FizzBuzz"				)

   snake_case_            =       ""
   while number <= iterations:
      if number % 3 == 0:
         out += "Fizz"
      if number % 5 == 0:
         out += "Buzz"
      if 0 not in (number % 3, number % 5):
         out += str(lowerCAmelCase_				)

      # print(out)
      number += 1
      out += " "
   return out


if __name__ == "__main__":
   import doctest

   doctest.testmod()




