19
19
"""
20
20
21
21
import string
22
-
23
22
import numpy as np
24
-
25
23
from maths .greatest_common_divisor import greatest_common_divisor
26
24
27
25
@@ -145,9 +143,10 @@ def check_determinant(self) -> None:
145
143
ValueError: determinant modular 36 of encryption key(0) is not co prime
146
144
w.r.t 36. Try another key.
147
145
"""
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
+
151
150
if det < 0 :
152
151
det = det % len (self .key_string )
153
152
@@ -158,7 +157,6 @@ def check_determinant(self) -> None:
158
157
f"w.r.t { req_l } .\n Try another key."
159
158
)
160
159
raise ValueError (msg )
161
-
162
160
def process_text (self , text : str ) -> str :
163
161
"""
164
162
Prepare text for encryption/decryption by:
@@ -185,17 +183,16 @@ def process_text(self, text: str) -> str:
185
183
'ABCC'
186
184
"""
187
185
chars = [char for char in text .upper () if char in self .key_string ]
188
-
186
+
189
187
# Handle empty input case
190
188
if not chars :
191
189
return ""
192
-
190
+
193
191
last = chars [- 1 ]
194
192
while len (chars ) % self .break_key != 0 :
195
193
chars .append (last )
196
-
194
+
197
195
return "" .join (chars )
198
-
199
196
def encrypt (self , text : str ) -> str :
200
197
"""
201
198
Encrypt plaintext using Hill Cipher.
@@ -223,21 +220,21 @@ def encrypt(self, text: str) -> str:
223
220
text = self .process_text (text .upper ())
224
221
if not text :
225
222
return ""
226
-
223
+
227
224
encrypted = ""
228
225
229
226
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
230
227
# Extract batch of characters
231
228
batch = text [i : i + self .break_key ]
232
-
229
+
233
230
# Convert to numerical vector
234
231
vec = [self .replace_letters (char ) for char in batch ]
235
232
batch_vec = np .array ([vec ]).T
236
-
233
+
237
234
# Matrix multiplication and mod 36
238
235
product = self .encrypt_key .dot (batch_vec )
239
236
batch_encrypted = self .modulus (product ).T .tolist ()[0 ]
240
-
237
+
241
238
# Convert back to characters
242
239
encrypted_batch = "" .join (
243
240
self .replace_digits (num ) for num in batch_encrypted
@@ -262,7 +259,7 @@ def make_decrypt_key(self) -> np.ndarray:
262
259
>>> cipher.make_decrypt_key()
263
260
array([[ 6, 25],
264
261
[ 5, 26]])
265
-
262
+
266
263
>>> key3x3 = np.array([[1,2,3],[4,5,6],[7,8,9]])
267
264
>>> cipher3 = HillCipher(key3x3)
268
265
>>> cipher3.make_decrypt_key() # Determinant 0 should be invalid
@@ -271,12 +268,13 @@ def make_decrypt_key(self) -> np.ndarray:
271
268
ValueError: determinant modular 36 of encryption key(0) is not co prime
272
269
w.r.t 36. Try another key.
273
270
"""
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
+
277
275
if det < 0 :
278
276
det = det % len (self .key_string )
279
-
277
+
280
278
det_inv : int | None = None
281
279
for i in range (len (self .key_string )):
282
280
if (det * i ) % len (self .key_string ) == 1 :
@@ -319,22 +317,22 @@ def decrypt(self, text: str) -> str:
319
317
text = self .process_text (text .upper ())
320
318
if not text :
321
319
return ""
322
-
320
+
323
321
decrypt_key = self .make_decrypt_key ()
324
322
decrypted = ""
325
323
326
324
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
327
325
# Extract batch of characters
328
326
batch = text [i : i + self .break_key ]
329
-
327
+
330
328
# Convert to numerical vector
331
329
vec = [self .replace_letters (char ) for char in batch ]
332
330
batch_vec = np .array ([vec ]).T
333
-
331
+
334
332
# Matrix multiplication and mod 36
335
333
product = decrypt_key .dot (batch_vec )
336
334
batch_decrypted = self .modulus (product ).T .tolist ()[0 ]
337
-
335
+
338
336
# Convert back to characters
339
337
decrypted_batch = "" .join (
340
338
self .replace_digits (num ) for num in batch_decrypted
@@ -344,10 +342,11 @@ def decrypt(self, text: str) -> str:
344
342
return decrypted
345
343
346
344
345
+
347
346
def main () -> None :
348
347
"""
349
348
Command-line interface for Hill Cipher operations.
350
-
349
+
351
350
Steps:
352
351
1. User inputs encryption key size
353
352
2. User inputs encryption key matrix rows
@@ -360,14 +359,14 @@ def main() -> None:
360
359
361
360
print ("Enter each row of the encryption key with space separated integers" )
362
361
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 ()]
364
363
hill_matrix .append (row )
365
364
366
365
hc = HillCipher (np .array (hill_matrix ))
367
366
368
367
print ("\n Would you like to encrypt or decrypt some text?" )
369
368
option = input ("1. Encrypt\n 2. Decrypt\n Enter choice (1/2): " )
370
-
369
+
371
370
if option == "1" :
372
371
text = input ("\n Enter text to encrypt: " )
373
372
print ("\n Encrypted text:" )
@@ -379,24 +378,22 @@ def main() -> None:
379
378
else :
380
379
print ("Invalid option selected" )
381
380
382
-
383
381
if __name__ == "__main__" :
384
382
import doctest
385
-
386
383
doctest .testmod ()
387
-
384
+
388
385
print ("\n Running sample tests..." )
389
386
key = np .array ([[2 , 5 ], [1 , 6 ]])
390
387
cipher = HillCipher (key )
391
-
388
+
392
389
# Test encryption/decryption round trip
393
390
plaintext = "HELLO123"
394
391
encrypted = cipher .encrypt (plaintext )
395
392
decrypted = cipher .decrypt (encrypted )
396
-
393
+
397
394
print (f"\n Original text: { plaintext } " )
398
395
print (f"Encrypted text: { encrypted } " )
399
396
print (f"Decrypted text: { decrypted } " )
400
-
397
+
401
398
# Run CLI interface
402
399
main ()
0 commit comments