1919"""
2020
2121import string
22-
2322import numpy as np
24-
2523from maths .greatest_common_divisor import greatest_common_divisor
2624
2725
@@ -145,9 +143,10 @@ def check_determinant(self) -> None:
145143 ValueError: determinant modular 36 of encryption key(0) is not co prime
146144 w.r.t 36. Try another key.
147145 """
148- det = round (np .linalg .det (self .encrypt_key ))
149- det = int (det ) # Convert to int after rounding float
150-
146+ # Optimized determinant calculation to avoid redundant rounding
147+ det_value = np .linalg .det (self .encrypt_key )
148+ det = int (round (det_value )) if not det_value .is_integer () else int (det_value )
149+
151150 if det < 0 :
152151 det = det % len (self .key_string )
153152
@@ -158,7 +157,6 @@ def check_determinant(self) -> None:
158157 f"w.r.t { req_l } .\n Try another key."
159158 )
160159 raise ValueError (msg )
161-
162160 def process_text (self , text : str ) -> str :
163161 """
164162 Prepare text for encryption/decryption by:
@@ -185,17 +183,16 @@ def process_text(self, text: str) -> str:
185183 'ABCC'
186184 """
187185 chars = [char for char in text .upper () if char in self .key_string ]
188-
186+
189187 # Handle empty input case
190188 if not chars :
191189 return ""
192-
190+
193191 last = chars [- 1 ]
194192 while len (chars ) % self .break_key != 0 :
195193 chars .append (last )
196-
194+
197195 return "" .join (chars )
198-
199196 def encrypt (self , text : str ) -> str :
200197 """
201198 Encrypt plaintext using Hill Cipher.
@@ -223,21 +220,21 @@ def encrypt(self, text: str) -> str:
223220 text = self .process_text (text .upper ())
224221 if not text :
225222 return ""
226-
223+
227224 encrypted = ""
228225
229226 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
230227 # Extract batch of characters
231228 batch = text [i : i + self .break_key ]
232-
229+
233230 # Convert to numerical vector
234231 vec = [self .replace_letters (char ) for char in batch ]
235232 batch_vec = np .array ([vec ]).T
236-
233+
237234 # Matrix multiplication and mod 36
238235 product = self .encrypt_key .dot (batch_vec )
239236 batch_encrypted = self .modulus (product ).T .tolist ()[0 ]
240-
237+
241238 # Convert back to characters
242239 encrypted_batch = "" .join (
243240 self .replace_digits (num ) for num in batch_encrypted
@@ -262,7 +259,7 @@ def make_decrypt_key(self) -> np.ndarray:
262259 >>> cipher.make_decrypt_key()
263260 array([[ 6, 25],
264261 [ 5, 26]])
265-
262+
266263 >>> key3x3 = np.array([[1,2,3],[4,5,6],[7,8,9]])
267264 >>> cipher3 = HillCipher(key3x3)
268265 >>> cipher3.make_decrypt_key() # Determinant 0 should be invalid
@@ -271,12 +268,13 @@ def make_decrypt_key(self) -> np.ndarray:
271268 ValueError: determinant modular 36 of encryption key(0) is not co prime
272269 w.r.t 36. Try another key.
273270 """
274- det = round (np .linalg .det (self .encrypt_key ))
275- det = int (det ) # Convert to int after rounding float
276-
271+ # Optimized determinant calculation to avoid redundant rounding
272+ det_value = np .linalg .det (self .encrypt_key )
273+ det = int (round (det_value )) if not det_value .is_integer () else int (det_value )
274+
277275 if det < 0 :
278276 det = det % len (self .key_string )
279-
277+
280278 det_inv : int | None = None
281279 for i in range (len (self .key_string )):
282280 if (det * i ) % len (self .key_string ) == 1 :
@@ -319,22 +317,22 @@ def decrypt(self, text: str) -> str:
319317 text = self .process_text (text .upper ())
320318 if not text :
321319 return ""
322-
320+
323321 decrypt_key = self .make_decrypt_key ()
324322 decrypted = ""
325323
326324 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
327325 # Extract batch of characters
328326 batch = text [i : i + self .break_key ]
329-
327+
330328 # Convert to numerical vector
331329 vec = [self .replace_letters (char ) for char in batch ]
332330 batch_vec = np .array ([vec ]).T
333-
331+
334332 # Matrix multiplication and mod 36
335333 product = decrypt_key .dot (batch_vec )
336334 batch_decrypted = self .modulus (product ).T .tolist ()[0 ]
337-
335+
338336 # Convert back to characters
339337 decrypted_batch = "" .join (
340338 self .replace_digits (num ) for num in batch_decrypted
@@ -344,10 +342,11 @@ def decrypt(self, text: str) -> str:
344342 return decrypted
345343
346344
345+
347346def main () -> None :
348347 """
349348 Command-line interface for Hill Cipher operations.
350-
349+
351350 Steps:
352351 1. User inputs encryption key size
353352 2. User inputs encryption key matrix rows
@@ -360,14 +359,14 @@ def main() -> None:
360359
361360 print ("Enter each row of the encryption key with space separated integers" )
362361 for i in range (n ):
363- row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
362+ row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
364363 hill_matrix .append (row )
365364
366365 hc = HillCipher (np .array (hill_matrix ))
367366
368367 print ("\n Would you like to encrypt or decrypt some text?" )
369368 option = input ("1. Encrypt\n 2. Decrypt\n Enter choice (1/2): " )
370-
369+
371370 if option == "1" :
372371 text = input ("\n Enter text to encrypt: " )
373372 print ("\n Encrypted text:" )
@@ -379,24 +378,22 @@ def main() -> None:
379378 else :
380379 print ("Invalid option selected" )
381380
382-
383381if __name__ == "__main__" :
384382 import doctest
385-
386383 doctest .testmod ()
387-
384+
388385 print ("\n Running sample tests..." )
389386 key = np .array ([[2 , 5 ], [1 , 6 ]])
390387 cipher = HillCipher (key )
391-
388+
392389 # Test encryption/decryption round trip
393390 plaintext = "HELLO123"
394391 encrypted = cipher .encrypt (plaintext )
395392 decrypted = cipher .decrypt (encrypted )
396-
393+
397394 print (f"\n Original text: { plaintext } " )
398395 print (f"Encrypted text: { encrypted } " )
399396 print (f"Decrypted text: { decrypted } " )
400-
397+
401398 # Run CLI interface
402399 main ()
0 commit comments