582f13e66e8d770ab069921aae5f2f21a6cef228
[szyfrow.git] / docs / szyfrow / column_transposition.html
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
6 <meta name="generator" content="pdoc 0.9.2" />
7 <title>szyfrow.column_transposition API documentation</title>
8 <meta name="description" content="Enciphering and deciphering using the [Column transposition cipher](https://en.wikipedia.org/wiki/Bifid_cipher).
9 Also attempts to break messages that …" />
10 <link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/sanitize.min.css" integrity="sha256-PK9q560IAAa6WVRRh76LtCaI8pjTJ2z11v0miyNNjrs=" crossorigin>
11 <link rel="preload stylesheet" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/11.0.1/typography.min.css" integrity="sha256-7l/o7C8jubJiy74VsKTidCy1yBkRtiUGbVkYBylBqUg=" crossorigin>
12 <link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
13 <style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
14 <style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
15 <style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
16 <script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
17 <script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
18 </head>
19 <body>
20 <main>
21 <article id="content">
22 <header>
23 <h1 class="title">Module <code>szyfrow.column_transposition</code></h1>
24 </header>
25 <section id="section-intro">
26 <p>Enciphering and deciphering using the <a href="https://en.wikipedia.org/wiki/Bifid_cipher">Column transposition cipher</a>.
27 Also attempts to break messages that use a column transpositon cipher.</p>
28 <p>A grid is layed out, with one column for each distinct letter in the keyword.
29 The grid is filled by the plaintext, one letter per cell, either in rows or
30 columns. The columns are rearranged so the keyword's letters are in alphabetical
31 order, then the ciphertext is read from the rearranged grid, either in rows
32 or columns. </p>
33 <p>The Scytale cipher is a column cipher with an identity transposition, where the
34 message is written in rows and read in columns. </p>
35 <p>Messages that do not fill the grid are padded with fillvalue. Note that
36 <code><a title="szyfrow.support.utilities.pad" href="support/utilities.html#szyfrow.support.utilities.pad">pad()</a></code> allows a callable, so that the message can be
37 padded by random letters, for instance by calling
38 <code><a title="szyfrow.support.language_models.random_english_letter" href="support/language_models.html#szyfrow.support.language_models.random_english_letter">random_english_letter()</a></code>.</p>
39 <details class="source">
40 <summary>
41 <span>Expand source code</span>
42 </summary>
43 <pre><code class="python">&#34;&#34;&#34;Enciphering and deciphering using the [Column transposition cipher](https://en.wikipedia.org/wiki/Bifid_cipher).
44 Also attempts to break messages that use a column transpositon cipher.
45
46 A grid is layed out, with one column for each distinct letter in the keyword.
47 The grid is filled by the plaintext, one letter per cell, either in rows or
48 columns. The columns are rearranged so the keyword&#39;s letters are in alphabetical
49 order, then the ciphertext is read from the rearranged grid, either in rows
50 or columns.
51
52 The Scytale cipher is a column cipher with an identity transposition, where the
53 message is written in rows and read in columns.
54
55 Messages that do not fill the grid are padded with fillvalue. Note that
56 `szyfrow.support.utilities.pad` allows a callable, so that the message can be
57 padded by random letters, for instance by calling
58 `szyfrow.support.language_models.random_english_letter`.
59 &#34;&#34;&#34;
60
61 import math
62 import multiprocessing
63 from itertools import chain
64 from szyfrow.support.utilities import *
65 from szyfrow.support.language_models import *
66
67 def column_transposition_encipher(message, keyword, fillvalue=&#39; &#39;,
68 fillcolumnwise=False,
69 emptycolumnwise=False):
70 &#34;&#34;&#34;Enciphers using the column transposition cipher.
71 Message is padded to allow all rows to be the same length.
72
73 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;, fillcolumnwise=True)
74 &#39;hlohr eltee &#39;
75 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;, fillcolumnwise=True, emptycolumnwise=True)
76 &#39;hellothere &#39;
77 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;)
78 &#39;hellothere &#39;
79 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;)
80 &#39;hellothere&#39;
81 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=True)
82 &#39;hellothere&#39;
83 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=False)
84 &#39;hlohreltee&#39;
85 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=True)
86 &#39;htehlelroe&#39;
87 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=False)
88 &#39;hellothere&#39;
89 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=True)
90 &#39;heotllrehe&#39;
91 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=False)
92 &#39;holrhetlee&#39;
93 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=True)
94 &#39;htleehoelr&#39;
95 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=False)
96 &#39;hleolteher&#39;
97 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;)
98 &#39;hleolthre e &#39;
99 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;, fillvalue=&#39;!&#39;)
100 &#39;hleolthre!e!&#39;
101 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;, fillvalue=lambda: &#39;*&#39;)
102 &#39;hleolthre*e*&#39;
103 &#34;&#34;&#34;
104 transpositions = transpositions_of(keyword)
105 message += pad(len(message), len(transpositions), fillvalue)
106 if fillcolumnwise:
107 rows = every_nth(message, len(message) // len(transpositions))
108 else:
109 rows = chunks(message, len(transpositions))
110 transposed = [transpose(r, transpositions) for r in rows]
111 if emptycolumnwise:
112 return combine_every_nth(transposed)
113 else:
114 return cat(chain(*transposed))
115
116 def column_transposition_decipher(message, keyword, fillvalue=&#39; &#39;,
117 fillcolumnwise=False,
118 emptycolumnwise=False):
119 &#34;&#34;&#34;Deciphers using the column transposition cipher.
120 Message is padded to allow all rows to be the same length.
121
122 Note that `fillcolumnwise` and `emptycolumnwise` refer to how the message
123 is enciphered. To decipher a message, the operations are performed as an
124 inverse-empty, then inverse-transposition, then inverse-fill.
125
126 &gt;&gt;&gt; column_transposition_decipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=True)
127 &#39;hellothere&#39;
128 &gt;&gt;&gt; column_transposition_decipher(&#39;hlohreltee&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=False)
129 &#39;hellothere&#39;
130 &gt;&gt;&gt; column_transposition_decipher(&#39;htehlelroe&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=True)
131 &#39;hellothere&#39;
132 &gt;&gt;&gt; column_transposition_decipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=False)
133 &#39;hellothere&#39;
134 &gt;&gt;&gt; column_transposition_decipher(&#39;heotllrehe&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=True)
135 &#39;hellothere&#39;
136 &gt;&gt;&gt; column_transposition_decipher(&#39;holrhetlee&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=False)
137 &#39;hellothere&#39;
138 &gt;&gt;&gt; column_transposition_decipher(&#39;htleehoelr&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=True)
139 &#39;hellothere&#39;
140 &gt;&gt;&gt; column_transposition_decipher(&#39;hleolteher&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=False)
141 &#39;hellothere&#39;
142 &#34;&#34;&#34;
143 transpositions = transpositions_of(keyword)
144 message += pad(len(message), len(transpositions), fillvalue)
145 if emptycolumnwise:
146 rows = every_nth(message, len(message) // len(transpositions))
147 else:
148 rows = chunks(message, len(transpositions))
149 untransposed = [untranspose(r, transpositions) for r in rows]
150 if fillcolumnwise:
151 return combine_every_nth(untransposed)
152 else:
153 return cat(chain(*untransposed))
154
155 def scytale_encipher(message, rows, fillvalue=&#39; &#39;):
156 &#34;&#34;&#34;Enciphers using the scytale transposition cipher. `rows` is the
157 circumference of the rod. The message is fitted inot columns so that
158 all rows are used.
159
160 Message is padded with spaces to allow all rows to be the same length.
161
162 For ease of implementation, the cipher is performed on the transpose
163 of the grid
164
165 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 3)
166 &#39;tcnhkfeboqrxuo iw &#39;
167 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 4)
168 &#39;tubnhirfecooqkwx&#39;
169 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 5)
170 &#39;tubn hirf ecoo qkwx &#39;
171 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 6)
172 &#39;tqcrnxhukof eibwo &#39;
173 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 7)
174 &#39;tqcrnx hukof eibwo &#39;
175 &#34;&#34;&#34;
176 # transpositions = [i for i in range(math.ceil(len(message) / rows))]
177 # return column_transposition_encipher(message, transpositions,
178 # fillvalue=fillvalue, fillcolumnwise=False, emptycolumnwise=True)
179 transpositions = (i for i in range(rows))
180 return column_transposition_encipher(message, transpositions,
181 fillvalue=fillvalue, fillcolumnwise=True, emptycolumnwise=False)
182
183 def scytale_decipher(message, rows):
184 &#34;&#34;&#34;Deciphers using the scytale transposition cipher.
185 Assumes the message is padded so that all rows are the same length.
186
187 &gt;&gt;&gt; scytale_decipher(&#39;tcnhkfeboqrxuo iw &#39;, 3)
188 &#39;thequickbrownfox &#39;
189 &gt;&gt;&gt; scytale_decipher(&#39;tubnhirfecooqkwx&#39;, 4)
190 &#39;thequickbrownfox&#39;
191 &gt;&gt;&gt; scytale_decipher(&#39;tubn hirf ecoo qkwx &#39;, 5)
192 &#39;thequickbrownfox &#39;
193 &gt;&gt;&gt; scytale_decipher(&#39;tqcrnxhukof eibwo &#39;, 6)
194 &#39;thequickbrownfox &#39;
195 &gt;&gt;&gt; scytale_decipher(&#39;tqcrnx hukof eibwo &#39;, 7)
196 &#39;thequickbrownfox &#39;
197 &#34;&#34;&#34;
198 # transpositions = [i for i in range(math.ceil(len(message) / rows))]
199 # return column_transposition_decipher(message, transpositions,
200 # fillcolumnwise=False, emptycolumnwise=True)
201 transpositions = [i for i in range(rows)]
202 return column_transposition_decipher(message, transpositions,
203 fillcolumnwise=True, emptycolumnwise=False)
204
205
206 def column_transposition_break(message, translist=None,
207 fitness=Pbigrams, chunksize=500):
208 &#34;&#34;&#34;Breaks a column transposition cipher using a dictionary and
209 n-gram frequency analysis
210
211 If `translist` is not specified, use
212 [`szyfrow.support.langauge_models.transpositions`](support/language_models.html#szyfrow.support.language_models.transpositions).
213
214 &gt;&gt;&gt; len(keywords)
215 20
216
217 &gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( \
218 &#34;It is a truth universally acknowledged, that a single man in \
219 possession of a good fortune, must be in want of a wife. However \
220 little known the feelings or views of such a man may be on his \
221 first entering a neighbourhood, this truth is so well fixed in \
222 the minds of the surrounding families, that he is considered the \
223 rightful property of some one or other of their daughters.&#34;), \
224 &#39;encipher&#39;), \
225 translist={(2, 0, 5, 3, 1, 4, 6): [&#39;encipher&#39;], \
226 (5, 0, 6, 1, 3, 4, 2): [&#39;fourteen&#39;], \
227 (6, 1, 0, 4, 5, 3, 2): [&#39;keyword&#39;]}) # doctest: +ELLIPSIS
228 (((2, 0, 5, 3, 1, 4, 6), False, False), -709.4646722...)
229 &gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( \
230 &#34;It is a truth universally acknowledged, that a single man in \
231 possession of a good fortune, must be in want of a wife. However \
232 little known the feelings or views of such a man may be on his \
233 first entering a neighbourhood, this truth is so well fixed in \
234 the minds of the surrounding families, that he is considered the \
235 rightful property of some one or other of their daughters.&#34;), \
236 &#39;encipher&#39;), \
237 translist={(2, 0, 5, 3, 1, 4, 6): [&#39;encipher&#39;], \
238 (5, 0, 6, 1, 3, 4, 2): [&#39;fourteen&#39;], \
239 (6, 1, 0, 4, 5, 3, 2): [&#39;keyword&#39;]}, \
240 fitness=Ptrigrams) # doctest: +ELLIPSIS
241 (((2, 0, 5, 3, 1, 4, 6), False, False), -997.0129085...)
242 &#34;&#34;&#34;
243 if translist is None:
244 translist = transpositions
245
246 with multiprocessing.Pool() as pool:
247 helper_args = [(message, trans, fillcolumnwise, emptycolumnwise,
248 fitness)
249 for trans in translist
250 for fillcolumnwise in [True, False]
251 for emptycolumnwise in [True, False]]
252 # Gotcha: the helper function here needs to be defined at the top level
253 # (limitation of Pool.starmap)
254 breaks = pool.starmap(column_transposition_break_worker,
255 helper_args, chunksize)
256 return max(breaks, key=lambda k: k[1])
257
258 def column_transposition_break_worker(message, transposition,
259 fillcolumnwise, emptycolumnwise, fitness):
260 plaintext = column_transposition_decipher(message, transposition,
261 fillcolumnwise=fillcolumnwise, emptycolumnwise=emptycolumnwise)
262 fit = fitness(sanitise(plaintext))
263 return (transposition, fillcolumnwise, emptycolumnwise), fit
264
265
266 def scytale_break(message, max_key_length=20,
267 fitness=Pbigrams, chunksize=500):
268 &#34;&#34;&#34;Breaks a scytale cipher using a range of lengths and
269 n-gram frequency analysis
270
271 &gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( \
272 &#34;It is a truth universally acknowledged, that a single man in \
273 possession of a good fortune, must be in want of a wife. However \
274 little known the feelings or views of such a man may be on his \
275 first entering a neighbourhood, this truth is so well fixed in \
276 the minds of the surrounding families, that he is considered the \
277 rightful property of some one or other of their daughters.&#34;), \
278 5)) # doctest: +ELLIPSIS
279 (5, -709.4646722...)
280 &gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( \
281 &#34;It is a truth universally acknowledged, that a single man in \
282 possession of a good fortune, must be in want of a wife. However \
283 little known the feelings or views of such a man may be on his \
284 first entering a neighbourhood, this truth is so well fixed in \
285 the minds of the surrounding families, that he is considered the \
286 rightful property of some one or other of their daughters.&#34;), \
287 5), \
288 fitness=Ptrigrams) # doctest: +ELLIPSIS
289 (5, -997.0129085...)
290 &#34;&#34;&#34;
291 with multiprocessing.Pool() as pool:
292 helper_args = [(message, trans, False, True, fitness)
293 for trans in
294 [[col for col in range(math.ceil(len(message)/rows))]
295 for rows in range(1,max_key_length+1)]]
296 # Gotcha: the helper function here needs to be defined at the top level
297 # (limitation of Pool.starmap)
298 breaks = pool.starmap(column_transposition_break_worker,
299 helper_args, chunksize)
300 best = max(breaks, key=lambda k: k[1])
301 return math.trunc(len(message) / len(best[0][0])), best[1]
302
303 if __name__ == &#34;__main__&#34;:
304 import doctest</code></pre>
305 </details>
306 </section>
307 <section>
308 </section>
309 <section>
310 </section>
311 <section>
312 <h2 class="section-title" id="header-functions">Functions</h2>
313 <dl>
314 <dt id="szyfrow.column_transposition.cat"><code class="name flex">
315 <span>def <span class="ident">cat</span></span>(<span>iterable, /)</span>
316 </code></dt>
317 <dd>
318 <div class="desc"><p>Concatenate any number of strings.</p>
319 <p>The string whose method is called is inserted in between each given string.
320 The result is returned as a new string.</p>
321 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
322 </dd>
323 <dt id="szyfrow.column_transposition.column_transposition_break"><code class="name flex">
324 <span>def <span class="ident">column_transposition_break</span></span>(<span>message, translist=None, fitness=&lt;function Pbigrams&gt;, chunksize=500)</span>
325 </code></dt>
326 <dd>
327 <div class="desc"><p>Breaks a column transposition cipher using a dictionary and
328 n-gram frequency analysis</p>
329 <p>If <code>translist</code> is not specified, use
330 <a href="support/language_models.html#szyfrow.support.language_models.transpositions"><code>szyfrow.support.langauge_models.transpositions</code></a>.</p>
331 <pre><code class="language-python-repl">&gt;&gt;&gt; len(keywords)
332 20
333 </code></pre>
334 <pre><code class="language-python-repl">&gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( &quot;It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.&quot;), 'encipher'), translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], (5, 0, 6, 1, 3, 4, 2): ['fourteen'], (6, 1, 0, 4, 5, 3, 2): ['keyword']}) # doctest: +ELLIPSIS
335 (((2, 0, 5, 3, 1, 4, 6), False, False), -709.4646722...)
336 &gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( &quot;It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.&quot;), 'encipher'), translist={(2, 0, 5, 3, 1, 4, 6): ['encipher'], (5, 0, 6, 1, 3, 4, 2): ['fourteen'], (6, 1, 0, 4, 5, 3, 2): ['keyword']}, fitness=Ptrigrams) # doctest: +ELLIPSIS
337 (((2, 0, 5, 3, 1, 4, 6), False, False), -997.0129085...)
338 </code></pre></div>
339 <details class="source">
340 <summary>
341 <span>Expand source code</span>
342 </summary>
343 <pre><code class="python">def column_transposition_break(message, translist=None,
344 fitness=Pbigrams, chunksize=500):
345 &#34;&#34;&#34;Breaks a column transposition cipher using a dictionary and
346 n-gram frequency analysis
347
348 If `translist` is not specified, use
349 [`szyfrow.support.langauge_models.transpositions`](support/language_models.html#szyfrow.support.language_models.transpositions).
350
351 &gt;&gt;&gt; len(keywords)
352 20
353
354 &gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( \
355 &#34;It is a truth universally acknowledged, that a single man in \
356 possession of a good fortune, must be in want of a wife. However \
357 little known the feelings or views of such a man may be on his \
358 first entering a neighbourhood, this truth is so well fixed in \
359 the minds of the surrounding families, that he is considered the \
360 rightful property of some one or other of their daughters.&#34;), \
361 &#39;encipher&#39;), \
362 translist={(2, 0, 5, 3, 1, 4, 6): [&#39;encipher&#39;], \
363 (5, 0, 6, 1, 3, 4, 2): [&#39;fourteen&#39;], \
364 (6, 1, 0, 4, 5, 3, 2): [&#39;keyword&#39;]}) # doctest: +ELLIPSIS
365 (((2, 0, 5, 3, 1, 4, 6), False, False), -709.4646722...)
366 &gt;&gt;&gt; column_transposition_break(column_transposition_encipher(sanitise( \
367 &#34;It is a truth universally acknowledged, that a single man in \
368 possession of a good fortune, must be in want of a wife. However \
369 little known the feelings or views of such a man may be on his \
370 first entering a neighbourhood, this truth is so well fixed in \
371 the minds of the surrounding families, that he is considered the \
372 rightful property of some one or other of their daughters.&#34;), \
373 &#39;encipher&#39;), \
374 translist={(2, 0, 5, 3, 1, 4, 6): [&#39;encipher&#39;], \
375 (5, 0, 6, 1, 3, 4, 2): [&#39;fourteen&#39;], \
376 (6, 1, 0, 4, 5, 3, 2): [&#39;keyword&#39;]}, \
377 fitness=Ptrigrams) # doctest: +ELLIPSIS
378 (((2, 0, 5, 3, 1, 4, 6), False, False), -997.0129085...)
379 &#34;&#34;&#34;
380 if translist is None:
381 translist = transpositions
382
383 with multiprocessing.Pool() as pool:
384 helper_args = [(message, trans, fillcolumnwise, emptycolumnwise,
385 fitness)
386 for trans in translist
387 for fillcolumnwise in [True, False]
388 for emptycolumnwise in [True, False]]
389 # Gotcha: the helper function here needs to be defined at the top level
390 # (limitation of Pool.starmap)
391 breaks = pool.starmap(column_transposition_break_worker,
392 helper_args, chunksize)
393 return max(breaks, key=lambda k: k[1])</code></pre>
394 </details>
395 </dd>
396 <dt id="szyfrow.column_transposition.column_transposition_break_worker"><code class="name flex">
397 <span>def <span class="ident">column_transposition_break_worker</span></span>(<span>message, transposition, fillcolumnwise, emptycolumnwise, fitness)</span>
398 </code></dt>
399 <dd>
400 <div class="desc"></div>
401 <details class="source">
402 <summary>
403 <span>Expand source code</span>
404 </summary>
405 <pre><code class="python">def column_transposition_break_worker(message, transposition,
406 fillcolumnwise, emptycolumnwise, fitness):
407 plaintext = column_transposition_decipher(message, transposition,
408 fillcolumnwise=fillcolumnwise, emptycolumnwise=emptycolumnwise)
409 fit = fitness(sanitise(plaintext))
410 return (transposition, fillcolumnwise, emptycolumnwise), fit</code></pre>
411 </details>
412 </dd>
413 <dt id="szyfrow.column_transposition.column_transposition_decipher"><code class="name flex">
414 <span>def <span class="ident">column_transposition_decipher</span></span>(<span>message, keyword, fillvalue=' ', fillcolumnwise=False, emptycolumnwise=False)</span>
415 </code></dt>
416 <dd>
417 <div class="desc"><p>Deciphers using the column transposition cipher.
418 Message is padded to allow all rows to be the same length.</p>
419 <p>Note that <code>fillcolumnwise</code> and <code>emptycolumnwise</code> refer to how the message
420 is enciphered. To decipher a message, the operations are performed as an
421 inverse-empty, then inverse-transposition, then inverse-fill.</p>
422 <pre><code class="language-python-repl">&gt;&gt;&gt; column_transposition_decipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=True)
423 'hellothere'
424 &gt;&gt;&gt; column_transposition_decipher('hlohreltee', 'abcde', fillcolumnwise=True, emptycolumnwise=False)
425 'hellothere'
426 &gt;&gt;&gt; column_transposition_decipher('htehlelroe', 'abcde', fillcolumnwise=False, emptycolumnwise=True)
427 'hellothere'
428 &gt;&gt;&gt; column_transposition_decipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=False)
429 'hellothere'
430 &gt;&gt;&gt; column_transposition_decipher('heotllrehe', 'clever', fillcolumnwise=True, emptycolumnwise=True)
431 'hellothere'
432 &gt;&gt;&gt; column_transposition_decipher('holrhetlee', 'clever', fillcolumnwise=True, emptycolumnwise=False)
433 'hellothere'
434 &gt;&gt;&gt; column_transposition_decipher('htleehoelr', 'clever', fillcolumnwise=False, emptycolumnwise=True)
435 'hellothere'
436 &gt;&gt;&gt; column_transposition_decipher('hleolteher', 'clever', fillcolumnwise=False, emptycolumnwise=False)
437 'hellothere'
438 </code></pre></div>
439 <details class="source">
440 <summary>
441 <span>Expand source code</span>
442 </summary>
443 <pre><code class="python">def column_transposition_decipher(message, keyword, fillvalue=&#39; &#39;,
444 fillcolumnwise=False,
445 emptycolumnwise=False):
446 &#34;&#34;&#34;Deciphers using the column transposition cipher.
447 Message is padded to allow all rows to be the same length.
448
449 Note that `fillcolumnwise` and `emptycolumnwise` refer to how the message
450 is enciphered. To decipher a message, the operations are performed as an
451 inverse-empty, then inverse-transposition, then inverse-fill.
452
453 &gt;&gt;&gt; column_transposition_decipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=True)
454 &#39;hellothere&#39;
455 &gt;&gt;&gt; column_transposition_decipher(&#39;hlohreltee&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=False)
456 &#39;hellothere&#39;
457 &gt;&gt;&gt; column_transposition_decipher(&#39;htehlelroe&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=True)
458 &#39;hellothere&#39;
459 &gt;&gt;&gt; column_transposition_decipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=False)
460 &#39;hellothere&#39;
461 &gt;&gt;&gt; column_transposition_decipher(&#39;heotllrehe&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=True)
462 &#39;hellothere&#39;
463 &gt;&gt;&gt; column_transposition_decipher(&#39;holrhetlee&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=False)
464 &#39;hellothere&#39;
465 &gt;&gt;&gt; column_transposition_decipher(&#39;htleehoelr&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=True)
466 &#39;hellothere&#39;
467 &gt;&gt;&gt; column_transposition_decipher(&#39;hleolteher&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=False)
468 &#39;hellothere&#39;
469 &#34;&#34;&#34;
470 transpositions = transpositions_of(keyword)
471 message += pad(len(message), len(transpositions), fillvalue)
472 if emptycolumnwise:
473 rows = every_nth(message, len(message) // len(transpositions))
474 else:
475 rows = chunks(message, len(transpositions))
476 untransposed = [untranspose(r, transpositions) for r in rows]
477 if fillcolumnwise:
478 return combine_every_nth(untransposed)
479 else:
480 return cat(chain(*untransposed))</code></pre>
481 </details>
482 </dd>
483 <dt id="szyfrow.column_transposition.column_transposition_encipher"><code class="name flex">
484 <span>def <span class="ident">column_transposition_encipher</span></span>(<span>message, keyword, fillvalue=' ', fillcolumnwise=False, emptycolumnwise=False)</span>
485 </code></dt>
486 <dd>
487 <div class="desc"><p>Enciphers using the column transposition cipher.
488 Message is padded to allow all rows to be the same length.</p>
489 <pre><code class="language-python-repl">&gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcdef', fillcolumnwise=True)
490 'hlohr eltee '
491 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcdef', fillcolumnwise=True, emptycolumnwise=True)
492 'hellothere '
493 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcdef')
494 'hellothere '
495 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcde')
496 'hellothere'
497 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=True)
498 'hellothere'
499 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=True, emptycolumnwise=False)
500 'hlohreltee'
501 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=True)
502 'htehlelroe'
503 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'abcde', fillcolumnwise=False, emptycolumnwise=False)
504 'hellothere'
505 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'clever', fillcolumnwise=True, emptycolumnwise=True)
506 'heotllrehe'
507 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'clever', fillcolumnwise=True, emptycolumnwise=False)
508 'holrhetlee'
509 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'clever', fillcolumnwise=False, emptycolumnwise=True)
510 'htleehoelr'
511 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'clever', fillcolumnwise=False, emptycolumnwise=False)
512 'hleolteher'
513 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'cleverly')
514 'hleolthre e '
515 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'cleverly', fillvalue='!')
516 'hleolthre!e!'
517 &gt;&gt;&gt; column_transposition_encipher('hellothere', 'cleverly', fillvalue=lambda: '*')
518 'hleolthre*e*'
519 </code></pre></div>
520 <details class="source">
521 <summary>
522 <span>Expand source code</span>
523 </summary>
524 <pre><code class="python">def column_transposition_encipher(message, keyword, fillvalue=&#39; &#39;,
525 fillcolumnwise=False,
526 emptycolumnwise=False):
527 &#34;&#34;&#34;Enciphers using the column transposition cipher.
528 Message is padded to allow all rows to be the same length.
529
530 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;, fillcolumnwise=True)
531 &#39;hlohr eltee &#39;
532 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;, fillcolumnwise=True, emptycolumnwise=True)
533 &#39;hellothere &#39;
534 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcdef&#39;)
535 &#39;hellothere &#39;
536 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;)
537 &#39;hellothere&#39;
538 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=True)
539 &#39;hellothere&#39;
540 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=True, emptycolumnwise=False)
541 &#39;hlohreltee&#39;
542 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=True)
543 &#39;htehlelroe&#39;
544 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;abcde&#39;, fillcolumnwise=False, emptycolumnwise=False)
545 &#39;hellothere&#39;
546 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=True)
547 &#39;heotllrehe&#39;
548 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=True, emptycolumnwise=False)
549 &#39;holrhetlee&#39;
550 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=True)
551 &#39;htleehoelr&#39;
552 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;clever&#39;, fillcolumnwise=False, emptycolumnwise=False)
553 &#39;hleolteher&#39;
554 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;)
555 &#39;hleolthre e &#39;
556 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;, fillvalue=&#39;!&#39;)
557 &#39;hleolthre!e!&#39;
558 &gt;&gt;&gt; column_transposition_encipher(&#39;hellothere&#39;, &#39;cleverly&#39;, fillvalue=lambda: &#39;*&#39;)
559 &#39;hleolthre*e*&#39;
560 &#34;&#34;&#34;
561 transpositions = transpositions_of(keyword)
562 message += pad(len(message), len(transpositions), fillvalue)
563 if fillcolumnwise:
564 rows = every_nth(message, len(message) // len(transpositions))
565 else:
566 rows = chunks(message, len(transpositions))
567 transposed = [transpose(r, transpositions) for r in rows]
568 if emptycolumnwise:
569 return combine_every_nth(transposed)
570 else:
571 return cat(chain(*transposed))</code></pre>
572 </details>
573 </dd>
574 <dt id="szyfrow.column_transposition.lcat"><code class="name flex">
575 <span>def <span class="ident">lcat</span></span>(<span>iterable, /)</span>
576 </code></dt>
577 <dd>
578 <div class="desc"><p>Concatenate any number of strings.</p>
579 <p>The string whose method is called is inserted in between each given string.
580 The result is returned as a new string.</p>
581 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
582 </dd>
583 <dt id="szyfrow.column_transposition.scytale_break"><code class="name flex">
584 <span>def <span class="ident">scytale_break</span></span>(<span>message, max_key_length=20, fitness=&lt;function Pbigrams&gt;, chunksize=500)</span>
585 </code></dt>
586 <dd>
587 <div class="desc"><p>Breaks a scytale cipher using a range of lengths and
588 n-gram frequency analysis</p>
589 <pre><code class="language-python-repl">&gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( &quot;It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.&quot;), 5)) # doctest: +ELLIPSIS
590 (5, -709.4646722...)
591 &gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( &quot;It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.&quot;), 5), fitness=Ptrigrams) # doctest: +ELLIPSIS
592 (5, -997.0129085...)
593 </code></pre></div>
594 <details class="source">
595 <summary>
596 <span>Expand source code</span>
597 </summary>
598 <pre><code class="python">def scytale_break(message, max_key_length=20,
599 fitness=Pbigrams, chunksize=500):
600 &#34;&#34;&#34;Breaks a scytale cipher using a range of lengths and
601 n-gram frequency analysis
602
603 &gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( \
604 &#34;It is a truth universally acknowledged, that a single man in \
605 possession of a good fortune, must be in want of a wife. However \
606 little known the feelings or views of such a man may be on his \
607 first entering a neighbourhood, this truth is so well fixed in \
608 the minds of the surrounding families, that he is considered the \
609 rightful property of some one or other of their daughters.&#34;), \
610 5)) # doctest: +ELLIPSIS
611 (5, -709.4646722...)
612 &gt;&gt;&gt; scytale_break(scytale_encipher(sanitise( \
613 &#34;It is a truth universally acknowledged, that a single man in \
614 possession of a good fortune, must be in want of a wife. However \
615 little known the feelings or views of such a man may be on his \
616 first entering a neighbourhood, this truth is so well fixed in \
617 the minds of the surrounding families, that he is considered the \
618 rightful property of some one or other of their daughters.&#34;), \
619 5), \
620 fitness=Ptrigrams) # doctest: +ELLIPSIS
621 (5, -997.0129085...)
622 &#34;&#34;&#34;
623 with multiprocessing.Pool() as pool:
624 helper_args = [(message, trans, False, True, fitness)
625 for trans in
626 [[col for col in range(math.ceil(len(message)/rows))]
627 for rows in range(1,max_key_length+1)]]
628 # Gotcha: the helper function here needs to be defined at the top level
629 # (limitation of Pool.starmap)
630 breaks = pool.starmap(column_transposition_break_worker,
631 helper_args, chunksize)
632 best = max(breaks, key=lambda k: k[1])
633 return math.trunc(len(message) / len(best[0][0])), best[1]</code></pre>
634 </details>
635 </dd>
636 <dt id="szyfrow.column_transposition.scytale_decipher"><code class="name flex">
637 <span>def <span class="ident">scytale_decipher</span></span>(<span>message, rows)</span>
638 </code></dt>
639 <dd>
640 <div class="desc"><p>Deciphers using the scytale transposition cipher.
641 Assumes the message is padded so that all rows are the same length.</p>
642 <pre><code class="language-python-repl">&gt;&gt;&gt; scytale_decipher('tcnhkfeboqrxuo iw ', 3)
643 'thequickbrownfox '
644 &gt;&gt;&gt; scytale_decipher('tubnhirfecooqkwx', 4)
645 'thequickbrownfox'
646 &gt;&gt;&gt; scytale_decipher('tubn hirf ecoo qkwx ', 5)
647 'thequickbrownfox '
648 &gt;&gt;&gt; scytale_decipher('tqcrnxhukof eibwo ', 6)
649 'thequickbrownfox '
650 &gt;&gt;&gt; scytale_decipher('tqcrnx hukof eibwo ', 7)
651 'thequickbrownfox '
652 </code></pre></div>
653 <details class="source">
654 <summary>
655 <span>Expand source code</span>
656 </summary>
657 <pre><code class="python">def scytale_decipher(message, rows):
658 &#34;&#34;&#34;Deciphers using the scytale transposition cipher.
659 Assumes the message is padded so that all rows are the same length.
660
661 &gt;&gt;&gt; scytale_decipher(&#39;tcnhkfeboqrxuo iw &#39;, 3)
662 &#39;thequickbrownfox &#39;
663 &gt;&gt;&gt; scytale_decipher(&#39;tubnhirfecooqkwx&#39;, 4)
664 &#39;thequickbrownfox&#39;
665 &gt;&gt;&gt; scytale_decipher(&#39;tubn hirf ecoo qkwx &#39;, 5)
666 &#39;thequickbrownfox &#39;
667 &gt;&gt;&gt; scytale_decipher(&#39;tqcrnxhukof eibwo &#39;, 6)
668 &#39;thequickbrownfox &#39;
669 &gt;&gt;&gt; scytale_decipher(&#39;tqcrnx hukof eibwo &#39;, 7)
670 &#39;thequickbrownfox &#39;
671 &#34;&#34;&#34;
672 # transpositions = [i for i in range(math.ceil(len(message) / rows))]
673 # return column_transposition_decipher(message, transpositions,
674 # fillcolumnwise=False, emptycolumnwise=True)
675 transpositions = [i for i in range(rows)]
676 return column_transposition_decipher(message, transpositions,
677 fillcolumnwise=True, emptycolumnwise=False)</code></pre>
678 </details>
679 </dd>
680 <dt id="szyfrow.column_transposition.scytale_encipher"><code class="name flex">
681 <span>def <span class="ident">scytale_encipher</span></span>(<span>message, rows, fillvalue=' ')</span>
682 </code></dt>
683 <dd>
684 <div class="desc"><p>Enciphers using the scytale transposition cipher. <code>rows</code> is the
685 circumference of the rod. The message is fitted inot columns so that
686 all rows are used.</p>
687 <p>Message is padded with spaces to allow all rows to be the same length.</p>
688 <p>For ease of implementation, the cipher is performed on the transpose
689 of the grid</p>
690 <pre><code class="language-python-repl">&gt;&gt;&gt; scytale_encipher('thequickbrownfox', 3)
691 'tcnhkfeboqrxuo iw '
692 &gt;&gt;&gt; scytale_encipher('thequickbrownfox', 4)
693 'tubnhirfecooqkwx'
694 &gt;&gt;&gt; scytale_encipher('thequickbrownfox', 5)
695 'tubn hirf ecoo qkwx '
696 &gt;&gt;&gt; scytale_encipher('thequickbrownfox', 6)
697 'tqcrnxhukof eibwo '
698 &gt;&gt;&gt; scytale_encipher('thequickbrownfox', 7)
699 'tqcrnx hukof eibwo '
700 </code></pre></div>
701 <details class="source">
702 <summary>
703 <span>Expand source code</span>
704 </summary>
705 <pre><code class="python">def scytale_encipher(message, rows, fillvalue=&#39; &#39;):
706 &#34;&#34;&#34;Enciphers using the scytale transposition cipher. `rows` is the
707 circumference of the rod. The message is fitted inot columns so that
708 all rows are used.
709
710 Message is padded with spaces to allow all rows to be the same length.
711
712 For ease of implementation, the cipher is performed on the transpose
713 of the grid
714
715 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 3)
716 &#39;tcnhkfeboqrxuo iw &#39;
717 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 4)
718 &#39;tubnhirfecooqkwx&#39;
719 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 5)
720 &#39;tubn hirf ecoo qkwx &#39;
721 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 6)
722 &#39;tqcrnxhukof eibwo &#39;
723 &gt;&gt;&gt; scytale_encipher(&#39;thequickbrownfox&#39;, 7)
724 &#39;tqcrnx hukof eibwo &#39;
725 &#34;&#34;&#34;
726 # transpositions = [i for i in range(math.ceil(len(message) / rows))]
727 # return column_transposition_encipher(message, transpositions,
728 # fillvalue=fillvalue, fillcolumnwise=False, emptycolumnwise=True)
729 transpositions = (i for i in range(rows))
730 return column_transposition_encipher(message, transpositions,
731 fillvalue=fillvalue, fillcolumnwise=True, emptycolumnwise=False)</code></pre>
732 </details>
733 </dd>
734 <dt id="szyfrow.column_transposition.wcat"><code class="name flex">
735 <span>def <span class="ident">wcat</span></span>(<span>iterable, /)</span>
736 </code></dt>
737 <dd>
738 <div class="desc"><p>Concatenate any number of strings.</p>
739 <p>The string whose method is called is inserted in between each given string.
740 The result is returned as a new string.</p>
741 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
742 </dd>
743 </dl>
744 </section>
745 <section>
746 </section>
747 </article>
748 <nav id="sidebar">
749 <h1>Index</h1>
750 <div class="toc">
751 <ul></ul>
752 </div>
753 <ul id="index">
754 <li><h3>Super-module</h3>
755 <ul>
756 <li><code><a title="szyfrow" href="index.html">szyfrow</a></code></li>
757 </ul>
758 </li>
759 <li><h3><a href="#header-functions">Functions</a></h3>
760 <ul class="">
761 <li><code><a title="szyfrow.column_transposition.cat" href="#szyfrow.column_transposition.cat">cat</a></code></li>
762 <li><code><a title="szyfrow.column_transposition.column_transposition_break" href="#szyfrow.column_transposition.column_transposition_break">column_transposition_break</a></code></li>
763 <li><code><a title="szyfrow.column_transposition.column_transposition_break_worker" href="#szyfrow.column_transposition.column_transposition_break_worker">column_transposition_break_worker</a></code></li>
764 <li><code><a title="szyfrow.column_transposition.column_transposition_decipher" href="#szyfrow.column_transposition.column_transposition_decipher">column_transposition_decipher</a></code></li>
765 <li><code><a title="szyfrow.column_transposition.column_transposition_encipher" href="#szyfrow.column_transposition.column_transposition_encipher">column_transposition_encipher</a></code></li>
766 <li><code><a title="szyfrow.column_transposition.lcat" href="#szyfrow.column_transposition.lcat">lcat</a></code></li>
767 <li><code><a title="szyfrow.column_transposition.scytale_break" href="#szyfrow.column_transposition.scytale_break">scytale_break</a></code></li>
768 <li><code><a title="szyfrow.column_transposition.scytale_decipher" href="#szyfrow.column_transposition.scytale_decipher">scytale_decipher</a></code></li>
769 <li><code><a title="szyfrow.column_transposition.scytale_encipher" href="#szyfrow.column_transposition.scytale_encipher">scytale_encipher</a></code></li>
770 <li><code><a title="szyfrow.column_transposition.wcat" href="#szyfrow.column_transposition.wcat">wcat</a></code></li>
771 </ul>
772 </li>
773 </ul>
774 </nav>
775 </main>
776 <footer id="footer">
777 <p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
778 </footer>
779 </body>
780 </html>