Skip to content

Commit 369562a

Browse files
mrmaxgunscclauss
andauthored
Upgrades to caesar_cipher.py (#1958)
* Added more flexibility to functions, decreased amount of repeating code * Added docstrings * Updated input functions * Added doctests * removed test piece of code * black . * Updated caesar cipher standard alphabet to fit python 3.8 * Update and rename sleepsort.py to sleep_sort.py * Or 4 Co-authored-by: Christian Clauss <[email protected]>
1 parent c18c677 commit 369562a

File tree

4 files changed

+275
-86
lines changed

4 files changed

+275
-86
lines changed

Diff for: ciphers/caesar_cipher.py

+225-38
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,250 @@
1-
def encrypt(input_string: str, key: int) -> str:
2-
result = ""
3-
for x in input_string:
4-
if not x.isalpha():
5-
result += x
6-
elif x.isupper():
7-
result += chr((ord(x) + key - 65) % 26 + 65)
8-
elif x.islower():
9-
result += chr((ord(x) + key - 97) % 26 + 97)
10-
return result
1+
from string import ascii_letters
2+
3+
4+
def encrypt(input_string: str, key: int, alphabet=None) -> str:
5+
"""
6+
encrypt
7+
=======
8+
Encodes a given string with the caesar cipher and returns the encoded
9+
message
10+
11+
Parameters:
12+
-----------
13+
* input_string: the plain-text that needs to be encoded
14+
* key: the number of letters to shift the message by
15+
16+
Optional:
17+
* alphabet (None): the alphabet used to encode the cipher, if not
18+
specified, the standard english alphabet with upper and lowercase
19+
letters is used
20+
21+
Returns:
22+
* A string containing the encoded cipher-text
23+
24+
More on the caesar cipher
25+
=========================
26+
The caesar cipher is named after Julius Caesar who used it when sending
27+
secret military messages to his troops. This is a simple substitution cipher
28+
where very character in the plain-text is shifted by a certain number known
29+
as the "key" or "shift".
30+
31+
Example:
32+
Say we have the following message:
33+
"Hello, captain"
34+
35+
And our alphabet is made up of lower and uppercase letters:
36+
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
37+
38+
And our shift is "2"
39+
40+
We can then encode the message, one letter at a time. "H" would become "J",
41+
since "J" is two letters away, and so on. If the shift is ever two large, or
42+
our letter is at the end of the alphabet, we just start at the beginning
43+
("Z" would shift to "a" then "b" and so on).
44+
45+
Our final message would be "Jgnnq, ecrvckp"
46+
47+
Further reading
48+
===============
49+
* https://en.m.wikipedia.org/wiki/Caesar_cipher
50+
51+
Doctests
52+
========
53+
>>> encrypt('The quick brown fox jumps over the lazy dog', 8)
54+
'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo'
1155
56+
>>> encrypt('A very large key', 8000)
57+
's nWjq dSjYW cWq'
1258
13-
def decrypt(input_string: str, key: int) -> str:
59+
>>> encrypt('a lowercase alphabet', 5, 'abcdefghijklmnopqrstuvwxyz')
60+
'f qtbjwhfxj fqumfgjy'
61+
"""
62+
# Set default alphabet to lower and upper case english chars
63+
alpha = alphabet or ascii_letters
64+
65+
# The final result string
1466
result = ""
15-
for x in input_string:
16-
if not x.isalpha():
17-
result += x
18-
elif x.isupper():
19-
result += chr((ord(x) - key - 65) % 26 + 65)
20-
elif x.islower():
21-
result += chr((ord(x) - key - 97) % 26 + 97)
67+
68+
for character in input_string:
69+
if character not in alpha:
70+
# Append without encryption if character is not in the alphabet
71+
result += character
72+
else:
73+
# Get the index of the new key and make sure it isn't too large
74+
new_key = (alpha.index(character) + key) % len(alpha)
75+
76+
# Append the encoded character to the alphabet
77+
result += alpha[new_key]
78+
2279
return result
2380

2481

25-
def brute_force(input_string: str) -> None:
82+
def decrypt(input_string: str, key: int, alphabet=None) -> str:
83+
"""
84+
decrypt
85+
=======
86+
Decodes a given string of cipher-text and returns the decoded plain-text
87+
88+
Parameters:
89+
-----------
90+
* input_string: the cipher-text that needs to be decoded
91+
* key: the number of letters to shift the message backwards by to decode
92+
93+
Optional:
94+
* alphabet (None): the alphabet used to decode the cipher, if not
95+
specified, the standard english alphabet with upper and lowercase
96+
letters is used
97+
98+
Returns:
99+
* A string containing the decoded plain-text
100+
101+
More on the caesar cipher
102+
=========================
103+
The caesar cipher is named after Julius Caesar who used it when sending
104+
secret military messages to his troops. This is a simple substitution cipher
105+
where very character in the plain-text is shifted by a certain number known
106+
as the "key" or "shift". Please keep in mind, here we will be focused on
107+
decryption.
108+
109+
Example:
110+
Say we have the following cipher-text:
111+
"Jgnnq, ecrvckp"
112+
113+
And our alphabet is made up of lower and uppercase letters:
114+
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
115+
116+
And our shift is "2"
117+
118+
To decode the message, we would do the same thing as encoding, but in
119+
reverse. The first letter, "J" would become "H" (remember: we are decoding)
120+
because "H" is two letters in reverse (to the left) of "J". We would
121+
continue doing this. A letter like "a" would shift back to the end of
122+
the alphabet, and would become "Z" or "Y" and so on.
123+
124+
Our final message would be "Hello, captain"
125+
126+
Further reading
127+
===============
128+
* https://en.m.wikipedia.org/wiki/Caesar_cipher
129+
130+
Doctests
131+
========
132+
>>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8)
133+
'The quick brown fox jumps over the lazy dog'
134+
135+
>>> decrypt('s nWjq dSjYW cWq', 8000)
136+
'A very large key'
137+
138+
>>> decrypt('f qtbjwhfxj fqumfgjy', 5, 'abcdefghijklmnopqrstuvwxyz')
139+
'a lowercase alphabet'
140+
"""
141+
# Turn on decode mode by making the key negative
142+
key *= -1
143+
144+
return encrypt(input_string, key, alphabet)
145+
146+
147+
def brute_force(input_string: str, alphabet=None) -> dict:
148+
"""
149+
brute_force
150+
===========
151+
Returns all the possible combinations of keys and the decoded strings in the
152+
form of a dictionary
153+
154+
Parameters:
155+
-----------
156+
* input_string: the cipher-text that needs to be used during brute-force
157+
158+
Optional:
159+
* alphabet: (None): the alphabet used to decode the cipher, if not
160+
specified, the standard english alphabet with upper and lowercase
161+
letters is used
162+
163+
More about brute force
164+
======================
165+
Brute force is when a person intercepts a message or password, not knowing
166+
the key and tries every single combination. This is easy with the caesar
167+
cipher since there are only all the letters in the alphabet. The more
168+
complex the cipher, the larger amount of time it will take to do brute force
169+
170+
Ex:
171+
Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the
172+
following message:
173+
174+
"dbc"
175+
176+
we could then just write out every combination:
177+
ecd... and so on, until we reach a combination that makes sense:
178+
"cab"
179+
180+
Further reading
181+
===============
182+
* https://en.wikipedia.org/wiki/Brute_force
183+
184+
Doctests
185+
========
186+
>>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20]
187+
"Please don't brute force me!"
188+
189+
>>> brute_force(1)
190+
Traceback (most recent call last):
191+
TypeError: 'int' object is not iterable
192+
"""
193+
# Set default alphabet to lower and upper case english chars
194+
alpha = alphabet or ascii_letters
195+
196+
# The key during testing (will increase)
26197
key = 1
198+
199+
# The encoded result
27200
result = ""
28-
while key <= 94:
29-
for x in input_string:
30-
indx = (ord(x) - key) % 256
31-
if indx < 32:
32-
indx = indx + 95
33-
result = result + chr(indx)
34-
print(f"Key: {key}\t| Message: {result}")
201+
202+
# To store data on all the combinations
203+
brute_force_data = {}
204+
205+
# Cycle through each combination
206+
while key <= len(alpha):
207+
# Decrypt the message
208+
result = decrypt(input_string, key, alpha)
209+
210+
# Update the data
211+
brute_force_data[key] = result
212+
213+
# Reset result and increase the key
35214
result = ""
36215
key += 1
37-
return None
216+
217+
return brute_force_data
38218

39219

40220
def main():
41221
while True:
42-
print(f'{"-" * 10}\n Menu\n{"-" * 10}')
222+
print(f'\n{"-" * 10}\n Menu\n{"-" * 10}')
43223
print(*["1.Encrpyt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n")
44-
choice = input("What would you like to do?: ")
45-
if choice not in ["1", "2", "3", "4"]:
224+
225+
# get user input
226+
choice = input("\nWhat would you like to do?: ").strip() or "4"
227+
228+
# run functions based on what the user chose
229+
if choice not in ("1", "2", "3", "4"):
46230
print("Invalid choice, please enter a valid choice")
47231
elif choice == "1":
48232
input_string = input("Please enter the string to be encrypted: ")
49-
key = int(input("Please enter off-set between 0-25: "))
50-
if key in range(1, 95):
51-
print(encrypt(input_string.lower(), key))
233+
key = int(input("Please enter off-set: ").strip())
234+
235+
print(encrypt(input_string, key))
52236
elif choice == "2":
53237
input_string = input("Please enter the string to be decrypted: ")
54-
key = int(input("Please enter off-set between 1-94: "))
55-
if key in range(1, 95):
56-
print(decrypt(input_string, key))
238+
key = int(input("Please enter off-set: ").strip())
239+
240+
print(decrypt(input_string, key))
57241
elif choice == "3":
58242
input_string = input("Please enter the string to be decrypted: ")
59-
brute_force(input_string)
60-
main()
243+
brute_force_data = brute_force(input_string)
244+
245+
for key, value in brute_force_data.items():
246+
print(f"Key: {key} | Message: {value}")
247+
61248
elif choice == "4":
62249
print("Goodbye.")
63250
break

Diff for: digital_image_processing/test_digital_image_processing.py

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def test_burkes(file_path: str = "digital_image_processing/image_data/lena_small
8484
burkes.process()
8585
assert burkes.output_img.any()
8686

87+
8788
def test_nearest_neighbour(
8889
file_path: str = "digital_image_processing/image_data/lena_small.jpg",
8990
):

Diff for: sorts/sleep_sort.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""
2+
Sleep sort is probably the wierdest of all sorting functions with time-complexity of
3+
O(max(input)+n) which is quite different from almost all other sorting techniques.
4+
If the number of inputs is small then the complexity can be approximated to be
5+
O(max(input)) which is a constant
6+
7+
If the number of inputs is large, the complexity is approximately O(n).
8+
9+
This function uses multithreading a kind of higher order programming and calls n
10+
functions, each with a sleep time equal to its number. Hence each of function wakes
11+
in sorted time.
12+
13+
This function is not stable for very large values.
14+
15+
https://rosettacode.org/wiki/Sorting_algorithms/Sleep_sort
16+
"""
17+
from threading import Timer
18+
from time import sleep
19+
from typing import List
20+
21+
22+
def sleep_sort(values: List[int]) -> List[int]:
23+
"""
24+
Sort the list using sleepsort.
25+
>>> sleep_sort([3, 2, 4, 7, 3, 6, 9, 1])
26+
[1, 2, 3, 3, 4, 6, 7, 9]
27+
>>> sleep_sort([3, 2, 1, 9, 8, 4, 2])
28+
[1, 2, 2, 3, 4, 8, 9]
29+
"""
30+
sleep_sort.result = []
31+
32+
def append_to_result(x):
33+
sleep_sort.result.append(x)
34+
35+
mx = values[0]
36+
for value in values:
37+
if mx < value:
38+
mx = value
39+
Timer(value, append_to_result, [value]).start()
40+
sleep(mx + 1)
41+
return sleep_sort.result
42+
43+
44+
if __name__ == "__main__":
45+
import doctest
46+
47+
doctest.testmod()
48+
49+
print(sleep_sort([3, 2, 4, 7, 3, 6, 9, 1]))

Diff for: sorts/sleepsort.py

-48
This file was deleted.

0 commit comments

Comments
 (0)