Source code for 2_caesar
"""
Caesar Cipher
==============
A Caesar cipher is a simple substitution cipher in which each letter
of the plain text is substituted with a letter found by moving
``n`` places down the alphabet.
For example, assume the input plain text is the following::
abcd xyz
If the shift value, ``n``, is ``4``,
then the encrypted text would be the following::
efgh bcd
You are to write a function that accepts two arguments, a plain-text
message and a number of letters to shift in the cipher. The function will
return an encrypted string with all letters transformed and all
punctuation and whitespace remaining unchanged.
.. note::
You can assume the plain text is all lowercase ASCII except for
whitespace and punctuation.
"""
import string
import functools
import time
[docs]def timer(func):
"""
Simple decorator to print how many microseconds
a function requires to run.
"""
@functools.wraps(func) # preserves the original function name
def wrapper_timer(*args, **kwargs):
tic = time.perf_counter()
value = func(*args, **kwargs)
toc = time.perf_counter()
elapsed_time = toc - tic
print(f"{func.__name__}: {elapsed_time * 10e6:0.0f} μseconds")
return value
return wrapper_timer
[docs]@timer
def cipher_caesar(text: str, n: int) -> str:
"""
Encrypt or decrypt the letters in a string preserving all non
letters characters (such as punctuation) according to the Caesar
method: shift the letters of ``n`` position.
:param text: string to be decrypted or encrypted.
:type text: str
:param n: number of position to shift the letters.
:type n: int
:return: decrypted/encrypted string.
:rtype: int
"""
# get the total number of lowercase letters
TOTAL_ASCII_LOWERCASE = len(string.ascii_lowercase)
# initialise the encrypted output string
encrypted_text = ''
# loop over all the characters of the input string
for character in text:
# perform the encryption only on lowercase letters
if character in string.ascii_lowercase:
char_position = string.ascii_lowercase.index(character)
encrypted_char = string.ascii_lowercase[
char_position + n - TOTAL_ASCII_LOWERCASE
]
encrypted_text += encrypted_char
# preserve the digits, punctuation and whitespaces
else:
encrypted_text += character
return encrypted_text
[docs]@timer
def cipher_caesar_v2(text: str, n: int) -> str:
"""
Second version of the using list comprehension. More concise.
Slower on single letters, faster on longer strings than V1.
Encrypt or decrypt the letters in a string preserving all non
letters characters (such as punctuation) according to the Caesar
method: shift the letters of ``n`` position.
:param text: string to be decrypted or encrypted.
:type text: str
:param n: number of position to shift the letters.
:type n: int
:return: decrypted/encrypted string.
:rtype: int
"""
# get the total number of lowercase letters
TOTAL_ASCII_LOWERCASE = len(string.ascii_lowercase)
# perform the encryption only on lowercase letters
# preserve the digits, punctuation and whitespaces
list_str_encrypt = [
string.ascii_lowercase[
string.ascii_lowercase.index(character) + n - TOTAL_ASCII_LOWERCASE
]
if character in string.ascii_lowercase else character
for character in text
]
encrypted_text = ''.join(list_str_encrypt)
return encrypted_text
[docs]@timer
def cipher_caesar_v3(text: str, n: int) -> str:
"""
Second version of the using ``str``'s ``translate`` function.
Even more concise, slower on single characters but definitely
faster than V2 on long strings.
Encrypt or decrypt the letters in a string preserving all non
letters characters (such as punctuation) according to the Caesar
method: shift the letters of ``n`` position.
:param text: string to be decrypted or encrypted.
:type text: str
:param n: number of position to shift the letters.
:type n: int
:return: decrypted/encrypted string.
:rtype: int
"""
# create a conversion table only for ascii letters
conversion_table = text.maketrans(
string.ascii_lowercase,
string.ascii_lowercase[n:] + string.ascii_lowercase[:n]
)
# apply the conversion table to the input text
encrypted_text = text.translate(conversion_table)
return encrypted_text
if __name__ == '__main__':
for letter in string.ascii_lowercase:
assert cipher_caesar(letter, 0) == letter
assert cipher_caesar_v2(letter, 0) == letter
assert cipher_caesar_v3(letter, 0) == letter
for dig in string.digits:
assert cipher_caesar(dig, 10) == dig
assert cipher_caesar_v2(dig, 10) == dig
assert cipher_caesar_v3(dig, 10) == dig
for punct in string.punctuation:
assert cipher_caesar(punct, 1) == punct
assert cipher_caesar_v2(punct, 1) == punct
assert cipher_caesar_v3(punct, 1) == punct
for whitespace in string.whitespace:
assert cipher_caesar(whitespace, 7) == whitespace
assert cipher_caesar_v2(whitespace, 7) == whitespace
assert cipher_caesar_v3(whitespace, 7) == whitespace
assert cipher_caesar('rkgerzr nhgbzngvba vf njrfbzr!', 13) == \
'extreme automation is awesome!'
assert cipher_caesar_v2('rkgerzr nhgbzngvba vf njrfbzr!', 13) == \
'extreme automation is awesome!'
assert cipher_caesar_v3('rkgerzr nhgbzngvba vf njrfbzr!', 13) == \
'extreme automation is awesome!'