11"""
2-
32Hill Cipher:
43The 'HillCipher' class below implements the Hill Cipher algorithm which uses
54modern linear algebra techniques to encode and decode text using an encryption
3332https://apprendre-en-ligne.net/crypto/hill/Hillciph.pdf
3433https://www.youtube.com/watch?v=kfmNeskzs2o
3534https://www.youtube.com/watch?v=4RhLNDqcjpA
36-
3735"""
38-
3936import string
40-
4137import numpy as np
42-
4338from maths .greatest_common_divisor import greatest_common_divisor
4439
4540
@@ -79,7 +74,8 @@ def replace_digits(self, num: int) -> str:
7974 >>> hill_cipher.replace_digits(26)
8075 '0'
8176 """
82- return self .key_string [round (num )]
77+ # Directly use integer index without rounding
78+ return self .key_string [num ]
8379
8480 def check_determinant (self ) -> None :
8581 """
@@ -107,8 +103,10 @@ def process_text(self, text: str) -> str:
107103 >>> hill_cipher.process_text('hello')
108104 'HELLOO'
109105 """
106+ # Filter valid characters and convert to uppercase
110107 chars = [char for char in text .upper () if char in self .key_string ]
111108
109+ # Pad with last character to make length multiple of break_key
112110 last = chars [- 1 ]
113111 while len (chars ) % self .break_key != 0 :
114112 chars .append (last )
@@ -123,40 +121,49 @@ def encrypt(self, text: str) -> str:
123121 >>> hill_cipher.encrypt('hello')
124122 '85FF00'
125123 """
124+ # Preprocess text and initialize encrypted string
126125 text = self .process_text (text .upper ())
127126 encrypted = ""
128127
128+ # Process text in batches of size break_key
129129 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
130130 batch = text [i : i + self .break_key ]
131+ # Convert characters to numerical values
131132 vec = [self .replace_letters (char ) for char in batch ]
132133 batch_vec = np .array ([vec ]).T
134+
135+ # Matrix multiplication with encryption key
133136 batch_encrypted = self .modulus (self .encrypt_key .dot (batch_vec )).T .tolist ()[
134137 0
135138 ]
139+ # Convert numerical results back to characters
136140 encrypted_batch = "" .join (
137- self .replace_digits (num ) for num in batch_encrypted
141+ self .replace_digits (int ( round ( num )) ) for num in batch_encrypted
138142 )
139143 encrypted += encrypted_batch
140144
141145 return encrypted
142-
143- def make_decrypt_key (self ) -> np .ndarray :
146+ def make_decrypt_key (self ) -> np .ndarray :
144147 """
145148 >>> hill_cipher = HillCipher(np.array([[2, 5], [1, 6]]))
146149 >>> hill_cipher.make_decrypt_key()
147150 array([[ 6, 25],
148151 [ 5, 26]])
149152 """
153+ # Calculate determinant of encryption key
150154 det = round (np .linalg .det (self .encrypt_key ))
151155
152156 if det < 0 :
153157 det = det % len (self .key_string )
154158 det_inv = None
159+
160+ # Find modular inverse of determinant
155161 for i in range (len (self .key_string )):
156162 if (det * i ) % len (self .key_string ) == 1 :
157163 det_inv = i
158164 break
159165
166+ # Calculate inverse key matrix
160167 inv_key = (
161168 det_inv * np .linalg .det (self .encrypt_key ) * np .linalg .inv (self .encrypt_key )
162169 )
@@ -171,24 +178,29 @@ def decrypt(self, text: str) -> str:
171178 >>> hill_cipher.decrypt('85FF00')
172179 'HELLOO'
173180 """
181+ # Get decryption key and preprocess text
174182 decrypt_key = self .make_decrypt_key ()
175183 text = self .process_text (text .upper ())
176184 decrypted = ""
177185
186+ # Process text in batches of size break_key
178187 for i in range (0 , len (text ) - self .break_key + 1 , self .break_key ):
179188 batch = text [i : i + self .break_key ]
189+ # Convert characters to numerical values
180190 vec = [self .replace_letters (char ) for char in batch ]
181191 batch_vec = np .array ([vec ]).T
192+
193+ # Matrix multiplication with decryption key
182194 batch_decrypted = self .modulus (decrypt_key .dot (batch_vec )).T .tolist ()[0 ]
195+ # Convert numerical results back to characters
183196 decrypted_batch = "" .join (
184- self .replace_digits (num ) for num in batch_decrypted
197+ self .replace_digits (int ( round ( num )) ) for num in batch_decrypted
185198 )
186199 decrypted += decrypted_batch
187200
188201 return decrypted
189-
190-
191- def main () -> None :
202+ def main () -> None :
203+ """Command-line interface for Hill Cipher"""
192204 n = int (input ("Enter the order of the encryption key: " ))
193205 hill_matrix = []
194206
@@ -215,5 +227,4 @@ def main() -> None:
215227 import doctest
216228
217229 doctest .testmod ()
218-
219230 main ()
0 commit comments