910ada2f456639160adad6ec4cbf3ad4e08ae567
1 """Enciphering and deciphering using the [Caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher).
2 Also attempts to break messages that use a Caesar cipher.
4 The Caesar cipher operates one letter at a time. It converts each letter to a
5 number, then enciphers that number by adding the key. The result is taken mod
6 26 and converted back into a letter.
10 from szyfrow
.support
.utilities
import *
11 from szyfrow
.support
.language_models
import *
13 def caesar_encipher_letter(accented_letter
, shift
):
14 """Encipher a letter, given a shift amount.
16 Accented version of latin letters (such as é and ö) are converted to their
17 non-accented versions before encryption.
19 >>> caesar_encipher_letter('a', 1)
21 >>> caesar_encipher_letter('a', 2)
23 >>> caesar_encipher_letter('b', 2)
25 >>> caesar_encipher_letter('x', 2)
27 >>> caesar_encipher_letter('y', 2)
29 >>> caesar_encipher_letter('z', 2)
31 >>> caesar_encipher_letter('z', -1)
33 >>> caesar_encipher_letter('a', -1)
35 >>> caesar_encipher_letter('A', 1)
37 >>> caesar_encipher_letter('é', 1)
40 # letter = unaccent(accented_letter)
41 # if letter in string.ascii_letters:
42 # if letter in string.ascii_uppercase:
43 # alphabet_start = ord('A')
45 # alphabet_start = ord('a')
46 # return chr(((ord(letter) - alphabet_start + shift) % 26) +
51 letter
= unaccent(accented_letter
)
52 if letter
in string
.ascii_letters
:
53 cipherletter
= unpos(pos(letter
) + shift
)
54 if letter
in string
.ascii_uppercase
:
55 return cipherletter
.upper()
61 def caesar_decipher_letter(letter
, shift
):
62 """Decipher a letter, given a shift amount
64 >>> caesar_decipher_letter('b', 1)
66 >>> caesar_decipher_letter('b', 2)
69 return caesar_encipher_letter(letter
, -shift
)
71 def caesar_encipher(message
, shift
):
72 """Encipher a message with the Caesar cipher of given shift
74 >>> caesar_encipher('abc', 1)
76 >>> caesar_encipher('abc', 2)
78 >>> caesar_encipher('abcxyz', 2)
80 >>> caesar_encipher('ab cx yz', 2)
82 >>> caesar_encipher('Héllo World!', 2)
85 enciphered
= [caesar_encipher_letter(l
, shift
) for l
in message
]
86 return cat(enciphered
)
88 def caesar_decipher(message
, shift
):
89 """Decipher a message with the Caesar cipher of given shift
91 >>> caesar_decipher('bcd', 1)
93 >>> caesar_decipher('cde', 2)
95 >>> caesar_decipher('cd ez ab', 2)
97 >>> caesar_decipher('Jgnnq Yqtnf!', 2)
100 return caesar_encipher(message
, -shift
)
103 def caesar_break(message
, fitness
=Pletters
):
104 """Breaks a Caesar cipher using frequency analysis
106 It tries all possible keys, scores the fitness of the text decipherd with
107 each key, and returns the key that produces the most fit deciphered text.
109 >>> caesar_break('ibxcsyorsaqcheyklxivoexlevmrimwxsfiqevvmihrsasrxliwyrh' \
110 'ecjsppsamrkwleppfmergefifvmhixscsymjcsyqeoixlm') # doctest: +ELLIPSIS
111 (4, -130.849989015...)
112 >>> caesar_break('wxwmaxdgheetgwuxztgptedbgznitgwwhpguxyhkxbmhvvtlbhgtee' \
113 'raxlmhiixweblmxgxwmhmaxybkbgztgwztsxwbgmxgmert') # doctest: +ELLIPSIS
114 (19, -128.82410410...)
115 >>> caesar_break('yltbbqnqnzvguvaxurorgenafsbezqvagbnornfgsbevpnaabjurer' \
116 'svaquvzyvxrnznazlybequrvfohgriraabjtbaruraprur') # doctest: +ELLIPSIS
117 (13, -126.25403935...)
119 sanitised_message
= sanitise(message
)
121 best_fit
= float('-inf')
122 for shift
in range(26):
123 plaintext
= caesar_decipher(sanitised_message
, shift
)
124 fit
= fitness(plaintext
)
130 return best_shift
, best_fit