44
55from maths .greatest_common_divisor import greatest_common_divisor
66
7+
78class HillCipher :
89 key_string = string .ascii_uppercase + string .digits # 36 chars
910 modulus = np .vectorize (lambda x : x % 36 ) # Mod 36
@@ -27,15 +28,15 @@ def check_determinant(self) -> None:
2728 det = round (np .linalg .det (self .encrypt_key ))
2829 if det < 0 :
2930 det %= len (self .key_string )
30-
31+
3132 error_msg = f"Det { det } not coprime with 36. Try another key."
3233 if greatest_common_divisor (det , len (self .key_string )) != 1 :
3334 raise ValueError (error_msg )
3435
3536 def process_text (self , text : str ) -> str :
3637 """Uppercase, filter, pad text"""
3738 chars = [c for c in text .upper () if c in self .key_string ]
38- last = chars [- 1 ] if chars else 'A'
39+ last = chars [- 1 ] if chars else "A"
3940 while len (chars ) % self .break_key != 0 :
4041 chars .append (last )
4142 return "" .join (chars )
@@ -45,7 +46,7 @@ def encrypt(self, text: str) -> str:
4546 text = self .process_text (text .upper ())
4647 encrypted = ""
4748 for i in range (0 , len (text ), self .break_key ):
48- batch = text [i : i + self .break_key ]
49+ batch = text [i : i + self .break_key ]
4950 vec = [self .replace_letters (c ) for c in batch ]
5051 batch_vec = np .array ([vec ]).T
5152 product = self .encrypt_key .dot (batch_vec )
@@ -62,15 +63,13 @@ def make_decrypt_key(self) -> np.ndarray:
6263 det = round (np .linalg .det (self .encrypt_key ))
6364 if det < 0 :
6465 det %= len (self .key_string )
65-
66+
6667 # Find det modular inverse
6768 det_inv = next (i for i in range (36 ) if (det * i ) % 36 == 1 )
68-
69+
6970 # Compute inverse key
7071 inv_key = (
71- det_inv *
72- np .linalg .det (self .encrypt_key ) *
73- np .linalg .inv (self .encrypt_key )
72+ det_inv * np .linalg .det (self .encrypt_key ) * np .linalg .inv (self .encrypt_key )
7473 )
7574 return self .to_int (self .modulus (inv_key ))
7675
@@ -80,7 +79,7 @@ def decrypt(self, text: str) -> str:
8079 text = self .process_text (text .upper ())
8180 decrypted = ""
8281 for i in range (0 , len (text ), self .break_key ):
83- batch = text [i : i + self .break_key ]
82+ batch = text [i : i + self .break_key ]
8483 vec = [self .replace_letters (c ) for c in batch ]
8584 batch_vec = np .array ([vec ]).T
8685 product = decrypt_key .dot (batch_vec )
@@ -92,23 +91,26 @@ def decrypt(self, text: str) -> str:
9291 decrypted += decrypted_batch
9392 return decrypted
9493
94+
9595def main () -> None :
9696 """Hill Cipher CLI"""
9797 n = int (input ("Enter key order: " ))
9898 print (f"Enter { n } rows of space-separated integers:" )
9999 matrix = [list (map (int , input ().split ())) for _ in range (n )]
100-
100+
101101 hc = HillCipher (np .array (matrix ))
102-
102+
103103 option = input ("1. Encrypt\n 2. Decrypt\n Choose: " )
104104 text = input ("Enter text: " )
105-
105+
106106 if option == "1" :
107107 print ("Encrypted:" , hc .encrypt (text ))
108108 elif option == "2" :
109109 print ("Decrypted:" , hc .decrypt (text ))
110110
111+
111112if __name__ == "__main__" :
112113 import doctest
114+
113115 doctest .testmod ()
114116 main ()
0 commit comments