fbfe05086ec85e492f07ef5b4b1d665d0c75a55c
[szyfrow.git] / caesar.py
1 from szyfrow.support.utilities import *
2 from szyfrow.support.language_models import *
3
4 def caesar_encipher_letter(accented_letter, shift):
5 """Encipher a letter, given a shift amount
6
7 >>> caesar_encipher_letter('a', 1)
8 'b'
9 >>> caesar_encipher_letter('a', 2)
10 'c'
11 >>> caesar_encipher_letter('b', 2)
12 'd'
13 >>> caesar_encipher_letter('x', 2)
14 'z'
15 >>> caesar_encipher_letter('y', 2)
16 'a'
17 >>> caesar_encipher_letter('z', 2)
18 'b'
19 >>> caesar_encipher_letter('z', -1)
20 'y'
21 >>> caesar_encipher_letter('a', -1)
22 'z'
23 >>> caesar_encipher_letter('A', 1)
24 'B'
25 >>> caesar_encipher_letter('é', 1)
26 'f'
27 """
28 # letter = unaccent(accented_letter)
29 # if letter in string.ascii_letters:
30 # if letter in string.ascii_uppercase:
31 # alphabet_start = ord('A')
32 # else:
33 # alphabet_start = ord('a')
34 # return chr(((ord(letter) - alphabet_start + shift) % 26) +
35 # alphabet_start)
36 # else:
37 # return letter
38
39 letter = unaccent(accented_letter)
40 if letter in string.ascii_letters:
41 cipherletter = unpos(pos(letter) + shift)
42 if letter in string.ascii_uppercase:
43 return cipherletter.upper()
44 else:
45 return cipherletter
46 else:
47 return letter
48
49 def caesar_decipher_letter(letter, shift):
50 """Decipher a letter, given a shift amount
51
52 >>> caesar_decipher_letter('b', 1)
53 'a'
54 >>> caesar_decipher_letter('b', 2)
55 'z'
56 """
57 return caesar_encipher_letter(letter, -shift)
58
59 def caesar_encipher(message, shift):
60 """Encipher a message with the Caesar cipher of given shift
61
62 >>> caesar_encipher('abc', 1)
63 'bcd'
64 >>> caesar_encipher('abc', 2)
65 'cde'
66 >>> caesar_encipher('abcxyz', 2)
67 'cdezab'
68 >>> caesar_encipher('ab cx yz', 2)
69 'cd ez ab'
70 >>> caesar_encipher('Héllo World!', 2)
71 'Jgnnq Yqtnf!'
72 """
73 enciphered = [caesar_encipher_letter(l, shift) for l in message]
74 return cat(enciphered)
75
76 def caesar_decipher(message, shift):
77 """Decipher a message with the Caesar cipher of given shift
78
79 >>> caesar_decipher('bcd', 1)
80 'abc'
81 >>> caesar_decipher('cde', 2)
82 'abc'
83 >>> caesar_decipher('cd ez ab', 2)
84 'ab cx yz'
85 >>> caesar_decipher('Jgnnq Yqtnf!', 2)
86 'Hello World!'
87 """
88 return caesar_encipher(message, -shift)
89
90
91 def caesar_break(message, fitness=Pletters):
92 """Breaks a Caesar cipher using frequency analysis
93
94 >>> caesar_break('ibxcsyorsaqcheyklxivoexlevmrimwxsfiqevvmihrsasrxliwyrh' \
95 'ecjsppsamrkwleppfmergefifvmhixscsymjcsyqeoixlm') # doctest: +ELLIPSIS
96 (4, -130.849989015...)
97 >>> caesar_break('wxwmaxdgheetgwuxztgptedbgznitgwwhpguxyhkxbmhvvtlbhgtee' \
98 'raxlmhiixweblmxgxwmhmaxybkbgztgwztsxwbgmxgmert') # doctest: +ELLIPSIS
99 (19, -128.82410410...)
100 >>> caesar_break('yltbbqnqnzvguvaxurorgenafsbezqvagbnornfgsbevpnaabjurer' \
101 'svaquvzyvxrnznazlybequrvfohgriraabjtbaruraprur') # doctest: +ELLIPSIS
102 (13, -126.25403935...)
103 """
104 sanitised_message = sanitise(message)
105 best_shift = 0
106 best_fit = float('-inf')
107 for shift in range(26):
108 plaintext = caesar_decipher(sanitised_message, shift)
109 fit = fitness(plaintext)
110
111 if fit > best_fit:
112 best_fit = fit
113 best_shift = shift
114
115 return best_shift, best_fit