1818 https://www.youtube.com/watch?v=4RhLNDqcjpA
1919"""
2020
21+ # Standard library imports
2122import string
2223
24+ # Third-party imports
2325import numpy as np
26+
27+ # Local application imports
2428from maths .greatest_common_divisor import greatest_common_divisor
2529
2630
@@ -117,6 +121,7 @@ def replace_digits(self, num: int) -> str:
117121 """
118122 return self .key_string [round (num )]
119123
124+
120125 def check_determinant (self ) -> None :
121126 """
122127 Validate encryption key determinant.
@@ -144,10 +149,10 @@ def check_determinant(self) -> None:
144149 ValueError: determinant modular 36 of encryption key(0) is not co prime
145150 w.r.t 36. Try another key.
146151 """
147- # Optimized determinant calculation to avoid redundant rounding
148152 det_value = np .linalg .det (self .encrypt_key )
149- det = int (round (det_value )) if not det_value .is_integer () else int (det_value )
150-
153+ # Only round if necessary
154+ det = int (det_value ) if det_value .is_integer () else int (round (det_value ))
155+
151156 if det < 0 :
152157 det = det % len (self .key_string )
153158
@@ -159,6 +164,7 @@ def check_determinant(self) -> None:
159164 )
160165 raise ValueError (msg )
161166
167+
162168 def process_text (self , text : str ) -> str :
163169 """
164170 Prepare text for encryption/decryption by:
@@ -185,15 +191,15 @@ def process_text(self, text: str) -> str:
185191 'ABCC'
186192 """
187193 chars = [char for char in text .upper () if char in self .key_string ]
188-
194+
189195 # Handle empty input case
190196 if not chars :
191197 return ""
192-
198+
193199 last = chars [- 1 ]
194200 while len (chars ) % self .break_key != 0 :
195201 chars .append (last )
196-
202+
197203 return "" .join (chars )
198204
199205 def encrypt (self , text : str ) -> str :
@@ -223,21 +229,21 @@ def encrypt(self, text: str) -> str:
223229 text = self .process_text (text .upper ())
224230 if not text :
225231 return ""
226-
232+
227233 encrypted = ""
228234
229235 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
230236 # Extract batch of characters
231237 batch = text [i : i + self .break_key ]
232-
238+
233239 # Convert to numerical vector
234240 vec = [self .replace_letters (char ) for char in batch ]
235241 batch_vec = np .array ([vec ]).T
236-
242+
237243 # Matrix multiplication and mod 36
238244 product = self .encrypt_key .dot (batch_vec )
239245 batch_encrypted = self .modulus (product ).T .tolist ()[0 ]
240-
246+
241247 # Convert back to characters
242248 encrypted_batch = "" .join (
243249 self .replace_digits (num ) for num in batch_encrypted
@@ -262,7 +268,7 @@ def make_decrypt_key(self) -> np.ndarray:
262268 >>> cipher.make_decrypt_key()
263269 array([[ 6, 25],
264270 [ 5, 26]])
265-
271+
266272 >>> key3x3 = np.array([[1,2,3],[4,5,6],[7,8,9]])
267273 >>> cipher3 = HillCipher(key3x3)
268274 >>> cipher3.make_decrypt_key() # Determinant 0 should be invalid
@@ -271,13 +277,13 @@ def make_decrypt_key(self) -> np.ndarray:
271277 ValueError: determinant modular 36 of encryption key(0) is not co prime
272278 w.r.t 36. Try another key.
273279 """
274- # Optimized determinant calculation to avoid redundant rounding
275280 det_value = np .linalg .det (self .encrypt_key )
276- det = int (round (det_value )) if not det_value .is_integer () else int (det_value )
277-
281+ # Only round if necessary
282+ det = int (det_value ) if det_value .is_integer () else int (round (det_value ))
283+
278284 if det < 0 :
279285 det = det % len (self .key_string )
280-
286+
281287 det_inv : int | None = None
282288 for i in range (len (self .key_string )):
283289 if (det * i ) % len (self .key_string ) == 1 :
@@ -320,22 +326,22 @@ def decrypt(self, text: str) -> str:
320326 text = self .process_text (text .upper ())
321327 if not text :
322328 return ""
323-
329+
324330 decrypt_key = self .make_decrypt_key ()
325331 decrypted = ""
326332
327333 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
328334 # Extract batch of characters
329335 batch = text [i : i + self .break_key ]
330-
336+
331337 # Convert to numerical vector
332338 vec = [self .replace_letters (char ) for char in batch ]
333339 batch_vec = np .array ([vec ]).T
334-
340+
335341 # Matrix multiplication and mod 36
336342 product = decrypt_key .dot (batch_vec )
337343 batch_decrypted = self .modulus (product ).T .tolist ()[0 ]
338-
344+
339345 # Convert back to characters
340346 decrypted_batch = "" .join (
341347 self .replace_digits (num ) for num in batch_decrypted
@@ -348,7 +354,7 @@ def decrypt(self, text: str) -> str:
348354def main () -> None :
349355 """
350356 Command-line interface for Hill Cipher operations.
351-
357+
352358 Steps:
353359 1. User inputs encryption key size
354360 2. User inputs encryption key matrix rows
@@ -361,14 +367,14 @@ def main() -> None:
361367
362368 print ("Enter each row of the encryption key with space separated integers" )
363369 for i in range (n ):
364- row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
370+ row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
365371 hill_matrix .append (row )
366372
367373 hc = HillCipher (np .array (hill_matrix ))
368374
369375 print ("\n Would you like to encrypt or decrypt some text?" )
370376 option = input ("1. Encrypt\n 2. Decrypt\n Enter choice (1/2): " )
371-
377+
372378 if option == "1" :
373379 text = input ("\n Enter text to encrypt: " )
374380 print ("\n Encrypted text:" )
@@ -383,21 +389,20 @@ def main() -> None:
383389
384390if __name__ == "__main__" :
385391 import doctest
386-
387392 doctest .testmod ()
388-
393+
389394 print ("\n Running sample tests..." )
390395 key = np .array ([[2 , 5 ], [1 , 6 ]])
391396 cipher = HillCipher (key )
392-
397+
393398 # Test encryption/decryption round trip
394399 plaintext = "HELLO123"
395400 encrypted = cipher .encrypt (plaintext )
396401 decrypted = cipher .decrypt (encrypted )
397-
402+
398403 print (f"\n Original text: { plaintext } " )
399404 print (f"Encrypted text: { encrypted } " )
400405 print (f"Decrypted text: { decrypted } " )
401-
406+
402407 # Run CLI interface
403408 main ()
0 commit comments