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