8dfe7242a4efb5f4ee40da253d888294dde9921c
[szyfrow.git] / docs / szyfrow / enigma.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.enigma API documentation</title>
8 <meta name="description" content="A simulator for Enigma machines …" />
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.enigma</code></h1>
23 </header>
24 <section id="section-intro">
25 <p>A simulator for Enigma machines.</p>
26 <p>See <code><a title="szyfrow.bombe.Bombe" href="bombe.html#szyfrow.bombe.Bombe">Bombe</a></code> for an implementation of the Bombe to break Enigma
27 messages.</p>
28 <p>Specification from <a href="http://www.codesandciphers.org.uk/enigma/rotorspec.htm">Codes and Ciphers</a> page.</p>
29 <p>Example Enigma machines from <a href="http://enigma.louisedade.co.uk/enigma.html">Louise Dale</a> (full simulation) and <a href="http://enigmaco.de/enigma/enigma.html">EnigmaCo</a> (good animation of the wheels, but no ring settings).</p>
30 <p>There's also the nice Enigma simulator for Android by <a href="https://franklinheath.co.uk/2012/02/04/our-first-app-published-enigma-simulator/">Franklin Heath</a>, available on the <a href="https://play.google.com/store/apps/details?id=uk.co.franklinheath.enigmasim&amp;hl=en_GB">Google Play store</a>.</p>
31 <details class="source">
32 <summary>
33 <span>Expand source code</span>
34 </summary>
35 <pre><code class="python">&#34;&#34;&#34;A simulator for Enigma machines.
36
37 See `szyfrow.bombe.Bombe` for an implementation of the Bombe to break Enigma
38 messages.
39
40 Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page.
41
42 Example Enigma machines from [Louise Dale](http://enigma.louisedade.co.uk/enigma.html) (full simulation) and [EnigmaCo](http://enigmaco.de/enigma/enigma.html) (good animation of the wheels, but no ring settings).
43
44 There&#39;s also the nice Enigma simulator for Android by [Franklin Heath](https://franklinheath.co.uk/2012/02/04/our-first-app-published-enigma-simulator/), available on the [Google Play store](https://play.google.com/store/apps/details?id=uk.co.franklinheath.enigmasim&amp;hl=en_GB).
45 &#34;&#34;&#34;
46
47 import string
48 import collections
49 import multiprocessing
50 import itertools
51
52 from szyfrow.support.utilities import *
53
54 # # Some convenience functions
55
56 # cat = &#39;&#39;.join
57
58 # def clean(text): return cat(l.lower() for l in text if l in string.ascii_letters)
59
60 # def pos(letter):
61 # if letter in string.ascii_lowercase:
62 # return ord(letter) - ord(&#39;a&#39;)
63 # elif letter in string.ascii_uppercase:
64 # return ord(letter) - ord(&#39;A&#39;)
65 # else:
66 # return &#39;&#39;
67
68 # def unpos(number): return chr(number % 26 + ord(&#39;a&#39;))
69
70
71 wheel_i_spec = &#39;ekmflgdqvzntowyhxuspaibrcj&#39;
72 wheel_ii_spec = &#39;ajdksiruxblhwtmcqgznpyfvoe&#39;
73 wheel_iii_spec = &#39;bdfhjlcprtxvznyeiwgakmusqo&#39;
74 wheel_iv_spec = &#39;esovpzjayquirhxlnftgkdcmwb&#39;
75 wheel_v_spec = &#39;vzbrgityupsdnhlxawmjqofeck&#39;
76 wheel_vi_spec = &#39;jpgvoumfyqbenhzrdkasxlictw&#39;
77 wheel_vii_spec = &#39;nzjhgrcxmyswboufaivlpekqdt&#39;
78 wheel_viii_spec = &#39;fkqhtlxocbjspdzramewniuygv&#39;
79 beta_wheel_spec = &#39;leyjvcnixwpbqmdrtakzgfuhos&#39;
80 gamma_wheel_spec = &#39;fsokanuerhmbtiycwlqpzxvgjd&#39;
81
82 wheel_i_notches = [&#39;q&#39;]
83 wheel_ii_notches = [&#39;e&#39;]
84 wheel_iii_notches = [&#39;v&#39;]
85 wheel_iv_notches = [&#39;j&#39;]
86 wheel_v_notches = [&#39;z&#39;]
87 wheel_vi_notches = [&#39;z&#39;, &#39;m&#39;]
88 wheel_vii_notches = [&#39;z&#39;, &#39;m&#39;]
89 wheel_viii_notches = [&#39;z&#39;, &#39;m&#39;]
90
91 reflector_b_spec = &#39;ay br cu dh eq fs gl ip jx kn mo tz vw&#39;
92 reflector_c_spec = &#39;af bv cp dj ei go hy kr lz mx nw tq su&#39;
93
94
95
96 class LetterTransformer(object):
97 &#34;&#34;&#34;A generic substitution cipher, that has different transforms in the
98 forward and backward directions. It requires that the transforms for all
99 letters by provided.
100
101 A `transform` is a list of letter pairs, like `[(&#39;a&#39;, &#39;b&#39;), (&#39;c&#39;, &#39;d&#39;)]`.
102 That would say that, in the forward direction `a` goes to `b` and
103 `c` goes to `d`. In the backward direction, `b` goes to `a` and `d` goes
104 to `c`.
105 &#34;&#34;&#34;
106 def __init__(self, specification, raw_transform=False):
107 &#34;&#34;&#34;Validate and create a new transformer. The transform is parsed by
108 `LetterTransformer.parse_specification` unless `raw_transform` is `True`
109 &#34;&#34;&#34;
110 if raw_transform:
111 transform = specification
112 else:
113 transform = self.parse_specification(specification)
114 self.validate_transform(transform)
115 self.make_transform_map(transform)
116
117 def parse_specification(self, specification):
118 &#34;&#34;&#34;Turn a `specification` string into a transform, by zipping it
119 with ASCII lowercase letters to generate the pairs. This assumes that
120 the `specification` defines the destination of the forward transform.
121 &#34;&#34;&#34;
122 return list(zip(string.ascii_lowercase, sanitise(specification)))
123 # return specification
124
125 def validate_transform(self, transform):
126 &#34;&#34;&#34;Checks that a transform is valid (every letter is mapped to
127 exactly one other letter, in both directions).
128 &#34;&#34;&#34;
129 if len(transform) != 26:
130 raise ValueError(&#34;Transform specification has {} pairs, requires 26&#34;.
131 format(len(transform)))
132 for p in transform:
133 if len(p) != 2:
134 raise ValueError(&#34;Not all mappings in transform &#34;
135 &#34;have two elements&#34;)
136 if len(set([p[0] for p in transform])) != 26:
137 raise ValueError(&#34;Transform specification must list 26 origin letters&#34;)
138 if len(set([p[1] for p in transform])) != 26:
139 raise ValueError(&#34;Transform specification must list 26 destination letters&#34;)
140
141 def make_empty_transform(self):
142 &#34;&#34;&#34;An empty transform is one that maps every letter to &#39;a&#39;.
143 &#34;&#34;&#34;
144 self.forward_map = [0] * 26
145 self.backward_map = [0] * 26
146
147 def make_transform_map(self, transform):
148 &#34;&#34;&#34;Create `forward_map` and `backward_map` from `transform`. The maps
149 work on letter positions, not letter values. This makes the arithmetic
150 for wheels much easier.
151 &#34;&#34;&#34;
152 self.make_empty_transform()
153 for p in transform:
154 self.forward_map[pos(p[0])] = pos(p[1])
155 self.backward_map[pos(p[1])] = pos(p[0])
156 return self.forward_map, self.backward_map
157
158 def forward(self, letter):
159 &#34;&#34;&#34;Apply a map in the forward direction.
160 &#34;&#34;&#34;
161 if letter in string.ascii_lowercase:
162 return unpos(self.forward_map[pos(letter)])
163 else:
164 return &#39;&#39;
165
166 def backward(self, letter):
167 &#34;&#34;&#34;Apply a map in the backward direction.
168 &#34;&#34;&#34;
169 if letter in string.ascii_lowercase:
170 return unpos(self.backward_map[pos(letter)])
171 else:
172 return &#39;&#39;
173
174
175 class Plugboard(LetterTransformer):
176 &#34;&#34;&#34;A plugboard, a type of letter transformer where forward and backward
177 transforms are the same. If a letter isn&#39;t explicitly transformed, it is
178 kept as it is.
179 &#34;&#34;&#34;
180
181 def parse_specification(self, specification):
182 &#34;&#34;&#34;Convert a specification into a transform. The specification is
183 given as a list of letter pairs.
184 &#34;&#34;&#34;
185 return [tuple(sanitise(p)) for p in specification.split()]
186
187 def validate_transform(self, transform):
188 &#34;&#34;&#34;A set of pairs, of from-to. Does not require all 26 letters
189 are in the transform.
190 &#34;&#34;&#34;
191 for p in transform:
192 if len(p) != 2:
193 raise ValueError(&#34;Not all mappings in transform&#34;
194 &#34;have two elements&#34;)
195
196 def make_empty_transform(self):
197 &#34;&#34;&#34;An empty transform maps every letter to itself.
198 &#34;&#34;&#34;
199 self.forward_map = list(range(26))
200 self.backward_map = list(range(26))
201
202 def make_transform_map(self, transform):
203 &#34;&#34;&#34;Makes the maps for a plugboard. Ensures that if the pair (&#39;a&#39;, &#39;b&#39;)
204 is in the specification, the pair (&#39;b&#39;, &#39;a&#39;) is also present.
205 &#34;&#34;&#34;
206 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
207 return super(Plugboard, self).make_transform_map(expanded_transform)
208
209
210
211
212 class Reflector(Plugboard):
213 &#34;&#34;&#34;A reflector is a plugboard that requires 13 transforms.
214 The &#39;plugboard&#39; superclass ensures that all 13 transforms are also applied
215 in reverse, making 26 transforms in all.
216 &#34;&#34;&#34;
217 def validate_transform(self, transform):
218 if len(transform) != 13:
219 raise ValueError(&#34;Reflector specification has {} pairs, requires 13&#34;.
220 format(len(transform)))
221 if len(set([p[0] for p in transform] +
222 [p[1] for p in transform])) != 26:
223 raise ValueError(&#34;Reflector specification does not contain 26 letters&#34;)
224 try:
225 super(Reflector, self).validate_transform(transform)
226 except ValueError as v:
227 raise ValueError(&#34;Not all mappings in reflector have two elements&#34;)
228
229
230
231
232 class SimpleWheel(LetterTransformer):
233 &#34;&#34;&#34;A wheel is a transform that rotates.
234
235 Looking from the right, letters go in sequence a-b-c clockwise around the
236 wheel.
237
238 The position of the wheel is the number of spaces anticlockwise the wheel
239 has turned.
240
241 Letter inputs and outputs are given relative to the frame holding the wheel,
242 so if the wheel is advanced three places, an input of &#39;p&#39; will enter the
243 wheel on the position under the wheel&#39;s &#39;s&#39; label.
244 &#34;&#34;&#34;
245 def __init__(self, transform, position=&#39;a&#39;, raw_transform=False):
246 super(SimpleWheel, self).__init__(transform, raw_transform)
247 self.set_position(position)
248
249 def __getattribute__(self,name):
250 if name==&#39;position_l&#39;:
251 return unpos(self.position)
252 else:
253 return object.__getattribute__(self, name)
254
255 def set_position(self, position):
256 &#34;&#34;&#34;Sets a wheel&#39;s position. If the `position` is a string, convert it
257 to a number and set the position.
258 &#34;&#34;&#34;
259 if isinstance(position, str):
260 self.position = pos(position)
261 else:
262 self.position = position
263
264 def forward(self, letter):
265 &#34;&#34;&#34;Give the transformed letter in the forward direction, accounting
266 for the position of the wheel.
267 &#34;&#34;&#34;
268 if letter in string.ascii_lowercase:
269 return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))
270 else:
271 return &#39;&#39;
272
273 def backward(self, letter):
274 &#34;&#34;&#34;Give the transformed letter in the backward direction, accounting
275 for the position of the wheel.
276 &#34;&#34;&#34;
277 if letter in string.ascii_lowercase:
278 return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))
279 else:
280 return &#39;&#39;
281
282 def advance(self):
283 &#34;&#34;&#34;Advance a wheel one position.&#34;&#34;&#34;
284 self.position = (self.position + 1) % 26
285
286
287
288 class Wheel(SimpleWheel):
289 &#34;&#34;&#34;A wheel with a movable ring.
290
291 The ring holds the letters and the notches that turn other wheels. The core
292 holds the wiring that does the transformation.
293
294 The ring position is how many steps the core is turned relative to the ring.
295 This is one-based, so a ring setting of 1 means the core and ring are
296 aligned.
297
298 The position of the wheel is the position of the core (the transforms)
299 relative to the neutral position.
300
301 The position_l is the position of the ring, or what would be observed
302 by the user of the Enigma machine.
303
304 The notch_positions are the number of advances of this wheel before it will
305 advance the next wheel.
306 &#34;&#34;&#34;
307 def __init__(self, transform, ring_notch_letters, ring_setting=1,
308 position=&#39;a&#39;, raw_transform=False):
309 self.ring_notch_letters = ring_notch_letters
310 self.ring_setting = ring_setting
311 super(Wheel, self).__init__(transform, position=position,
312 raw_transform=raw_transform)
313 self.set_position(position)
314
315 def __getattribute__(self,name):
316 if name==&#39;position_l&#39;:
317 return unpos(self.position + self.ring_setting - 1)
318 else:
319 return object.__getattribute__(self, name)
320
321 def set_position(self, position):
322 if isinstance(position, str):
323 self.position = (pos(position) - self.ring_setting + 1) % 26
324 else:
325 self.position = (position - self.ring_setting) % 26
326 # # self.notch_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_notch_letters]
327 # self.notch_positions = [(pos(p) - (self.position + self.ring_setting - 1)) % 26 for p in self.ring_notch_letters]
328 self.notch_positions = [(self.position + self.ring_setting - 1 - pos(p)) % 26 for p in self.ring_notch_letters]
329
330 def advance(self):
331 &#34;&#34;&#34;Advance a wheel&#39;s core, then advance the ring position to match.
332 &#34;&#34;&#34;
333 super(Wheel, self).advance()
334 self.notch_positions = [(p + 1) % 26 for p in self.notch_positions]
335 return self.position
336
337
338 class Enigma(object):
339 &#34;&#34;&#34;An Enigma machine.
340
341
342 &#34;&#34;&#34;
343 def __init__(self, reflector_spec,
344 left_wheel_spec, left_wheel_notches,
345 middle_wheel_spec, middle_wheel_notches,
346 right_wheel_spec, right_wheel_notches,
347 left_ring_setting, middle_ring_setting, right_ring_setting,
348 plugboard_setting):
349 self.reflector = Reflector(reflector_spec)
350 self.left_wheel = Wheel(left_wheel_spec, left_wheel_notches,
351 ring_setting=left_ring_setting)
352 self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_notches,
353 ring_setting=middle_ring_setting)
354 self.right_wheel = Wheel(right_wheel_spec, right_wheel_notches,
355 ring_setting=right_ring_setting)
356 self.plugboard = Plugboard(plugboard_setting)
357
358 def __getattribute__(self,name):
359 if name==&#39;wheel_positions&#39;:
360 return (self.left_wheel.position,
361 self.middle_wheel.position,
362 self.right_wheel.position
363 )
364 elif name==&#39;wheel_positions_l&#39;:
365 return (self.left_wheel.position_l,
366 self.middle_wheel.position_l,
367 self.right_wheel.position_l
368 )
369 elif name==&#39;notch_positions&#39;:
370 return (self.left_wheel.notch_positions,
371 self.middle_wheel.notch_positions,
372 self.right_wheel.notch_positions
373 )
374 else:
375 return object.__getattribute__(self, name)
376
377 def set_wheels(self, left_wheel_position, middle_wheel_position,
378 right_wheel_position):
379 &#34;&#34;&#34;Set the Enigma&#39;s wheels to the specified positions.
380 &#34;&#34;&#34;
381 self.left_wheel.set_position(left_wheel_position)
382 self.middle_wheel.set_position(middle_wheel_position)
383 self.right_wheel.set_position(right_wheel_position)
384
385 def lookup(self, letter):
386 &#34;&#34;&#34;Lookup the enciphering of a letter, without advancing any wheels
387 &#34;&#34;&#34;
388 a = self.plugboard.forward(letter)
389 b = self.right_wheel.forward(a)
390 c = self.middle_wheel.forward(b)
391 d = self.left_wheel.forward(c)
392 e = self.reflector.forward(d)
393 f = self.left_wheel.backward(e)
394 g = self.middle_wheel.backward(f)
395 h = self.right_wheel.backward(g)
396 i = self.plugboard.backward(h)
397 return i
398
399 def advance(self):
400 &#34;&#34;&#34;Advance the Enigma&#39;s wheels one step. The right wheel always
401 advances. The middle and right wheels may advance if the notches
402 line up correctly.
403 &#34;&#34;&#34;
404 advance_middle = False
405 advance_left = False
406 if 0 in self.right_wheel.notch_positions:
407 advance_middle = True
408 if 0 in self.middle_wheel.notch_positions:
409 advance_left = True
410 advance_middle = True
411 self.right_wheel.advance()
412 if advance_middle: self.middle_wheel.advance()
413 if advance_left: self.left_wheel.advance()
414
415 def encipher_letter(self, letter):
416 &#34;&#34;&#34;Encipher a letter. Advance the Enigma machine, then lookup the
417 encryption of a letter.
418 &#34;&#34;&#34;
419 self.advance()
420 return self.lookup(letter)
421
422 def encipher(self, message):
423 &#34;&#34;&#34;Encipher a message.&#34;&#34;&#34;
424 enciphered = &#39;&#39;
425 for letter in sanitise(message):
426 enciphered += self.encipher_letter(letter)
427 return enciphered
428
429 decipher = encipher
430
431
432 # for i in range(26):
433 # enigma.advance()
434 # print(&#39;enigma.advance()&#39;)
435 # print(&#34;assert(enigma.wheel_positions == {})&#34;.format(enigma.wheel_positions))
436 # print(&#34;assert(cat(enigma.wheel_positions_l) == &#39;{}&#39;)&#34;.format(cat(enigma.wheel_positions_l)))
437 # print(&#34;assert(enigma.notch_positions == {})&#34;.format(enigma.notch_positions))
438 # print(&#34;assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == &#39;{}&#39;)&#34;.format(cat(enigma.lookup(l) for l in string.ascii_lowercase)))
439 # print()
440
441
442 if __name__ == &#34;__main__&#34;:
443 import doctest
444 # doctest.testmod(extraglobs={&#39;lt&#39;: LetterTransformer(1, &#39;a&#39;)})
445 doctest.testmod()</code></pre>
446 </details>
447 </section>
448 <section>
449 </section>
450 <section>
451 </section>
452 <section>
453 <h2 class="section-title" id="header-functions">Functions</h2>
454 <dl>
455 <dt id="szyfrow.enigma.cat"><code class="name flex">
456 <span>def <span class="ident">cat</span></span>(<span>iterable, /)</span>
457 </code></dt>
458 <dd>
459 <div class="desc"><p>Concatenate any number of strings.</p>
460 <p>The string whose method is called is inserted in between each given string.
461 The result is returned as a new string.</p>
462 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
463 </dd>
464 <dt id="szyfrow.enigma.lcat"><code class="name flex">
465 <span>def <span class="ident">lcat</span></span>(<span>iterable, /)</span>
466 </code></dt>
467 <dd>
468 <div class="desc"><p>Concatenate any number of strings.</p>
469 <p>The string whose method is called is inserted in between each given string.
470 The result is returned as a new string.</p>
471 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
472 </dd>
473 <dt id="szyfrow.enigma.wcat"><code class="name flex">
474 <span>def <span class="ident">wcat</span></span>(<span>iterable, /)</span>
475 </code></dt>
476 <dd>
477 <div class="desc"><p>Concatenate any number of strings.</p>
478 <p>The string whose method is called is inserted in between each given string.
479 The result is returned as a new string.</p>
480 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
481 </dd>
482 </dl>
483 </section>
484 <section>
485 <h2 class="section-title" id="header-classes">Classes</h2>
486 <dl>
487 <dt id="szyfrow.enigma.Enigma"><code class="flex name class">
488 <span>class <span class="ident">Enigma</span></span>
489 <span>(</span><span>reflector_spec, left_wheel_spec, left_wheel_notches, middle_wheel_spec, middle_wheel_notches, right_wheel_spec, right_wheel_notches, left_ring_setting, middle_ring_setting, right_ring_setting, plugboard_setting)</span>
490 </code></dt>
491 <dd>
492 <div class="desc"><p>An Enigma machine.</p></div>
493 <details class="source">
494 <summary>
495 <span>Expand source code</span>
496 </summary>
497 <pre><code class="python">class Enigma(object):
498 &#34;&#34;&#34;An Enigma machine.
499
500
501 &#34;&#34;&#34;
502 def __init__(self, reflector_spec,
503 left_wheel_spec, left_wheel_notches,
504 middle_wheel_spec, middle_wheel_notches,
505 right_wheel_spec, right_wheel_notches,
506 left_ring_setting, middle_ring_setting, right_ring_setting,
507 plugboard_setting):
508 self.reflector = Reflector(reflector_spec)
509 self.left_wheel = Wheel(left_wheel_spec, left_wheel_notches,
510 ring_setting=left_ring_setting)
511 self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_notches,
512 ring_setting=middle_ring_setting)
513 self.right_wheel = Wheel(right_wheel_spec, right_wheel_notches,
514 ring_setting=right_ring_setting)
515 self.plugboard = Plugboard(plugboard_setting)
516
517 def __getattribute__(self,name):
518 if name==&#39;wheel_positions&#39;:
519 return (self.left_wheel.position,
520 self.middle_wheel.position,
521 self.right_wheel.position
522 )
523 elif name==&#39;wheel_positions_l&#39;:
524 return (self.left_wheel.position_l,
525 self.middle_wheel.position_l,
526 self.right_wheel.position_l
527 )
528 elif name==&#39;notch_positions&#39;:
529 return (self.left_wheel.notch_positions,
530 self.middle_wheel.notch_positions,
531 self.right_wheel.notch_positions
532 )
533 else:
534 return object.__getattribute__(self, name)
535
536 def set_wheels(self, left_wheel_position, middle_wheel_position,
537 right_wheel_position):
538 &#34;&#34;&#34;Set the Enigma&#39;s wheels to the specified positions.
539 &#34;&#34;&#34;
540 self.left_wheel.set_position(left_wheel_position)
541 self.middle_wheel.set_position(middle_wheel_position)
542 self.right_wheel.set_position(right_wheel_position)
543
544 def lookup(self, letter):
545 &#34;&#34;&#34;Lookup the enciphering of a letter, without advancing any wheels
546 &#34;&#34;&#34;
547 a = self.plugboard.forward(letter)
548 b = self.right_wheel.forward(a)
549 c = self.middle_wheel.forward(b)
550 d = self.left_wheel.forward(c)
551 e = self.reflector.forward(d)
552 f = self.left_wheel.backward(e)
553 g = self.middle_wheel.backward(f)
554 h = self.right_wheel.backward(g)
555 i = self.plugboard.backward(h)
556 return i
557
558 def advance(self):
559 &#34;&#34;&#34;Advance the Enigma&#39;s wheels one step. The right wheel always
560 advances. The middle and right wheels may advance if the notches
561 line up correctly.
562 &#34;&#34;&#34;
563 advance_middle = False
564 advance_left = False
565 if 0 in self.right_wheel.notch_positions:
566 advance_middle = True
567 if 0 in self.middle_wheel.notch_positions:
568 advance_left = True
569 advance_middle = True
570 self.right_wheel.advance()
571 if advance_middle: self.middle_wheel.advance()
572 if advance_left: self.left_wheel.advance()
573
574 def encipher_letter(self, letter):
575 &#34;&#34;&#34;Encipher a letter. Advance the Enigma machine, then lookup the
576 encryption of a letter.
577 &#34;&#34;&#34;
578 self.advance()
579 return self.lookup(letter)
580
581 def encipher(self, message):
582 &#34;&#34;&#34;Encipher a message.&#34;&#34;&#34;
583 enciphered = &#39;&#39;
584 for letter in sanitise(message):
585 enciphered += self.encipher_letter(letter)
586 return enciphered
587
588 decipher = encipher</code></pre>
589 </details>
590 <h3>Methods</h3>
591 <dl>
592 <dt id="szyfrow.enigma.Enigma.advance"><code class="name flex">
593 <span>def <span class="ident">advance</span></span>(<span>self)</span>
594 </code></dt>
595 <dd>
596 <div class="desc"><p>Advance the Enigma's wheels one step. The right wheel always
597 advances. The middle and right wheels may advance if the notches
598 line up correctly.</p></div>
599 <details class="source">
600 <summary>
601 <span>Expand source code</span>
602 </summary>
603 <pre><code class="python">def advance(self):
604 &#34;&#34;&#34;Advance the Enigma&#39;s wheels one step. The right wheel always
605 advances. The middle and right wheels may advance if the notches
606 line up correctly.
607 &#34;&#34;&#34;
608 advance_middle = False
609 advance_left = False
610 if 0 in self.right_wheel.notch_positions:
611 advance_middle = True
612 if 0 in self.middle_wheel.notch_positions:
613 advance_left = True
614 advance_middle = True
615 self.right_wheel.advance()
616 if advance_middle: self.middle_wheel.advance()
617 if advance_left: self.left_wheel.advance()</code></pre>
618 </details>
619 </dd>
620 <dt id="szyfrow.enigma.Enigma.decipher"><code class="name flex">
621 <span>def <span class="ident">decipher</span></span>(<span>self, message)</span>
622 </code></dt>
623 <dd>
624 <div class="desc"><p>Encipher a message.</p></div>
625 <details class="source">
626 <summary>
627 <span>Expand source code</span>
628 </summary>
629 <pre><code class="python">def encipher(self, message):
630 &#34;&#34;&#34;Encipher a message.&#34;&#34;&#34;
631 enciphered = &#39;&#39;
632 for letter in sanitise(message):
633 enciphered += self.encipher_letter(letter)
634 return enciphered</code></pre>
635 </details>
636 </dd>
637 <dt id="szyfrow.enigma.Enigma.encipher"><code class="name flex">
638 <span>def <span class="ident">encipher</span></span>(<span>self, message)</span>
639 </code></dt>
640 <dd>
641 <div class="desc"><p>Encipher a message.</p></div>
642 <details class="source">
643 <summary>
644 <span>Expand source code</span>
645 </summary>
646 <pre><code class="python">def encipher(self, message):
647 &#34;&#34;&#34;Encipher a message.&#34;&#34;&#34;
648 enciphered = &#39;&#39;
649 for letter in sanitise(message):
650 enciphered += self.encipher_letter(letter)
651 return enciphered</code></pre>
652 </details>
653 </dd>
654 <dt id="szyfrow.enigma.Enigma.encipher_letter"><code class="name flex">
655 <span>def <span class="ident">encipher_letter</span></span>(<span>self, letter)</span>
656 </code></dt>
657 <dd>
658 <div class="desc"><p>Encipher a letter. Advance the Enigma machine, then lookup the
659 encryption of a letter.</p></div>
660 <details class="source">
661 <summary>
662 <span>Expand source code</span>
663 </summary>
664 <pre><code class="python">def encipher_letter(self, letter):
665 &#34;&#34;&#34;Encipher a letter. Advance the Enigma machine, then lookup the
666 encryption of a letter.
667 &#34;&#34;&#34;
668 self.advance()
669 return self.lookup(letter)</code></pre>
670 </details>
671 </dd>
672 <dt id="szyfrow.enigma.Enigma.lookup"><code class="name flex">
673 <span>def <span class="ident">lookup</span></span>(<span>self, letter)</span>
674 </code></dt>
675 <dd>
676 <div class="desc"><p>Lookup the enciphering of a letter, without advancing any wheels</p></div>
677 <details class="source">
678 <summary>
679 <span>Expand source code</span>
680 </summary>
681 <pre><code class="python">def lookup(self, letter):
682 &#34;&#34;&#34;Lookup the enciphering of a letter, without advancing any wheels
683 &#34;&#34;&#34;
684 a = self.plugboard.forward(letter)
685 b = self.right_wheel.forward(a)
686 c = self.middle_wheel.forward(b)
687 d = self.left_wheel.forward(c)
688 e = self.reflector.forward(d)
689 f = self.left_wheel.backward(e)
690 g = self.middle_wheel.backward(f)
691 h = self.right_wheel.backward(g)
692 i = self.plugboard.backward(h)
693 return i</code></pre>
694 </details>
695 </dd>
696 <dt id="szyfrow.enigma.Enigma.set_wheels"><code class="name flex">
697 <span>def <span class="ident">set_wheels</span></span>(<span>self, left_wheel_position, middle_wheel_position, right_wheel_position)</span>
698 </code></dt>
699 <dd>
700 <div class="desc"><p>Set the Enigma's wheels to the specified positions.</p></div>
701 <details class="source">
702 <summary>
703 <span>Expand source code</span>
704 </summary>
705 <pre><code class="python">def set_wheels(self, left_wheel_position, middle_wheel_position,
706 right_wheel_position):
707 &#34;&#34;&#34;Set the Enigma&#39;s wheels to the specified positions.
708 &#34;&#34;&#34;
709 self.left_wheel.set_position(left_wheel_position)
710 self.middle_wheel.set_position(middle_wheel_position)
711 self.right_wheel.set_position(right_wheel_position)</code></pre>
712 </details>
713 </dd>
714 </dl>
715 </dd>
716 <dt id="szyfrow.enigma.LetterTransformer"><code class="flex name class">
717 <span>class <span class="ident">LetterTransformer</span></span>
718 <span>(</span><span>specification, raw_transform=False)</span>
719 </code></dt>
720 <dd>
721 <div class="desc"><p>A generic substitution cipher, that has different transforms in the
722 forward and backward directions. It requires that the transforms for all
723 letters by provided.</p>
724 <p>A <code>transform</code> is a list of letter pairs, like <code>[('a', 'b'), ('c', 'd')]</code>.
725 That would say that, in the forward direction <code>a</code> goes to <code>b</code> and
726 <code>c</code> goes to <code>d</code>. In the backward direction, <code>b</code> goes to <code>a</code> and <code>d</code> goes
727 to <code>c</code>. </p>
728 <p>Validate and create a new transformer. The transform is parsed by
729 <code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">LetterTransformer.parse_specification()</a></code> unless <code>raw_transform</code> is <code>True</code></p></div>
730 <details class="source">
731 <summary>
732 <span>Expand source code</span>
733 </summary>
734 <pre><code class="python">class LetterTransformer(object):
735 &#34;&#34;&#34;A generic substitution cipher, that has different transforms in the
736 forward and backward directions. It requires that the transforms for all
737 letters by provided.
738
739 A `transform` is a list of letter pairs, like `[(&#39;a&#39;, &#39;b&#39;), (&#39;c&#39;, &#39;d&#39;)]`.
740 That would say that, in the forward direction `a` goes to `b` and
741 `c` goes to `d`. In the backward direction, `b` goes to `a` and `d` goes
742 to `c`.
743 &#34;&#34;&#34;
744 def __init__(self, specification, raw_transform=False):
745 &#34;&#34;&#34;Validate and create a new transformer. The transform is parsed by
746 `LetterTransformer.parse_specification` unless `raw_transform` is `True`
747 &#34;&#34;&#34;
748 if raw_transform:
749 transform = specification
750 else:
751 transform = self.parse_specification(specification)
752 self.validate_transform(transform)
753 self.make_transform_map(transform)
754
755 def parse_specification(self, specification):
756 &#34;&#34;&#34;Turn a `specification` string into a transform, by zipping it
757 with ASCII lowercase letters to generate the pairs. This assumes that
758 the `specification` defines the destination of the forward transform.
759 &#34;&#34;&#34;
760 return list(zip(string.ascii_lowercase, sanitise(specification)))
761 # return specification
762
763 def validate_transform(self, transform):
764 &#34;&#34;&#34;Checks that a transform is valid (every letter is mapped to
765 exactly one other letter, in both directions).
766 &#34;&#34;&#34;
767 if len(transform) != 26:
768 raise ValueError(&#34;Transform specification has {} pairs, requires 26&#34;.
769 format(len(transform)))
770 for p in transform:
771 if len(p) != 2:
772 raise ValueError(&#34;Not all mappings in transform &#34;
773 &#34;have two elements&#34;)
774 if len(set([p[0] for p in transform])) != 26:
775 raise ValueError(&#34;Transform specification must list 26 origin letters&#34;)
776 if len(set([p[1] for p in transform])) != 26:
777 raise ValueError(&#34;Transform specification must list 26 destination letters&#34;)
778
779 def make_empty_transform(self):
780 &#34;&#34;&#34;An empty transform is one that maps every letter to &#39;a&#39;.
781 &#34;&#34;&#34;
782 self.forward_map = [0] * 26
783 self.backward_map = [0] * 26
784
785 def make_transform_map(self, transform):
786 &#34;&#34;&#34;Create `forward_map` and `backward_map` from `transform`. The maps
787 work on letter positions, not letter values. This makes the arithmetic
788 for wheels much easier.
789 &#34;&#34;&#34;
790 self.make_empty_transform()
791 for p in transform:
792 self.forward_map[pos(p[0])] = pos(p[1])
793 self.backward_map[pos(p[1])] = pos(p[0])
794 return self.forward_map, self.backward_map
795
796 def forward(self, letter):
797 &#34;&#34;&#34;Apply a map in the forward direction.
798 &#34;&#34;&#34;
799 if letter in string.ascii_lowercase:
800 return unpos(self.forward_map[pos(letter)])
801 else:
802 return &#39;&#39;
803
804 def backward(self, letter):
805 &#34;&#34;&#34;Apply a map in the backward direction.
806 &#34;&#34;&#34;
807 if letter in string.ascii_lowercase:
808 return unpos(self.backward_map[pos(letter)])
809 else:
810 return &#39;&#39;</code></pre>
811 </details>
812 <h3>Subclasses</h3>
813 <ul class="hlist">
814 <li><a title="szyfrow.enigma.Plugboard" href="#szyfrow.enigma.Plugboard">Plugboard</a></li>
815 <li><a title="szyfrow.enigma.SimpleWheel" href="#szyfrow.enigma.SimpleWheel">SimpleWheel</a></li>
816 </ul>
817 <h3>Methods</h3>
818 <dl>
819 <dt id="szyfrow.enigma.LetterTransformer.backward"><code class="name flex">
820 <span>def <span class="ident">backward</span></span>(<span>self, letter)</span>
821 </code></dt>
822 <dd>
823 <div class="desc"><p>Apply a map in the backward direction.</p></div>
824 <details class="source">
825 <summary>
826 <span>Expand source code</span>
827 </summary>
828 <pre><code class="python">def backward(self, letter):
829 &#34;&#34;&#34;Apply a map in the backward direction.
830 &#34;&#34;&#34;
831 if letter in string.ascii_lowercase:
832 return unpos(self.backward_map[pos(letter)])
833 else:
834 return &#39;&#39;</code></pre>
835 </details>
836 </dd>
837 <dt id="szyfrow.enigma.LetterTransformer.forward"><code class="name flex">
838 <span>def <span class="ident">forward</span></span>(<span>self, letter)</span>
839 </code></dt>
840 <dd>
841 <div class="desc"><p>Apply a map in the forward direction.</p></div>
842 <details class="source">
843 <summary>
844 <span>Expand source code</span>
845 </summary>
846 <pre><code class="python">def forward(self, letter):
847 &#34;&#34;&#34;Apply a map in the forward direction.
848 &#34;&#34;&#34;
849 if letter in string.ascii_lowercase:
850 return unpos(self.forward_map[pos(letter)])
851 else:
852 return &#39;&#39;</code></pre>
853 </details>
854 </dd>
855 <dt id="szyfrow.enigma.LetterTransformer.make_empty_transform"><code class="name flex">
856 <span>def <span class="ident">make_empty_transform</span></span>(<span>self)</span>
857 </code></dt>
858 <dd>
859 <div class="desc"><p>An empty transform is one that maps every letter to 'a'.</p></div>
860 <details class="source">
861 <summary>
862 <span>Expand source code</span>
863 </summary>
864 <pre><code class="python">def make_empty_transform(self):
865 &#34;&#34;&#34;An empty transform is one that maps every letter to &#39;a&#39;.
866 &#34;&#34;&#34;
867 self.forward_map = [0] * 26
868 self.backward_map = [0] * 26</code></pre>
869 </details>
870 </dd>
871 <dt id="szyfrow.enigma.LetterTransformer.make_transform_map"><code class="name flex">
872 <span>def <span class="ident">make_transform_map</span></span>(<span>self, transform)</span>
873 </code></dt>
874 <dd>
875 <div class="desc"><p>Create <code>forward_map</code> and <code>backward_map</code> from <code>transform</code>. The maps
876 work on letter positions, not letter values. This makes the arithmetic
877 for wheels much easier.</p></div>
878 <details class="source">
879 <summary>
880 <span>Expand source code</span>
881 </summary>
882 <pre><code class="python">def make_transform_map(self, transform):
883 &#34;&#34;&#34;Create `forward_map` and `backward_map` from `transform`. The maps
884 work on letter positions, not letter values. This makes the arithmetic
885 for wheels much easier.
886 &#34;&#34;&#34;
887 self.make_empty_transform()
888 for p in transform:
889 self.forward_map[pos(p[0])] = pos(p[1])
890 self.backward_map[pos(p[1])] = pos(p[0])
891 return self.forward_map, self.backward_map</code></pre>
892 </details>
893 </dd>
894 <dt id="szyfrow.enigma.LetterTransformer.parse_specification"><code class="name flex">
895 <span>def <span class="ident">parse_specification</span></span>(<span>self, specification)</span>
896 </code></dt>
897 <dd>
898 <div class="desc"><p>Turn a <code>specification</code> string into a transform, by zipping it
899 with ASCII lowercase letters to generate the pairs. This assumes that
900 the <code>specification</code> defines the destination of the forward transform.</p></div>
901 <details class="source">
902 <summary>
903 <span>Expand source code</span>
904 </summary>
905 <pre><code class="python">def parse_specification(self, specification):
906 &#34;&#34;&#34;Turn a `specification` string into a transform, by zipping it
907 with ASCII lowercase letters to generate the pairs. This assumes that
908 the `specification` defines the destination of the forward transform.
909 &#34;&#34;&#34;
910 return list(zip(string.ascii_lowercase, sanitise(specification)))
911 # return specification</code></pre>
912 </details>
913 </dd>
914 <dt id="szyfrow.enigma.LetterTransformer.validate_transform"><code class="name flex">
915 <span>def <span class="ident">validate_transform</span></span>(<span>self, transform)</span>
916 </code></dt>
917 <dd>
918 <div class="desc"><p>Checks that a transform is valid (every letter is mapped to
919 exactly one other letter, in both directions).</p></div>
920 <details class="source">
921 <summary>
922 <span>Expand source code</span>
923 </summary>
924 <pre><code class="python">def validate_transform(self, transform):
925 &#34;&#34;&#34;Checks that a transform is valid (every letter is mapped to
926 exactly one other letter, in both directions).
927 &#34;&#34;&#34;
928 if len(transform) != 26:
929 raise ValueError(&#34;Transform specification has {} pairs, requires 26&#34;.
930 format(len(transform)))
931 for p in transform:
932 if len(p) != 2:
933 raise ValueError(&#34;Not all mappings in transform &#34;
934 &#34;have two elements&#34;)
935 if len(set([p[0] for p in transform])) != 26:
936 raise ValueError(&#34;Transform specification must list 26 origin letters&#34;)
937 if len(set([p[1] for p in transform])) != 26:
938 raise ValueError(&#34;Transform specification must list 26 destination letters&#34;) </code></pre>
939 </details>
940 </dd>
941 </dl>
942 </dd>
943 <dt id="szyfrow.enigma.Plugboard"><code class="flex name class">
944 <span>class <span class="ident">Plugboard</span></span>
945 <span>(</span><span>specification, raw_transform=False)</span>
946 </code></dt>
947 <dd>
948 <div class="desc"><p>A plugboard, a type of letter transformer where forward and backward
949 transforms are the same. If a letter isn't explicitly transformed, it is
950 kept as it is.</p>
951 <p>Validate and create a new transformer. The transform is parsed by
952 <code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">LetterTransformer.parse_specification()</a></code> unless <code>raw_transform</code> is <code>True</code></p></div>
953 <details class="source">
954 <summary>
955 <span>Expand source code</span>
956 </summary>
957 <pre><code class="python">class Plugboard(LetterTransformer):
958 &#34;&#34;&#34;A plugboard, a type of letter transformer where forward and backward
959 transforms are the same. If a letter isn&#39;t explicitly transformed, it is
960 kept as it is.
961 &#34;&#34;&#34;
962
963 def parse_specification(self, specification):
964 &#34;&#34;&#34;Convert a specification into a transform. The specification is
965 given as a list of letter pairs.
966 &#34;&#34;&#34;
967 return [tuple(sanitise(p)) for p in specification.split()]
968
969 def validate_transform(self, transform):
970 &#34;&#34;&#34;A set of pairs, of from-to. Does not require all 26 letters
971 are in the transform.
972 &#34;&#34;&#34;
973 for p in transform:
974 if len(p) != 2:
975 raise ValueError(&#34;Not all mappings in transform&#34;
976 &#34;have two elements&#34;)
977
978 def make_empty_transform(self):
979 &#34;&#34;&#34;An empty transform maps every letter to itself.
980 &#34;&#34;&#34;
981 self.forward_map = list(range(26))
982 self.backward_map = list(range(26))
983
984 def make_transform_map(self, transform):
985 &#34;&#34;&#34;Makes the maps for a plugboard. Ensures that if the pair (&#39;a&#39;, &#39;b&#39;)
986 is in the specification, the pair (&#39;b&#39;, &#39;a&#39;) is also present.
987 &#34;&#34;&#34;
988 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
989 return super(Plugboard, self).make_transform_map(expanded_transform)</code></pre>
990 </details>
991 <h3>Ancestors</h3>
992 <ul class="hlist">
993 <li><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></li>
994 </ul>
995 <h3>Subclasses</h3>
996 <ul class="hlist">
997 <li><a title="szyfrow.enigma.Reflector" href="#szyfrow.enigma.Reflector">Reflector</a></li>
998 </ul>
999 <h3>Methods</h3>
1000 <dl>
1001 <dt id="szyfrow.enigma.Plugboard.make_empty_transform"><code class="name flex">
1002 <span>def <span class="ident">make_empty_transform</span></span>(<span>self)</span>
1003 </code></dt>
1004 <dd>
1005 <div class="desc"><p>An empty transform maps every letter to itself.</p></div>
1006 <details class="source">
1007 <summary>
1008 <span>Expand source code</span>
1009 </summary>
1010 <pre><code class="python">def make_empty_transform(self):
1011 &#34;&#34;&#34;An empty transform maps every letter to itself.
1012 &#34;&#34;&#34;
1013 self.forward_map = list(range(26))
1014 self.backward_map = list(range(26))</code></pre>
1015 </details>
1016 </dd>
1017 <dt id="szyfrow.enigma.Plugboard.make_transform_map"><code class="name flex">
1018 <span>def <span class="ident">make_transform_map</span></span>(<span>self, transform)</span>
1019 </code></dt>
1020 <dd>
1021 <div class="desc"><p>Makes the maps for a plugboard. Ensures that if the pair ('a', 'b')
1022 is in the specification, the pair ('b', 'a') is also present.</p></div>
1023 <details class="source">
1024 <summary>
1025 <span>Expand source code</span>
1026 </summary>
1027 <pre><code class="python">def make_transform_map(self, transform):
1028 &#34;&#34;&#34;Makes the maps for a plugboard. Ensures that if the pair (&#39;a&#39;, &#39;b&#39;)
1029 is in the specification, the pair (&#39;b&#39;, &#39;a&#39;) is also present.
1030 &#34;&#34;&#34;
1031 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
1032 return super(Plugboard, self).make_transform_map(expanded_transform)</code></pre>
1033 </details>
1034 </dd>
1035 <dt id="szyfrow.enigma.Plugboard.parse_specification"><code class="name flex">
1036 <span>def <span class="ident">parse_specification</span></span>(<span>self, specification)</span>
1037 </code></dt>
1038 <dd>
1039 <div class="desc"><p>Convert a specification into a transform. The specification is
1040 given as a list of letter pairs.</p></div>
1041 <details class="source">
1042 <summary>
1043 <span>Expand source code</span>
1044 </summary>
1045 <pre><code class="python">def parse_specification(self, specification):
1046 &#34;&#34;&#34;Convert a specification into a transform. The specification is
1047 given as a list of letter pairs.
1048 &#34;&#34;&#34;
1049 return [tuple(sanitise(p)) for p in specification.split()]</code></pre>
1050 </details>
1051 </dd>
1052 <dt id="szyfrow.enigma.Plugboard.validate_transform"><code class="name flex">
1053 <span>def <span class="ident">validate_transform</span></span>(<span>self, transform)</span>
1054 </code></dt>
1055 <dd>
1056 <div class="desc"><p>A set of pairs, of from-to. Does not require all 26 letters
1057 are in the transform.</p></div>
1058 <details class="source">
1059 <summary>
1060 <span>Expand source code</span>
1061 </summary>
1062 <pre><code class="python">def validate_transform(self, transform):
1063 &#34;&#34;&#34;A set of pairs, of from-to. Does not require all 26 letters
1064 are in the transform.
1065 &#34;&#34;&#34;
1066 for p in transform:
1067 if len(p) != 2:
1068 raise ValueError(&#34;Not all mappings in transform&#34;
1069 &#34;have two elements&#34;)</code></pre>
1070 </details>
1071 </dd>
1072 </dl>
1073 <h3>Inherited members</h3>
1074 <ul class="hlist">
1075 <li><code><b><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></b></code>:
1076 <ul class="hlist">
1077 <li><code><a title="szyfrow.enigma.LetterTransformer.backward" href="#szyfrow.enigma.LetterTransformer.backward">backward</a></code></li>
1078 <li><code><a title="szyfrow.enigma.LetterTransformer.forward" href="#szyfrow.enigma.LetterTransformer.forward">forward</a></code></li>
1079 </ul>
1080 </li>
1081 </ul>
1082 </dd>
1083 <dt id="szyfrow.enigma.Reflector"><code class="flex name class">
1084 <span>class <span class="ident">Reflector</span></span>
1085 <span>(</span><span>specification, raw_transform=False)</span>
1086 </code></dt>
1087 <dd>
1088 <div class="desc"><p>A reflector is a plugboard that requires 13 transforms.
1089 The 'plugboard' superclass ensures that all 13 transforms are also applied
1090 in reverse, making 26 transforms in all.</p>
1091 <p>Validate and create a new transformer. The transform is parsed by
1092 <code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">LetterTransformer.parse_specification()</a></code> unless <code>raw_transform</code> is <code>True</code></p></div>
1093 <details class="source">
1094 <summary>
1095 <span>Expand source code</span>
1096 </summary>
1097 <pre><code class="python">class Reflector(Plugboard):
1098 &#34;&#34;&#34;A reflector is a plugboard that requires 13 transforms.
1099 The &#39;plugboard&#39; superclass ensures that all 13 transforms are also applied
1100 in reverse, making 26 transforms in all.
1101 &#34;&#34;&#34;
1102 def validate_transform(self, transform):
1103 if len(transform) != 13:
1104 raise ValueError(&#34;Reflector specification has {} pairs, requires 13&#34;.
1105 format(len(transform)))
1106 if len(set([p[0] for p in transform] +
1107 [p[1] for p in transform])) != 26:
1108 raise ValueError(&#34;Reflector specification does not contain 26 letters&#34;)
1109 try:
1110 super(Reflector, self).validate_transform(transform)
1111 except ValueError as v:
1112 raise ValueError(&#34;Not all mappings in reflector have two elements&#34;)</code></pre>
1113 </details>
1114 <h3>Ancestors</h3>
1115 <ul class="hlist">
1116 <li><a title="szyfrow.enigma.Plugboard" href="#szyfrow.enigma.Plugboard">Plugboard</a></li>
1117 <li><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></li>
1118 </ul>
1119 <h3>Inherited members</h3>
1120 <ul class="hlist">
1121 <li><code><b><a title="szyfrow.enigma.Plugboard" href="#szyfrow.enigma.Plugboard">Plugboard</a></b></code>:
1122 <ul class="hlist">
1123 <li><code><a title="szyfrow.enigma.Plugboard.backward" href="#szyfrow.enigma.LetterTransformer.backward">backward</a></code></li>
1124 <li><code><a title="szyfrow.enigma.Plugboard.forward" href="#szyfrow.enigma.LetterTransformer.forward">forward</a></code></li>
1125 <li><code><a title="szyfrow.enigma.Plugboard.make_empty_transform" href="#szyfrow.enigma.Plugboard.make_empty_transform">make_empty_transform</a></code></li>
1126 <li><code><a title="szyfrow.enigma.Plugboard.make_transform_map" href="#szyfrow.enigma.Plugboard.make_transform_map">make_transform_map</a></code></li>
1127 <li><code><a title="szyfrow.enigma.Plugboard.parse_specification" href="#szyfrow.enigma.Plugboard.parse_specification">parse_specification</a></code></li>
1128 <li><code><a title="szyfrow.enigma.Plugboard.validate_transform" href="#szyfrow.enigma.Plugboard.validate_transform">validate_transform</a></code></li>
1129 </ul>
1130 </li>
1131 </ul>
1132 </dd>
1133 <dt id="szyfrow.enigma.SimpleWheel"><code class="flex name class">
1134 <span>class <span class="ident">SimpleWheel</span></span>
1135 <span>(</span><span>transform, position='a', raw_transform=False)</span>
1136 </code></dt>
1137 <dd>
1138 <div class="desc"><p>A wheel is a transform that rotates.</p>
1139 <p>Looking from the right, letters go in sequence a-b-c clockwise around the
1140 wheel. </p>
1141 <p>The position of the wheel is the number of spaces anticlockwise the wheel
1142 has turned.</p>
1143 <p>Letter inputs and outputs are given relative to the frame holding the wheel,
1144 so if the wheel is advanced three places, an input of 'p' will enter the
1145 wheel on the position under the wheel's 's' label.</p>
1146 <p>Validate and create a new transformer. The transform is parsed by
1147 <code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">LetterTransformer.parse_specification()</a></code> unless <code>raw_transform</code> is <code>True</code></p></div>
1148 <details class="source">
1149 <summary>
1150 <span>Expand source code</span>
1151 </summary>
1152 <pre><code class="python">class SimpleWheel(LetterTransformer):
1153 &#34;&#34;&#34;A wheel is a transform that rotates.
1154
1155 Looking from the right, letters go in sequence a-b-c clockwise around the
1156 wheel.
1157
1158 The position of the wheel is the number of spaces anticlockwise the wheel
1159 has turned.
1160
1161 Letter inputs and outputs are given relative to the frame holding the wheel,
1162 so if the wheel is advanced three places, an input of &#39;p&#39; will enter the
1163 wheel on the position under the wheel&#39;s &#39;s&#39; label.
1164 &#34;&#34;&#34;
1165 def __init__(self, transform, position=&#39;a&#39;, raw_transform=False):
1166 super(SimpleWheel, self).__init__(transform, raw_transform)
1167 self.set_position(position)
1168
1169 def __getattribute__(self,name):
1170 if name==&#39;position_l&#39;:
1171 return unpos(self.position)
1172 else:
1173 return object.__getattribute__(self, name)
1174
1175 def set_position(self, position):
1176 &#34;&#34;&#34;Sets a wheel&#39;s position. If the `position` is a string, convert it
1177 to a number and set the position.
1178 &#34;&#34;&#34;
1179 if isinstance(position, str):
1180 self.position = pos(position)
1181 else:
1182 self.position = position
1183
1184 def forward(self, letter):
1185 &#34;&#34;&#34;Give the transformed letter in the forward direction, accounting
1186 for the position of the wheel.
1187 &#34;&#34;&#34;
1188 if letter in string.ascii_lowercase:
1189 return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))
1190 else:
1191 return &#39;&#39;
1192
1193 def backward(self, letter):
1194 &#34;&#34;&#34;Give the transformed letter in the backward direction, accounting
1195 for the position of the wheel.
1196 &#34;&#34;&#34;
1197 if letter in string.ascii_lowercase:
1198 return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))
1199 else:
1200 return &#39;&#39;
1201
1202 def advance(self):
1203 &#34;&#34;&#34;Advance a wheel one position.&#34;&#34;&#34;
1204 self.position = (self.position + 1) % 26</code></pre>
1205 </details>
1206 <h3>Ancestors</h3>
1207 <ul class="hlist">
1208 <li><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></li>
1209 </ul>
1210 <h3>Subclasses</h3>
1211 <ul class="hlist">
1212 <li><a title="szyfrow.enigma.Wheel" href="#szyfrow.enigma.Wheel">Wheel</a></li>
1213 </ul>
1214 <h3>Methods</h3>
1215 <dl>
1216 <dt id="szyfrow.enigma.SimpleWheel.advance"><code class="name flex">
1217 <span>def <span class="ident">advance</span></span>(<span>self)</span>
1218 </code></dt>
1219 <dd>
1220 <div class="desc"><p>Advance a wheel one position.</p></div>
1221 <details class="source">
1222 <summary>
1223 <span>Expand source code</span>
1224 </summary>
1225 <pre><code class="python">def advance(self):
1226 &#34;&#34;&#34;Advance a wheel one position.&#34;&#34;&#34;
1227 self.position = (self.position + 1) % 26</code></pre>
1228 </details>
1229 </dd>
1230 <dt id="szyfrow.enigma.SimpleWheel.backward"><code class="name flex">
1231 <span>def <span class="ident">backward</span></span>(<span>self, letter)</span>
1232 </code></dt>
1233 <dd>
1234 <div class="desc"><p>Give the transformed letter in the backward direction, accounting
1235 for the position of the wheel.</p></div>
1236 <details class="source">
1237 <summary>
1238 <span>Expand source code</span>
1239 </summary>
1240 <pre><code class="python">def backward(self, letter):
1241 &#34;&#34;&#34;Give the transformed letter in the backward direction, accounting
1242 for the position of the wheel.
1243 &#34;&#34;&#34;
1244 if letter in string.ascii_lowercase:
1245 return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))
1246 else:
1247 return &#39;&#39;</code></pre>
1248 </details>
1249 </dd>
1250 <dt id="szyfrow.enigma.SimpleWheel.forward"><code class="name flex">
1251 <span>def <span class="ident">forward</span></span>(<span>self, letter)</span>
1252 </code></dt>
1253 <dd>
1254 <div class="desc"><p>Give the transformed letter in the forward direction, accounting
1255 for the position of the wheel.</p></div>
1256 <details class="source">
1257 <summary>
1258 <span>Expand source code</span>
1259 </summary>
1260 <pre><code class="python">def forward(self, letter):
1261 &#34;&#34;&#34;Give the transformed letter in the forward direction, accounting
1262 for the position of the wheel.
1263 &#34;&#34;&#34;
1264 if letter in string.ascii_lowercase:
1265 return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))
1266 else:
1267 return &#39;&#39;</code></pre>
1268 </details>
1269 </dd>
1270 <dt id="szyfrow.enigma.SimpleWheel.set_position"><code class="name flex">
1271 <span>def <span class="ident">set_position</span></span>(<span>self, position)</span>
1272 </code></dt>
1273 <dd>
1274 <div class="desc"><p>Sets a wheel's position. If the <code>position</code> is a string, convert it
1275 to a number and set the position.</p></div>
1276 <details class="source">
1277 <summary>
1278 <span>Expand source code</span>
1279 </summary>
1280 <pre><code class="python">def set_position(self, position):
1281 &#34;&#34;&#34;Sets a wheel&#39;s position. If the `position` is a string, convert it
1282 to a number and set the position.
1283 &#34;&#34;&#34;
1284 if isinstance(position, str):
1285 self.position = pos(position)
1286 else:
1287 self.position = position</code></pre>
1288 </details>
1289 </dd>
1290 </dl>
1291 <h3>Inherited members</h3>
1292 <ul class="hlist">
1293 <li><code><b><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></b></code>:
1294 <ul class="hlist">
1295 <li><code><a title="szyfrow.enigma.LetterTransformer.make_empty_transform" href="#szyfrow.enigma.LetterTransformer.make_empty_transform">make_empty_transform</a></code></li>
1296 <li><code><a title="szyfrow.enigma.LetterTransformer.make_transform_map" href="#szyfrow.enigma.LetterTransformer.make_transform_map">make_transform_map</a></code></li>
1297 <li><code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">parse_specification</a></code></li>
1298 <li><code><a title="szyfrow.enigma.LetterTransformer.validate_transform" href="#szyfrow.enigma.LetterTransformer.validate_transform">validate_transform</a></code></li>
1299 </ul>
1300 </li>
1301 </ul>
1302 </dd>
1303 <dt id="szyfrow.enigma.Wheel"><code class="flex name class">
1304 <span>class <span class="ident">Wheel</span></span>
1305 <span>(</span><span>transform, ring_notch_letters, ring_setting=1, position='a', raw_transform=False)</span>
1306 </code></dt>
1307 <dd>
1308 <div class="desc"><p>A wheel with a movable ring.</p>
1309 <p>The ring holds the letters and the notches that turn other wheels. The core
1310 holds the wiring that does the transformation.</p>
1311 <p>The ring position is how many steps the core is turned relative to the ring.
1312 This is one-based, so a ring setting of 1 means the core and ring are
1313 aligned.</p>
1314 <p>The position of the wheel is the position of the core (the transforms)
1315 relative to the neutral position. </p>
1316 <p>The position_l is the position of the ring, or what would be observed
1317 by the user of the Enigma machine. </p>
1318 <p>The notch_positions are the number of advances of this wheel before it will
1319 advance the next wheel.</p>
1320 <p>Validate and create a new transformer. The transform is parsed by
1321 <code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">LetterTransformer.parse_specification()</a></code> unless <code>raw_transform</code> is <code>True</code></p></div>
1322 <details class="source">
1323 <summary>
1324 <span>Expand source code</span>
1325 </summary>
1326 <pre><code class="python">class Wheel(SimpleWheel):
1327 &#34;&#34;&#34;A wheel with a movable ring.
1328
1329 The ring holds the letters and the notches that turn other wheels. The core
1330 holds the wiring that does the transformation.
1331
1332 The ring position is how many steps the core is turned relative to the ring.
1333 This is one-based, so a ring setting of 1 means the core and ring are
1334 aligned.
1335
1336 The position of the wheel is the position of the core (the transforms)
1337 relative to the neutral position.
1338
1339 The position_l is the position of the ring, or what would be observed
1340 by the user of the Enigma machine.
1341
1342 The notch_positions are the number of advances of this wheel before it will
1343 advance the next wheel.
1344 &#34;&#34;&#34;
1345 def __init__(self, transform, ring_notch_letters, ring_setting=1,
1346 position=&#39;a&#39;, raw_transform=False):
1347 self.ring_notch_letters = ring_notch_letters
1348 self.ring_setting = ring_setting
1349 super(Wheel, self).__init__(transform, position=position,
1350 raw_transform=raw_transform)
1351 self.set_position(position)
1352
1353 def __getattribute__(self,name):
1354 if name==&#39;position_l&#39;:
1355 return unpos(self.position + self.ring_setting - 1)
1356 else:
1357 return object.__getattribute__(self, name)
1358
1359 def set_position(self, position):
1360 if isinstance(position, str):
1361 self.position = (pos(position) - self.ring_setting + 1) % 26
1362 else:
1363 self.position = (position - self.ring_setting) % 26
1364 # # self.notch_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_notch_letters]
1365 # self.notch_positions = [(pos(p) - (self.position + self.ring_setting - 1)) % 26 for p in self.ring_notch_letters]
1366 self.notch_positions = [(self.position + self.ring_setting - 1 - pos(p)) % 26 for p in self.ring_notch_letters]
1367
1368 def advance(self):
1369 &#34;&#34;&#34;Advance a wheel&#39;s core, then advance the ring position to match.
1370 &#34;&#34;&#34;
1371 super(Wheel, self).advance()
1372 self.notch_positions = [(p + 1) % 26 for p in self.notch_positions]
1373 return self.position</code></pre>
1374 </details>
1375 <h3>Ancestors</h3>
1376 <ul class="hlist">
1377 <li><a title="szyfrow.enigma.SimpleWheel" href="#szyfrow.enigma.SimpleWheel">SimpleWheel</a></li>
1378 <li><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></li>
1379 </ul>
1380 <h3>Methods</h3>
1381 <dl>
1382 <dt id="szyfrow.enigma.Wheel.advance"><code class="name flex">
1383 <span>def <span class="ident">advance</span></span>(<span>self)</span>
1384 </code></dt>
1385 <dd>
1386 <div class="desc"><p>Advance a wheel's core, then advance the ring position to match.</p></div>
1387 <details class="source">
1388 <summary>
1389 <span>Expand source code</span>
1390 </summary>
1391 <pre><code class="python">def advance(self):
1392 &#34;&#34;&#34;Advance a wheel&#39;s core, then advance the ring position to match.
1393 &#34;&#34;&#34;
1394 super(Wheel, self).advance()
1395 self.notch_positions = [(p + 1) % 26 for p in self.notch_positions]
1396 return self.position</code></pre>
1397 </details>
1398 </dd>
1399 </dl>
1400 <h3>Inherited members</h3>
1401 <ul class="hlist">
1402 <li><code><b><a title="szyfrow.enigma.SimpleWheel" href="#szyfrow.enigma.SimpleWheel">SimpleWheel</a></b></code>:
1403 <ul class="hlist">
1404 <li><code><a title="szyfrow.enigma.SimpleWheel.backward" href="#szyfrow.enigma.SimpleWheel.backward">backward</a></code></li>
1405 <li><code><a title="szyfrow.enigma.SimpleWheel.forward" href="#szyfrow.enigma.SimpleWheel.forward">forward</a></code></li>
1406 <li><code><a title="szyfrow.enigma.SimpleWheel.make_empty_transform" href="#szyfrow.enigma.LetterTransformer.make_empty_transform">make_empty_transform</a></code></li>
1407 <li><code><a title="szyfrow.enigma.SimpleWheel.make_transform_map" href="#szyfrow.enigma.LetterTransformer.make_transform_map">make_transform_map</a></code></li>
1408 <li><code><a title="szyfrow.enigma.SimpleWheel.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">parse_specification</a></code></li>
1409 <li><code><a title="szyfrow.enigma.SimpleWheel.set_position" href="#szyfrow.enigma.SimpleWheel.set_position">set_position</a></code></li>
1410 <li><code><a title="szyfrow.enigma.SimpleWheel.validate_transform" href="#szyfrow.enigma.LetterTransformer.validate_transform">validate_transform</a></code></li>
1411 </ul>
1412 </li>
1413 </ul>
1414 </dd>
1415 </dl>
1416 </section>
1417 </article>
1418 <nav id="sidebar">
1419 <h1>Index</h1>
1420 <div class="toc">
1421 <ul></ul>
1422 </div>
1423 <ul id="index">
1424 <li><h3>Super-module</h3>
1425 <ul>
1426 <li><code><a title="szyfrow" href="index.html">szyfrow</a></code></li>
1427 </ul>
1428 </li>
1429 <li><h3><a href="#header-functions">Functions</a></h3>
1430 <ul class="">
1431 <li><code><a title="szyfrow.enigma.cat" href="#szyfrow.enigma.cat">cat</a></code></li>
1432 <li><code><a title="szyfrow.enigma.lcat" href="#szyfrow.enigma.lcat">lcat</a></code></li>
1433 <li><code><a title="szyfrow.enigma.wcat" href="#szyfrow.enigma.wcat">wcat</a></code></li>
1434 </ul>
1435 </li>
1436 <li><h3><a href="#header-classes">Classes</a></h3>
1437 <ul>
1438 <li>
1439 <h4><code><a title="szyfrow.enigma.Enigma" href="#szyfrow.enigma.Enigma">Enigma</a></code></h4>
1440 <ul class="two-column">
1441 <li><code><a title="szyfrow.enigma.Enigma.advance" href="#szyfrow.enigma.Enigma.advance">advance</a></code></li>
1442 <li><code><a title="szyfrow.enigma.Enigma.decipher" href="#szyfrow.enigma.Enigma.decipher">decipher</a></code></li>
1443 <li><code><a title="szyfrow.enigma.Enigma.encipher" href="#szyfrow.enigma.Enigma.encipher">encipher</a></code></li>
1444 <li><code><a title="szyfrow.enigma.Enigma.encipher_letter" href="#szyfrow.enigma.Enigma.encipher_letter">encipher_letter</a></code></li>
1445 <li><code><a title="szyfrow.enigma.Enigma.lookup" href="#szyfrow.enigma.Enigma.lookup">lookup</a></code></li>
1446 <li><code><a title="szyfrow.enigma.Enigma.set_wheels" href="#szyfrow.enigma.Enigma.set_wheels">set_wheels</a></code></li>
1447 </ul>
1448 </li>
1449 <li>
1450 <h4><code><a title="szyfrow.enigma.LetterTransformer" href="#szyfrow.enigma.LetterTransformer">LetterTransformer</a></code></h4>
1451 <ul class="">
1452 <li><code><a title="szyfrow.enigma.LetterTransformer.backward" href="#szyfrow.enigma.LetterTransformer.backward">backward</a></code></li>
1453 <li><code><a title="szyfrow.enigma.LetterTransformer.forward" href="#szyfrow.enigma.LetterTransformer.forward">forward</a></code></li>
1454 <li><code><a title="szyfrow.enigma.LetterTransformer.make_empty_transform" href="#szyfrow.enigma.LetterTransformer.make_empty_transform">make_empty_transform</a></code></li>
1455 <li><code><a title="szyfrow.enigma.LetterTransformer.make_transform_map" href="#szyfrow.enigma.LetterTransformer.make_transform_map">make_transform_map</a></code></li>
1456 <li><code><a title="szyfrow.enigma.LetterTransformer.parse_specification" href="#szyfrow.enigma.LetterTransformer.parse_specification">parse_specification</a></code></li>
1457 <li><code><a title="szyfrow.enigma.LetterTransformer.validate_transform" href="#szyfrow.enigma.LetterTransformer.validate_transform">validate_transform</a></code></li>
1458 </ul>
1459 </li>
1460 <li>
1461 <h4><code><a title="szyfrow.enigma.Plugboard" href="#szyfrow.enigma.Plugboard">Plugboard</a></code></h4>
1462 <ul class="">
1463 <li><code><a title="szyfrow.enigma.Plugboard.make_empty_transform" href="#szyfrow.enigma.Plugboard.make_empty_transform">make_empty_transform</a></code></li>
1464 <li><code><a title="szyfrow.enigma.Plugboard.make_transform_map" href="#szyfrow.enigma.Plugboard.make_transform_map">make_transform_map</a></code></li>
1465 <li><code><a title="szyfrow.enigma.Plugboard.parse_specification" href="#szyfrow.enigma.Plugboard.parse_specification">parse_specification</a></code></li>
1466 <li><code><a title="szyfrow.enigma.Plugboard.validate_transform" href="#szyfrow.enigma.Plugboard.validate_transform">validate_transform</a></code></li>
1467 </ul>
1468 </li>
1469 <li>
1470 <h4><code><a title="szyfrow.enigma.Reflector" href="#szyfrow.enigma.Reflector">Reflector</a></code></h4>
1471 </li>
1472 <li>
1473 <h4><code><a title="szyfrow.enigma.SimpleWheel" href="#szyfrow.enigma.SimpleWheel">SimpleWheel</a></code></h4>
1474 <ul class="">
1475 <li><code><a title="szyfrow.enigma.SimpleWheel.advance" href="#szyfrow.enigma.SimpleWheel.advance">advance</a></code></li>
1476 <li><code><a title="szyfrow.enigma.SimpleWheel.backward" href="#szyfrow.enigma.SimpleWheel.backward">backward</a></code></li>
1477 <li><code><a title="szyfrow.enigma.SimpleWheel.forward" href="#szyfrow.enigma.SimpleWheel.forward">forward</a></code></li>
1478 <li><code><a title="szyfrow.enigma.SimpleWheel.set_position" href="#szyfrow.enigma.SimpleWheel.set_position">set_position</a></code></li>
1479 </ul>
1480 </li>
1481 <li>
1482 <h4><code><a title="szyfrow.enigma.Wheel" href="#szyfrow.enigma.Wheel">Wheel</a></code></h4>
1483 <ul class="">
1484 <li><code><a title="szyfrow.enigma.Wheel.advance" href="#szyfrow.enigma.Wheel.advance">advance</a></code></li>
1485 </ul>
1486 </li>
1487 </ul>
1488 </li>
1489 </ul>
1490 </nav>
1491 </main>
1492 <footer id="footer">
1493 <p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
1494 </footer>
1495 </body>
1496 </html>