41
41
import numpy
42
42
43
43
44
- def gcd (a , b ):
44
+ def gcd (a : int , b : int ) -> int :
45
+ """
46
+ >>> gcd(4, 8)
47
+ 4
48
+ >>> gcd(8, 4)
49
+ 4
50
+ >>> gcd(4, 7)
51
+ 1
52
+ >>> gcd(0, 10)
53
+ 10
54
+ """
45
55
if a == 0 :
46
56
return b
47
57
return gcd (b % a , a )
@@ -52,9 +62,6 @@ class HillCipher:
52
62
# This cipher takes alphanumerics into account
53
63
# i.e. a total of 36 characters
54
64
55
- replaceLetters = lambda self , letter : self .key_string .index (letter )
56
- replaceNumbers = lambda self , num : self .key_string [round (num )]
57
-
58
65
# take x and return x % len(key_string)
59
66
modulus = numpy .vectorize (lambda x : x % 36 )
60
67
@@ -69,7 +76,31 @@ def __init__(self, encrypt_key):
69
76
self .decrypt_key = None
70
77
self .break_key = encrypt_key .shape [0 ]
71
78
72
- def check_determinant (self ):
79
+ def replaceLetters (self , letter : str ) -> int :
80
+ """
81
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
82
+ >>> hill_cipher.replaceLetters('T')
83
+ 19
84
+ >>> hill_cipher.replaceLetters('0')
85
+ 26
86
+ """
87
+ return self .key_string .index (letter )
88
+
89
+ def replaceNumbers (self , num : int ) -> str :
90
+ """
91
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
92
+ >>> hill_cipher.replaceNumbers(19)
93
+ 'T'
94
+ >>> hill_cipher.replaceNumbers(26)
95
+ '0'
96
+ """
97
+ return self .key_string [round (num )]
98
+
99
+ def check_determinant (self ) -> None :
100
+ """
101
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
102
+ >>> hill_cipher.check_determinant()
103
+ """
73
104
det = round (numpy .linalg .det (self .encrypt_key ))
74
105
75
106
if det < 0 :
@@ -78,38 +109,54 @@ def check_determinant(self):
78
109
req_l = len (self .key_string )
79
110
if gcd (det , len (self .key_string )) != 1 :
80
111
raise ValueError (
81
- "discriminant modular {} of encryption key({}) is not co prime w.r.t {}.\n Try another key." .format (
82
- req_l , det , req_l
83
- )
112
+ f"determinant modular { req_l } of encryption key({ det } ) is not co prime w.r.t { req_l } .\n Try another key."
84
113
)
85
114
86
- def process_text (self , text ):
115
+ def process_text (self , text : str ) -> str :
116
+ """
117
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
118
+ >>> hill_cipher.process_text('Testing Hill Cipher')
119
+ 'TESTINGHILLCIPHERR'
120
+ """
87
121
text = list (text .upper ())
88
- text = [char for char in text if char in self .key_string ]
122
+ chars = [char for char in text if char in self .key_string ]
89
123
90
- last = text [- 1 ]
91
- while len (text ) % self .break_key != 0 :
92
- text .append (last )
124
+ last = chars [- 1 ]
125
+ while len (chars ) % self .break_key != 0 :
126
+ chars .append (last )
93
127
94
- return "" .join (text )
128
+ return "" .join (chars )
95
129
96
- def encrypt (self , text ):
130
+ def encrypt (self , text : str ) -> str :
131
+ """
132
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
133
+ >>> hill_cipher.encrypt('testing hill cipher')
134
+ 'WHXYJOLM9C6XT085LL'
135
+ """
97
136
text = self .process_text (text .upper ())
98
137
encrypted = ""
99
138
100
139
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
101
140
batch = text [i : i + self .break_key ]
102
- batch_vec = list ( map ( self .replaceLetters , batch ))
141
+ batch_vec = [ self .replaceLetters ( char ) for char in batch ]
103
142
batch_vec = numpy .matrix ([batch_vec ]).T
104
143
batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[
105
144
0
106
145
]
107
- encrypted_batch = "" .join (list (map (self .replaceNumbers , batch_encrypted )))
146
+ encrypted_batch = "" .join (
147
+ self .replaceNumbers (num ) for num in batch_encrypted
148
+ )
108
149
encrypted += encrypted_batch
109
150
110
151
return encrypted
111
152
112
153
def make_decrypt_key (self ):
154
+ """
155
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
156
+ >>> hill_cipher.make_decrypt_key()
157
+ matrix([[ 6., 25.],
158
+ [ 5., 26.]])
159
+ """
113
160
det = round (numpy .linalg .det (self .encrypt_key ))
114
161
115
162
if det < 0 :
@@ -128,19 +175,26 @@ def make_decrypt_key(self):
128
175
129
176
return self .toInt (self .modulus (inv_key ))
130
177
131
- def decrypt (self , text ):
178
+ def decrypt (self , text : str ) -> str :
179
+ """
180
+ >>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
181
+ >>> hill_cipher.decrypt('WHXYJOLM9C6XT085LL')
182
+ 'TESTINGHILLCIPHERR'
183
+ """
132
184
self .decrypt_key = self .make_decrypt_key ()
133
185
text = self .process_text (text .upper ())
134
186
decrypted = ""
135
187
136
188
for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
137
189
batch = text [i : i + self .break_key ]
138
- batch_vec = list ( map ( self .replaceLetters , batch ))
190
+ batch_vec = [ self .replaceLetters ( char ) for char in batch ]
139
191
batch_vec = numpy .matrix ([batch_vec ]).T
140
192
batch_decrypted = self .modulus (self .decrypt_key .dot (batch_vec )).T .tolist ()[
141
193
0
142
194
]
143
- decrypted_batch = "" .join (list (map (self .replaceNumbers , batch_decrypted )))
195
+ decrypted_batch = "" .join (
196
+ self .replaceNumbers (num ) for num in batch_decrypted
197
+ )
144
198
decrypted += decrypted_batch
145
199
146
200
return decrypted
@@ -176,4 +230,7 @@ def main():
176
230
177
231
178
232
if __name__ == "__main__" :
233
+ import doctest
234
+ doctest .testmod ()
235
+
179
236
main ()
0 commit comments