Skip to content

Commit a0bbc67

Browse files
authored
hill_cipher.py: gcd() -> greatest_common_divisor()
1 parent aa120ce commit a0bbc67

File tree

1 file changed

+32
-42
lines changed

1 file changed

+32
-42
lines changed

ciphers/hill_cipher.py

+32-42
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
"""
22
33
Hill Cipher:
4-
The below defined class 'HillCipher' implements the Hill Cipher algorithm.
5-
The Hill Cipher is an algorithm that implements modern linear algebra techniques
6-
In this algorithm, you have an encryption key matrix. This is what will be used
7-
in encoding and decoding your text.
4+
The 'HillCipher' class below implements the Hill Cipher algorithm which uses
5+
modern linear algebra techniques to encode and decode text using an encryption
6+
key matrix.
87
98
Algorithm:
109
Let the order of the encryption key be N (as it is a square matrix).
@@ -24,12 +23,11 @@
2423
The determinant of the encryption key matrix must be relatively prime w.r.t 36.
2524
2625
Note:
27-
The algorithm implemented in this code considers only alphanumerics in the text.
28-
If the length of the text to be encrypted is not a multiple of the
29-
break key(the length of one batch of letters),the last character of the text
30-
is added to the text until the length of the text reaches a multiple of
31-
the break_key. So the text after decrypting might be a little different than
32-
the original text.
26+
This implementation only considers alphanumerics in the text. If the length of
27+
the text to be encrypted is not a multiple of the break key(the length of one
28+
batch of letters),the last character of the text is added to the text until the
29+
length of the text reaches a multiple of the break_key. So the text after
30+
decrypting might be a little different than the original text.
3331
3432
References:
3533
https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf
@@ -38,34 +36,33 @@
3836
3937
"""
4038

39+
from string import ascii_upper
4140
import numpy
4241

4342

44-
def gcd(a: int, b: int) -> int:
43+
def greatest_common_divisor(a: int, b: int) -> int:
4544
"""
46-
>>> gcd(4, 8)
45+
>>> greatest_common_divisor(4, 8)
4746
4
48-
>>> gcd(8, 4)
47+
>>> greatest_common_divisor(8, 4)
4948
4
50-
>>> gcd(4, 7)
49+
>>> greatest_common_divisor(4, 7)
5150
1
52-
>>> gcd(0, 10)
51+
>>> greatest_common_divisor(0, 10)
5352
10
5453
"""
55-
if a == 0:
56-
return b
57-
return gcd(b % a, a)
54+
return b if a == 0 else greatest_common_divisor(b % a, a)
5855

5956

6057
class HillCipher:
61-
key_string = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
58+
key_string = string.ascii_uppercase + string.digits
6259
# This cipher takes alphanumerics into account
6360
# i.e. a total of 36 characters
6461

6562
# take x and return x % len(key_string)
6663
modulus = numpy.vectorize(lambda x: x % 36)
6764

68-
toInt = numpy.vectorize(lambda x: round(x))
65+
to_int = numpy.vectorize(lambda x: round(x))
6966

7067
def __init__(self, encrypt_key):
7168
"""
@@ -76,22 +73,22 @@ def __init__(self, encrypt_key):
7673
self.decrypt_key = None
7774
self.break_key = encrypt_key.shape[0]
7875

79-
def replaceLetters(self, letter: str) -> int:
76+
def replace_letters(self, letter: str) -> int:
8077
"""
8178
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
82-
>>> hill_cipher.replaceLetters('T')
79+
>>> hill_cipher.replace_letters('T')
8380
19
84-
>>> hill_cipher.replaceLetters('0')
81+
>>> hill_cipher.replace_letters('0')
8582
26
8683
"""
8784
return self.key_string.index(letter)
8885

89-
def replaceNumbers(self, num: int) -> str:
86+
def replace_digits(self, num: int) -> str:
9087
"""
9188
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
92-
>>> hill_cipher.replaceNumbers(19)
89+
>>> hill_cipher.replace_digits(19)
9390
'T'
94-
>>> hill_cipher.replaceNumbers(26)
91+
>>> hill_cipher.replace_digits(26)
9592
'0'
9693
"""
9794
return self.key_string[round(num)]
@@ -107,7 +104,7 @@ def check_determinant(self) -> None:
107104
det = det % len(self.key_string)
108105

109106
req_l = len(self.key_string)
110-
if gcd(det, len(self.key_string)) != 1:
107+
if greatest_common_divisor(det, len(self.key_string)) != 1:
111108
raise ValueError(
112109
f"determinant modular {req_l} of encryption key({det}) is not co prime w.r.t {req_l}.\nTry another key."
113110
)
@@ -118,8 +115,7 @@ def process_text(self, text: str) -> str:
118115
>>> hill_cipher.process_text('Testing Hill Cipher')
119116
'TESTINGHILLCIPHERR'
120117
"""
121-
text = list(text.upper())
122-
chars = [char for char in text if char in self.key_string]
118+
chars = [char for char in text.upper() if char in self.key_string]
123119

124120
last = chars[-1]
125121
while len(chars) % self.break_key != 0:
@@ -138,13 +134,13 @@ def encrypt(self, text: str) -> str:
138134

139135
for i in range(0, len(text) - self.break_key + 1, self.break_key):
140136
batch = text[i : i + self.break_key]
141-
batch_vec = [self.replaceLetters(char) for char in batch]
137+
batch_vec = [self.replace_letters(char) for char in batch]
142138
batch_vec = numpy.matrix([batch_vec]).T
143139
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[
144140
0
145141
]
146142
encrypted_batch = "".join(
147-
self.replaceNumbers(num) for num in batch_encrypted
143+
self.replace_digits(num) for num in batch_encrypted
148144
)
149145
encrypted += encrypted_batch
150146

@@ -173,7 +169,7 @@ def make_decrypt_key(self):
173169
* numpy.linalg.inv(self.encrypt_key)
174170
)
175171

176-
return self.toInt(self.modulus(inv_key))
172+
return self.to_int(self.modulus(inv_key))
177173

178174
def decrypt(self, text: str) -> str:
179175
"""
@@ -187,13 +183,13 @@ def decrypt(self, text: str) -> str:
187183

188184
for i in range(0, len(text) - self.break_key + 1, self.break_key):
189185
batch = text[i : i + self.break_key]
190-
batch_vec = [self.replaceLetters(char) for char in batch]
186+
batch_vec = [self.replace_letters(char) for char in batch]
191187
batch_vec = numpy.matrix([batch_vec]).T
192188
batch_decrypted = self.modulus(self.decrypt_key.dot(batch_vec)).T.tolist()[
193189
0
194190
]
195191
decrypted_batch = "".join(
196-
self.replaceNumbers(num) for num in batch_decrypted
192+
self.replace_digits(num) for num in batch_decrypted
197193
)
198194
decrypted += decrypted_batch
199195

@@ -206,19 +202,13 @@ def main():
206202

207203
print("Enter each row of the encryption key with space separated integers")
208204
for i in range(N):
209-
row = list(map(int, input().split()))
205+
row = [int(x) for x in input().split()]
210206
hill_matrix.append(row)
211207

212208
hc = HillCipher(numpy.matrix(hill_matrix))
213209

214210
print("Would you like to encrypt or decrypt some text? (1 or 2)")
215-
option = input(
216-
"""
217-
1. Encrypt
218-
2. Decrypt
219-
"""
220-
)
221-
211+
option = input("\n1. Encrypt\n2. Decrypt\n")
222212
if option == "1":
223213
text_e = input("What text would you like to encrypt?: ")
224214
print("Your encrypted text is:")

0 commit comments

Comments
 (0)