4141import numpy
4242
4343
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+ """
4555 if a == 0 :
4656 return b
4757 return gcd (b % a , a )
@@ -52,9 +62,6 @@ class HillCipher:
5262 # This cipher takes alphanumerics into account
5363 # i.e. a total of 36 characters
5464
55- replaceLetters = lambda self , letter : self .key_string .index (letter )
56- replaceNumbers = lambda self , num : self .key_string [round (num )]
57-
5865 # take x and return x % len(key_string)
5966 modulus = numpy .vectorize (lambda x : x % 36 )
6067
@@ -69,7 +76,31 @@ def __init__(self, encrypt_key):
6976 self .decrypt_key = None
7077 self .break_key = encrypt_key .shape [0 ]
7178
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+ """
73104 det = round (numpy .linalg .det (self .encrypt_key ))
74105
75106 if det < 0 :
@@ -78,38 +109,54 @@ def check_determinant(self):
78109 req_l = len (self .key_string )
79110 if gcd (det , len (self .key_string )) != 1 :
80111 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."
84113 )
85114
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+ """
87121 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 ]
89123
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 )
93127
94- return "" .join (text )
128+ return "" .join (chars )
95129
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+ """
97136 text = self .process_text (text .upper ())
98137 encrypted = ""
99138
100139 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
101140 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 ]
103142 batch_vec = numpy .matrix ([batch_vec ]).T
104143 batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[
105144 0
106145 ]
107- encrypted_batch = "" .join (list (map (self .replaceNumbers , batch_encrypted )))
146+ encrypted_batch = "" .join (
147+ self .replaceNumbers (num ) for num in batch_encrypted
148+ )
108149 encrypted += encrypted_batch
109150
110151 return encrypted
111152
112153 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+ """
113160 det = round (numpy .linalg .det (self .encrypt_key ))
114161
115162 if det < 0 :
@@ -128,19 +175,26 @@ def make_decrypt_key(self):
128175
129176 return self .toInt (self .modulus (inv_key ))
130177
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+ """
132184 self .decrypt_key = self .make_decrypt_key ()
133185 text = self .process_text (text .upper ())
134186 decrypted = ""
135187
136188 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
137189 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 ]
139191 batch_vec = numpy .matrix ([batch_vec ]).T
140192 batch_decrypted = self .modulus (self .decrypt_key .dot (batch_vec )).T .tolist ()[
141193 0
142194 ]
143- decrypted_batch = "" .join (list (map (self .replaceNumbers , batch_decrypted )))
195+ decrypted_batch = "" .join (
196+ self .replaceNumbers (num ) for num in batch_decrypted
197+ )
144198 decrypted += decrypted_batch
145199
146200 return decrypted
@@ -176,4 +230,7 @@ def main():
176230
177231
178232if __name__ == "__main__" :
233+ import doctest
234+ doctest .testmod ()
235+
179236 main ()
0 commit comments