Skip to content

Commit a5cc16e

Browse files
authored
Update hill_cipher.py
1 parent 8185cc0 commit a5cc16e

File tree

1 file changed

+32
-27
lines changed

1 file changed

+32
-27
lines changed

ciphers/hill_cipher.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@
1818
https://www.youtube.com/watch?v=4RhLNDqcjpA
1919
"""
2020

21+
# Standard library imports
2122
import string
2223

24+
# Third-party imports
2325
import numpy as np
26+
27+
# Local application imports
2428
from 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:
348354
def 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("\nWould you like to encrypt or decrypt some text?")
370376
option = input("1. Encrypt\n2. Decrypt\nEnter choice (1/2): ")
371-
377+
372378
if option == "1":
373379
text = input("\nEnter text to encrypt: ")
374380
print("\nEncrypted text:")
@@ -383,21 +389,20 @@ def main() -> None:
383389

384390
if __name__ == "__main__":
385391
import doctest
386-
387392
doctest.testmod()
388-
393+
389394
print("\nRunning 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"\nOriginal 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

Comments
 (0)