Breaking caesar ciphers
[cipher-training.git] / cipher.py
1 """A set of ciphers with implementations for both enciphering and deciphering
2 them. See cipherbreak for automatic breaking of these ciphers
3 """
4
5 import string
6 import collections
7 from enum import Enum
8 from itertools import zip_longest, cycle, chain
9 from language_models import unaccent, sanitise
10
11
12 def caesar_encipher_letter(accented_letter, shift):
13 """Encipher a letter, given a shift amount
14
15 >>> caesar_encipher_letter('a', 1)
16 'b'
17 >>> caesar_encipher_letter('a', 2)
18 'c'
19 >>> caesar_encipher_letter('b', 2)
20 'd'
21 >>> caesar_encipher_letter('x', 2)
22 'z'
23 >>> caesar_encipher_letter('y', 2)
24 'a'
25 >>> caesar_encipher_letter('z', 2)
26 'b'
27 >>> caesar_encipher_letter('z', -1)
28 'y'
29 >>> caesar_encipher_letter('a', -1)
30 'z'
31 >>> caesar_encipher_letter('A', 1)
32 'B'
33 >>> caesar_encipher_letter('é', 1)
34 'f'
35 """
36 letter = unaccent(accented_letter)
37 if letter in string.ascii_letters:
38 if letter in string.ascii_uppercase:
39 alphabet_start = ord('A')
40 else:
41 alphabet_start = ord('a')
42 return chr(((ord(letter) - alphabet_start + shift) % 26) +
43 alphabet_start)
44 else:
45 return letter
46
47 def caesar_decipher_letter(letter, shift):
48 """Decipher a letter, given a shift amount
49
50 >>> caesar_decipher_letter('b', 1)
51 'a'
52 >>> caesar_decipher_letter('b', 2)
53 'z'
54 """
55 return caesar_encipher_letter(letter, -shift)
56
57 def caesar_encipher(message, shift):
58 """Encipher a message with the Caesar cipher of given shift
59
60 >>> caesar_encipher('abc', 1)
61 'bcd'
62 >>> caesar_encipher('abc', 2)
63 'cde'
64 >>> caesar_encipher('abcxyz', 2)
65 'cdezab'
66 >>> caesar_encipher('ab cx yz', 2)
67 'cd ez ab'
68 >>> caesar_encipher('Héllo World!', 2)
69 'Jgnnq Yqtnf!'
70 """
71 enciphered = [caesar_encipher_letter(l, shift) for l in message]
72 return ''.join(enciphered)
73
74 def caesar_decipher(message, shift):
75 """Decipher a message with the Caesar cipher of given shift
76
77 >>> caesar_decipher('bcd', 1)
78 'abc'
79 >>> caesar_decipher('cde', 2)
80 'abc'
81 >>> caesar_decipher('cd ez ab', 2)
82 'ab cx yz'
83 >>> caesar_decipher('Jgnnq Yqtnf!', 2)
84 'Hello World!'
85 """
86 return caesar_encipher(message, -shift)
87
88
89 if __name__ == "__main__":
90 import doctest
91 doctest.testmod()