3333https://www.youtube.com/watch?v=kfmNeskzs2o
3434https://www.youtube.com/watch?v=4RhLNDqcjpA
3535"""
36-
3736import string
3837import numpy as np
3938from maths .greatest_common_divisor import greatest_common_divisor
4039
41-
4240class HillCipher :
4341 key_string = string .ascii_uppercase + string .digits # 36 alphanumeric chars
4442 modulus = np .vectorize (lambda x : x % 36 ) # Mod 36 operation
@@ -62,14 +60,15 @@ def check_determinant(self) -> None:
6260 det = round (np .linalg .det (self .encrypt_key ))
6361 if det < 0 :
6462 det %= len (self .key_string )
65-
63+
64+ error_msg = f"Det { det } not coprime with 36. Try another key."
6665 if greatest_common_divisor (det , len (self .key_string )) != 1 :
67- raise ValueError (f"Det { det } not coprime with 36. Try another key." )
66+ raise ValueError (error_msg )
6867
6968 def process_text (self , text : str ) -> str :
7069 """Convert to uppercase, remove invalid chars, pad to multiple of break_key"""
7170 chars = [c for c in text .upper () if c in self .key_string ]
72- last = chars [- 1 ] if chars else "A"
71+ last = chars [- 1 ] if chars else 'A'
7372 while len (chars ) % self .break_key != 0 :
7473 chars .append (last )
7574 return "" .join (chars )
@@ -79,30 +78,24 @@ def encrypt(self, text: str) -> str:
7978 text = self .process_text (text .upper ())
8079 encrypted = ""
8180 for i in range (0 , len (text ), self .break_key ):
82- batch = text [i : i + self .break_key ]
81+ batch = text [i : i + self .break_key ]
8382 vec = [self .replace_letters (c ) for c in batch ]
8483 batch_vec = np .array ([vec ]).T
85- batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[
86- 0
87- ]
88- encrypted += "" .join (
89- self .replace_digits (int (round (n ))) for n in batch_encrypted
90- )
84+ batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[0 ]
85+ encrypted += "" .join (self .replace_digits (round (n )) for n in batch_encrypted )
9186 return encrypted
9287
9388 def make_decrypt_key (self ) -> np .ndarray :
9489 """Calculate decryption key matrix"""
9590 det = round (np .linalg .det (self .encrypt_key ))
9691 if det < 0 :
9792 det %= len (self .key_string )
98-
93+
9994 # Find modular inverse of det
10095 det_inv = next (i for i in range (36 ) if (det * i ) % 36 == 1 )
101-
96+
10297 # Calculate inverse key
103- inv_key = (
104- det_inv * np .linalg .det (self .encrypt_key ) * np .linalg .inv (self .encrypt_key )
105- )
98+ inv_key = det_inv * np .linalg .det (self .encrypt_key ) * np .linalg .inv (self .encrypt_key )
10699 return self .to_int (self .modulus (inv_key ))
107100
108101 def decrypt (self , text : str ) -> str :
@@ -111,35 +104,30 @@ def decrypt(self, text: str) -> str:
111104 text = self .process_text (text .upper ())
112105 decrypted = ""
113106 for i in range (0 , len (text ), self .break_key ):
114- batch = text [i : i + self .break_key ]
107+ batch = text [i : i + self .break_key ]
115108 vec = [self .replace_letters (c ) for c in batch ]
116109 batch_vec = np .array ([vec ]).T
117110 batch_decrypted = self .modulus (decrypt_key .dot (batch_vec )).T .tolist ()[0 ]
118- decrypted += "" .join (
119- self .replace_digits (int (round (n ))) for n in batch_decrypted
120- )
111+ decrypted += "" .join (self .replace_digits (round (n )) for n in batch_decrypted )
121112 return decrypted
122113
123-
124114def main () -> None :
125115 """CLI for Hill Cipher"""
126116 n = int (input ("Enter key order: " ))
127117 print (f"Enter { n } rows of space-separated integers:" )
128118 matrix = [list (map (int , input ().split ())) for _ in range (n )]
129-
119+
130120 hc = HillCipher (np .array (matrix ))
131-
121+
132122 option = input ("1. Encrypt\n 2. Decrypt\n Choose: " )
133123 text = input ("Enter text: " )
134-
124+
135125 if option == "1" :
136126 print ("Encrypted:" , hc .encrypt (text ))
137127 elif option == "2" :
138128 print ("Decrypted:" , hc .decrypt (text ))
139129
140-
141130if __name__ == "__main__" :
142131 import doctest
143-
144132 doctest .testmod ()
145133 main ()
0 commit comments