Michiel Florent van Langren1 (Amsterdam, 1598 – Brussels, 1675), who Latinized his name as Langrenus, was an astronomer and cartographer of the Low Countries in the service of the Spanish Monarchy. In 1645, he published the first known map of the Moon with a nomenclature.
In 1634 he described a solution for determining the position of a ship at sea, more specifically its longitude. To prevent it from being leaked prematurely, he used a code that could not be deciphered for nearly 400 years. Until the Belgian Jarl Van Eycke was finally able to crack the code in January 2021, shortly after he made world news together with an American and an Australian for deciphering the 50-year-old code of the famous "Zodiac Killer" serial killer.
Langrenus' cipher uses two operations: entangling and disentangling a message. Both operations use a step size $$n \in \mathbb{N}_0$$ and cancel each other out: if we entangle a message with step size $$n$$ and disentangle the result with the same step size, we get back the original message. The reverse is also true. We explain how these operations work for step size $$n=4$$ using a message consisting of the letters of the alphabet.
To entangle a message of $$m$$ characters, we first create a sequence of $$m$$ empty boxes. We start filling the first box with the first letter of the message. Then we repeatedly jump forward $$n$$ boxes and fill it with the next letter of the message, until we jump past the last box in the sequence. We then repeat this procedure by successively starting at the second box, the third box, … and the $$n$$-th box, while we continue filling the boxes with the next letters of the message. For the $$m=26$$ letters of the alphabet, we first fill the blue boxes from left to right, then the yellow boxes, the green boxes and finally the orange boxes.
To disentangle a message of $$m$$ characters, we start at the first letter of the message and repeatedly jump forward $$n$$ letters until we jump past the end of the message. Then we repeat this procedure starting with the second, third, … and the $$n$$-th letter of the message. For the $$m=26$$ letters of the alphabet, we first read the blue letters from left to right, then the yellow letters, the green letters and finally the orange letters.
Encoding a message starts by discarding all characters that are not letters. Only the letters are retained and are converted to lowercase. In addition, the first nine letters of the alphabet are also replaced by a digit that indicates their position in the alphabet: the letter a is at position 1, the letter b at position 2, … and the letter i at position 9. This way, the message
When the sails are strong a ship has no reason to fear turbulence.
is converted to
w85nt85s19ls1r5stron71s89p81snor51sonto651rtur2ul5n35
The resulting message is entangled. If we entangle the previous message with step size $$n=3$$ we get
won8nt57on16ts588159rspt18u91rls2snu1olrr555ns13ts5ro
A ciphertext (an encoded message) therefore only contains digits and lowercase letters. To make everything even more illegible, Langrenus inserts some random characters that are no digits or lowercase letters, for example spaces, uppercase letters or punctuation. For example, the above ciphertext could be scrambled somewhat more as
won8ZT MnJWOM Rt5M7o n16ts5 88159r spKVt1 8u91rl s2snAu 1olrr5 55ns13 tsAIJH V5ro
Decoding a ciphertext starts by discarding all characters that are no digits or lowercase letters. Then, the digits are converted back to their corresponding letter: the digit 1 is replaced by the letter a, the digit 2 by the letter b, … and the digit 9 by the letter i. The ciphertext from the example above then becomes
wonhntegonaftsehhaeirsptahuiarlsbsnuaolrreeensactsero
The resulting message is disentangled. Disentangling the previous message with step size $$n=3$$ yields the consecutive (lowercase) letters of the original message:
whenthesailsarestrongashiphasnoreasontofearturbulence
Your task:
Write a function letter2digit that takes a lowercase letter (str). If the letter is one of the first nine letters of the alphabet, the function must return the digit (str) that corresponds to the position of the letter in the alphabet: a is at position 1, b at position 2, and so on. Otherwise, the function must return the letter (str) itself.
Write a function digit2letter that takes a single character (str). If the character is a digit, the function must return the lowercase letter (str) at the corresponding position in the alphabet: position 1 has the letter a, position 2 the letter b, and so on. Otherwise, the function must return the character (str) itself.
Write a function disentangle that takes a message (str) and a step size $$n \in \mathbb{N}_0$$ (int). The function must return the result (str) of disentangling the message with step size $$n$$.
Write a function decode that takes a message (str) encoded by Langrenus' cipher. The function also has a second optional parameter step_size that may take the step size (int; default value: 3) of the cipher. The function must return the consecutive (lowercase) letters of the original message.
Write a function entangle that takes a message (str) and a step size $$n \in \mathbb{N}_0$$ (int). The function must return the result (str) of entangling the message with step size $$n$$.
Write a function encode that takes a message (str). The function must return the result (str) of encoding the message with Langrenus' cipher. The function should not implement the encoding step where random characters are inserted after entanglement. The function also has a second optional parameter step_size that may take the step size (int; default value: 3) of the cipher.
>>> letter2digit('c')
'3'
>>> letter2digit('e')
'5'
>>> letter2digit('i')
'9'
>>> letter2digit('s')
's'
>>> digit2letter('3')
'c'
>>> digit2letter('5')
'e'
>>> digit2letter('9')
'i'
>>> digit2letter('s')
's'
>>> disentangle('abcdefghijklmnopqrstuvwxyz', 3)
'adgjmpsvybehknqtwzcfilorux'
>>> disentangle('abcdefghijklmnopqrstuvwxyz', 4)
'aeimquybfjnrvzcgkoswdhlptx'
>>> disentangle('abcdefghijklmnopqrstuvwxyz', 5)
'afkpuzbglqvchmrwdinsxejoty'
>>> decode('won8nt57on16ts588159rspt18u91rls2snu1olrr555ns13ts5ro')
'whenthesailsarestrongashiphasnoreasontofearturbulence'
>>> decode('won8ZT MnJWOM Rt5M7o n16ts5 88159r spKVt1 8u91rl s2snAu 1olrr5 55ns13 tsAIJH V5ro')
'whenthesailsarestrongashiphasnoreasontofearturbulence'
>>> decode('tol2r859 515skr9s n55n5ots 51tot44', step_size=5)
'theseadoesnotliketoberestrained'
>>> decode('tol2ZTMr JWOMR85M 9515skr9 sn55n5ot KVs51tot 44', 5)
'theseadoesnotliketoberestrained'
>>> entangle('abcdefghijklmnopqrstuvwxyz', 3)
'ajsbktcludmvenwfoxgpyhqzir'
>>> entangle('abcdefghijklmnopqrstuvwxyz', 4)
'ahoubipvcjqwdkrxelsyfmtzgn'
>>> entangle('abcdefghijklmnopqrstuvwxyz', 5)
'aglqvbhmrwcinsxdjotyekpuzf'
>>> encode('When the sails are strong a ship has no reason to fear turbulence.')
'won8nt57on16ts588159rspt18u91rls2snu1olrr555ns13ts5ro'
>>> encode('The sea does not like to be restrained.', step_size=5)
'tol2r859515skr9sn55n5ots51tot44'