Skip to content

Commit 2ad5a1f

Browse files
achance6cclauss
authored andcommitted
Implemented simple keyword cipher (#1589)
* Implemented simple keyword cipher * Added documentation and improved input processing * Allow object's hash function to be called * added to string functionality * reverted * Revised according to pull request #1589 * Optimized imports * Update simple_keyword_cypher.py * Update hash_table.py
1 parent 4c75f86 commit 2ad5a1f

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

Diff for: ciphers/simple_keyword_cypher.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
def remove_duplicates(key: str) -> str:
2+
"""
3+
Removes duplicate alphabetic characters in a keyword (letter is ignored after its
4+
first appearance).
5+
:param key: Keyword to use
6+
:return: String with duplicates removed
7+
>>> remove_duplicates('Hello World!!')
8+
'Helo Wrd'
9+
"""
10+
11+
key_no_dups = ""
12+
for ch in key:
13+
if ch == " " or ch not in key_no_dups and ch.isalpha():
14+
key_no_dups += ch
15+
return key_no_dups
16+
17+
18+
def create_cipher_map(key: str) -> dict:
19+
"""
20+
Returns a cipher map given a keyword.
21+
:param key: keyword to use
22+
:return: dictionary cipher map
23+
"""
24+
# Create alphabet list
25+
alphabet = [chr(i + 65) for i in range(26)]
26+
# Remove duplicate characters from key
27+
key = remove_duplicates(key.upper())
28+
offset = len(key)
29+
# First fill cipher with key characters
30+
cipher_alphabet = {alphabet[i]: char for i, char in enumerate(key)}
31+
# Then map remaining characters in alphabet to
32+
# the alphabet from the beginning
33+
for i in range(len(cipher_alphabet), 26):
34+
char = alphabet[i - offset]
35+
# Ensure we are not mapping letters to letters previously mapped
36+
while char in key:
37+
offset -= 1
38+
char = alphabet[i - offset]
39+
cipher_alphabet[alphabet[i]] = char
40+
return cipher_alphabet
41+
42+
43+
def encipher(message: str, cipher_map: dict) -> str:
44+
"""
45+
Enciphers a message given a cipher map.
46+
:param message: Message to encipher
47+
:param cipher_map: Cipher map
48+
:return: enciphered string
49+
>>> encipher('Hello World!!', create_cipher_map('Goodbye!!'))
50+
'CYJJM VMQJB!!'
51+
"""
52+
return "".join(cipher_map.get(ch, ch) for ch in message.upper())
53+
54+
55+
def decipher(message: str, cipher_map: dict) -> str:
56+
"""
57+
Deciphers a message given a cipher map
58+
:param message: Message to decipher
59+
:param cipher_map: Dictionary mapping to use
60+
:return: Deciphered string
61+
>>> cipher_map = create_cipher_map('Goodbye!!')
62+
>>> decipher(encipher('Hello World!!', cipher_map), cipher_map)
63+
'HELLO WORLD!!'
64+
"""
65+
# Reverse our cipher mappings
66+
rev_cipher_map = {v: k for k, v in cipher_map.items()}
67+
return "".join(rev_cipher_map.get(ch, ch) for ch in message.upper())
68+
69+
70+
def main():
71+
"""
72+
Handles I/O
73+
:return: void
74+
"""
75+
message = input("Enter message to encode or decode: ").strip()
76+
key = input("Enter keyword: ").strip()
77+
option = input("Encipher or decipher? E/D:").strip()[0].lower()
78+
try:
79+
func = {"e": encipher, "d": decipher}[option]
80+
except KeyError:
81+
raise KeyError("invalid input option")
82+
cipher_map = create_cipher_map(key)
83+
print(func(message, cipher_map))
84+
85+
86+
if __name__ == "__main__":
87+
import doctest
88+
89+
doctest.testmod()
90+
main()

0 commit comments

Comments
 (0)