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>
20 <article id=
"content">
22 <h1 class=
"title">Module
<code>szyfrow.enigma
</code></h1>
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
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&hl=en_GB">Google Play store
</a>.
</p>
31 <details class=
"source">
33 <span>Expand source code
</span>
35 <pre><code class=
"python">"""A simulator for Enigma machines.
37 See `szyfrow.bombe.Bombe` for an implementation of the Bombe to break Enigma
40 Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page.
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).
44 There
'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
&hl=en_GB).
49 import multiprocessing
52 from szyfrow.support.utilities import *
54 # # Some convenience functions
56 # cat =
''.join
58 # def clean(text): return cat(l.lower() for l in text if l in string.ascii_letters)
61 # if letter in string.ascii_lowercase:
62 # return ord(letter) - ord(
'a
')
63 # elif letter in string.ascii_uppercase:
64 # return ord(letter) - ord(
'A
')
68 # def unpos(number): return chr(number %
26 + ord(
'a
'))
71 wheel_i_spec =
'ekmflgdqvzntowyhxuspaibrcj
'
72 wheel_ii_spec =
'ajdksiruxblhwtmcqgznpyfvoe
'
73 wheel_iii_spec =
'bdfhjlcprtxvznyeiwgakmusqo
'
74 wheel_iv_spec =
'esovpzjayquirhxlnftgkdcmwb
'
75 wheel_v_spec =
'vzbrgityupsdnhlxawmjqofeck
'
76 wheel_vi_spec =
'jpgvoumfyqbenhzrdkasxlictw
'
77 wheel_vii_spec =
'nzjhgrcxmyswboufaivlpekqdt
'
78 wheel_viii_spec =
'fkqhtlxocbjspdzramewniuygv
'
79 beta_wheel_spec =
'leyjvcnixwpbqmdrtakzgfuhos
'
80 gamma_wheel_spec =
'fsokanuerhmbtiycwlqpzxvgjd
'
82 wheel_i_notches = [
'q
']
83 wheel_ii_notches = [
'e
']
84 wheel_iii_notches = [
'v
']
85 wheel_iv_notches = [
'j
']
86 wheel_v_notches = [
'z
']
87 wheel_vi_notches = [
'z
',
'm
']
88 wheel_vii_notches = [
'z
',
'm
']
89 wheel_viii_notches = [
'z
',
'm
']
91 reflector_b_spec =
'ay br cu dh eq fs gl ip jx kn mo tz vw
'
92 reflector_c_spec =
'af bv cp dj ei go hy kr lz mx nw tq su
'
96 class LetterTransformer(object):
97 """A generic substitution cipher, that has different transforms in the
98 forward and backward directions. It requires that the transforms for all
101 A `transform` is a list of letter pairs, like `[(
'a
',
'b
'), (
'c
',
'd
')]`.
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
106 def __init__(self, specification, raw_transform=False):
107 """Validate and create a new transformer. The transform is parsed by
108 `LetterTransformer.parse_specification` unless `raw_transform` is `True`
111 transform = specification
113 transform = self.parse_specification(specification)
114 self.validate_transform(transform)
115 self.make_transform_map(transform)
117 def parse_specification(self, specification):
118 """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.
122 return list(zip(string.ascii_lowercase, sanitise(specification)))
123 # return specification
125 def validate_transform(self, transform):
126 """Checks that a transform is valid (every letter is mapped to
127 exactly one other letter, in both directions).
129 if len(transform) !=
26:
130 raise ValueError(
"Transform specification has {} pairs, requires
26".
131 format(len(transform)))
134 raise ValueError(
"Not all mappings in transform
"
135 "have two elements
")
136 if len(set([p[
0] for p in transform])) !=
26:
137 raise ValueError(
"Transform specification must list
26 origin letters
")
138 if len(set([p[
1] for p in transform])) !=
26:
139 raise ValueError(
"Transform specification must list
26 destination letters
")
141 def make_empty_transform(self):
142 """An empty transform is one that maps every letter to
'a
'.
144 self.forward_map = [
0] *
26
145 self.backward_map = [
0] *
26
147 def make_transform_map(self, transform):
148 """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.
152 self.make_empty_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
158 def forward(self, letter):
159 """Apply a map in the forward direction.
161 if letter in string.ascii_lowercase:
162 return unpos(self.forward_map[pos(letter)])
166 def backward(self, letter):
167 """Apply a map in the backward direction.
169 if letter in string.ascii_lowercase:
170 return unpos(self.backward_map[pos(letter)])
175 class Plugboard(LetterTransformer):
176 """A plugboard, a type of letter transformer where forward and backward
177 transforms are the same. If a letter isn
't explicitly transformed, it is
181 def parse_specification(self, specification):
182 """Convert a specification into a transform. The specification is
183 given as a list of letter pairs.
185 return [tuple(sanitise(p)) for p in specification.split()]
187 def validate_transform(self, transform):
188 """A set of pairs, of from-to. Does not require all
26 letters
189 are in the transform.
193 raise ValueError(
"Not all mappings in transform
"
194 "have two elements
")
196 def make_empty_transform(self):
197 """An empty transform maps every letter to itself.
199 self.forward_map = list(range(
26))
200 self.backward_map = list(range(
26))
202 def make_transform_map(self, transform):
203 """Makes the maps for a plugboard. Ensures that if the pair (
'a
',
'b
')
204 is in the specification, the pair (
'b
',
'a
') is also present.
206 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
207 return super(Plugboard, self).make_transform_map(expanded_transform)
212 class Reflector(Plugboard):
213 """A reflector is a plugboard that requires
13 transforms.
214 The
'plugboard
' superclass ensures that all
13 transforms are also applied
215 in reverse, making
26 transforms in all.
217 def validate_transform(self, transform):
218 if len(transform) !=
13:
219 raise ValueError(
"Reflector specification has {} pairs, requires
13".
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(
"Reflector specification does not contain
26 letters
")
225 super(Reflector, self).validate_transform(transform)
226 except ValueError as v:
227 raise ValueError(
"Not all mappings in reflector have two elements
")
232 class SimpleWheel(LetterTransformer):
233 """A wheel is a transform that rotates.
235 Looking from the right, letters go in sequence a-b-c clockwise around the
238 The position of the wheel is the number of spaces anticlockwise the wheel
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
'p
' will enter the
243 wheel on the position under the wheel
's
's
' label.
245 def __init__(self, transform, position=
'a
', raw_transform=False):
246 super(SimpleWheel, self).__init__(transform, raw_transform)
247 self.set_position(position)
249 def __getattribute__(self,name):
250 if name==
'position_l
':
251 return unpos(self.position)
253 return object.__getattribute__(self, name)
255 def set_position(self, position):
256 """Sets a wheel
's position. If the `position` is a string, convert it
257 to a number and set the position.
259 if isinstance(position, str):
260 self.position = pos(position)
262 self.position = position
264 def forward(self, letter):
265 """Give the transformed letter in the forward direction, accounting
266 for the position of the wheel.
268 if letter in string.ascii_lowercase:
269 return unpos((self.forward_map[(pos(letter) + self.position) %
26] - self.position))
273 def backward(self, letter):
274 """Give the transformed letter in the backward direction, accounting
275 for the position of the wheel.
277 if letter in string.ascii_lowercase:
278 return unpos((self.backward_map[(pos(letter) + self.position) %
26] - self.position))
283 """Advance a wheel one position.
"""
284 self.position = (self.position +
1) %
26
288 class Wheel(SimpleWheel):
289 """A wheel with a movable ring.
291 The ring holds the letters and the notches that turn other wheels. The core
292 holds the wiring that does the transformation.
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
298 The position of the wheel is the position of the core (the transforms)
299 relative to the neutral position.
301 The position_l is the position of the ring, or what would be observed
302 by the user of the Enigma machine.
304 The notch_positions are the number of advances of this wheel before it will
305 advance the next wheel.
307 def __init__(self, transform, ring_notch_letters, ring_setting=
1,
308 position=
'a
', 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)
315 def __getattribute__(self,name):
316 if name==
'position_l
':
317 return unpos(self.position + self.ring_setting -
1)
319 return object.__getattribute__(self, name)
321 def set_position(self, position):
322 if isinstance(position, str):
323 self.position = (pos(position) - self.ring_setting +
1) %
26
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]
331 """Advance a wheel
's core, then advance the ring position to match.
333 super(Wheel, self).advance()
334 self.notch_positions = [(p +
1) %
26 for p in self.notch_positions]
338 class Enigma(object):
339 """An Enigma machine.
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,
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)
358 def __getattribute__(self,name):
359 if name==
'wheel_positions
':
360 return (self.left_wheel.position,
361 self.middle_wheel.position,
362 self.right_wheel.position
364 elif name==
'wheel_positions_l
':
365 return (self.left_wheel.position_l,
366 self.middle_wheel.position_l,
367 self.right_wheel.position_l
369 elif name==
'notch_positions
':
370 return (self.left_wheel.notch_positions,
371 self.middle_wheel.notch_positions,
372 self.right_wheel.notch_positions
375 return object.__getattribute__(self, name)
377 def set_wheels(self, left_wheel_position, middle_wheel_position,
378 right_wheel_position):
379 """Set the Enigma
's wheels to the specified positions.
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)
385 def lookup(self, letter):
386 """Lookup the enciphering of a letter, without advancing any wheels
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)
400 """Advance the Enigma
's wheels one step. The right wheel always
401 advances. The middle and right wheels may advance if the notches
404 advance_middle = False
406 if
0 in self.right_wheel.notch_positions:
407 advance_middle = True
408 if
0 in self.middle_wheel.notch_positions:
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()
415 def encipher_letter(self, letter):
416 """Encipher a letter. Advance the Enigma machine, then lookup the
417 encryption of a letter.
420 return self.lookup(letter)
422 def encipher(self, message):
423 """Encipher a message.
"""
424 enciphered =
''
425 for letter in sanitise(message):
426 enciphered += self.encipher_letter(letter)
432 # for i in range(
26):
434 # print(
'enigma.advance()
')
435 # print(
"assert(enigma.wheel_positions == {})
".format(enigma.wheel_positions))
436 # print(
"assert(cat(enigma.wheel_positions_l) ==
'{}
')
".format(cat(enigma.wheel_positions_l)))
437 # print(
"assert(enigma.notch_positions == {})
".format(enigma.notch_positions))
438 # print(
"assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) ==
'{}
')
".format(cat(enigma.lookup(l) for l in string.ascii_lowercase)))
442 if __name__ ==
"__main__
":
444 # doctest.testmod(extraglobs={
'lt
': LetterTransformer(
1,
'a
')})
445 doctest.testmod()
</code></pre>
453 <h2 class=
"section-title" id=
"header-functions">Functions
</h2>
455 <dt id=
"szyfrow.enigma.cat"><code class=
"name flex">
456 <span>def
<span class=
"ident">cat
</span></span>(
<span>iterable, /)
</span>
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']) -
> 'ab.pq.rs'
</p></div>
464 <dt id=
"szyfrow.enigma.lcat"><code class=
"name flex">
465 <span>def
<span class=
"ident">lcat
</span></span>(
<span>iterable, /)
</span>
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']) -
> 'ab.pq.rs'
</p></div>
473 <dt id=
"szyfrow.enigma.wcat"><code class=
"name flex">
474 <span>def
<span class=
"ident">wcat
</span></span>(
<span>iterable, /)
</span>
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']) -
> 'ab.pq.rs'
</p></div>
485 <h2 class=
"section-title" id=
"header-classes">Classes
</h2>
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>
492 <div class=
"desc"><p>An Enigma machine.
</p></div>
493 <details class=
"source">
495 <span>Expand source code
</span>
497 <pre><code class=
"python">class Enigma(object):
498 """An Enigma machine.
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,
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)
517 def __getattribute__(self,name):
518 if name==
'wheel_positions
':
519 return (self.left_wheel.position,
520 self.middle_wheel.position,
521 self.right_wheel.position
523 elif name==
'wheel_positions_l
':
524 return (self.left_wheel.position_l,
525 self.middle_wheel.position_l,
526 self.right_wheel.position_l
528 elif name==
'notch_positions
':
529 return (self.left_wheel.notch_positions,
530 self.middle_wheel.notch_positions,
531 self.right_wheel.notch_positions
534 return object.__getattribute__(self, name)
536 def set_wheels(self, left_wheel_position, middle_wheel_position,
537 right_wheel_position):
538 """Set the Enigma
's wheels to the specified positions.
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)
544 def lookup(self, letter):
545 """Lookup the enciphering of a letter, without advancing any wheels
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)
559 """Advance the Enigma
's wheels one step. The right wheel always
560 advances. The middle and right wheels may advance if the notches
563 advance_middle = False
565 if
0 in self.right_wheel.notch_positions:
566 advance_middle = True
567 if
0 in self.middle_wheel.notch_positions:
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()
574 def encipher_letter(self, letter):
575 """Encipher a letter. Advance the Enigma machine, then lookup the
576 encryption of a letter.
579 return self.lookup(letter)
581 def encipher(self, message):
582 """Encipher a message.
"""
583 enciphered =
''
584 for letter in sanitise(message):
585 enciphered += self.encipher_letter(letter)
588 decipher = encipher
</code></pre>
592 <dt id=
"szyfrow.enigma.Enigma.advance"><code class=
"name flex">
593 <span>def
<span class=
"ident">advance
</span></span>(
<span>self)
</span>
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">
601 <span>Expand source code
</span>
603 <pre><code class=
"python">def advance(self):
604 """Advance the Enigma
's wheels one step. The right wheel always
605 advances. The middle and right wheels may advance if the notches
608 advance_middle = False
610 if
0 in self.right_wheel.notch_positions:
611 advance_middle = True
612 if
0 in self.middle_wheel.notch_positions:
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>
620 <dt id=
"szyfrow.enigma.Enigma.decipher"><code class=
"name flex">
621 <span>def
<span class=
"ident">decipher
</span></span>(
<span>self, message)
</span>
624 <div class=
"desc"><p>Encipher a message.
</p></div>
625 <details class=
"source">
627 <span>Expand source code
</span>
629 <pre><code class=
"python">def encipher(self, message):
630 """Encipher a message.
"""
631 enciphered =
''
632 for letter in sanitise(message):
633 enciphered += self.encipher_letter(letter)
634 return enciphered
</code></pre>
637 <dt id=
"szyfrow.enigma.Enigma.encipher"><code class=
"name flex">
638 <span>def
<span class=
"ident">encipher
</span></span>(
<span>self, message)
</span>
641 <div class=
"desc"><p>Encipher a message.
</p></div>
642 <details class=
"source">
644 <span>Expand source code
</span>
646 <pre><code class=
"python">def encipher(self, message):
647 """Encipher a message.
"""
648 enciphered =
''
649 for letter in sanitise(message):
650 enciphered += self.encipher_letter(letter)
651 return enciphered
</code></pre>
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>
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">
662 <span>Expand source code
</span>
664 <pre><code class=
"python">def encipher_letter(self, letter):
665 """Encipher a letter. Advance the Enigma machine, then lookup the
666 encryption of a letter.
669 return self.lookup(letter)
</code></pre>
672 <dt id=
"szyfrow.enigma.Enigma.lookup"><code class=
"name flex">
673 <span>def
<span class=
"ident">lookup
</span></span>(
<span>self, letter)
</span>
676 <div class=
"desc"><p>Lookup the enciphering of a letter, without advancing any wheels
</p></div>
677 <details class=
"source">
679 <span>Expand source code
</span>
681 <pre><code class=
"python">def lookup(self, letter):
682 """Lookup the enciphering of a letter, without advancing any wheels
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>
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>
700 <div class=
"desc"><p>Set the Enigma's wheels to the specified positions.
</p></div>
701 <details class=
"source">
703 <span>Expand source code
</span>
705 <pre><code class=
"python">def set_wheels(self, left_wheel_position, middle_wheel_position,
706 right_wheel_position):
707 """Set the Enigma
's wheels to the specified positions.
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>
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>
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">
732 <span>Expand source code
</span>
734 <pre><code class=
"python">class LetterTransformer(object):
735 """A generic substitution cipher, that has different transforms in the
736 forward and backward directions. It requires that the transforms for all
739 A `transform` is a list of letter pairs, like `[(
'a
',
'b
'), (
'c
',
'd
')]`.
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
744 def __init__(self, specification, raw_transform=False):
745 """Validate and create a new transformer. The transform is parsed by
746 `LetterTransformer.parse_specification` unless `raw_transform` is `True`
749 transform = specification
751 transform = self.parse_specification(specification)
752 self.validate_transform(transform)
753 self.make_transform_map(transform)
755 def parse_specification(self, specification):
756 """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.
760 return list(zip(string.ascii_lowercase, sanitise(specification)))
761 # return specification
763 def validate_transform(self, transform):
764 """Checks that a transform is valid (every letter is mapped to
765 exactly one other letter, in both directions).
767 if len(transform) !=
26:
768 raise ValueError(
"Transform specification has {} pairs, requires
26".
769 format(len(transform)))
772 raise ValueError(
"Not all mappings in transform
"
773 "have two elements
")
774 if len(set([p[
0] for p in transform])) !=
26:
775 raise ValueError(
"Transform specification must list
26 origin letters
")
776 if len(set([p[
1] for p in transform])) !=
26:
777 raise ValueError(
"Transform specification must list
26 destination letters
")
779 def make_empty_transform(self):
780 """An empty transform is one that maps every letter to
'a
'.
782 self.forward_map = [
0] *
26
783 self.backward_map = [
0] *
26
785 def make_transform_map(self, transform):
786 """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.
790 self.make_empty_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
796 def forward(self, letter):
797 """Apply a map in the forward direction.
799 if letter in string.ascii_lowercase:
800 return unpos(self.forward_map[pos(letter)])
804 def backward(self, letter):
805 """Apply a map in the backward direction.
807 if letter in string.ascii_lowercase:
808 return unpos(self.backward_map[pos(letter)])
810 return
''</code></pre>
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>
819 <dt id=
"szyfrow.enigma.LetterTransformer.backward"><code class=
"name flex">
820 <span>def
<span class=
"ident">backward
</span></span>(
<span>self, letter)
</span>
823 <div class=
"desc"><p>Apply a map in the backward direction.
</p></div>
824 <details class=
"source">
826 <span>Expand source code
</span>
828 <pre><code class=
"python">def backward(self, letter):
829 """Apply a map in the backward direction.
831 if letter in string.ascii_lowercase:
832 return unpos(self.backward_map[pos(letter)])
834 return
''</code></pre>
837 <dt id=
"szyfrow.enigma.LetterTransformer.forward"><code class=
"name flex">
838 <span>def
<span class=
"ident">forward
</span></span>(
<span>self, letter)
</span>
841 <div class=
"desc"><p>Apply a map in the forward direction.
</p></div>
842 <details class=
"source">
844 <span>Expand source code
</span>
846 <pre><code class=
"python">def forward(self, letter):
847 """Apply a map in the forward direction.
849 if letter in string.ascii_lowercase:
850 return unpos(self.forward_map[pos(letter)])
852 return
''</code></pre>
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>
859 <div class=
"desc"><p>An empty transform is one that maps every letter to 'a'.
</p></div>
860 <details class=
"source">
862 <span>Expand source code
</span>
864 <pre><code class=
"python">def make_empty_transform(self):
865 """An empty transform is one that maps every letter to
'a
'.
867 self.forward_map = [
0] *
26
868 self.backward_map = [
0] *
26</code></pre>
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>
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">
880 <span>Expand source code
</span>
882 <pre><code class=
"python">def make_transform_map(self, transform):
883 """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.
887 self.make_empty_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>
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>
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">
903 <span>Expand source code
</span>
905 <pre><code class=
"python">def parse_specification(self, specification):
906 """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.
910 return list(zip(string.ascii_lowercase, sanitise(specification)))
911 # return specification
</code></pre>
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>
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">
922 <span>Expand source code
</span>
924 <pre><code class=
"python">def validate_transform(self, transform):
925 """Checks that a transform is valid (every letter is mapped to
926 exactly one other letter, in both directions).
928 if len(transform) !=
26:
929 raise ValueError(
"Transform specification has {} pairs, requires
26".
930 format(len(transform)))
933 raise ValueError(
"Not all mappings in transform
"
934 "have two elements
")
935 if len(set([p[
0] for p in transform])) !=
26:
936 raise ValueError(
"Transform specification must list
26 origin letters
")
937 if len(set([p[
1] for p in transform])) !=
26:
938 raise ValueError(
"Transform specification must list
26 destination letters
")
</code></pre>
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>
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
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">
955 <span>Expand source code
</span>
957 <pre><code class=
"python">class Plugboard(LetterTransformer):
958 """A plugboard, a type of letter transformer where forward and backward
959 transforms are the same. If a letter isn
't explicitly transformed, it is
963 def parse_specification(self, specification):
964 """Convert a specification into a transform. The specification is
965 given as a list of letter pairs.
967 return [tuple(sanitise(p)) for p in specification.split()]
969 def validate_transform(self, transform):
970 """A set of pairs, of from-to. Does not require all
26 letters
971 are in the transform.
975 raise ValueError(
"Not all mappings in transform
"
976 "have two elements
")
978 def make_empty_transform(self):
979 """An empty transform maps every letter to itself.
981 self.forward_map = list(range(
26))
982 self.backward_map = list(range(
26))
984 def make_transform_map(self, transform):
985 """Makes the maps for a plugboard. Ensures that if the pair (
'a
',
'b
')
986 is in the specification, the pair (
'b
',
'a
') is also present.
988 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
989 return super(Plugboard, self).make_transform_map(expanded_transform)
</code></pre>
993 <li><a title=
"szyfrow.enigma.LetterTransformer" href=
"#szyfrow.enigma.LetterTransformer">LetterTransformer
</a></li>
997 <li><a title=
"szyfrow.enigma.Reflector" href=
"#szyfrow.enigma.Reflector">Reflector
</a></li>
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>
1005 <div class=
"desc"><p>An empty transform maps every letter to itself.
</p></div>
1006 <details class=
"source">
1008 <span>Expand source code
</span>
1010 <pre><code class=
"python">def make_empty_transform(self):
1011 """An empty transform maps every letter to itself.
1013 self.forward_map = list(range(
26))
1014 self.backward_map = list(range(
26))
</code></pre>
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>
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">
1025 <span>Expand source code
</span>
1027 <pre><code class=
"python">def make_transform_map(self, transform):
1028 """Makes the maps for a plugboard. Ensures that if the pair (
'a
',
'b
')
1029 is in the specification, the pair (
'b
',
'a
') is also present.
1031 expanded_transform = transform + [tuple(reversed(p)) for p in transform]
1032 return super(Plugboard, self).make_transform_map(expanded_transform)
</code></pre>
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>
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">
1043 <span>Expand source code
</span>
1045 <pre><code class=
"python">def parse_specification(self, specification):
1046 """Convert a specification into a transform. The specification is
1047 given as a list of letter pairs.
1049 return [tuple(sanitise(p)) for p in specification.split()]
</code></pre>
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>
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">
1060 <span>Expand source code
</span>
1062 <pre><code class=
"python">def validate_transform(self, transform):
1063 """A set of pairs, of from-to. Does not require all
26 letters
1064 are in the transform.
1068 raise ValueError(
"Not all mappings in transform
"
1069 "have two elements
")
</code></pre>
1073 <h3>Inherited members
</h3>
1075 <li><code><b><a title=
"szyfrow.enigma.LetterTransformer" href=
"#szyfrow.enigma.LetterTransformer">LetterTransformer
</a></b></code>:
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>
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>
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">
1095 <span>Expand source code
</span>
1097 <pre><code class=
"python">class Reflector(Plugboard):
1098 """A reflector is a plugboard that requires
13 transforms.
1099 The
'plugboard
' superclass ensures that all
13 transforms are also applied
1100 in reverse, making
26 transforms in all.
1102 def validate_transform(self, transform):
1103 if len(transform) !=
13:
1104 raise ValueError(
"Reflector specification has {} pairs, requires
13".
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(
"Reflector specification does not contain
26 letters
")
1110 super(Reflector, self).validate_transform(transform)
1111 except ValueError as v:
1112 raise ValueError(
"Not all mappings in reflector have two elements
")
</code></pre>
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>
1119 <h3>Inherited members
</h3>
1121 <li><code><b><a title=
"szyfrow.enigma.Plugboard" href=
"#szyfrow.enigma.Plugboard">Plugboard
</a></b></code>:
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>
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>
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
1141 <p>The position of the wheel is the number of spaces anticlockwise the wheel
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">
1150 <span>Expand source code
</span>
1152 <pre><code class=
"python">class SimpleWheel(LetterTransformer):
1153 """A wheel is a transform that rotates.
1155 Looking from the right, letters go in sequence a-b-c clockwise around the
1158 The position of the wheel is the number of spaces anticlockwise the wheel
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
'p
' will enter the
1163 wheel on the position under the wheel
's
's
' label.
1165 def __init__(self, transform, position=
'a
', raw_transform=False):
1166 super(SimpleWheel, self).__init__(transform, raw_transform)
1167 self.set_position(position)
1169 def __getattribute__(self,name):
1170 if name==
'position_l
':
1171 return unpos(self.position)
1173 return object.__getattribute__(self, name)
1175 def set_position(self, position):
1176 """Sets a wheel
's position. If the `position` is a string, convert it
1177 to a number and set the position.
1179 if isinstance(position, str):
1180 self.position = pos(position)
1182 self.position = position
1184 def forward(self, letter):
1185 """Give the transformed letter in the forward direction, accounting
1186 for the position of the wheel.
1188 if letter in string.ascii_lowercase:
1189 return unpos((self.forward_map[(pos(letter) + self.position) %
26] - self.position))
1193 def backward(self, letter):
1194 """Give the transformed letter in the backward direction, accounting
1195 for the position of the wheel.
1197 if letter in string.ascii_lowercase:
1198 return unpos((self.backward_map[(pos(letter) + self.position) %
26] - self.position))
1203 """Advance a wheel one position.
"""
1204 self.position = (self.position +
1) %
26</code></pre>
1208 <li><a title=
"szyfrow.enigma.LetterTransformer" href=
"#szyfrow.enigma.LetterTransformer">LetterTransformer
</a></li>
1212 <li><a title=
"szyfrow.enigma.Wheel" href=
"#szyfrow.enigma.Wheel">Wheel
</a></li>
1216 <dt id=
"szyfrow.enigma.SimpleWheel.advance"><code class=
"name flex">
1217 <span>def
<span class=
"ident">advance
</span></span>(
<span>self)
</span>
1220 <div class=
"desc"><p>Advance a wheel one position.
</p></div>
1221 <details class=
"source">
1223 <span>Expand source code
</span>
1225 <pre><code class=
"python">def advance(self):
1226 """Advance a wheel one position.
"""
1227 self.position = (self.position +
1) %
26</code></pre>
1230 <dt id=
"szyfrow.enigma.SimpleWheel.backward"><code class=
"name flex">
1231 <span>def
<span class=
"ident">backward
</span></span>(
<span>self, letter)
</span>
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">
1238 <span>Expand source code
</span>
1240 <pre><code class=
"python">def backward(self, letter):
1241 """Give the transformed letter in the backward direction, accounting
1242 for the position of the wheel.
1244 if letter in string.ascii_lowercase:
1245 return unpos((self.backward_map[(pos(letter) + self.position) %
26] - self.position))
1247 return
''</code></pre>
1250 <dt id=
"szyfrow.enigma.SimpleWheel.forward"><code class=
"name flex">
1251 <span>def
<span class=
"ident">forward
</span></span>(
<span>self, letter)
</span>
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">
1258 <span>Expand source code
</span>
1260 <pre><code class=
"python">def forward(self, letter):
1261 """Give the transformed letter in the forward direction, accounting
1262 for the position of the wheel.
1264 if letter in string.ascii_lowercase:
1265 return unpos((self.forward_map[(pos(letter) + self.position) %
26] - self.position))
1267 return
''</code></pre>
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>
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">
1278 <span>Expand source code
</span>
1280 <pre><code class=
"python">def set_position(self, position):
1281 """Sets a wheel
's position. If the `position` is a string, convert it
1282 to a number and set the position.
1284 if isinstance(position, str):
1285 self.position = pos(position)
1287 self.position = position
</code></pre>
1291 <h3>Inherited members
</h3>
1293 <li><code><b><a title=
"szyfrow.enigma.LetterTransformer" href=
"#szyfrow.enigma.LetterTransformer">LetterTransformer
</a></b></code>:
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>
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>
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
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">
1324 <span>Expand source code
</span>
1326 <pre><code class=
"python">class Wheel(SimpleWheel):
1327 """A wheel with a movable ring.
1329 The ring holds the letters and the notches that turn other wheels. The core
1330 holds the wiring that does the transformation.
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
1336 The position of the wheel is the position of the core (the transforms)
1337 relative to the neutral position.
1339 The position_l is the position of the ring, or what would be observed
1340 by the user of the Enigma machine.
1342 The notch_positions are the number of advances of this wheel before it will
1343 advance the next wheel.
1345 def __init__(self, transform, ring_notch_letters, ring_setting=
1,
1346 position=
'a
', 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)
1353 def __getattribute__(self,name):
1354 if name==
'position_l
':
1355 return unpos(self.position + self.ring_setting -
1)
1357 return object.__getattribute__(self, name)
1359 def set_position(self, position):
1360 if isinstance(position, str):
1361 self.position = (pos(position) - self.ring_setting +
1) %
26
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]
1369 """Advance a wheel
's core, then advance the ring position to match.
1371 super(Wheel, self).advance()
1372 self.notch_positions = [(p +
1) %
26 for p in self.notch_positions]
1373 return self.position
</code></pre>
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>
1382 <dt id=
"szyfrow.enigma.Wheel.advance"><code class=
"name flex">
1383 <span>def
<span class=
"ident">advance
</span></span>(
<span>self)
</span>
1386 <div class=
"desc"><p>Advance a wheel's core, then advance the ring position to match.
</p></div>
1387 <details class=
"source">
1389 <span>Expand source code
</span>
1391 <pre><code class=
"python">def advance(self):
1392 """Advance a wheel
's core, then advance the ring position to match.
1394 super(Wheel, self).advance()
1395 self.notch_positions = [(p +
1) %
26 for p in self.notch_positions]
1396 return self.position
</code></pre>
1400 <h3>Inherited members
</h3>
1402 <li><code><b><a title=
"szyfrow.enigma.SimpleWheel" href=
"#szyfrow.enigma.SimpleWheel">SimpleWheel
</a></b></code>:
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>
1424 <li><h3>Super-module
</h3>
1426 <li><code><a title=
"szyfrow" href=
"index.html">szyfrow
</a></code></li>
1429 <li><h3><a href=
"#header-functions">Functions
</a></h3>
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>
1436 <li><h3><a href=
"#header-classes">Classes
</a></h3>
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>
1450 <h4><code><a title=
"szyfrow.enigma.LetterTransformer" href=
"#szyfrow.enigma.LetterTransformer">LetterTransformer
</a></code></h4>
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>
1461 <h4><code><a title=
"szyfrow.enigma.Plugboard" href=
"#szyfrow.enigma.Plugboard">Plugboard
</a></code></h4>
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>
1470 <h4><code><a title=
"szyfrow.enigma.Reflector" href=
"#szyfrow.enigma.Reflector">Reflector
</a></code></h4>
1473 <h4><code><a title=
"szyfrow.enigma.SimpleWheel" href=
"#szyfrow.enigma.SimpleWheel">SimpleWheel
</a></code></h4>
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>
1482 <h4><code><a title=
"szyfrow.enigma.Wheel" href=
"#szyfrow.enigma.Wheel">Wheel
</a></code></h4>
1484 <li><code><a title=
"szyfrow.enigma.Wheel.advance" href=
"#szyfrow.enigma.Wheel.advance">advance
</a></code></li>
1492 <footer id=
"footer">
1493 <p>Generated by
<a href=
"https://pdoc3.github.io/pdoc"><cite>pdoc
</cite> 0.9.2</a>.
</p>