Skip to content

Commit 6e83656

Browse files
authored
Update hill_cipher.py
1 parent d971725 commit 6e83656

File tree

1 file changed

+29
-32
lines changed

1 file changed

+29
-32
lines changed

ciphers/hill_cipher.py

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
"""
2020

2121
import string
22-
2322
import numpy as np
24-
2523
from 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}.\nTry 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+
347346
def 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("\nWould you like to encrypt or decrypt some text?")
369368
option = input("1. Encrypt\n2. Decrypt\nEnter choice (1/2): ")
370-
369+
371370
if option == "1":
372371
text = input("\nEnter text to encrypt: ")
373372
print("\nEncrypted text:")
@@ -379,24 +378,22 @@ def main() -> None:
379378
else:
380379
print("Invalid option selected")
381380

382-
383381
if __name__ == "__main__":
384382
import doctest
385-
386383
doctest.testmod()
387-
384+
388385
print("\nRunning 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"\nOriginal 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

Comments
 (0)