Skip to content

Commit e60ccc9

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent c8755d6 commit e60ccc9

1 file changed

Lines changed: 20 additions & 11 deletions

File tree

ciphers/hill_cipher.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
https://www.youtube.com/watch?v=kfmNeskzs2o
3434
https://www.youtube.com/watch?v=4RhLNDqcjpA
3535
"""
36+
3637
import string
3738
import numpy as np
3839
from maths.greatest_common_divisor import greatest_common_divisor
3940

41+
4042
class HillCipher:
4143
key_string = string.ascii_uppercase + string.digits # 36 alphanumeric chars
4244
modulus = np.vectorize(lambda x: x % 36) # Mod 36 operation
@@ -60,15 +62,15 @@ def check_determinant(self) -> None:
6062
det = round(np.linalg.det(self.encrypt_key))
6163
if det < 0:
6264
det %= len(self.key_string)
63-
65+
6466
error_msg = f"Det {det} not coprime with 36. Try another key."
6567
if greatest_common_divisor(det, len(self.key_string)) != 1:
6668
raise ValueError(error_msg)
6769

6870
def process_text(self, text: str) -> str:
6971
"""Convert to uppercase, remove invalid chars, pad to multiple of break_key"""
7072
chars = [c for c in text.upper() if c in self.key_string]
71-
last = chars[-1] if chars else 'A'
73+
last = chars[-1] if chars else "A"
7274
while len(chars) % self.break_key != 0:
7375
chars.append(last)
7476
return "".join(chars)
@@ -78,10 +80,12 @@ def encrypt(self, text: str) -> str:
7880
text = self.process_text(text.upper())
7981
encrypted = ""
8082
for i in range(0, len(text), self.break_key):
81-
batch = text[i:i+self.break_key]
83+
batch = text[i : i + self.break_key]
8284
vec = [self.replace_letters(c) for c in batch]
8385
batch_vec = np.array([vec]).T
84-
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[0]
86+
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[
87+
0
88+
]
8589
encrypted += "".join(self.replace_digits(round(n)) for n in batch_encrypted)
8690
return encrypted
8791

@@ -90,12 +94,14 @@ def make_decrypt_key(self) -> np.ndarray:
9094
det = round(np.linalg.det(self.encrypt_key))
9195
if det < 0:
9296
det %= len(self.key_string)
93-
97+
9498
# Find modular inverse of det
9599
det_inv = next(i for i in range(36) if (det * i) % 36 == 1)
96-
100+
97101
# Calculate inverse key
98-
inv_key = det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key)
102+
inv_key = (
103+
det_inv * np.linalg.det(self.encrypt_key) * np.linalg.inv(self.encrypt_key)
104+
)
99105
return self.to_int(self.modulus(inv_key))
100106

101107
def decrypt(self, text: str) -> str:
@@ -104,30 +110,33 @@ def decrypt(self, text: str) -> str:
104110
text = self.process_text(text.upper())
105111
decrypted = ""
106112
for i in range(0, len(text), self.break_key):
107-
batch = text[i:i+self.break_key]
113+
batch = text[i : i + self.break_key]
108114
vec = [self.replace_letters(c) for c in batch]
109115
batch_vec = np.array([vec]).T
110116
batch_decrypted = self.modulus(decrypt_key.dot(batch_vec)).T.tolist()[0]
111117
decrypted += "".join(self.replace_digits(round(n)) for n in batch_decrypted)
112118
return decrypted
113119

120+
114121
def main() -> None:
115122
"""CLI for Hill Cipher"""
116123
n = int(input("Enter key order: "))
117124
print(f"Enter {n} rows of space-separated integers:")
118125
matrix = [list(map(int, input().split())) for _ in range(n)]
119-
126+
120127
hc = HillCipher(np.array(matrix))
121-
128+
122129
option = input("1. Encrypt\n2. Decrypt\nChoose: ")
123130
text = input("Enter text: ")
124-
131+
125132
if option == "1":
126133
print("Encrypted:", hc.encrypt(text))
127134
elif option == "2":
128135
print("Decrypted:", hc.decrypt(text))
129136

137+
130138
if __name__ == "__main__":
131139
import doctest
140+
132141
doctest.testmod()
133142
main()

0 commit comments

Comments
 (0)