Started on documentation
[szyfrow.git] / docs / szyfrow / bombe.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.bombe API documentation</title>
8 <meta name="description" content="A simulator for Bombe 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.bombe</code></h1>
23 </header>
24 <section id="section-intro">
25 <p>A simulator for Bombe machines.</p>
26 <p>See <code><a title="szyfrow.enigma.Enigma" href="enigma.html#szyfrow.enigma.Enigma">Enigma</a></code> for an implementation of the Enigma to create
27 messages.</p>
28 <p>There is a good explanation of <a href="http://www.ellsbury.com/enigmabombe.htm">how the bombe worked</a>
29 by Graham Ellsbury.</p>
30 <p>In this implementation, there are <em>banks</em> of wires (what Ellsbury refers to
31 as "cables"), one bank for each position that appears in the menu. A bank
32 comprises 26 wires, represented as a <code>dict</code> of <code>bool</code>s, depending on whether
33 that wire is live ("energised") or not.</p>
34 <p>The menu, derived from the crib, determines how the scramblers connect the
35 banks. A <code><a title="szyfrow.bombe.Connection" href="#szyfrow.bombe.Connection">Connection</a></code> represents this.</p>
36 <details class="source">
37 <summary>
38 <span>Expand source code</span>
39 </summary>
40 <pre><code class="python">&#34;&#34;&#34;A simulator for Bombe machines.
41
42 See `szyfrow.enigma.Enigma` for an implementation of the Enigma to create
43 messages.
44
45 There is a good explanation of [how the bombe worked](http://www.ellsbury.com/enigmabombe.htm)
46 by Graham Ellsbury.
47
48 In this implementation, there are *banks* of wires (what Ellsbury refers to
49 as &#34;cables&#34;), one bank for each position that appears in the menu. A bank
50 comprises 26 wires, represented as a `dict` of `bool`s, depending on whether
51 that wire is live (&#34;energised&#34;) or not.
52
53 The menu, derived from the crib, determines how the scramblers connect the
54 banks. A `Connection` represents this.
55 &#34;&#34;&#34;
56
57 import string
58 import collections
59 import multiprocessing
60 import itertools
61 import logging
62
63 from szyfrow.enigma import *
64
65 __pdoc__ = {}
66
67 Signal = collections.namedtuple(&#39;Signal&#39;, [&#39;bank&#39;, &#39;wire&#39;])
68 __pdoc__[&#39;Signal&#39;] = &#34;&#34;&#34;Current propogation through the Bombe indicates that
69 this wire in this bank is live, and the effects need to be proogated further
70 through the machine.
71 &#34;&#34;&#34;
72 __pdoc__[&#39;Signal.bank&#39;] = &#34;&#34;&#34;The bank of a signal.&#34;&#34;&#34;
73 __pdoc__[&#39;Signal.wire&#39;] = &#34;&#34;&#34;The wire of a signal.&#34;&#34;&#34;
74
75 Connection = collections.namedtuple(&#39;Connection&#39;, [&#39;banks&#39;, &#39;scrambler&#39;])
76 __pdoc__[&#39;Connection&#39;] = &#34;&#34;&#34;A connection between banks made by a particular
77 scrambler (the scrambler state given by its position in the crib).
78 &#34;&#34;&#34;
79 __pdoc__[&#39;Connection.banks&#39;] = &#34;&#34;&#34;A list of two items, holding the bnaks of
80 a connection.&#34;&#34;&#34;
81 __pdoc__[&#39;Connection.scrambler&#39;] = &#34;&#34;&#34;The bnaks of a connection.&#34;&#34;&#34;
82
83
84 MenuItem = collections.namedtuple(&#39;MenuIem&#39;, [&#39;before&#39;, &#39;after&#39;, &#39;number&#39;])
85 __pdoc__[&#39;MenuItem&#39;] = &#34;&#34;&#34;One item in the menu, derived from the crib.
86 &#34;&#34;&#34;
87 __pdoc__[&#39;MenuItem.before&#39;] = &#34;The letter before the transform (plaintext).&#34;
88 __pdoc__[&#39;MenuItem.after&#39;] = &#34;The letter after the transform (ciphertext).&#34;
89 __pdoc__[&#39;MenuItem.number&#39;] = &#34;The position of this item in the menu.&#34;
90
91
92 def make_menu(plaintext, ciphertext):
93 &#34;&#34;&#34;Create a menu from a crib: a given plaintext and ciphertext.
94
95 No validation is done to ensure that this is a viable crib (e.g. no
96 checking for length, no checking that a letter is enciphered to itself).
97 &#34;&#34;&#34;
98 return [MenuItem(p, c, i+1)
99 for i, (p, c) in enumerate(zip(plaintext, ciphertext))]
100
101
102 class Scrambler(object):
103 &#34;&#34;&#34;A scrambler is a collection of three `szyfrow.enigma.SimpleWheel`s.
104 &#34;&#34;&#34;
105 def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,
106 wheel1_pos=&#39;a&#39;, wheel2_pos=&#39;a&#39;, wheel3_pos=&#39;a&#39;):
107 self.wheel1 = SimpleWheel(wheel1_spec, position=wheel1_pos)
108 self.wheel2 = SimpleWheel(wheel2_spec, position=wheel2_pos)
109 self.wheel3 = SimpleWheel(wheel3_spec, position=wheel3_pos)
110 self.reflector = Reflector(reflector_spec)
111
112 __pdoc__[&#39;Scrambler.wheel_positions&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
113 positions (as numbers)&#34;&#34;&#34;
114 __pdoc__[&#39;Scrambler.wheel_positions_l&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
115 positions (as letters)&#34;&#34;&#34;
116 def __getattribute__(self, name):
117 if name==&#39;wheel_positions&#39;:
118 return self.wheel1.position, self.wheel2.position, self.wheel3.position
119 elif name==&#39;wheel_positions_l&#39;:
120 return self.wheel1.position_l, self.wheel2.position_l, self.wheel3.position_l
121 else:
122 return object.__getattribute__(self, name)
123
124 def advance(self, wheel1=False, wheel2=False, wheel3=True):
125 &#34;&#34;&#34;Advance some wheels of a scrambler.
126 &#34;&#34;&#34;
127 if wheel1: self.wheel1.advance()
128 if wheel2: self.wheel2.advance()
129 if wheel3: self.wheel3.advance()
130
131 def lookup(self, letter):
132 &#34;&#34;&#34;Lookup the decipherment of a letter, given a particular scrambler
133 orientation.
134 &#34;&#34;&#34;
135 a = self.wheel3.forward(letter)
136 b = self.wheel2.forward(a)
137 c = self.wheel1.forward(b)
138 d = self.reflector.forward(c)
139 e = self.wheel1.backward(d)
140 f = self.wheel2.backward(e)
141 g = self.wheel3.backward(f)
142 return g
143
144 def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
145 &#34;&#34;&#34;Set the positions of a scrambler&#39;s wheels.
146 &#34;&#34;&#34;
147 self.wheel1.set_position(wheel1_pos)
148 self.wheel2.set_position(wheel2_pos)
149 self.wheel3.set_position(wheel3_pos)
150
151
152 class Bombe(object):
153 &#34;&#34;&#34;An entire Bombe machine.
154
155 This specifies the pattern of the wheels and reflectors used. The
156 scramblers are connected and wired up according the to the specification
157 given by the menu.
158
159 Bombe objects are callable. Calling a Bombe (with the starting scrambler
160 positions) calls the `test` method and returns the pair of
161 `start_positions` and the result of `test`.
162
163 Bombe objects have attributes `wheel_positions` and `wheel_positions_l`,
164 which return the results of the scramblers&#39; `Scrambler.wheel_positions`
165 and `Scrambler.wheel_positions_l`.
166 &#34;&#34;&#34;
167
168 def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,
169 menu=None, start_signal=None, use_diagonal_board=True,
170 verify_plugboard=True):
171 self.connections = []
172 self.wheel1_spec = wheel1_spec
173 self.wheel2_spec = wheel2_spec
174 self.wheel3_spec = wheel3_spec
175 self.reflector_spec = reflector_spec
176 if menu:
177 self.read_menu(menu)
178 if start_signal:
179 self.test_start = start_signal
180 self.use_diagonal_board = use_diagonal_board
181 self.verify_plugboard = verify_plugboard
182
183 __pdoc__[&#39;Bombe.wheel_positions&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
184 positions (as numbers)&#34;&#34;&#34;
185 __pdoc__[&#39;Bomb3.wheel_positions_l&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
186 positions (as letters)&#34;&#34;&#34;
187 def __getattribute__(self, name):
188 if name==&#39;wheel_positions&#39;:
189 return self.connections[0].scrambler.wheel_positions
190 elif name==&#39;wheel_positions_l&#39;:
191 return self.connections[0].scrambler.wheel_positions_l
192 else:
193 return object.__getattribute__(self, name)
194
195 def __call__(self, start_positions):
196 return start_positions, self.test(initial_signal=self.test_start,
197 start_positions=start_positions,
198 use_diagonal_board=self.use_diagonal_board,
199 verify_plugboard=self.verify_plugboard)
200
201 def add_connection(self, bank_before, bank_after, scrambler):
202 &#34;&#34;&#34;Create a new connection between banks.
203 &#34;&#34;&#34;
204 self.connections += [Connection([bank_before, bank_after], scrambler)]
205
206 def read_menu(self, menu):
207 &#34;&#34;&#34;Read a menu, creating one scrambler for each element of the menu
208 and setting up the connections it implies. Also defines the most
209 common letter in the menu&#39;s plaintext as the default letter to start
210 testing with.&#34;&#34;&#34;
211 self.connections = []
212 for item in menu:
213 scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec,
214 self.reflector_spec,
215 wheel3_pos=unpos(item.number - 1))
216 self.add_connection(item.before, item.after, scrambler)
217 most_common_letter = (collections.Counter(m.before for m in menu) +\
218 collections.Counter(m.after for m in menu)).most_common(1)[0][0]
219 self.test_start = Signal(most_common_letter, most_common_letter)
220
221 def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
222 &#34;&#34;&#34;Set positions of all scramblers. The first scrambler will be set
223 to the specified positions. The second scrambler will have its
224 third wheel advanced one position; the third scramber will have its
225 third wheel advanced two positios; and so on. Not that the first and
226 second wheels of the scramblers are never advanced in setup.&#34;&#34;&#34;
227 for i, c in enumerate(self.connections):
228 c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i))
229
230 def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True,
231 verify_plugboard=True):
232 &#34;&#34;&#34;Test a scrambler setting. It creates a signal (held in
233 `self.pending`) on the `initial_signal` wire then uses
234 `Bombe.propagate` to propagate the signal across the Bombe.
235
236 Returns a Boolean recording if this scrambler setting with
237 this signal is a &#34;stop&#34; (potential valid scrambler setting).
238
239 * If `initial_signal` is `None`, use the default starting signal set in
240 `Bombe.read_menu`
241 * If `start_positions` is `None`, use the existing scramber positions.&#34;&#34;&#34;
242 self.banks = {label:
243 dict(zip(string.ascii_lowercase,
244 [False]*len(string.ascii_lowercase)))
245 for label in string.ascii_lowercase}
246 if start_positions:
247 self.set_positions(*start_positions)
248 if not initial_signal:
249 initial_signal = self.test_start
250 self.pending = [initial_signal]
251 self.propagate(use_diagonal_board)
252 live_wire_count = len([self.banks[self.test_start.bank][w]
253 for w in self.banks[self.test_start.bank]
254 if self.banks[self.test_start.bank][w]])
255 if live_wire_count &lt; 26:
256 if verify_plugboard:
257 possibles = self.possible_plugboards()
258 return all(s0.isdisjoint(s1)
259 for s0 in possibles
260 for s1 in possibles
261 if s0 != s1)
262 else:
263 return True
264 else:
265 return False
266
267 def propagate(self, use_diagonal_board):
268 &#34;&#34;&#34;Propagate a signal through the Bombe. Uses `self.pending` as an
269 agenda for a breadth-first search. Each element on the agenda represents
270 a particular wire in a bank that is being &#34;energised&#34; (set to `True`).
271 The first element in the agenda is removed, the wire/bank is set,
272 then all connected wire/banks are added to the `self.pending`
273 agenda.
274 &#34;&#34;&#34;
275 while self.pending:
276 current = self.pending[0]
277 # print(&#34;processing&#34;, current)
278 self.pending = self.pending[1:]
279 if not self.banks[current.bank][current.wire]:
280 self.banks[current.bank][current.wire] = True
281 if use_diagonal_board:
282 self.pending += [Signal(current.wire, current.bank)]
283 for c in self.connections:
284 if current.bank in c.banks:
285 other_bank = [b for b in c.banks if b != current.bank][0]
286 other_wire = c.scrambler.lookup(current.wire)
287 # print(&#34; adding&#34;, other_bank, other_wire, &#34;because&#34;, c.banks)
288 self.pending += [Signal(other_bank, other_wire)]
289
290 def run(self, run_start=None, wheel1_pos=&#39;a&#39;, wheel2_pos=&#39;a&#39;, wheel3_pos=&#39;a&#39;,
291 use_diagonal_board=True):
292 &#34;&#34;&#34;Run a Bombe after setup with a menu, by trying all scramber
293 positions. For each scrambler position, `Bombe.test` is run. If the
294 test is successful, the scrambler positiions are added to `self.solutions`.
295 `self.Solutions` is returned.
296 &#34;&#34;&#34;
297 if not run_start:
298 run_start = self.test_start
299 self.solutions = []
300 self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos)
301 for run_index in range(26*26*26):
302 if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board):
303 self.solutions += [self.connections[0].scrambler.wheel_positions_l]
304 advance3 = True
305 advance2 = False
306 advance1 = False
307 if (run_index + 1) % 26 == 0: advance2 = True
308 if (run_index + 1) % (26*26) == 0: advance1 = True
309 for c in self.connections:
310 c.scrambler.advance(advance1, advance2, advance3)
311 return self.solutions
312
313 def possible_plugboards(self):
314 &#34;&#34;&#34;Given a Bombe after a `Bombe.test` has been performed, determine
315 what plugboard settings can be derived from the solution.
316 &#34;&#34;&#34;
317 possibles = set()
318 for b in self.banks:
319 active = [w for w in self.banks[b] if self.banks[b][w]]
320 inactive = [w for w in self.banks[b] if not self.banks[b][w]]
321 if len(active) == 1:
322 possibles = possibles.union({frozenset((b, active[0]))})
323 if len(inactive) == 1:
324 possibles = possibles.union({frozenset((b, inactive[0]))})
325 return possibles
326
327
328 def run_multi_bombe(wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu,
329 start_signal=None, use_diagonal_board=True,
330 verify_plugboard=True):
331 &#34;&#34;&#34;Run a Bombe solution, spreading the load across multiple CPU cores.
332 Similar to `Bombe.run` in effects, but quicker on a multi-core machine.
333 &#34;&#34;&#34;
334 allwheels = itertools.product(string.ascii_lowercase, repeat=3)
335
336 with multiprocessing.Pool() as pool:
337 res = pool.map(Bombe(wheel1_spec, wheel2_spec, wheel3_spec,
338 reflector_spec, menu=menu, start_signal=start_signal,
339 use_diagonal_board=use_diagonal_board,
340 verify_plugboard=verify_plugboard),
341 allwheels)
342 return [r[0] for r in res if r[1]]</code></pre>
343 </details>
344 </section>
345 <section>
346 </section>
347 <section>
348 </section>
349 <section>
350 <h2 class="section-title" id="header-functions">Functions</h2>
351 <dl>
352 <dt id="szyfrow.bombe.cat"><code class="name flex">
353 <span>def <span class="ident">cat</span></span>(<span>iterable, /)</span>
354 </code></dt>
355 <dd>
356 <div class="desc"><p>Concatenate any number of strings.</p>
357 <p>The string whose method is called is inserted in between each given string.
358 The result is returned as a new string.</p>
359 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
360 </dd>
361 <dt id="szyfrow.bombe.lcat"><code class="name flex">
362 <span>def <span class="ident">lcat</span></span>(<span>iterable, /)</span>
363 </code></dt>
364 <dd>
365 <div class="desc"><p>Concatenate any number of strings.</p>
366 <p>The string whose method is called is inserted in between each given string.
367 The result is returned as a new string.</p>
368 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
369 </dd>
370 <dt id="szyfrow.bombe.make_menu"><code class="name flex">
371 <span>def <span class="ident">make_menu</span></span>(<span>plaintext, ciphertext)</span>
372 </code></dt>
373 <dd>
374 <div class="desc"><p>Create a menu from a crib: a given plaintext and ciphertext.</p>
375 <p>No validation is done to ensure that this is a viable crib (e.g. no
376 checking for length, no checking that a letter is enciphered to itself).</p></div>
377 <details class="source">
378 <summary>
379 <span>Expand source code</span>
380 </summary>
381 <pre><code class="python">def make_menu(plaintext, ciphertext):
382 &#34;&#34;&#34;Create a menu from a crib: a given plaintext and ciphertext.
383
384 No validation is done to ensure that this is a viable crib (e.g. no
385 checking for length, no checking that a letter is enciphered to itself).
386 &#34;&#34;&#34;
387 return [MenuItem(p, c, i+1)
388 for i, (p, c) in enumerate(zip(plaintext, ciphertext))]</code></pre>
389 </details>
390 </dd>
391 <dt id="szyfrow.bombe.run_multi_bombe"><code class="name flex">
392 <span>def <span class="ident">run_multi_bombe</span></span>(<span>wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu, start_signal=None, use_diagonal_board=True, verify_plugboard=True)</span>
393 </code></dt>
394 <dd>
395 <div class="desc"><p>Run a Bombe solution, spreading the load across multiple CPU cores.
396 Similar to <code><a title="szyfrow.bombe.Bombe.run" href="#szyfrow.bombe.Bombe.run">Bombe.run()</a></code> in effects, but quicker on a multi-core machine.</p></div>
397 <details class="source">
398 <summary>
399 <span>Expand source code</span>
400 </summary>
401 <pre><code class="python">def run_multi_bombe(wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu,
402 start_signal=None, use_diagonal_board=True,
403 verify_plugboard=True):
404 &#34;&#34;&#34;Run a Bombe solution, spreading the load across multiple CPU cores.
405 Similar to `Bombe.run` in effects, but quicker on a multi-core machine.
406 &#34;&#34;&#34;
407 allwheels = itertools.product(string.ascii_lowercase, repeat=3)
408
409 with multiprocessing.Pool() as pool:
410 res = pool.map(Bombe(wheel1_spec, wheel2_spec, wheel3_spec,
411 reflector_spec, menu=menu, start_signal=start_signal,
412 use_diagonal_board=use_diagonal_board,
413 verify_plugboard=verify_plugboard),
414 allwheels)
415 return [r[0] for r in res if r[1]]</code></pre>
416 </details>
417 </dd>
418 <dt id="szyfrow.bombe.wcat"><code class="name flex">
419 <span>def <span class="ident">wcat</span></span>(<span>iterable, /)</span>
420 </code></dt>
421 <dd>
422 <div class="desc"><p>Concatenate any number of strings.</p>
423 <p>The string whose method is called is inserted in between each given string.
424 The result is returned as a new string.</p>
425 <p>Example: '.'.join(['ab', 'pq', 'rs']) -&gt; 'ab.pq.rs'</p></div>
426 </dd>
427 </dl>
428 </section>
429 <section>
430 <h2 class="section-title" id="header-classes">Classes</h2>
431 <dl>
432 <dt id="szyfrow.bombe.Bombe"><code class="flex name class">
433 <span>class <span class="ident">Bombe</span></span>
434 <span>(</span><span>wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu=None, start_signal=None, use_diagonal_board=True, verify_plugboard=True)</span>
435 </code></dt>
436 <dd>
437 <div class="desc"><p>An entire Bombe machine.</p>
438 <p>This specifies the pattern of the wheels and reflectors used. The
439 scramblers are connected and wired up according the to the specification
440 given by the menu. </p>
441 <p>Bombe objects are callable. Calling a Bombe (with the starting scrambler
442 positions) calls the <code>test</code> method and
443 returns the pair of
444 <code>start_positions</code> and the result of <code>test</code>.</p>
445 <p>Bombe objects have attributes <code>wheel_positions</code> and <code>wheel_positions_l</code>,
446 which return the results of the scramblers' <code>Scrambler.wheel_positions</code>
447 and <code>Scrambler.wheel_positions_l</code>.</p></div>
448 <details class="source">
449 <summary>
450 <span>Expand source code</span>
451 </summary>
452 <pre><code class="python">class Bombe(object):
453 &#34;&#34;&#34;An entire Bombe machine.
454
455 This specifies the pattern of the wheels and reflectors used. The
456 scramblers are connected and wired up according the to the specification
457 given by the menu.
458
459 Bombe objects are callable. Calling a Bombe (with the starting scrambler
460 positions) calls the `test` method and returns the pair of
461 `start_positions` and the result of `test`.
462
463 Bombe objects have attributes `wheel_positions` and `wheel_positions_l`,
464 which return the results of the scramblers&#39; `Scrambler.wheel_positions`
465 and `Scrambler.wheel_positions_l`.
466 &#34;&#34;&#34;
467
468 def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,
469 menu=None, start_signal=None, use_diagonal_board=True,
470 verify_plugboard=True):
471 self.connections = []
472 self.wheel1_spec = wheel1_spec
473 self.wheel2_spec = wheel2_spec
474 self.wheel3_spec = wheel3_spec
475 self.reflector_spec = reflector_spec
476 if menu:
477 self.read_menu(menu)
478 if start_signal:
479 self.test_start = start_signal
480 self.use_diagonal_board = use_diagonal_board
481 self.verify_plugboard = verify_plugboard
482
483 __pdoc__[&#39;Bombe.wheel_positions&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
484 positions (as numbers)&#34;&#34;&#34;
485 __pdoc__[&#39;Bomb3.wheel_positions_l&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
486 positions (as letters)&#34;&#34;&#34;
487 def __getattribute__(self, name):
488 if name==&#39;wheel_positions&#39;:
489 return self.connections[0].scrambler.wheel_positions
490 elif name==&#39;wheel_positions_l&#39;:
491 return self.connections[0].scrambler.wheel_positions_l
492 else:
493 return object.__getattribute__(self, name)
494
495 def __call__(self, start_positions):
496 return start_positions, self.test(initial_signal=self.test_start,
497 start_positions=start_positions,
498 use_diagonal_board=self.use_diagonal_board,
499 verify_plugboard=self.verify_plugboard)
500
501 def add_connection(self, bank_before, bank_after, scrambler):
502 &#34;&#34;&#34;Create a new connection between banks.
503 &#34;&#34;&#34;
504 self.connections += [Connection([bank_before, bank_after], scrambler)]
505
506 def read_menu(self, menu):
507 &#34;&#34;&#34;Read a menu, creating one scrambler for each element of the menu
508 and setting up the connections it implies. Also defines the most
509 common letter in the menu&#39;s plaintext as the default letter to start
510 testing with.&#34;&#34;&#34;
511 self.connections = []
512 for item in menu:
513 scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec,
514 self.reflector_spec,
515 wheel3_pos=unpos(item.number - 1))
516 self.add_connection(item.before, item.after, scrambler)
517 most_common_letter = (collections.Counter(m.before for m in menu) +\
518 collections.Counter(m.after for m in menu)).most_common(1)[0][0]
519 self.test_start = Signal(most_common_letter, most_common_letter)
520
521 def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
522 &#34;&#34;&#34;Set positions of all scramblers. The first scrambler will be set
523 to the specified positions. The second scrambler will have its
524 third wheel advanced one position; the third scramber will have its
525 third wheel advanced two positios; and so on. Not that the first and
526 second wheels of the scramblers are never advanced in setup.&#34;&#34;&#34;
527 for i, c in enumerate(self.connections):
528 c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i))
529
530 def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True,
531 verify_plugboard=True):
532 &#34;&#34;&#34;Test a scrambler setting. It creates a signal (held in
533 `self.pending`) on the `initial_signal` wire then uses
534 `Bombe.propagate` to propagate the signal across the Bombe.
535
536 Returns a Boolean recording if this scrambler setting with
537 this signal is a &#34;stop&#34; (potential valid scrambler setting).
538
539 * If `initial_signal` is `None`, use the default starting signal set in
540 `Bombe.read_menu`
541 * If `start_positions` is `None`, use the existing scramber positions.&#34;&#34;&#34;
542 self.banks = {label:
543 dict(zip(string.ascii_lowercase,
544 [False]*len(string.ascii_lowercase)))
545 for label in string.ascii_lowercase}
546 if start_positions:
547 self.set_positions(*start_positions)
548 if not initial_signal:
549 initial_signal = self.test_start
550 self.pending = [initial_signal]
551 self.propagate(use_diagonal_board)
552 live_wire_count = len([self.banks[self.test_start.bank][w]
553 for w in self.banks[self.test_start.bank]
554 if self.banks[self.test_start.bank][w]])
555 if live_wire_count &lt; 26:
556 if verify_plugboard:
557 possibles = self.possible_plugboards()
558 return all(s0.isdisjoint(s1)
559 for s0 in possibles
560 for s1 in possibles
561 if s0 != s1)
562 else:
563 return True
564 else:
565 return False
566
567 def propagate(self, use_diagonal_board):
568 &#34;&#34;&#34;Propagate a signal through the Bombe. Uses `self.pending` as an
569 agenda for a breadth-first search. Each element on the agenda represents
570 a particular wire in a bank that is being &#34;energised&#34; (set to `True`).
571 The first element in the agenda is removed, the wire/bank is set,
572 then all connected wire/banks are added to the `self.pending`
573 agenda.
574 &#34;&#34;&#34;
575 while self.pending:
576 current = self.pending[0]
577 # print(&#34;processing&#34;, current)
578 self.pending = self.pending[1:]
579 if not self.banks[current.bank][current.wire]:
580 self.banks[current.bank][current.wire] = True
581 if use_diagonal_board:
582 self.pending += [Signal(current.wire, current.bank)]
583 for c in self.connections:
584 if current.bank in c.banks:
585 other_bank = [b for b in c.banks if b != current.bank][0]
586 other_wire = c.scrambler.lookup(current.wire)
587 # print(&#34; adding&#34;, other_bank, other_wire, &#34;because&#34;, c.banks)
588 self.pending += [Signal(other_bank, other_wire)]
589
590 def run(self, run_start=None, wheel1_pos=&#39;a&#39;, wheel2_pos=&#39;a&#39;, wheel3_pos=&#39;a&#39;,
591 use_diagonal_board=True):
592 &#34;&#34;&#34;Run a Bombe after setup with a menu, by trying all scramber
593 positions. For each scrambler position, `Bombe.test` is run. If the
594 test is successful, the scrambler positiions are added to `self.solutions`.
595 `self.Solutions` is returned.
596 &#34;&#34;&#34;
597 if not run_start:
598 run_start = self.test_start
599 self.solutions = []
600 self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos)
601 for run_index in range(26*26*26):
602 if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board):
603 self.solutions += [self.connections[0].scrambler.wheel_positions_l]
604 advance3 = True
605 advance2 = False
606 advance1 = False
607 if (run_index + 1) % 26 == 0: advance2 = True
608 if (run_index + 1) % (26*26) == 0: advance1 = True
609 for c in self.connections:
610 c.scrambler.advance(advance1, advance2, advance3)
611 return self.solutions
612
613 def possible_plugboards(self):
614 &#34;&#34;&#34;Given a Bombe after a `Bombe.test` has been performed, determine
615 what plugboard settings can be derived from the solution.
616 &#34;&#34;&#34;
617 possibles = set()
618 for b in self.banks:
619 active = [w for w in self.banks[b] if self.banks[b][w]]
620 inactive = [w for w in self.banks[b] if not self.banks[b][w]]
621 if len(active) == 1:
622 possibles = possibles.union({frozenset((b, active[0]))})
623 if len(inactive) == 1:
624 possibles = possibles.union({frozenset((b, inactive[0]))})
625 return possibles</code></pre>
626 </details>
627 <h3>Methods</h3>
628 <dl>
629 <dt id="szyfrow.bombe.Bombe.add_connection"><code class="name flex">
630 <span>def <span class="ident">add_connection</span></span>(<span>self, bank_before, bank_after, scrambler)</span>
631 </code></dt>
632 <dd>
633 <div class="desc"><p>Create a new connection between banks.</p></div>
634 <details class="source">
635 <summary>
636 <span>Expand source code</span>
637 </summary>
638 <pre><code class="python">def add_connection(self, bank_before, bank_after, scrambler):
639 &#34;&#34;&#34;Create a new connection between banks.
640 &#34;&#34;&#34;
641 self.connections += [Connection([bank_before, bank_after], scrambler)]</code></pre>
642 </details>
643 </dd>
644 <dt id="szyfrow.bombe.Bombe.possible_plugboards"><code class="name flex">
645 <span>def <span class="ident">possible_plugboards</span></span>(<span>self)</span>
646 </code></dt>
647 <dd>
648 <div class="desc"><p>Given a Bombe after a <code><a title="szyfrow.bombe.Bombe.test" href="#szyfrow.bombe.Bombe.test">Bombe.test()</a></code> has been performed, determine
649 what plugboard settings can be derived from the solution.</p></div>
650 <details class="source">
651 <summary>
652 <span>Expand source code</span>
653 </summary>
654 <pre><code class="python">def possible_plugboards(self):
655 &#34;&#34;&#34;Given a Bombe after a `Bombe.test` has been performed, determine
656 what plugboard settings can be derived from the solution.
657 &#34;&#34;&#34;
658 possibles = set()
659 for b in self.banks:
660 active = [w for w in self.banks[b] if self.banks[b][w]]
661 inactive = [w for w in self.banks[b] if not self.banks[b][w]]
662 if len(active) == 1:
663 possibles = possibles.union({frozenset((b, active[0]))})
664 if len(inactive) == 1:
665 possibles = possibles.union({frozenset((b, inactive[0]))})
666 return possibles</code></pre>
667 </details>
668 </dd>
669 <dt id="szyfrow.bombe.Bombe.propagate"><code class="name flex">
670 <span>def <span class="ident">propagate</span></span>(<span>self, use_diagonal_board)</span>
671 </code></dt>
672 <dd>
673 <div class="desc"><p>Propagate a signal through the Bombe. Uses <code>self.pending</code> as an
674 agenda for a breadth-first search. Each element on the agenda represents
675 a particular wire in a bank that is being "energised" (set to <code>True</code>).
676 The first element in the agenda is removed, the wire/bank is set,
677 then all connected wire/banks are added to the <code>self.pending</code>
678 agenda.</p></div>
679 <details class="source">
680 <summary>
681 <span>Expand source code</span>
682 </summary>
683 <pre><code class="python">def propagate(self, use_diagonal_board):
684 &#34;&#34;&#34;Propagate a signal through the Bombe. Uses `self.pending` as an
685 agenda for a breadth-first search. Each element on the agenda represents
686 a particular wire in a bank that is being &#34;energised&#34; (set to `True`).
687 The first element in the agenda is removed, the wire/bank is set,
688 then all connected wire/banks are added to the `self.pending`
689 agenda.
690 &#34;&#34;&#34;
691 while self.pending:
692 current = self.pending[0]
693 # print(&#34;processing&#34;, current)
694 self.pending = self.pending[1:]
695 if not self.banks[current.bank][current.wire]:
696 self.banks[current.bank][current.wire] = True
697 if use_diagonal_board:
698 self.pending += [Signal(current.wire, current.bank)]
699 for c in self.connections:
700 if current.bank in c.banks:
701 other_bank = [b for b in c.banks if b != current.bank][0]
702 other_wire = c.scrambler.lookup(current.wire)
703 # print(&#34; adding&#34;, other_bank, other_wire, &#34;because&#34;, c.banks)
704 self.pending += [Signal(other_bank, other_wire)]</code></pre>
705 </details>
706 </dd>
707 <dt id="szyfrow.bombe.Bombe.read_menu"><code class="name flex">
708 <span>def <span class="ident">read_menu</span></span>(<span>self, menu)</span>
709 </code></dt>
710 <dd>
711 <div class="desc"><p>Read a menu, creating one scrambler for each element of the menu
712 and setting up the connections it implies. Also defines the most
713 common letter in the menu's plaintext as the default letter to start
714 testing with.</p></div>
715 <details class="source">
716 <summary>
717 <span>Expand source code</span>
718 </summary>
719 <pre><code class="python">def read_menu(self, menu):
720 &#34;&#34;&#34;Read a menu, creating one scrambler for each element of the menu
721 and setting up the connections it implies. Also defines the most
722 common letter in the menu&#39;s plaintext as the default letter to start
723 testing with.&#34;&#34;&#34;
724 self.connections = []
725 for item in menu:
726 scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec,
727 self.reflector_spec,
728 wheel3_pos=unpos(item.number - 1))
729 self.add_connection(item.before, item.after, scrambler)
730 most_common_letter = (collections.Counter(m.before for m in menu) +\
731 collections.Counter(m.after for m in menu)).most_common(1)[0][0]
732 self.test_start = Signal(most_common_letter, most_common_letter)</code></pre>
733 </details>
734 </dd>
735 <dt id="szyfrow.bombe.Bombe.run"><code class="name flex">
736 <span>def <span class="ident">run</span></span>(<span>self, run_start=None, wheel1_pos='a', wheel2_pos='a', wheel3_pos='a', use_diagonal_board=True)</span>
737 </code></dt>
738 <dd>
739 <div class="desc"><p>Run a Bombe after setup with a menu, by trying all scramber
740 positions. For each scrambler position, <code><a title="szyfrow.bombe.Bombe.test" href="#szyfrow.bombe.Bombe.test">Bombe.test()</a></code> is run. If the
741 test is successful, the scrambler positiions are added to <code>self.solutions</code>.
742 <code>self.Solutions</code> is returned.</p></div>
743 <details class="source">
744 <summary>
745 <span>Expand source code</span>
746 </summary>
747 <pre><code class="python">def run(self, run_start=None, wheel1_pos=&#39;a&#39;, wheel2_pos=&#39;a&#39;, wheel3_pos=&#39;a&#39;,
748 use_diagonal_board=True):
749 &#34;&#34;&#34;Run a Bombe after setup with a menu, by trying all scramber
750 positions. For each scrambler position, `Bombe.test` is run. If the
751 test is successful, the scrambler positiions are added to `self.solutions`.
752 `self.Solutions` is returned.
753 &#34;&#34;&#34;
754 if not run_start:
755 run_start = self.test_start
756 self.solutions = []
757 self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos)
758 for run_index in range(26*26*26):
759 if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board):
760 self.solutions += [self.connections[0].scrambler.wheel_positions_l]
761 advance3 = True
762 advance2 = False
763 advance1 = False
764 if (run_index + 1) % 26 == 0: advance2 = True
765 if (run_index + 1) % (26*26) == 0: advance1 = True
766 for c in self.connections:
767 c.scrambler.advance(advance1, advance2, advance3)
768 return self.solutions</code></pre>
769 </details>
770 </dd>
771 <dt id="szyfrow.bombe.Bombe.set_positions"><code class="name flex">
772 <span>def <span class="ident">set_positions</span></span>(<span>self, wheel1_pos, wheel2_pos, wheel3_pos)</span>
773 </code></dt>
774 <dd>
775 <div class="desc"><p>Set positions of all scramblers. The first scrambler will be set
776 to the specified positions. The second scrambler will have its
777 third wheel advanced one position; the third scramber will have its
778 third wheel advanced two positios; and so on. Not that the first and
779 second wheels of the scramblers are never advanced in setup.</p></div>
780 <details class="source">
781 <summary>
782 <span>Expand source code</span>
783 </summary>
784 <pre><code class="python">def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
785 &#34;&#34;&#34;Set positions of all scramblers. The first scrambler will be set
786 to the specified positions. The second scrambler will have its
787 third wheel advanced one position; the third scramber will have its
788 third wheel advanced two positios; and so on. Not that the first and
789 second wheels of the scramblers are never advanced in setup.&#34;&#34;&#34;
790 for i, c in enumerate(self.connections):
791 c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i))</code></pre>
792 </details>
793 </dd>
794 <dt id="szyfrow.bombe.Bombe.test"><code class="name flex">
795 <span>def <span class="ident">test</span></span>(<span>self, initial_signal=None, start_positions=None, use_diagonal_board=True, verify_plugboard=True)</span>
796 </code></dt>
797 <dd>
798 <div class="desc"><p>Test a scrambler setting. It creates a signal (held in
799 <code>self.pending</code>) on the <code>initial_signal</code> wire then uses
800 <code><a title="szyfrow.bombe.Bombe.propagate" href="#szyfrow.bombe.Bombe.propagate">Bombe.propagate()</a></code> to propagate the signal across the Bombe. </p>
801 <p>Returns a Boolean recording if this scrambler setting with
802 this signal is a "stop" (potential valid scrambler setting).</p>
803 <ul>
804 <li>If <code>initial_signal</code> is <code>None</code>, use the default starting signal set in
805 <code><a title="szyfrow.bombe.Bombe.read_menu" href="#szyfrow.bombe.Bombe.read_menu">Bombe.read_menu()</a></code></li>
806 <li>If <code>start_positions</code> is <code>None</code>, use the existing scramber positions.</li>
807 </ul></div>
808 <details class="source">
809 <summary>
810 <span>Expand source code</span>
811 </summary>
812 <pre><code class="python">def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True,
813 verify_plugboard=True):
814 &#34;&#34;&#34;Test a scrambler setting. It creates a signal (held in
815 `self.pending`) on the `initial_signal` wire then uses
816 `Bombe.propagate` to propagate the signal across the Bombe.
817
818 Returns a Boolean recording if this scrambler setting with
819 this signal is a &#34;stop&#34; (potential valid scrambler setting).
820
821 * If `initial_signal` is `None`, use the default starting signal set in
822 `Bombe.read_menu`
823 * If `start_positions` is `None`, use the existing scramber positions.&#34;&#34;&#34;
824 self.banks = {label:
825 dict(zip(string.ascii_lowercase,
826 [False]*len(string.ascii_lowercase)))
827 for label in string.ascii_lowercase}
828 if start_positions:
829 self.set_positions(*start_positions)
830 if not initial_signal:
831 initial_signal = self.test_start
832 self.pending = [initial_signal]
833 self.propagate(use_diagonal_board)
834 live_wire_count = len([self.banks[self.test_start.bank][w]
835 for w in self.banks[self.test_start.bank]
836 if self.banks[self.test_start.bank][w]])
837 if live_wire_count &lt; 26:
838 if verify_plugboard:
839 possibles = self.possible_plugboards()
840 return all(s0.isdisjoint(s1)
841 for s0 in possibles
842 for s1 in possibles
843 if s0 != s1)
844 else:
845 return True
846 else:
847 return False</code></pre>
848 </details>
849 </dd>
850 </dl>
851 </dd>
852 <dt id="szyfrow.bombe.Connection"><code class="flex name class">
853 <span>class <span class="ident">Connection</span></span>
854 <span>(</span><span>banks, scrambler)</span>
855 </code></dt>
856 <dd>
857 <div class="desc"><p>A connection between banks made by a particular
858 scrambler (the scrambler state given by its position in the crib).</p></div>
859 <h3>Ancestors</h3>
860 <ul class="hlist">
861 <li>builtins.tuple</li>
862 </ul>
863 <h3>Instance variables</h3>
864 <dl>
865 <dt id="szyfrow.bombe.Connection.banks"><code class="name">var <span class="ident">banks</span></code></dt>
866 <dd>
867 <div class="desc"><p>A list of two items, holding the bnaks of
868 a connection.</p></div>
869 </dd>
870 <dt id="szyfrow.bombe.Connection.scrambler"><code class="name">var <span class="ident">scrambler</span></code></dt>
871 <dd>
872 <div class="desc"><p>The bnaks of a connection.</p></div>
873 </dd>
874 </dl>
875 </dd>
876 <dt id="szyfrow.bombe.MenuIem"><code class="flex name class">
877 <span>class <span class="ident">MenuItem</span></span>
878 <span>(</span><span>before, after, number)</span>
879 </code></dt>
880 <dd>
881 <div class="desc"><p>MenuIem(before, after, number)</p></div>
882 <h3>Ancestors</h3>
883 <ul class="hlist">
884 <li>builtins.tuple</li>
885 </ul>
886 <h3>Instance variables</h3>
887 <dl>
888 <dt id="szyfrow.bombe.MenuIem.after"><code class="name">var <span class="ident">after</span></code></dt>
889 <dd>
890 <div class="desc"><p>Alias for field number 1</p></div>
891 </dd>
892 <dt id="szyfrow.bombe.MenuIem.before"><code class="name">var <span class="ident">before</span></code></dt>
893 <dd>
894 <div class="desc"><p>Alias for field number 0</p></div>
895 </dd>
896 <dt id="szyfrow.bombe.MenuIem.number"><code class="name">var <span class="ident">number</span></code></dt>
897 <dd>
898 <div class="desc"><p>Alias for field number 2</p></div>
899 </dd>
900 </dl>
901 </dd>
902 <dt id="szyfrow.bombe.Scrambler"><code class="flex name class">
903 <span>class <span class="ident">Scrambler</span></span>
904 <span>(</span><span>wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, wheel1_pos='a', wheel2_pos='a', wheel3_pos='a')</span>
905 </code></dt>
906 <dd>
907 <div class="desc"><p>A scrambler is a collection of three <code><a title="szyfrow.enigma.SimpleWheel" href="enigma.html#szyfrow.enigma.SimpleWheel">SimpleWheel</a></code>s.</p></div>
908 <details class="source">
909 <summary>
910 <span>Expand source code</span>
911 </summary>
912 <pre><code class="python">class Scrambler(object):
913 &#34;&#34;&#34;A scrambler is a collection of three `szyfrow.enigma.SimpleWheel`s.
914 &#34;&#34;&#34;
915 def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,
916 wheel1_pos=&#39;a&#39;, wheel2_pos=&#39;a&#39;, wheel3_pos=&#39;a&#39;):
917 self.wheel1 = SimpleWheel(wheel1_spec, position=wheel1_pos)
918 self.wheel2 = SimpleWheel(wheel2_spec, position=wheel2_pos)
919 self.wheel3 = SimpleWheel(wheel3_spec, position=wheel3_pos)
920 self.reflector = Reflector(reflector_spec)
921
922 __pdoc__[&#39;Scrambler.wheel_positions&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
923 positions (as numbers)&#34;&#34;&#34;
924 __pdoc__[&#39;Scrambler.wheel_positions_l&#39;] = &#34;&#34;&#34;Return a 3-tuple of the wheel
925 positions (as letters)&#34;&#34;&#34;
926 def __getattribute__(self, name):
927 if name==&#39;wheel_positions&#39;:
928 return self.wheel1.position, self.wheel2.position, self.wheel3.position
929 elif name==&#39;wheel_positions_l&#39;:
930 return self.wheel1.position_l, self.wheel2.position_l, self.wheel3.position_l
931 else:
932 return object.__getattribute__(self, name)
933
934 def advance(self, wheel1=False, wheel2=False, wheel3=True):
935 &#34;&#34;&#34;Advance some wheels of a scrambler.
936 &#34;&#34;&#34;
937 if wheel1: self.wheel1.advance()
938 if wheel2: self.wheel2.advance()
939 if wheel3: self.wheel3.advance()
940
941 def lookup(self, letter):
942 &#34;&#34;&#34;Lookup the decipherment of a letter, given a particular scrambler
943 orientation.
944 &#34;&#34;&#34;
945 a = self.wheel3.forward(letter)
946 b = self.wheel2.forward(a)
947 c = self.wheel1.forward(b)
948 d = self.reflector.forward(c)
949 e = self.wheel1.backward(d)
950 f = self.wheel2.backward(e)
951 g = self.wheel3.backward(f)
952 return g
953
954 def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
955 &#34;&#34;&#34;Set the positions of a scrambler&#39;s wheels.
956 &#34;&#34;&#34;
957 self.wheel1.set_position(wheel1_pos)
958 self.wheel2.set_position(wheel2_pos)
959 self.wheel3.set_position(wheel3_pos) </code></pre>
960 </details>
961 <h3>Methods</h3>
962 <dl>
963 <dt id="szyfrow.bombe.Scrambler.advance"><code class="name flex">
964 <span>def <span class="ident">advance</span></span>(<span>self, wheel1=False, wheel2=False, wheel3=True)</span>
965 </code></dt>
966 <dd>
967 <div class="desc"><p>Advance some wheels of a scrambler.</p></div>
968 <details class="source">
969 <summary>
970 <span>Expand source code</span>
971 </summary>
972 <pre><code class="python">def advance(self, wheel1=False, wheel2=False, wheel3=True):
973 &#34;&#34;&#34;Advance some wheels of a scrambler.
974 &#34;&#34;&#34;
975 if wheel1: self.wheel1.advance()
976 if wheel2: self.wheel2.advance()
977 if wheel3: self.wheel3.advance()</code></pre>
978 </details>
979 </dd>
980 <dt id="szyfrow.bombe.Scrambler.lookup"><code class="name flex">
981 <span>def <span class="ident">lookup</span></span>(<span>self, letter)</span>
982 </code></dt>
983 <dd>
984 <div class="desc"><p>Lookup the decipherment of a letter, given a particular scrambler
985 orientation.</p></div>
986 <details class="source">
987 <summary>
988 <span>Expand source code</span>
989 </summary>
990 <pre><code class="python">def lookup(self, letter):
991 &#34;&#34;&#34;Lookup the decipherment of a letter, given a particular scrambler
992 orientation.
993 &#34;&#34;&#34;
994 a = self.wheel3.forward(letter)
995 b = self.wheel2.forward(a)
996 c = self.wheel1.forward(b)
997 d = self.reflector.forward(c)
998 e = self.wheel1.backward(d)
999 f = self.wheel2.backward(e)
1000 g = self.wheel3.backward(f)
1001 return g</code></pre>
1002 </details>
1003 </dd>
1004 <dt id="szyfrow.bombe.Scrambler.set_positions"><code class="name flex">
1005 <span>def <span class="ident">set_positions</span></span>(<span>self, wheel1_pos, wheel2_pos, wheel3_pos)</span>
1006 </code></dt>
1007 <dd>
1008 <div class="desc"><p>Set the positions of a scrambler's wheels.</p></div>
1009 <details class="source">
1010 <summary>
1011 <span>Expand source code</span>
1012 </summary>
1013 <pre><code class="python">def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):
1014 &#34;&#34;&#34;Set the positions of a scrambler&#39;s wheels.
1015 &#34;&#34;&#34;
1016 self.wheel1.set_position(wheel1_pos)
1017 self.wheel2.set_position(wheel2_pos)
1018 self.wheel3.set_position(wheel3_pos) </code></pre>
1019 </details>
1020 </dd>
1021 </dl>
1022 </dd>
1023 <dt id="szyfrow.bombe.Signal"><code class="flex name class">
1024 <span>class <span class="ident">Signal</span></span>
1025 <span>(</span><span>bank, wire)</span>
1026 </code></dt>
1027 <dd>
1028 <div class="desc"><p>Current propogation through the Bombe indicates that
1029 this wire in this bank is live, and the effects need to be proogated further
1030 through the machine.</p></div>
1031 <h3>Ancestors</h3>
1032 <ul class="hlist">
1033 <li>builtins.tuple</li>
1034 </ul>
1035 <h3>Instance variables</h3>
1036 <dl>
1037 <dt id="szyfrow.bombe.Signal.bank"><code class="name">var <span class="ident">bank</span></code></dt>
1038 <dd>
1039 <div class="desc"><p>The bank of a signal.</p></div>
1040 </dd>
1041 <dt id="szyfrow.bombe.Signal.wire"><code class="name">var <span class="ident">wire</span></code></dt>
1042 <dd>
1043 <div class="desc"><p>The wire of a signal.</p></div>
1044 </dd>
1045 </dl>
1046 </dd>
1047 </dl>
1048 </section>
1049 </article>
1050 <nav id="sidebar">
1051 <h1>Index</h1>
1052 <div class="toc">
1053 <ul></ul>
1054 </div>
1055 <ul id="index">
1056 <li><h3>Super-module</h3>
1057 <ul>
1058 <li><code><a title="szyfrow" href="index.html">szyfrow</a></code></li>
1059 </ul>
1060 </li>
1061 <li><h3><a href="#header-functions">Functions</a></h3>
1062 <ul class="">
1063 <li><code><a title="szyfrow.bombe.cat" href="#szyfrow.bombe.cat">cat</a></code></li>
1064 <li><code><a title="szyfrow.bombe.lcat" href="#szyfrow.bombe.lcat">lcat</a></code></li>
1065 <li><code><a title="szyfrow.bombe.make_menu" href="#szyfrow.bombe.make_menu">make_menu</a></code></li>
1066 <li><code><a title="szyfrow.bombe.run_multi_bombe" href="#szyfrow.bombe.run_multi_bombe">run_multi_bombe</a></code></li>
1067 <li><code><a title="szyfrow.bombe.wcat" href="#szyfrow.bombe.wcat">wcat</a></code></li>
1068 </ul>
1069 </li>
1070 <li><h3><a href="#header-classes">Classes</a></h3>
1071 <ul>
1072 <li>
1073 <h4><code><a title="szyfrow.bombe.Bombe" href="#szyfrow.bombe.Bombe">Bombe</a></code></h4>
1074 <ul class="two-column">
1075 <li><code><a title="szyfrow.bombe.Bombe.add_connection" href="#szyfrow.bombe.Bombe.add_connection">add_connection</a></code></li>
1076 <li><code><a title="szyfrow.bombe.Bombe.possible_plugboards" href="#szyfrow.bombe.Bombe.possible_plugboards">possible_plugboards</a></code></li>
1077 <li><code><a title="szyfrow.bombe.Bombe.propagate" href="#szyfrow.bombe.Bombe.propagate">propagate</a></code></li>
1078 <li><code><a title="szyfrow.bombe.Bombe.read_menu" href="#szyfrow.bombe.Bombe.read_menu">read_menu</a></code></li>
1079 <li><code><a title="szyfrow.bombe.Bombe.run" href="#szyfrow.bombe.Bombe.run">run</a></code></li>
1080 <li><code><a title="szyfrow.bombe.Bombe.set_positions" href="#szyfrow.bombe.Bombe.set_positions">set_positions</a></code></li>
1081 <li><code><a title="szyfrow.bombe.Bombe.test" href="#szyfrow.bombe.Bombe.test">test</a></code></li>
1082 </ul>
1083 </li>
1084 <li>
1085 <h4><code><a title="szyfrow.bombe.Connection" href="#szyfrow.bombe.Connection">Connection</a></code></h4>
1086 <ul class="">
1087 <li><code><a title="szyfrow.bombe.Connection.banks" href="#szyfrow.bombe.Connection.banks">banks</a></code></li>
1088 <li><code><a title="szyfrow.bombe.Connection.scrambler" href="#szyfrow.bombe.Connection.scrambler">scrambler</a></code></li>
1089 </ul>
1090 </li>
1091 <li>
1092 <h4><code><a title="szyfrow.bombe.MenuIem" href="#szyfrow.bombe.MenuIem">MenuIem</a></code></h4>
1093 <ul class="">
1094 <li><code><a title="szyfrow.bombe.MenuIem.after" href="#szyfrow.bombe.MenuIem.after">after</a></code></li>
1095 <li><code><a title="szyfrow.bombe.MenuIem.before" href="#szyfrow.bombe.MenuIem.before">before</a></code></li>
1096 <li><code><a title="szyfrow.bombe.MenuIem.number" href="#szyfrow.bombe.MenuIem.number">number</a></code></li>
1097 </ul>
1098 </li>
1099 <li>
1100 <h4><code><a title="szyfrow.bombe.Scrambler" href="#szyfrow.bombe.Scrambler">Scrambler</a></code></h4>
1101 <ul class="">
1102 <li><code><a title="szyfrow.bombe.Scrambler.advance" href="#szyfrow.bombe.Scrambler.advance">advance</a></code></li>
1103 <li><code><a title="szyfrow.bombe.Scrambler.lookup" href="#szyfrow.bombe.Scrambler.lookup">lookup</a></code></li>
1104 <li><code><a title="szyfrow.bombe.Scrambler.set_positions" href="#szyfrow.bombe.Scrambler.set_positions">set_positions</a></code></li>
1105 </ul>
1106 </li>
1107 <li>
1108 <h4><code><a title="szyfrow.bombe.Signal" href="#szyfrow.bombe.Signal">Signal</a></code></h4>
1109 <ul class="">
1110 <li><code><a title="szyfrow.bombe.Signal.bank" href="#szyfrow.bombe.Signal.bank">bank</a></code></li>
1111 <li><code><a title="szyfrow.bombe.Signal.wire" href="#szyfrow.bombe.Signal.wire">wire</a></code></li>
1112 </ul>
1113 </li>
1114 </ul>
1115 </li>
1116 </ul>
1117 </nav>
1118 </main>
1119 <footer id="footer">
1120 <p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.9.2</a>.</p>
1121 </footer>
1122 </body>
1123 </html>