mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-24 05:21:09 +00:00
Add tests and type hints to hill cipher (#1991)
* Added tests and type hints to hill cipher * Remove extra >>> * import doctest Co-authored-by: John Law <johnlaw.po@gmail.com>
This commit is contained in:
parent
bc8e8f03fd
commit
aa120cea12
|
@ -41,7 +41,17 @@ https://www.youtube.com/watch?v=4RhLNDqcjpA
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
|
||||||
def gcd(a, b):
|
def gcd(a: int, b: int) -> int:
|
||||||
|
"""
|
||||||
|
>>> gcd(4, 8)
|
||||||
|
4
|
||||||
|
>>> gcd(8, 4)
|
||||||
|
4
|
||||||
|
>>> gcd(4, 7)
|
||||||
|
1
|
||||||
|
>>> gcd(0, 10)
|
||||||
|
10
|
||||||
|
"""
|
||||||
if a == 0:
|
if a == 0:
|
||||||
return b
|
return b
|
||||||
return gcd(b % a, a)
|
return gcd(b % a, a)
|
||||||
|
@ -52,9 +62,6 @@ class HillCipher:
|
||||||
# This cipher takes alphanumerics into account
|
# This cipher takes alphanumerics into account
|
||||||
# i.e. a total of 36 characters
|
# i.e. a total of 36 characters
|
||||||
|
|
||||||
replaceLetters = lambda self, letter: self.key_string.index(letter)
|
|
||||||
replaceNumbers = lambda self, num: self.key_string[round(num)]
|
|
||||||
|
|
||||||
# take x and return x % len(key_string)
|
# take x and return x % len(key_string)
|
||||||
modulus = numpy.vectorize(lambda x: x % 36)
|
modulus = numpy.vectorize(lambda x: x % 36)
|
||||||
|
|
||||||
|
@ -69,7 +76,31 @@ class HillCipher:
|
||||||
self.decrypt_key = None
|
self.decrypt_key = None
|
||||||
self.break_key = encrypt_key.shape[0]
|
self.break_key = encrypt_key.shape[0]
|
||||||
|
|
||||||
def check_determinant(self):
|
def replaceLetters(self, letter: str) -> int:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.replaceLetters('T')
|
||||||
|
19
|
||||||
|
>>> hill_cipher.replaceLetters('0')
|
||||||
|
26
|
||||||
|
"""
|
||||||
|
return self.key_string.index(letter)
|
||||||
|
|
||||||
|
def replaceNumbers(self, num: int) -> str:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.replaceNumbers(19)
|
||||||
|
'T'
|
||||||
|
>>> hill_cipher.replaceNumbers(26)
|
||||||
|
'0'
|
||||||
|
"""
|
||||||
|
return self.key_string[round(num)]
|
||||||
|
|
||||||
|
def check_determinant(self) -> None:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.check_determinant()
|
||||||
|
"""
|
||||||
det = round(numpy.linalg.det(self.encrypt_key))
|
det = round(numpy.linalg.det(self.encrypt_key))
|
||||||
|
|
||||||
if det < 0:
|
if det < 0:
|
||||||
|
@ -78,38 +109,54 @@ class HillCipher:
|
||||||
req_l = len(self.key_string)
|
req_l = len(self.key_string)
|
||||||
if gcd(det, len(self.key_string)) != 1:
|
if gcd(det, len(self.key_string)) != 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"discriminant modular {} of encryption key({}) is not co prime w.r.t {}.\nTry another key.".format(
|
f"determinant modular {req_l} of encryption key({det}) is not co prime w.r.t {req_l}.\nTry another key."
|
||||||
req_l, det, req_l
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_text(self, text):
|
def process_text(self, text: str) -> str:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.process_text('Testing Hill Cipher')
|
||||||
|
'TESTINGHILLCIPHERR'
|
||||||
|
"""
|
||||||
text = list(text.upper())
|
text = list(text.upper())
|
||||||
text = [char for char in text if char in self.key_string]
|
chars = [char for char in text if char in self.key_string]
|
||||||
|
|
||||||
last = text[-1]
|
last = chars[-1]
|
||||||
while len(text) % self.break_key != 0:
|
while len(chars) % self.break_key != 0:
|
||||||
text.append(last)
|
chars.append(last)
|
||||||
|
|
||||||
return "".join(text)
|
return "".join(chars)
|
||||||
|
|
||||||
def encrypt(self, text):
|
def encrypt(self, text: str) -> str:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.encrypt('testing hill cipher')
|
||||||
|
'WHXYJOLM9C6XT085LL'
|
||||||
|
"""
|
||||||
text = self.process_text(text.upper())
|
text = self.process_text(text.upper())
|
||||||
encrypted = ""
|
encrypted = ""
|
||||||
|
|
||||||
for i in range(0, len(text) - self.break_key + 1, self.break_key):
|
for i in range(0, len(text) - self.break_key + 1, self.break_key):
|
||||||
batch = text[i : i + self.break_key]
|
batch = text[i : i + self.break_key]
|
||||||
batch_vec = list(map(self.replaceLetters, batch))
|
batch_vec = [self.replaceLetters(char) for char in batch]
|
||||||
batch_vec = numpy.matrix([batch_vec]).T
|
batch_vec = numpy.matrix([batch_vec]).T
|
||||||
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[
|
batch_encrypted = self.modulus(self.encrypt_key.dot(batch_vec)).T.tolist()[
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
encrypted_batch = "".join(list(map(self.replaceNumbers, batch_encrypted)))
|
encrypted_batch = "".join(
|
||||||
|
self.replaceNumbers(num) for num in batch_encrypted
|
||||||
|
)
|
||||||
encrypted += encrypted_batch
|
encrypted += encrypted_batch
|
||||||
|
|
||||||
return encrypted
|
return encrypted
|
||||||
|
|
||||||
def make_decrypt_key(self):
|
def make_decrypt_key(self):
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.make_decrypt_key()
|
||||||
|
matrix([[ 6., 25.],
|
||||||
|
[ 5., 26.]])
|
||||||
|
"""
|
||||||
det = round(numpy.linalg.det(self.encrypt_key))
|
det = round(numpy.linalg.det(self.encrypt_key))
|
||||||
|
|
||||||
if det < 0:
|
if det < 0:
|
||||||
|
@ -128,19 +175,26 @@ class HillCipher:
|
||||||
|
|
||||||
return self.toInt(self.modulus(inv_key))
|
return self.toInt(self.modulus(inv_key))
|
||||||
|
|
||||||
def decrypt(self, text):
|
def decrypt(self, text: str) -> str:
|
||||||
|
"""
|
||||||
|
>>> hill_cipher = HillCipher(numpy.matrix([[2, 5], [1, 6]]))
|
||||||
|
>>> hill_cipher.decrypt('WHXYJOLM9C6XT085LL')
|
||||||
|
'TESTINGHILLCIPHERR'
|
||||||
|
"""
|
||||||
self.decrypt_key = self.make_decrypt_key()
|
self.decrypt_key = self.make_decrypt_key()
|
||||||
text = self.process_text(text.upper())
|
text = self.process_text(text.upper())
|
||||||
decrypted = ""
|
decrypted = ""
|
||||||
|
|
||||||
for i in range(0, len(text) - self.break_key + 1, self.break_key):
|
for i in range(0, len(text) - self.break_key + 1, self.break_key):
|
||||||
batch = text[i : i + self.break_key]
|
batch = text[i : i + self.break_key]
|
||||||
batch_vec = list(map(self.replaceLetters, batch))
|
batch_vec = [self.replaceLetters(char) for char in batch]
|
||||||
batch_vec = numpy.matrix([batch_vec]).T
|
batch_vec = numpy.matrix([batch_vec]).T
|
||||||
batch_decrypted = self.modulus(self.decrypt_key.dot(batch_vec)).T.tolist()[
|
batch_decrypted = self.modulus(self.decrypt_key.dot(batch_vec)).T.tolist()[
|
||||||
0
|
0
|
||||||
]
|
]
|
||||||
decrypted_batch = "".join(list(map(self.replaceNumbers, batch_decrypted)))
|
decrypted_batch = "".join(
|
||||||
|
self.replaceNumbers(num) for num in batch_decrypted
|
||||||
|
)
|
||||||
decrypted += decrypted_batch
|
decrypted += decrypted_batch
|
||||||
|
|
||||||
return decrypted
|
return decrypted
|
||||||
|
@ -176,4 +230,7 @@ def main():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
doctest.testmod()
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user