00365f96f1330d5dc68f6e6b4351254bbc1aad40
[szyfrow.git] / docs / szyfrow / support / utilities.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.support.utilities API documentation</title>
8 <meta name="description" content="A mish-mash of utility functions" />
9 <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>
10 <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>
11 <link rel="stylesheet preload" as="style" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/styles/github.min.css" crossorigin>
12 <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>
13 <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>
14 <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>
15 <script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.1/highlight.min.js" integrity="sha256-Uv3H6lx7dJmRfRvH8TH6kJD1TSK1aFcwgx+mdg3epi8=" crossorigin></script>
16 <script>window.addEventListener('DOMContentLoaded', () => hljs.initHighlighting())</script>
17 </head>
18 <body>
19 <main>
20 <article id="content">
21 <header>
22 <h1 class="title">Module <code>szyfrow.support.utilities</code></h1>
23 </header>
24 <section id="section-intro">
25 <p>A mish-mash of utility functions</p>
26 <details class="source">
27 <summary>
28 <span>Expand source code</span>
29 </summary>
30 <pre><code class="python">&#34;&#34;&#34;A mish-mash of utility functions&#34;&#34;&#34;
31
32 import string
33 import collections
34 import unicodedata
35 from itertools import zip_longest
36
37 cat = &#39;&#39;.join
38 &#34;&#34;&#34;join a a list of letters into a string.&#34;&#34;&#34;
39
40 wcat = &#39; &#39;.join
41 &#34;&#34;&#34;join a list of words into a string, separated by spaces&#34;&#34;&#34;
42
43 lcat = &#39;\n&#39;.join
44 &#34;&#34;&#34;join a list of lines, separated by newline&#34;&#34;&#34;
45
46 def pos(letter):
47 &#34;&#34;&#34;Return the position of a letter in the alphabet (0-25)&#34;&#34;&#34;
48 if letter in string.ascii_lowercase:
49 return ord(letter) - ord(&#39;a&#39;)
50 elif letter in string.ascii_uppercase:
51 return ord(letter) - ord(&#39;A&#39;)
52 else:
53 raise ValueError(&#39;pos requires input of {} to be an ascii letter&#39;.format(letter))
54
55 def unpos(number):
56 &#34;&#34;&#34;Return the letter in the given position in the alphabet (mod 26)&#34;&#34;&#34;
57 return chr(number % 26 + ord(&#39;a&#39;))
58
59 def pad(message_len, group_len, fillvalue):
60 &#34;&#34;&#34;Return the padding needed to extend a message to a multiple of group_len
61 in length.
62
63 fillvalue can be a function or a literal value. If a function, it is called
64 once for each padded character. Use this with fillvalue=random_english_letter
65 to pad a message with random letters.
66 &#34;&#34;&#34;
67 padding_length = group_len - message_len % group_len
68 if padding_length == group_len: padding_length = 0
69 padding = &#39;&#39;
70 if callable(fillvalue):
71 for i in range(padding_length):
72 padding += fillvalue()
73 else:
74 padding += fillvalue * padding_length
75 return padding
76
77 def every_nth(text, n, fillvalue=&#39;&#39;):
78 &#34;&#34;&#34;Returns n strings, each of which consists of every nth character,
79 starting with the 0th, 1st, 2nd, ... (n-1)th character
80
81 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 5)
82 [&#39;afkpuz&#39;, &#39;bglqv&#39;, &#39;chmrw&#39;, &#39;dinsx&#39;, &#39;ejoty&#39;]
83 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 1)
84 [&#39;abcdefghijklmnopqrstuvwxyz&#39;]
85 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 26) # doctest: +NORMALIZE_WHITESPACE
86 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;, &#39;f&#39;, &#39;g&#39;, &#39;h&#39;, &#39;i&#39;, &#39;j&#39;, &#39;k&#39;, &#39;l&#39;, &#39;m&#39;, &#39;n&#39;,
87 &#39;o&#39;, &#39;p&#39;, &#39;q&#39;, &#39;r&#39;, &#39;s&#39;, &#39;t&#39;, &#39;u&#39;, &#39;v&#39;, &#39;w&#39;, &#39;x&#39;, &#39;y&#39;, &#39;z&#39;]
88 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 5, fillvalue=&#39;!&#39;)
89 [&#39;afkpuz&#39;, &#39;bglqv!&#39;, &#39;chmrw!&#39;, &#39;dinsx!&#39;, &#39;ejoty!&#39;]
90 &#34;&#34;&#34;
91 split_text = chunks(text, n, fillvalue)
92 return [cat(l) for l in zip_longest(*split_text, fillvalue=fillvalue)]
93
94 def combine_every_nth(split_text):
95 &#34;&#34;&#34;Reforms a text split into every_nth strings
96
97 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 5))
98 &#39;abcdefghijklmnopqrstuvwxyz&#39;
99 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 1))
100 &#39;abcdefghijklmnopqrstuvwxyz&#39;
101 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 26))
102 &#39;abcdefghijklmnopqrstuvwxyz&#39;
103 &#34;&#34;&#34;
104 return cat([cat(l)
105 for l in zip_longest(*split_text, fillvalue=&#39;&#39;)])
106
107 def chunks(text, n, fillvalue=None):
108 &#34;&#34;&#34;Split a text into chunks of n characters
109
110 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 3)
111 [&#39;abc&#39;, &#39;def&#39;, &#39;ghi&#39;]
112 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 4)
113 [&#39;abcd&#39;, &#39;efgh&#39;, &#39;i&#39;]
114 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 4, fillvalue=&#39;!&#39;)
115 [&#39;abcd&#39;, &#39;efgh&#39;, &#39;i!!!&#39;]
116 &#34;&#34;&#34;
117 if fillvalue:
118 # padding = fillvalue[0] * (n - len(text) % n)
119 padding = pad(len(text), n, fillvalue)
120 padded_text = text + padding
121 else:
122 padded_text = text
123 return [(padded_text)[i:i+n] for i in range(0, len(text), n)]
124
125 def transpose(items, transposition):
126 &#34;&#34;&#34;Moves items around according to the given transposition
127
128 &gt;&gt;&gt; transpose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], (0,1,2,3))
129 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
130 &gt;&gt;&gt; transpose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], (3,1,2,0))
131 [&#39;d&#39;, &#39;b&#39;, &#39;c&#39;, &#39;a&#39;]
132 &gt;&gt;&gt; transpose([10,11,12,13,14,15], (3,2,4,1,5,0))
133 [13, 12, 14, 11, 15, 10]
134 &#34;&#34;&#34;
135 transposed = [&#39;&#39;] * len(transposition)
136 for p, t in enumerate(transposition):
137 transposed[p] = items[t]
138 return transposed
139
140 def untranspose(items, transposition):
141 &#34;&#34;&#34;Undoes a transpose
142
143 &gt;&gt;&gt; untranspose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], [0,1,2,3])
144 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
145 &gt;&gt;&gt; untranspose([&#39;d&#39;, &#39;b&#39;, &#39;c&#39;, &#39;a&#39;], [3,1,2,0])
146 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
147 &gt;&gt;&gt; untranspose([13, 12, 14, 11, 15, 10], [3,2,4,1,5,0])
148 [10, 11, 12, 13, 14, 15]
149 &#34;&#34;&#34;
150 transposed = [&#39;&#39;] * len(transposition)
151 for p, t in enumerate(transposition):
152 transposed[t] = items[p]
153 return transposed
154
155 def deduplicate(text):
156 &#34;&#34;&#34;Return the input string, but with second (and subsequent) occurrences
157 of a character removed.
158 &#34;&#34;&#34;
159 return list(collections.OrderedDict.fromkeys(text))
160
161
162 def letters(text):
163 &#34;&#34;&#34;Remove all non-alphabetic characters from a text
164 &gt;&gt;&gt; letters(&#39;The Quick&#39;)
165 &#39;TheQuick&#39;
166 &gt;&gt;&gt; letters(&#39;The Quick BROWN fox jumped! over... the (9lazy) DOG&#39;)
167 &#39;TheQuickBROWNfoxjumpedoverthelazyDOG&#39;
168 &#34;&#34;&#34;
169 return &#39;&#39;.join([c for c in text if c in string.ascii_letters])
170
171 # Special characters for conversion, such as smart quotes.
172 unaccent_specials = &#39;&#39;.maketrans({&#34;&#34;: &#34;&#39;&#34;, &#39;&#39;: &#39;&#34;&#39;, &#39;&#39;: &#39;&#34;&#39;})
173
174 def unaccent(text):
175 &#34;&#34;&#34;Remove all accents from letters.
176 It does this by converting the unicode string to decomposed compatability
177 form, dropping all the combining accents, then re-encoding the bytes.
178
179 &gt;&gt;&gt; unaccent(&#39;hello&#39;)
180 &#39;hello&#39;
181 &gt;&gt;&gt; unaccent(&#39;HELLO&#39;)
182 &#39;HELLO&#39;
183 &gt;&gt;&gt; unaccent(&#39;héllo&#39;)
184 &#39;hello&#39;
185 &gt;&gt;&gt; unaccent(&#39;héllö&#39;)
186 &#39;hello&#39;
187 &gt;&gt;&gt; unaccent(&#39;HÉLLÖ&#39;)
188 &#39;HELLO&#39;
189 &#34;&#34;&#34;
190 translated_text = text.translate(unaccent_specials)
191 return unicodedata.normalize(&#39;NFKD&#39;, translated_text).\
192 encode(&#39;ascii&#39;, &#39;ignore&#39;).\
193 decode(&#39;utf-8&#39;)
194
195 def sanitise(text):
196 &#34;&#34;&#34;Remove all non-alphabetic characters and convert the text to lowercase
197
198 &gt;&gt;&gt; sanitise(&#39;The Quick&#39;)
199 &#39;thequick&#39;
200 &gt;&gt;&gt; sanitise(&#39;The Quick BROWN fox jumped! over... the (9lazy) DOG&#39;)
201 &#39;thequickbrownfoxjumpedoverthelazydog&#39;
202 &gt;&gt;&gt; sanitise(&#39;HÉLLÖ&#39;)
203 &#39;hello&#39;
204 &#34;&#34;&#34;
205 return letters(unaccent(text)).lower()
206
207
208 def index_of_coincidence(text):
209 &#34;&#34;&#34;Index of coincidence of a string. This is low for random text,
210 higher for natural langauge.
211 &#34;&#34;&#34;
212 stext = sanitise(text)
213 counts = collections.Counter(stext)
214 denom = len(stext) * (len(text) - 1) / 26
215 return (
216 sum(max(counts[l] * counts[l] - 1, 0) for l in string.ascii_lowercase)
217 /
218 denom
219 )
220
221
222 def frequencies(text):
223 &#34;&#34;&#34;Count the number of occurrences of each character in text
224
225 &gt;&gt;&gt; sorted(frequencies(&#39;abcdefabc&#39;).items())
226 [(&#39;a&#39;, 2), (&#39;b&#39;, 2), (&#39;c&#39;, 2), (&#39;d&#39;, 1), (&#39;e&#39;, 1), (&#39;f&#39;, 1)]
227 &gt;&gt;&gt; sorted(frequencies(&#39;the quick brown fox jumped over the lazy &#39; \
228 &#39;dog&#39;).items()) # doctest: +NORMALIZE_WHITESPACE
229 [(&#39; &#39;, 8), (&#39;a&#39;, 1), (&#39;b&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 2), (&#39;e&#39;, 4), (&#39;f&#39;, 1),
230 (&#39;g&#39;, 1), (&#39;h&#39;, 2), (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1),
231 (&#39;n&#39;, 1), (&#39;o&#39;, 4), (&#39;p&#39;, 1), (&#39;q&#39;, 1), (&#39;r&#39;, 2), (&#39;t&#39;, 2), (&#39;u&#39;, 2),
232 (&#39;v&#39;, 1), (&#39;w&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
233 &gt;&gt;&gt; sorted(frequencies(&#39;The Quick BROWN fox jumped! over... the &#39; \
234 &#39;(9lazy) DOG&#39;).items()) # doctest: +NORMALIZE_WHITESPACE
235 [(&#39; &#39;, 8), (&#39;!&#39;, 1), (&#39;(&#39;, 1), (&#39;)&#39;, 1), (&#39;.&#39;, 3), (&#39;9&#39;, 1), (&#39;B&#39;, 1),
236 (&#39;D&#39;, 1), (&#39;G&#39;, 1), (&#39;N&#39;, 1), (&#39;O&#39;, 2), (&#39;Q&#39;, 1), (&#39;R&#39;, 1), (&#39;T&#39;, 1),
237 (&#39;W&#39;, 1), (&#39;a&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 1), (&#39;e&#39;, 4), (&#39;f&#39;, 1), (&#39;h&#39;, 2),
238 (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1), (&#39;o&#39;, 2), (&#39;p&#39;, 1),
239 (&#39;r&#39;, 1), (&#39;t&#39;, 1), (&#39;u&#39;, 2), (&#39;v&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
240 &gt;&gt;&gt; sorted(frequencies(sanitise(&#39;The Quick BROWN fox jumped! over... &#39;\
241 &#39;the (9lazy) DOG&#39;)).items()) # doctest: +NORMALIZE_WHITESPACE
242 [(&#39;a&#39;, 1), (&#39;b&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 2), (&#39;e&#39;, 4), (&#39;f&#39;, 1), (&#39;g&#39;, 1),
243 (&#39;h&#39;, 2), (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1), (&#39;n&#39;, 1),
244 (&#39;o&#39;, 4), (&#39;p&#39;, 1), (&#39;q&#39;, 1), (&#39;r&#39;, 2), (&#39;t&#39;, 2), (&#39;u&#39;, 2), (&#39;v&#39;, 1),
245 (&#39;w&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
246 &gt;&gt;&gt; frequencies(&#39;abcdefabcdef&#39;)[&#39;x&#39;]
247 0
248 &#34;&#34;&#34;
249 return collections.Counter(c for c in text)
250
251 if __name__ == &#34;__main__&#34;:
252 import doctest
253 doctest.testmod()</code></pre>
254 </details>
255 </section>
256 <section>
257 </section>
258 <section>
259 </section>
260 <section>
261 <h2 class="section-title" id="header-functions">Functions</h2>
262 <dl>
263 <dt id="szyfrow.support.utilities.cat"><code class="name flex">
264 <span>def <span class="ident">cat</span></span>(<span>iterable, /)</span>
265 </code></dt>
266 <dd>
267 <div class="desc"><p>Concatenate any number of strings.</p>
268 <p>The string whose method is called is inserted in between each given string.
269 The result is returned as a new string.</p>
270 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
271 </dd>
272 <dt id="szyfrow.support.utilities.chunks"><code class="name flex">
273 <span>def <span class="ident">chunks</span></span>(<span>text, n, fillvalue=None)</span>
274 </code></dt>
275 <dd>
276 <div class="desc"><p>Split a text into chunks of n characters</p>
277 <pre><code class="language-python-repl">&gt;&gt;&gt; chunks('abcdefghi', 3)
278 ['abc', 'def', 'ghi']
279 &gt;&gt;&gt; chunks('abcdefghi', 4)
280 ['abcd', 'efgh', 'i']
281 &gt;&gt;&gt; chunks('abcdefghi', 4, fillvalue='!')
282 ['abcd', 'efgh', 'i!!!']
283 </code></pre></div>
284 <details class="source">
285 <summary>
286 <span>Expand source code</span>
287 </summary>
288 <pre><code class="python">def chunks(text, n, fillvalue=None):
289 &#34;&#34;&#34;Split a text into chunks of n characters
290
291 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 3)
292 [&#39;abc&#39;, &#39;def&#39;, &#39;ghi&#39;]
293 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 4)
294 [&#39;abcd&#39;, &#39;efgh&#39;, &#39;i&#39;]
295 &gt;&gt;&gt; chunks(&#39;abcdefghi&#39;, 4, fillvalue=&#39;!&#39;)
296 [&#39;abcd&#39;, &#39;efgh&#39;, &#39;i!!!&#39;]
297 &#34;&#34;&#34;
298 if fillvalue:
299 # padding = fillvalue[0] * (n - len(text) % n)
300 padding = pad(len(text), n, fillvalue)
301 padded_text = text + padding
302 else:
303 padded_text = text
304 return [(padded_text)[i:i+n] for i in range(0, len(text), n)]</code></pre>
305 </details>
306 </dd>
307 <dt id="szyfrow.support.utilities.combine_every_nth"><code class="name flex">
308 <span>def <span class="ident">combine_every_nth</span></span>(<span>split_text)</span>
309 </code></dt>
310 <dd>
311 <div class="desc"><p>Reforms a text split into every_nth strings</p>
312 <pre><code class="language-python-repl">&gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 5))
313 'abcdefghijklmnopqrstuvwxyz'
314 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 1))
315 'abcdefghijklmnopqrstuvwxyz'
316 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 26))
317 'abcdefghijklmnopqrstuvwxyz'
318 </code></pre></div>
319 <details class="source">
320 <summary>
321 <span>Expand source code</span>
322 </summary>
323 <pre><code class="python">def combine_every_nth(split_text):
324 &#34;&#34;&#34;Reforms a text split into every_nth strings
325
326 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 5))
327 &#39;abcdefghijklmnopqrstuvwxyz&#39;
328 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 1))
329 &#39;abcdefghijklmnopqrstuvwxyz&#39;
330 &gt;&gt;&gt; combine_every_nth(every_nth(string.ascii_lowercase, 26))
331 &#39;abcdefghijklmnopqrstuvwxyz&#39;
332 &#34;&#34;&#34;
333 return cat([cat(l)
334 for l in zip_longest(*split_text, fillvalue=&#39;&#39;)])</code></pre>
335 </details>
336 </dd>
337 <dt id="szyfrow.support.utilities.deduplicate"><code class="name flex">
338 <span>def <span class="ident">deduplicate</span></span>(<span>text)</span>
339 </code></dt>
340 <dd>
341 <div class="desc"><p>Return the input string, but with second (and subsequent) occurrences
342 of a character removed.</p></div>
343 <details class="source">
344 <summary>
345 <span>Expand source code</span>
346 </summary>
347 <pre><code class="python">def deduplicate(text):
348 &#34;&#34;&#34;Return the input string, but with second (and subsequent) occurrences
349 of a character removed.
350 &#34;&#34;&#34;
351 return list(collections.OrderedDict.fromkeys(text))</code></pre>
352 </details>
353 </dd>
354 <dt id="szyfrow.support.utilities.every_nth"><code class="name flex">
355 <span>def <span class="ident">every_nth</span></span>(<span>text, n, fillvalue='')</span>
356 </code></dt>
357 <dd>
358 <div class="desc"><p>Returns n strings, each of which consists of every nth character,
359 starting with the 0th, 1st, 2nd, &hellip; (n-1)th character</p>
360 <pre><code class="language-python-repl">&gt;&gt;&gt; every_nth(string.ascii_lowercase, 5)
361 ['afkpuz', 'bglqv', 'chmrw', 'dinsx', 'ejoty']
362 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 1)
363 ['abcdefghijklmnopqrstuvwxyz']
364 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 26) # doctest: +NORMALIZE_WHITESPACE
365 ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
366 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
367 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 5, fillvalue='!')
368 ['afkpuz', 'bglqv!', 'chmrw!', 'dinsx!', 'ejoty!']
369 </code></pre></div>
370 <details class="source">
371 <summary>
372 <span>Expand source code</span>
373 </summary>
374 <pre><code class="python">def every_nth(text, n, fillvalue=&#39;&#39;):
375 &#34;&#34;&#34;Returns n strings, each of which consists of every nth character,
376 starting with the 0th, 1st, 2nd, ... (n-1)th character
377
378 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 5)
379 [&#39;afkpuz&#39;, &#39;bglqv&#39;, &#39;chmrw&#39;, &#39;dinsx&#39;, &#39;ejoty&#39;]
380 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 1)
381 [&#39;abcdefghijklmnopqrstuvwxyz&#39;]
382 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 26) # doctest: +NORMALIZE_WHITESPACE
383 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;, &#39;f&#39;, &#39;g&#39;, &#39;h&#39;, &#39;i&#39;, &#39;j&#39;, &#39;k&#39;, &#39;l&#39;, &#39;m&#39;, &#39;n&#39;,
384 &#39;o&#39;, &#39;p&#39;, &#39;q&#39;, &#39;r&#39;, &#39;s&#39;, &#39;t&#39;, &#39;u&#39;, &#39;v&#39;, &#39;w&#39;, &#39;x&#39;, &#39;y&#39;, &#39;z&#39;]
385 &gt;&gt;&gt; every_nth(string.ascii_lowercase, 5, fillvalue=&#39;!&#39;)
386 [&#39;afkpuz&#39;, &#39;bglqv!&#39;, &#39;chmrw!&#39;, &#39;dinsx!&#39;, &#39;ejoty!&#39;]
387 &#34;&#34;&#34;
388 split_text = chunks(text, n, fillvalue)
389 return [cat(l) for l in zip_longest(*split_text, fillvalue=fillvalue)]</code></pre>
390 </details>
391 </dd>
392 <dt id="szyfrow.support.utilities.frequencies"><code class="name flex">
393 <span>def <span class="ident">frequencies</span></span>(<span>text)</span>
394 </code></dt>
395 <dd>
396 <div class="desc"><p>Count the number of occurrences of each character in text</p>
397 <pre><code class="language-python-repl">&gt;&gt;&gt; sorted(frequencies('abcdefabc').items())
398 [('a', 2), ('b', 2), ('c', 2), ('d', 1), ('e', 1), ('f', 1)]
399 &gt;&gt;&gt; sorted(frequencies('the quick brown fox jumped over the lazy ' 'dog').items()) # doctest: +NORMALIZE_WHITESPACE
400 [(' ', 8), ('a', 1), ('b', 1), ('c', 1), ('d', 2), ('e', 4), ('f', 1),
401 ('g', 1), ('h', 2), ('i', 1), ('j', 1), ('k', 1), ('l', 1), ('m', 1),
402 ('n', 1), ('o', 4), ('p', 1), ('q', 1), ('r', 2), ('t', 2), ('u', 2),
403 ('v', 1), ('w', 1), ('x', 1), ('y', 1), ('z', 1)]
404 &gt;&gt;&gt; sorted(frequencies('The Quick BROWN fox jumped! over... the ' '(9lazy) DOG').items()) # doctest: +NORMALIZE_WHITESPACE
405 [(' ', 8), ('!', 1), ('(', 1), (')', 1), ('.', 3), ('9', 1), ('B', 1),
406 ('D', 1), ('G', 1), ('N', 1), ('O', 2), ('Q', 1), ('R', 1), ('T', 1),
407 ('W', 1), ('a', 1), ('c', 1), ('d', 1), ('e', 4), ('f', 1), ('h', 2),
408 ('i', 1), ('j', 1), ('k', 1), ('l', 1), ('m', 1), ('o', 2), ('p', 1),
409 ('r', 1), ('t', 1), ('u', 2), ('v', 1), ('x', 1), ('y', 1), ('z', 1)]
410 &gt;&gt;&gt; sorted(frequencies(sanitise('The Quick BROWN fox jumped! over... ' 'the (9lazy) DOG')).items()) # doctest: +NORMALIZE_WHITESPACE
411 [('a', 1), ('b', 1), ('c', 1), ('d', 2), ('e', 4), ('f', 1), ('g', 1),
412 ('h', 2), ('i', 1), ('j', 1), ('k', 1), ('l', 1), ('m', 1), ('n', 1),
413 ('o', 4), ('p', 1), ('q', 1), ('r', 2), ('t', 2), ('u', 2), ('v', 1),
414 ('w', 1), ('x', 1), ('y', 1), ('z', 1)]
415 &gt;&gt;&gt; frequencies('abcdefabcdef')['x']
416 0
417 </code></pre></div>
418 <details class="source">
419 <summary>
420 <span>Expand source code</span>
421 </summary>
422 <pre><code class="python">def frequencies(text):
423 &#34;&#34;&#34;Count the number of occurrences of each character in text
424
425 &gt;&gt;&gt; sorted(frequencies(&#39;abcdefabc&#39;).items())
426 [(&#39;a&#39;, 2), (&#39;b&#39;, 2), (&#39;c&#39;, 2), (&#39;d&#39;, 1), (&#39;e&#39;, 1), (&#39;f&#39;, 1)]
427 &gt;&gt;&gt; sorted(frequencies(&#39;the quick brown fox jumped over the lazy &#39; \
428 &#39;dog&#39;).items()) # doctest: +NORMALIZE_WHITESPACE
429 [(&#39; &#39;, 8), (&#39;a&#39;, 1), (&#39;b&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 2), (&#39;e&#39;, 4), (&#39;f&#39;, 1),
430 (&#39;g&#39;, 1), (&#39;h&#39;, 2), (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1),
431 (&#39;n&#39;, 1), (&#39;o&#39;, 4), (&#39;p&#39;, 1), (&#39;q&#39;, 1), (&#39;r&#39;, 2), (&#39;t&#39;, 2), (&#39;u&#39;, 2),
432 (&#39;v&#39;, 1), (&#39;w&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
433 &gt;&gt;&gt; sorted(frequencies(&#39;The Quick BROWN fox jumped! over... the &#39; \
434 &#39;(9lazy) DOG&#39;).items()) # doctest: +NORMALIZE_WHITESPACE
435 [(&#39; &#39;, 8), (&#39;!&#39;, 1), (&#39;(&#39;, 1), (&#39;)&#39;, 1), (&#39;.&#39;, 3), (&#39;9&#39;, 1), (&#39;B&#39;, 1),
436 (&#39;D&#39;, 1), (&#39;G&#39;, 1), (&#39;N&#39;, 1), (&#39;O&#39;, 2), (&#39;Q&#39;, 1), (&#39;R&#39;, 1), (&#39;T&#39;, 1),
437 (&#39;W&#39;, 1), (&#39;a&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 1), (&#39;e&#39;, 4), (&#39;f&#39;, 1), (&#39;h&#39;, 2),
438 (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1), (&#39;o&#39;, 2), (&#39;p&#39;, 1),
439 (&#39;r&#39;, 1), (&#39;t&#39;, 1), (&#39;u&#39;, 2), (&#39;v&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
440 &gt;&gt;&gt; sorted(frequencies(sanitise(&#39;The Quick BROWN fox jumped! over... &#39;\
441 &#39;the (9lazy) DOG&#39;)).items()) # doctest: +NORMALIZE_WHITESPACE
442 [(&#39;a&#39;, 1), (&#39;b&#39;, 1), (&#39;c&#39;, 1), (&#39;d&#39;, 2), (&#39;e&#39;, 4), (&#39;f&#39;, 1), (&#39;g&#39;, 1),
443 (&#39;h&#39;, 2), (&#39;i&#39;, 1), (&#39;j&#39;, 1), (&#39;k&#39;, 1), (&#39;l&#39;, 1), (&#39;m&#39;, 1), (&#39;n&#39;, 1),
444 (&#39;o&#39;, 4), (&#39;p&#39;, 1), (&#39;q&#39;, 1), (&#39;r&#39;, 2), (&#39;t&#39;, 2), (&#39;u&#39;, 2), (&#39;v&#39;, 1),
445 (&#39;w&#39;, 1), (&#39;x&#39;, 1), (&#39;y&#39;, 1), (&#39;z&#39;, 1)]
446 &gt;&gt;&gt; frequencies(&#39;abcdefabcdef&#39;)[&#39;x&#39;]
447 0
448 &#34;&#34;&#34;
449 return collections.Counter(c for c in text)</code></pre>
450 </details>
451 </dd>
452 <dt id="szyfrow.support.utilities.index_of_coincidence"><code class="name flex">
453 <span>def <span class="ident">index_of_coincidence</span></span>(<span>text)</span>
454 </code></dt>
455 <dd>
456 <div class="desc"><p>Index of coincidence of a string. This is low for random text,
457 higher for natural langauge.</p></div>
458 <details class="source">
459 <summary>
460 <span>Expand source code</span>
461 </summary>
462 <pre><code class="python">def index_of_coincidence(text):
463 &#34;&#34;&#34;Index of coincidence of a string. This is low for random text,
464 higher for natural langauge.
465 &#34;&#34;&#34;
466 stext = sanitise(text)
467 counts = collections.Counter(stext)
468 denom = len(stext) * (len(text) - 1) / 26
469 return (
470 sum(max(counts[l] * counts[l] - 1, 0) for l in string.ascii_lowercase)
471 /
472 denom
473 )</code></pre>
474 </details>
475 </dd>
476 <dt id="szyfrow.support.utilities.lcat"><code class="name flex">
477 <span>def <span class="ident">lcat</span></span>(<span>iterable, /)</span>
478 </code></dt>
479 <dd>
480 <div class="desc"><p>Concatenate any number of strings.</p>
481 <p>The string whose method is called is inserted in between each given string.
482 The result is returned as a new string.</p>
483 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
484 </dd>
485 <dt id="szyfrow.support.utilities.letters"><code class="name flex">
486 <span>def <span class="ident">letters</span></span>(<span>text)</span>
487 </code></dt>
488 <dd>
489 <div class="desc"><p>Remove all non-alphabetic characters from a text</p>
490 <pre><code class="language-python-repl">&gt;&gt;&gt; letters('The Quick')
491 'TheQuick'
492 &gt;&gt;&gt; letters('The Quick BROWN fox jumped! over... the (9lazy) DOG')
493 'TheQuickBROWNfoxjumpedoverthelazyDOG'
494 </code></pre></div>
495 <details class="source">
496 <summary>
497 <span>Expand source code</span>
498 </summary>
499 <pre><code class="python">def letters(text):
500 &#34;&#34;&#34;Remove all non-alphabetic characters from a text
501 &gt;&gt;&gt; letters(&#39;The Quick&#39;)
502 &#39;TheQuick&#39;
503 &gt;&gt;&gt; letters(&#39;The Quick BROWN fox jumped! over... the (9lazy) DOG&#39;)
504 &#39;TheQuickBROWNfoxjumpedoverthelazyDOG&#39;
505 &#34;&#34;&#34;
506 return &#39;&#39;.join([c for c in text if c in string.ascii_letters])</code></pre>
507 </details>
508 </dd>
509 <dt id="szyfrow.support.utilities.pad"><code class="name flex">
510 <span>def <span class="ident">pad</span></span>(<span>message_len, group_len, fillvalue)</span>
511 </code></dt>
512 <dd>
513 <div class="desc"><p>Return the padding needed to extend a message to a multiple of group_len
514 in length.</p>
515 <p>fillvalue can be a function or a literal value. If a function, it is called
516 once for each padded character. Use this with fillvalue=random_english_letter
517 to pad a message with random letters.</p></div>
518 <details class="source">
519 <summary>
520 <span>Expand source code</span>
521 </summary>
522 <pre><code class="python">def pad(message_len, group_len, fillvalue):
523 &#34;&#34;&#34;Return the padding needed to extend a message to a multiple of group_len
524 in length.
525
526 fillvalue can be a function or a literal value. If a function, it is called
527 once for each padded character. Use this with fillvalue=random_english_letter
528 to pad a message with random letters.
529 &#34;&#34;&#34;
530 padding_length = group_len - message_len % group_len
531 if padding_length == group_len: padding_length = 0
532 padding = &#39;&#39;
533 if callable(fillvalue):
534 for i in range(padding_length):
535 padding += fillvalue()
536 else:
537 padding += fillvalue * padding_length
538 return padding</code></pre>
539 </details>
540 </dd>
541 <dt id="szyfrow.support.utilities.pos"><code class="name flex">
542 <span>def <span class="ident">pos</span></span>(<span>letter)</span>
543 </code></dt>
544 <dd>
545 <div class="desc"><p>Return the position of a letter in the alphabet (0-25)</p></div>
546 <details class="source">
547 <summary>
548 <span>Expand source code</span>
549 </summary>
550 <pre><code class="python">def pos(letter):
551 &#34;&#34;&#34;Return the position of a letter in the alphabet (0-25)&#34;&#34;&#34;
552 if letter in string.ascii_lowercase:
553 return ord(letter) - ord(&#39;a&#39;)
554 elif letter in string.ascii_uppercase:
555 return ord(letter) - ord(&#39;A&#39;)
556 else:
557 raise ValueError(&#39;pos requires input of {} to be an ascii letter&#39;.format(letter))</code></pre>
558 </details>
559 </dd>
560 <dt id="szyfrow.support.utilities.sanitise"><code class="name flex">
561 <span>def <span class="ident">sanitise</span></span>(<span>text)</span>
562 </code></dt>
563 <dd>
564 <div class="desc"><p>Remove all non-alphabetic characters and convert the text to lowercase</p>
565 <pre><code class="language-python-repl">&gt;&gt;&gt; sanitise('The Quick')
566 'thequick'
567 &gt;&gt;&gt; sanitise('The Quick BROWN fox jumped! over... the (9lazy) DOG')
568 'thequickbrownfoxjumpedoverthelazydog'
569 &gt;&gt;&gt; sanitise('HÉLLÖ')
570 'hello'
571 </code></pre></div>
572 <details class="source">
573 <summary>
574 <span>Expand source code</span>
575 </summary>
576 <pre><code class="python">def sanitise(text):
577 &#34;&#34;&#34;Remove all non-alphabetic characters and convert the text to lowercase
578
579 &gt;&gt;&gt; sanitise(&#39;The Quick&#39;)
580 &#39;thequick&#39;
581 &gt;&gt;&gt; sanitise(&#39;The Quick BROWN fox jumped! over... the (9lazy) DOG&#39;)
582 &#39;thequickbrownfoxjumpedoverthelazydog&#39;
583 &gt;&gt;&gt; sanitise(&#39;HÉLLÖ&#39;)
584 &#39;hello&#39;
585 &#34;&#34;&#34;
586 return letters(unaccent(text)).lower()</code></pre>
587 </details>
588 </dd>
589 <dt id="szyfrow.support.utilities.transpose"><code class="name flex">
590 <span>def <span class="ident">transpose</span></span>(<span>items, transposition)</span>
591 </code></dt>
592 <dd>
593 <div class="desc"><p>Moves items around according to the given transposition</p>
594 <pre><code class="language-python-repl">&gt;&gt;&gt; transpose(['a', 'b', 'c', 'd'], (0,1,2,3))
595 ['a', 'b', 'c', 'd']
596 &gt;&gt;&gt; transpose(['a', 'b', 'c', 'd'], (3,1,2,0))
597 ['d', 'b', 'c', 'a']
598 &gt;&gt;&gt; transpose([10,11,12,13,14,15], (3,2,4,1,5,0))
599 [13, 12, 14, 11, 15, 10]
600 </code></pre></div>
601 <details class="source">
602 <summary>
603 <span>Expand source code</span>
604 </summary>
605 <pre><code class="python">def transpose(items, transposition):
606 &#34;&#34;&#34;Moves items around according to the given transposition
607
608 &gt;&gt;&gt; transpose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], (0,1,2,3))
609 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
610 &gt;&gt;&gt; transpose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], (3,1,2,0))
611 [&#39;d&#39;, &#39;b&#39;, &#39;c&#39;, &#39;a&#39;]
612 &gt;&gt;&gt; transpose([10,11,12,13,14,15], (3,2,4,1,5,0))
613 [13, 12, 14, 11, 15, 10]
614 &#34;&#34;&#34;
615 transposed = [&#39;&#39;] * len(transposition)
616 for p, t in enumerate(transposition):
617 transposed[p] = items[t]
618 return transposed</code></pre>
619 </details>
620 </dd>
621 <dt id="szyfrow.support.utilities.unaccent"><code class="name flex">
622 <span>def <span class="ident">unaccent</span></span>(<span>text)</span>
623 </code></dt>
624 <dd>
625 <div class="desc"><p>Remove all accents from letters.
626 It does this by converting the unicode string to decomposed compatability
627 form, dropping all the combining accents, then re-encoding the bytes.</p>
628 <pre><code class="language-python-repl">&gt;&gt;&gt; unaccent('hello')
629 'hello'
630 &gt;&gt;&gt; unaccent('HELLO')
631 'HELLO'
632 &gt;&gt;&gt; unaccent('héllo')
633 'hello'
634 &gt;&gt;&gt; unaccent('héllö')
635 'hello'
636 &gt;&gt;&gt; unaccent('HÉLLÖ')
637 'HELLO'
638 </code></pre></div>
639 <details class="source">
640 <summary>
641 <span>Expand source code</span>
642 </summary>
643 <pre><code class="python">def unaccent(text):
644 &#34;&#34;&#34;Remove all accents from letters.
645 It does this by converting the unicode string to decomposed compatability
646 form, dropping all the combining accents, then re-encoding the bytes.
647
648 &gt;&gt;&gt; unaccent(&#39;hello&#39;)
649 &#39;hello&#39;
650 &gt;&gt;&gt; unaccent(&#39;HELLO&#39;)
651 &#39;HELLO&#39;
652 &gt;&gt;&gt; unaccent(&#39;héllo&#39;)
653 &#39;hello&#39;
654 &gt;&gt;&gt; unaccent(&#39;héllö&#39;)
655 &#39;hello&#39;
656 &gt;&gt;&gt; unaccent(&#39;HÉLLÖ&#39;)
657 &#39;HELLO&#39;
658 &#34;&#34;&#34;
659 translated_text = text.translate(unaccent_specials)
660 return unicodedata.normalize(&#39;NFKD&#39;, translated_text).\
661 encode(&#39;ascii&#39;, &#39;ignore&#39;).\
662 decode(&#39;utf-8&#39;)</code></pre>
663 </details>
664 </dd>
665 <dt id="szyfrow.support.utilities.unpos"><code class="name flex">
666 <span>def <span class="ident">unpos</span></span>(<span>number)</span>
667 </code></dt>
668 <dd>
669 <div class="desc"><p>Return the letter in the given position in the alphabet (mod 26)</p></div>
670 <details class="source">
671 <summary>
672 <span>Expand source code</span>
673 </summary>
674 <pre><code class="python">def unpos(number):
675 &#34;&#34;&#34;Return the letter in the given position in the alphabet (mod 26)&#34;&#34;&#34;
676 return chr(number % 26 + ord(&#39;a&#39;))</code></pre>
677 </details>
678 </dd>
679 <dt id="szyfrow.support.utilities.untranspose"><code class="name flex">
680 <span>def <span class="ident">untranspose</span></span>(<span>items, transposition)</span>
681 </code></dt>
682 <dd>
683 <div class="desc"><p>Undoes a transpose</p>
684 <pre><code class="language-python-repl">&gt;&gt;&gt; untranspose(['a', 'b', 'c', 'd'], [0,1,2,3])
685 ['a', 'b', 'c', 'd']
686 &gt;&gt;&gt; untranspose(['d', 'b', 'c', 'a'], [3,1,2,0])
687 ['a', 'b', 'c', 'd']
688 &gt;&gt;&gt; untranspose([13, 12, 14, 11, 15, 10], [3,2,4,1,5,0])
689 [10, 11, 12, 13, 14, 15]
690 </code></pre></div>
691 <details class="source">
692 <summary>
693 <span>Expand source code</span>
694 </summary>
695 <pre><code class="python">def untranspose(items, transposition):
696 &#34;&#34;&#34;Undoes a transpose
697
698 &gt;&gt;&gt; untranspose([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;], [0,1,2,3])
699 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
700 &gt;&gt;&gt; untranspose([&#39;d&#39;, &#39;b&#39;, &#39;c&#39;, &#39;a&#39;], [3,1,2,0])
701 [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;]
702 &gt;&gt;&gt; untranspose([13, 12, 14, 11, 15, 10], [3,2,4,1,5,0])
703 [10, 11, 12, 13, 14, 15]
704 &#34;&#34;&#34;
705 transposed = [&#39;&#39;] * len(transposition)
706 for p, t in enumerate(transposition):
707 transposed[t] = items[p]
708 return transposed</code></pre>
709 </details>
710 </dd>
711 <dt id="szyfrow.support.utilities.wcat"><code class="name flex">
712 <span>def <span class="ident">wcat</span></span>(<span>iterable, /)</span>
713 </code></dt>
714 <dd>
715 <div class="desc"><p>Concatenate any number of strings.</p>
716 <p>The string whose method is called is inserted in between each given string.
717 The result is returned as a new string.</p>
718 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
719 </dd>
720 </dl>
721 </section>
722 <section>
723 </section>
724 </article>
725 <nav id="sidebar">
726 <h1>Index</h1>
727 <div class="toc">
728 <ul></ul>
729 </div>
730 <ul id="index">
731 <li><h3>Super-module</h3>
732 <ul>
733 <li><code><a title="szyfrow.support" href="index.html">szyfrow.support</a></code></li>
734 </ul>
735 </li>
736 <li><h3><a href="#header-functions">Functions</a></h3>
737 <ul class="">
738 <li><code><a title="szyfrow.support.utilities.cat" href="#szyfrow.support.utilities.cat">cat</a></code></li>
739 <li><code><a title="szyfrow.support.utilities.chunks" href="#szyfrow.support.utilities.chunks">chunks</a></code></li>
740 <li><code><a title="szyfrow.support.utilities.combine_every_nth" href="#szyfrow.support.utilities.combine_every_nth">combine_every_nth</a></code></li>
741 <li><code><a title="szyfrow.support.utilities.deduplicate" href="#szyfrow.support.utilities.deduplicate">deduplicate</a></code></li>
742 <li><code><a title="szyfrow.support.utilities.every_nth" href="#szyfrow.support.utilities.every_nth">every_nth</a></code></li>
743 <li><code><a title="szyfrow.support.utilities.frequencies" href="#szyfrow.support.utilities.frequencies">frequencies</a></code></li>
744 <li><code><a title="szyfrow.support.utilities.index_of_coincidence" href="#szyfrow.support.utilities.index_of_coincidence">index_of_coincidence</a></code></li>
745 <li><code><a title="szyfrow.support.utilities.lcat" href="#szyfrow.support.utilities.lcat">lcat</a></code></li>
746 <li><code><a title="szyfrow.support.utilities.letters" href="#szyfrow.support.utilities.letters">letters</a></code></li>
747 <li><code><a title="szyfrow.support.utilities.pad" href="#szyfrow.support.utilities.pad">pad</a></code></li>
748 <li><code><a title="szyfrow.support.utilities.pos" href="#szyfrow.support.utilities.pos">pos</a></code></li>
749 <li><code><a title="szyfrow.support.utilities.sanitise" href="#szyfrow.support.utilities.sanitise">sanitise</a></code></li>
750 <li><code><a title="szyfrow.support.utilities.transpose" href="#szyfrow.support.utilities.transpose">transpose</a></code></li>
751 <li><code><a title="szyfrow.support.utilities.unaccent" href="#szyfrow.support.utilities.unaccent">unaccent</a></code></li>
752 <li><code><a title="szyfrow.support.utilities.unpos" href="#szyfrow.support.utilities.unpos">unpos</a></code></li>
753 <li><code><a title="szyfrow.support.utilities.untranspose" href="#szyfrow.support.utilities.untranspose">untranspose</a></code></li>
754 <li><code><a title="szyfrow.support.utilities.wcat" href="#szyfrow.support.utilities.wcat">wcat</a></code></li>
755 </ul>
756 </li>
757 </ul>
758 </nav>
759 </main>
760 <footer id="footer">
761 <p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
762 </footer>
763 </body>
764 </html>