@@ -121,7 +121,6 @@ def replace_digits(self, num: int) -> str:
121
121
"""
122
122
return self .key_string [round (num )]
123
123
124
-
125
124
def check_determinant (self ) -> None :
126
125
"""
127
126
Validate encryption key determinant.
@@ -152,7 +151,7 @@ def check_determinant(self) -> None:
152
151
det_value = np .linalg .det (self .encrypt_key )
153
152
# Only round if necessary
154
153
det = int (det_value ) if det_value .is_integer () else int (round (det_value ))
155
-
154
+
156
155
if det < 0 :
157
156
det = det % len (self .key_string )
158
157
@@ -164,7 +163,6 @@ def check_determinant(self) -> None:
164
163
)
165
164
raise ValueError (msg )
166
165
167
-
168
166
def process_text (self , text : str ) -> str :
169
167
"""
170
168
Prepare text for encryption/decryption by:
@@ -191,15 +189,15 @@ def process_text(self, text: str) -> str:
191
189
'ABCC'
192
190
"""
193
191
chars = [char for char in text .upper () if char in self .key_string ]
194
-
192
+
195
193
# Handle empty input case
196
194
if not chars :
197
195
return ""
198
-
196
+
199
197
last = chars [- 1 ]
200
198
while len (chars ) % self .break_key != 0 :
201
199
chars .append (last )
202
-
200
+
203
201
return "" .join (chars )
204
202
205
203
def encrypt (self , text : str ) -> str :
@@ -229,21 +227,21 @@ def encrypt(self, text: str) -> str:
229
227
text = self .process_text (text .upper ())
230
228
if not text :
231
229
return ""
232
-
230
+
233
231
encrypted = ""
234
232
235
233
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
236
234
# Extract batch of characters
237
235
batch = text [i : i + self .break_key ]
238
-
236
+
239
237
# Convert to numerical vector
240
238
vec = [self .replace_letters (char ) for char in batch ]
241
239
batch_vec = np .array ([vec ]).T
242
-
240
+
243
241
# Matrix multiplication and mod 36
244
242
product = self .encrypt_key .dot (batch_vec )
245
243
batch_encrypted = self .modulus (product ).T .tolist ()[0 ]
246
-
244
+
247
245
# Convert back to characters
248
246
encrypted_batch = "" .join (
249
247
self .replace_digits (num ) for num in batch_encrypted
@@ -268,7 +266,7 @@ def make_decrypt_key(self) -> np.ndarray:
268
266
>>> cipher.make_decrypt_key()
269
267
array([[ 6, 25],
270
268
[ 5, 26]])
271
-
269
+
272
270
>>> key3x3 = np.array([[1,2,3],[4,5,6],[7,8,9]])
273
271
>>> cipher3 = HillCipher(key3x3)
274
272
>>> cipher3.make_decrypt_key() # Determinant 0 should be invalid
@@ -280,10 +278,10 @@ def make_decrypt_key(self) -> np.ndarray:
280
278
det_value = np .linalg .det (self .encrypt_key )
281
279
# Only round if necessary
282
280
det = int (det_value ) if det_value .is_integer () else int (round (det_value ))
283
-
281
+
284
282
if det < 0 :
285
283
det = det % len (self .key_string )
286
-
284
+
287
285
det_inv : int | None = None
288
286
for i in range (len (self .key_string )):
289
287
if (det * i ) % len (self .key_string ) == 1 :
@@ -326,22 +324,22 @@ def decrypt(self, text: str) -> str:
326
324
text = self .process_text (text .upper ())
327
325
if not text :
328
326
return ""
329
-
327
+
330
328
decrypt_key = self .make_decrypt_key ()
331
329
decrypted = ""
332
330
333
331
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
334
332
# Extract batch of characters
335
333
batch = text [i : i + self .break_key ]
336
-
334
+
337
335
# Convert to numerical vector
338
336
vec = [self .replace_letters (char ) for char in batch ]
339
337
batch_vec = np .array ([vec ]).T
340
-
338
+
341
339
# Matrix multiplication and mod 36
342
340
product = decrypt_key .dot (batch_vec )
343
341
batch_decrypted = self .modulus (product ).T .tolist ()[0 ]
344
-
342
+
345
343
# Convert back to characters
346
344
decrypted_batch = "" .join (
347
345
self .replace_digits (num ) for num in batch_decrypted
@@ -354,7 +352,7 @@ def decrypt(self, text: str) -> str:
354
352
def main () -> None :
355
353
"""
356
354
Command-line interface for Hill Cipher operations.
357
-
355
+
358
356
Steps:
359
357
1. User inputs encryption key size
360
358
2. User inputs encryption key matrix rows
@@ -367,14 +365,14 @@ def main() -> None:
367
365
368
366
print ("Enter each row of the encryption key with space separated integers" )
369
367
for i in range (n ):
370
- row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
368
+ row = [int (x ) for x in input (f"Row { i + 1 } : " ).split ()]
371
369
hill_matrix .append (row )
372
370
373
371
hc = HillCipher (np .array (hill_matrix ))
374
372
375
373
print ("\n Would you like to encrypt or decrypt some text?" )
376
374
option = input ("1. Encrypt\n 2. Decrypt\n Enter choice (1/2): " )
377
-
375
+
378
376
if option == "1" :
379
377
text = input ("\n Enter text to encrypt: " )
380
378
print ("\n Encrypted text:" )
@@ -389,20 +387,21 @@ def main() -> None:
389
387
390
388
if __name__ == "__main__" :
391
389
import doctest
390
+
392
391
doctest .testmod ()
393
-
392
+
394
393
print ("\n Running sample tests..." )
395
394
key = np .array ([[2 , 5 ], [1 , 6 ]])
396
395
cipher = HillCipher (key )
397
-
396
+
398
397
# Test encryption/decryption round trip
399
398
plaintext = "HELLO123"
400
399
encrypted = cipher .encrypt (plaintext )
401
400
decrypted = cipher .decrypt (encrypted )
402
-
401
+
403
402
print (f"\n Original text: { plaintext } " )
404
403
print (f"Encrypted text: { encrypted } " )
405
404
print (f"Decrypted text: { decrypted } " )
406
-
405
+
407
406
# Run CLI interface
408
407
main ()
0 commit comments