Rear admiral Sir Francis Beaufort1 was an Irish hydrographer and officer in the Royal Navy who became famous for the wind force scale named after him: the Beaufort Scale2. Like other patrons of exploration, he also had his name given to geographical places, including the Beaufort Sea3 and Beaufort Island4.
Perhaps less known is the Beaufort cipher: a cipher he invented for encoding and decoding messages. Its most famous application was in a rotor-based cipher machine used in World War II: the Hagelin M-2095. In the Beaufort cipher, each letter (A–Z) has a value (0–25) that corresponds to its position in the alphabet.
To encode a message $$M = M_0\ldots M_{n-1}$$ of $$n$$ letters, the Beaufort cipher uses a keyword whose letters are repeated or truncated to obtain a second sequence $$K = K_0\ldots K_{n-1}$$ of $$n$$ letters. The ciphertext $$C = C_0\ldots C_{n-1}$$ (the encoded message) of $$n$$ letters is obtained by subtracting (the values of) the letters at corresponding positions (modulo 26): \[ C_i = (K_i - M_i)\ \textrm{mod}\ 26 \ \ \ i = 0, \ldots, n-1\] If we apply this to encode the message DEFENDTHEEASTWALLOFTHECASTLE with keyword FORTIFICATION, we get
A ciphertext can be decoded by encoding it using the same procedure. After all, it applies that \[ M_i = (K_i - C_i)\ \textrm{mod}\ 26 \ \ \ i = 0, \ldots, n-1\] If we apply this to the above example, we get
Because decoding with a Beaufort cipher uses the same procedure as encoding, it is called a symmetrical cipher.
Define a class Beaufort that can be used for encoding and decoding messages with a Beaufort cipher that uses a keyword $$K$$ that is passed when creating objects of the class. The class may assume that all messages, keywords and ciphertexts only contain uppercase letters and that keywords have at least four letters, without the need to check this explicitly. In addition, a Beaufort cipher (Beaufort) must support at least the following methods:
A method encode_letter that takes two arguments: i) the $$i$$-the letter $$M_i$$ (str) of a message and ii) the position $$i$$ (int) of that letter in the message, where zero-based indexing is used for the positions. The method must return the $$i$$-th letter $$C_i$$ in the corresponding ciphertext.
A method encode that takes a message $$M$$ (str) and returns its corresponding ciphertext $$C$$ (str).
A method decode_letter that takes two arguments: i) the $$i$$-the letter $$C_i$$ (str) of a ciphertext and ii) the position $$i$$ (int) of that letter in the ciphertext, where zero-based indexing is used for the positions. The method must return the $$i$$-th letter $$M_i$$ in the original message.
A method decode that takes a ciphertext $$C$$ (str) and returns the original message $$M$$ (str).
If a Beaufort cipher (Beaufort) is passed to the built-in function str, its keyword (str) must be returned with all but the first two and the last two letters replaced by asterisks (*). Similarly, the built-in function repr must return a string (str) whose format can be derived from the example below.
The addition (+) of two Beaufort ciphers (Beaufort) with keywords $$r = r_0\ldots r_{a-1}$$ and $$s = s_0\ldots s_{b-1}$$ must yield a new Beaufort cipher (Beaufort) with keyword $$t^{+} = t^{+}_0\ldots t^{+}_{c-1}$$. With $$c$$ being the least common multiple of $$a$$ and $$b$$, we can determine $$r' = r'_0\ldots r'_{c-1}$$ as $$\frac{c}{a}$$ repetitions of $$r$$ and $$s' = s'_0\ldots s'_{c-1}$$ as $$\frac{c}{b}$$ repetitions of $$s$$. The keyword $$t^{+}$$ is obtained by adding (the value of) the letters at corresponding positions (modulo 26): \[ t^{+}_i = (r'_i + s'_i)\ \textrm{mod}\ 26 \ \ \ i = 0, \ldots, c-1\] If we apply this to the keywords SECRET and WORD, we get
For the least common multiple $$\textrm{lcm}(a,b)$$ of two natural numbers $$a$$ and $$b$$ it holds that \[ \textrm{lcm}(a,b) = \frac{a \times b}{\textrm{gcd}(a, b)} \] where $$\textrm{gcd}(a, b)$$ is the greatest common divisor of $$a$$ and $$b$$. In Python, the latter can be determined using the gcd6 function from the math-module of The Python Standard Library7.
Similarly, the subtraction (-) of two Beaufort ciphers (Beaufort) must yield a new Beaufort cipher (Beaufort) with a keyword $$t^{-} = t^{-}_0\ldots t^{-}_{c-1}$$ that is obtained by subtracting (the value of) the letters at corresponding positions (modulo 26): \[ t^{-}_i = (r'_i - s'_i)\ \textrm{mod}\ 26 \ \ \ i = 0, \ldots, c-1\] If we apply this to the keywords SECRET and WORD, we get
The multiplication (*) of an integer $$k$$ (int) with a Beaufort cipher (Beaufort) with keyword $$r = r_0\ldots r_{a-1}$$ — or vice versa — must yield a new Beaufort cipher (Beaufort) with a keyword $$t^{*} = t^{*}_0\ldots t^{*}_{a-1}$$ that is obtained by multiplying (the value of) all letters with $$k$$ (modulo 26): \[ t^{*}_i = k\,r_i\ \textrm{mod}\ 26 \ \ \ i = 0, \ldots, a-1\].
>>> cipher = Beaufort('FORTIFICATION')
>>> print(cipher)
FO*********ON
>>> cipher
Beaufort('FORTIFICATION')
>>> cipher.encode_letter('D', 0)
'C'
>>> cipher.encode_letter('E', 1)
'K'
>>> cipher.encode_letter('F', 2)
'M'
>>> cipher.encode('DEFENDTHEEASTWALLOFTHECASTLE')
'CKMPVCPVWPIWUJOGIUAPVWRIWUUK'
>>> cipher.decode_letter('C', 0)
'D'
>>> cipher.decode_letter('K', 1)
'E'
>>> cipher.decode_letter('M', 2)
'F'
>>> cipher.decode('CKMPVCPVWPIWUJOGIUAPVWRIWUUK')
'DEFENDTHEEASTWALLOFTHECASTLE'
>>> Beaufort('SECRET') + Beaufort('WORD')
Beaufort('OSTUAHJHYFVW')
>>> Beaufort('SECRET') - Beaufort('WORD')
Beaufort('WQLOIFBBGDNQ')
>>> Beaufort('SECRET') * 3
Beaufort('CMGZMF')
>>> 3 * Beaufort('SECRET')
Beaufort('CMGZMF')
>>> Beaufort('SECRET') + Beaufort('SECRET') + Beaufort('SECRET')
Beaufort('CMGZMF')
Don Martin’s cartoons in MAD8 magazine were famous for their sound effects:
Doug Gilford maintains an online database9.