<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>flowblok’s blog</title><link href="/" rel="alternate"/><link href="feeds/all.atom.xml" rel="self"/><id>/</id><updated>2024-04-13T00:00:00+10:00</updated><link href="http://pubsubhubbub.appspot.com/" rel="hub"/><entry><title>Advent of Code 2023: Day 6</title><link href="2024-04/advent-of-code-2023-day-6.html" rel="alternate"/><published>2024-04-13T00:00:00+10:00</published><updated>2024-04-13T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2024-04-13:2024-04/advent-of-code-2023-day-6.html</id><summary type="html">&lt;p&gt;This boat race makes me want to Hurl.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="http://hurl.wtf/"&gt;Hurl&lt;/a&gt; is a dynamically-typed programming language that uses
exception handling as its only form of control flow.
Much of the syntax is unsurprising, but the forms of exception raising and
handling deserve close examination.&lt;/p&gt;
&lt;p&gt;Here is the canonical way to do conditional branches in Hurl:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;hooray&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;what weird machine are you running on this on?&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, the &lt;code&gt;hurl&lt;/code&gt; keyword raises an exception with the value &lt;code&gt;true&lt;/code&gt;,
which then is matched by the &lt;code&gt;catch (true)&lt;/code&gt; block.
There are three forms of the &lt;code&gt;catch&lt;/code&gt; block:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;catch (&amp;lt;expr&amp;gt;) { ... }&lt;/code&gt; will match if the exception value evaluates equal to
   &lt;code&gt;&amp;lt;expr&amp;gt;&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catch as &amp;lt;identifier&amp;gt; { ... }&lt;/code&gt; will match any value and make it available as
   an &lt;code&gt;&amp;lt;identifier&amp;gt;&lt;/code&gt; variable in the catch body&lt;/li&gt;
&lt;li&gt;&lt;code&gt;catch into &amp;lt;identifier&amp;gt;&lt;/code&gt; will match any value and assign it to an
   already-declared variable &lt;code&gt;&amp;lt;identifier&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hurl has functions, but since we have exceptions, there is no need for that
pesky &lt;code&gt;return&lt;/code&gt; keyword: we can just &lt;code&gt;hurl&lt;/code&gt; the value we want to return:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a second way to throw exceptions: if you &lt;code&gt;toss&lt;/code&gt; a value, it will be
match against the parent &lt;code&gt;catch&lt;/code&gt; blocks, but when that block calls &lt;code&gt;return&lt;/code&gt;&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;
execution will resume from after the &lt;code&gt;toss&lt;/code&gt;. Crucially, &lt;code&gt;toss&lt;/code&gt; can be called
multiple times to invoke the parent &lt;code&gt;catch&lt;/code&gt; block as many times as desired,
which gives us a tool to build continuations.&lt;/p&gt;
&lt;h2&gt;Making Hurl even more exceptional&lt;/h2&gt;
&lt;p&gt;Hurl is an exceptional language, but some parts seem unexceptional to me.
For example, we only really need the &lt;code&gt;let &amp;lt;ident&amp;gt;&lt;/code&gt; part of
&lt;code&gt;let &amp;lt;ident&amp;gt; = &amp;lt;value&amp;gt;&lt;/code&gt;, since we can write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;# Today&amp;#39;s Hurl:&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;# Tomorrow&amp;#39;s Hurl?&lt;/span&gt;
&lt;span class="cp"&gt;#let add;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since the value part is unnecessary and the language is dynamically-typed,
I will restrict myself to only ever assigning the value 0 with &lt;code&gt;let&lt;/code&gt;, with the
hope that this is removed from the language in the future.&lt;/p&gt;
&lt;p&gt;Another unexceptional part of the language is the collection of &lt;a href="https://hurl.wtf/stdlib#built-ins"&gt;built-in
functions&lt;/a&gt; for I/O and manipulating numbers,
strings and lists. Having such a collection is necessary to make the language
practical to use, but they currently evaluate as expressions rather than
&lt;code&gt;hurl&lt;/code&gt;'ing their results—something that user-defined functions cannot do.&lt;/p&gt;
&lt;p&gt;Fortunately, it is not too difficult to build a compatibility layer to remove
their special status. Wherever you see &lt;code&gt;FOO_proper&lt;/code&gt;, this is a version of the
built-in function &lt;code&gt;FOO&lt;/code&gt; that &lt;code&gt;hurl&lt;/code&gt;'s the result rather than evaluating to it.
For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_file_proper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read_file_proper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, there is no need for builtin comparison operators in the language.
We can make these truly exceptional with a &lt;a href="https://en.wikipedia.org/wiki/Three-way_comparison"&gt;three-way comparison
operator&lt;/a&gt; for numbers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;# Some enums or list unpacking would be neat...&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cp"&gt;# cmp(a, b) hurls lt (if a &amp;lt; b), eq (if a == b) or gt (if a &amp;gt; b).&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And for strings (which do not support lexicographic comparison in Hurl) we need
an explicit equality function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Wait, what was I doing again?&lt;/h2&gt;
&lt;p&gt;Oh, that’s right, we’ve got an Advent of Code problem to solve.
Today we have data about some boat races. Each race has a duration:
we can use some portion of this duration to charge our boat, and then
we spend the remainder of the duration moving at a speed determined by
how long we charged our boat for.
If we hold the button for &lt;code&gt;hold&lt;/code&gt; of the &lt;code&gt;duration&lt;/code&gt; milliseconds, then
we will travel &lt;code&gt;hold * (duration - hold)&lt;/code&gt; millimeters.&lt;/p&gt;
&lt;p&gt;If we wanted to maximise that distance, we'd want the two parts of that
multiplication to be as close to each other as possible, so &lt;code&gt;hold&lt;/code&gt; would
be either &lt;code&gt;floor(duration / 2)&lt;/code&gt; or &lt;code&gt;ceil(duration / 2)&lt;/code&gt;. But that's not
our problem: we want to know how many different ways we could beat a
competitor who achieved a certain distance (&lt;code&gt;target&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;We could do maths to find an analytical solution, but that sounds like work,
so let's not. We could also just evaluate every possible &lt;code&gt;hold&lt;/code&gt; value, but
it seems likely that Part 2 of the problem will make the problem bigger or
more complicated, and maybe that would be too slow (but maybe not?).
&lt;em&gt;Or&lt;/em&gt; we could implement binary search. That sounds more fun.&lt;/p&gt;
&lt;p&gt;In order to binary search, we make use of the half-way point: we can split
our problem into the intervals &lt;code&gt;[0, floor(duration / 2)]&lt;/code&gt; and
&lt;code&gt;[ceil(duration / 2), duration]&lt;/code&gt;: &lt;code&gt;hold * (duration - hold)&lt;/code&gt; will be
strictly increasing for the first interval, and strictly decreasing for the
second. So we can binary search the first interval for the smallest value of
&lt;code&gt;hold&lt;/code&gt; where &lt;code&gt;hold * (duration - hold) &amp;gt; target&lt;/code&gt; (we start beating the
competitor), and the second interval for the smallest value where
&lt;code&gt;hold * (duration - hold) &amp;lt;= target&lt;/code&gt; (we stop beating the competitor).
The difference between these &lt;code&gt;hold&lt;/code&gt; values is the number of ways we can beat
them.&lt;/p&gt;
&lt;p&gt;So anyhow, we need an implementation of binary search.
Here’s one using the &lt;code&gt;loop&lt;/code&gt; function from Hurl’s standard library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binary_search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;half_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;half_length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;toss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;half_length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;half_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;half_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;toss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binary_search&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;On each iteration, &lt;code&gt;loop&lt;/code&gt; invokes the function in its first argument with
the parameter &lt;code&gt;locals&lt;/code&gt;, which starts as the second argument
(&lt;code&gt;[lower, upper - lower]&lt;/code&gt;).
Each time the function is invoked, it should &lt;code&gt;toss&lt;/code&gt; a new value which will
replace &lt;code&gt;locals&lt;/code&gt; for the next invocation, and &lt;code&gt;hurl&lt;/code&gt; either &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
to indicate if the loop should continue.
Finally, &lt;code&gt;loop&lt;/code&gt; will &lt;code&gt;hurl locals&lt;/code&gt; so the caller can produce a result.&lt;/p&gt;
&lt;p&gt;This example also shows off our three-way comparison operator&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;: we catch
&lt;code&gt;eq&lt;/code&gt; explicitly, but then aggregate &lt;code&gt;lt&lt;/code&gt; and &lt;code&gt;gt&lt;/code&gt; into a variable
(the name doesn’t matter, but &lt;code&gt;ne&lt;/code&gt; aids readability).
You will also notice a bunch of faces that look like they want to Hurl
(&lt;code&gt;}; }; }; }; };&lt;/code&gt;). These are from using &lt;code&gt;catch as&lt;/code&gt; blocks in sequence,
to avoid the use of &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Anyhow, we define a distance function and implement &lt;code&gt;lower&lt;/code&gt; and &lt;code&gt;upper&lt;/code&gt; as
functions which do the comparisons used by the binary search, find the two
values of &lt;code&gt;hold&lt;/code&gt; and subtract them.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ways_to_beat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;binary_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;binary_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ways_to_beat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s the core of the problem, but we also need to parse the input and
multiply together the results of multiple races. The second part is fairly
straightforward, but parsing the input is a little more tricky, since we have
numbers with any number of spaces between them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Time:      7  15   30
Distance:  9  40  200
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a &lt;code&gt;split&lt;/code&gt; function in the standard library, but it will turn &lt;code&gt;"7  15"&lt;/code&gt;
into &lt;code&gt;["7", "", "15"]&lt;/code&gt;, so we need to be able to remove empty elements.
All that’s needed for this is &lt;code&gt;filter(xs, pred)&lt;/code&gt; (returns a new list of the
items in &lt;code&gt;xs&lt;/code&gt; for which &lt;code&gt;pred(item)&lt;/code&gt; returned &lt;code&gt;true&lt;/code&gt;), but I thought I might
need &lt;code&gt;map&lt;/code&gt; later&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;, so implemented &lt;code&gt;map_filter_nil(xs, fn)&lt;/code&gt; (returns a new
list of &lt;code&gt;fn(item)&lt;/code&gt; for each item in &lt;code&gt;xs&lt;/code&gt;, skipping items when &lt;code&gt;fn&lt;/code&gt; returns
&lt;code&gt;nil&lt;/code&gt;). Unfortunately, while &lt;code&gt;nil&lt;/code&gt; is a valid value at runtime, it’s not part
of the language syntax, so we need to jump through a hoop to make it accessible:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nil_producer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;nil_producer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can implement &lt;code&gt;map_filter_nil&lt;/code&gt;. It might be possible to write this
better using one of the other loop functions in the standard library,
but I didn’t immediately see anything which would work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map_filter_nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;at_proper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="n"&gt;toss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;new_list&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locals&lt;/span&gt;&lt;span class="mf"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]]);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="mf"&gt;.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;map_filter_nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can construct a split function that drops empty values:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;split_ignoring_whitespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;map_filter_nil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ne&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="n"&gt;hurl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;split_ignoring_whitespace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Part 2&lt;/h2&gt;
&lt;p&gt;My guess from earlier turned out correct: Part 2 makes the numbers bigger by
combining the races into one (in the example above, &lt;code&gt;"7  15   30"&lt;/code&gt; is read as
71530 and &lt;code&gt;"9  40  200"&lt;/code&gt; as 940200). These numbers don’t seem that big though,
so I suspect trying every &lt;code&gt;hold&lt;/code&gt; value is perfectly viable.&lt;/p&gt;
&lt;p&gt;So there were some minor changes to input parsing, but nothing particularly interesting.&lt;/p&gt;
&lt;p&gt;Anyhow, I recommend having a read of Hurl’s documentation, there are a bunch of
interesting examples that demonstrate the capabilities of exceptional control flow.
And if you’re the kind of person who thinks this is all very sensible, I can also
recommend &lt;a href="https://sigbovik.org/"&gt;SIGBOVIK&lt;/a&gt;, whose 2024 proceedings are how I
discovered this language.&lt;/p&gt;
&lt;p&gt;As usual, a complete code listing is available in
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/06"&gt;the repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The documentation says that it merely matches literals, but the
  implementation handles expressions, which seems intentional.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;There are some bugs (features?) in the implementation, so I can get this
  working without doing this. Also, &lt;code&gt;hurl&lt;/code&gt; and &lt;code&gt;toss&lt;/code&gt; behave slightly
  differently when invoked from a function within a &lt;code&gt;try&lt;/code&gt; block than if
  inlined in that block.
  None of this really matters, if you hold the language as intended, it
  works fine.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;This example could also be achieved with &lt;code&gt;equals&lt;/code&gt;, but you would still
  need two catch blocks to invert the boolean&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Or &lt;em&gt;I guess&lt;/em&gt; you &lt;em&gt;could&lt;/em&gt; use the &lt;em&gt;decidedly unexceptional&lt;/em&gt; unary not
  operator.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I didn’t. Oh well.&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Day 5</title><link href="2024-03/advent-of-code-2023-day-5.html" rel="alternate"/><published>2024-03-16T00:00:00+11:00</published><updated>2024-03-16T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2024-03-16:2024-03/advent-of-code-2023-day-5.html</id><summary type="html">&lt;p&gt;Using Inform 7 to write interactive fiction about gardening.&lt;/p&gt;</summary><content type="html">&lt;p&gt;My language choice for &lt;a href="https://adventofcode.com/2023/day/5"&gt;Day 5’s puzzle&lt;/a&gt;
is brought to you by the wonderful combination of a &lt;em&gt;what if&lt;/em&gt; spark of
inspiration and a concerning amount of stubbornness.
The inspiration was this: &lt;em&gt;what if&lt;/em&gt; we could make a text adventure game where
the puzzle input resulted in the game computing the correct output?&lt;/p&gt;
&lt;p&gt;Inform 7 is a domain specific language for creating interactive fiction, where
the source code is largely written in natural language, though punctuation and
indentation is structurally significant, and (unsurprisingly) sentences often
need to be written in specific ways to avoid ambiguity.&lt;/p&gt;
&lt;h2&gt;Part 1&lt;/h2&gt;
&lt;p&gt;Our input is structured as a list of seeds, followed by a number of maps that
transform the seed numbers to soil numbers, to fertilizer numbers, and so on.
The types of things don’t really matter, so all we need to do is read the
seed list, then (repeated seven times) read a map and transform the numbers,
and finally output the smallest value.&lt;/p&gt;
&lt;h3&gt;Numbers&lt;/h3&gt;
&lt;p&gt;There is an unfortunate complication though. The actual input has unsigned
32-bit integers, but the best native integer type Inform offers is signed
32-bit (targeting Glulx) or 16-bit (targeting Z-machine) integers.
There’s a few options for working around this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Restrict to Glulx, use signed 32-bit integers, representing 0 as -2&lt;sup&gt;31&lt;/sup&gt;,
   up to 2&lt;sup&gt;32&lt;/sup&gt;-1 as 2&lt;sup&gt;31&lt;/sup&gt;-1. This lets us use signed comparison,
   addition and subtraction, but would require custom parsing and display routines.
   The representation is also somewhat annoying to understand when debugging.&lt;/li&gt;
&lt;li&gt;Restrict to Glulx, combine the unsigned parts of two 32-bit integers to
   implement up to 62-bit unsigned integers. This would require custom operators
   for comparison, addition, subtraction, parsing and display.
   The representation is also somewhat annoying to understand when debugging.&lt;/li&gt;
&lt;li&gt;Combine the unsigned parts of at least three 16-bit integers to implement up
   to 45-bit unsigned integers. Again, requires implementing everything, and the
   representation is also not suited to debugging.&lt;/li&gt;
&lt;li&gt;Use a string of the decimal representation. This does not require parsing
   and display routines&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;, but would require custom comparison, addition and
   subtraction routines. This is likely the least performant option, but does
   have the advantage of being arbitrary precision and having an easily 
   understood representation when debugging.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since performance is not a concern (and because, as far as I can tell, bitwise
operators are only available by embedding Inform 6 code), I went with the last
option.&lt;/p&gt;
&lt;h3&gt;Reading the seeds list&lt;/h3&gt;
&lt;p&gt;Let’s get onto some code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;&amp;quot;Advent of Code 2023 Day 5.1&amp;quot; by flowblok&lt;/span&gt;

&lt;span class="gh"&gt;Chapter 0 - The Setting&lt;/span&gt;

The Garden is a room.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is just a preamble, specifying the title and author of the story.
Inform 7 source code can be organised by a hierarchy of headings
(volumes, books, parts, chapters and sections), primarily to improve
navigation of the source.
Inform 7 organises the world into rooms, so we need at least one room
(for the player to start in), so we make one called "The Garden".&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 1 - On the Identification of Seeds&lt;/span&gt;

Defining the seed list is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;seeds &lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; as defining the seed list.
The seed list is a list of text which varies.

Carry out defining the seed list:
    now the seed list is the words of the topic understood.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s a couple of things going on here: firstly, we define an &lt;em&gt;action&lt;/em&gt;
called "defining the seed list", which applies to (has a parameter of) one
topic (some text). We also need to specify how the player can trigger this
action, so we configure Inform 7’s player command parsing to understand that
&lt;code&gt;seeds [text]&lt;/code&gt; should invoke that action
(with everything after the word seeds as the topic).
We then define a global variable called "the seed list" which is a list of
text (since we’re using the decimal representation for our numbers).
And finally, we specify what should happen when the "defining the seed list"
action is carried out: &lt;code&gt;now the seed list is the words of the topic understood&lt;/code&gt;
would be written in traditional programming languages something like
&lt;code&gt;seed_list = input_topic.split(" ")&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There’s no input validation here, we’re just going to assume that the player
actually entered numbers.
Keen readers may also note that the puzzle input looks like "seeds: 79 14 55 13",
but our parsing only works with "seeds 79 14 55 13".
Unfortunately, Inform assumes that the colon means we’re asking something called
"seeds" to perform the "79" action on "14 55 13"&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;, which is not very easy to
make work for our processing. To work around this, we can hook into the command
parsing and remove the colons before the parser tries to understand what action
is being invoked on what:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Section - Input Sanitization&lt;/span&gt;

After reading a command:
    let T be &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[the player&amp;#39;s command]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;;
    replace the regular expression &lt;span class="s2"&gt;&amp;quot;:&amp;quot;&lt;/span&gt; in T with &lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;
    change the text of the player&amp;#39;s command to T.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Reading maps&lt;/h3&gt;
&lt;p&gt;Each map is a list of &lt;code&gt;(destination start, source start, range length)&lt;/code&gt;
entries that specifies the interval
&lt;code&gt;[source start, source start + range length)&lt;/code&gt; should be remapped to the interval
&lt;code&gt;[destination start, destination start + range length)&lt;/code&gt;.
We only need to store one map at a time, so we do so in
&lt;a href="https://ganelson.github.io/inform-website/book/WI_16_1.html"&gt;a table&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 2 - On the Reading of Various Kinds of Maps&lt;/span&gt;

Table of Translation
Destination start (text)    Source start (text) Source end (text)
with 100 blank rows

Defining a map is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt; map&amp;quot;&lt;/span&gt; as defining a map.

Carry out defining a map:
    blank out the whole of the Table of Translation.

Entering a map row is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; as entering a map row.

Carry out entering a map row:
    let the number list be the words of the topic understood;
    choose a blank row in the Table of Translation;
    now destination start entry is entry 1 of the number list;
    now source start entry is entry 2 of the number list;
    now source end entry is (source start entry + entry 3 of the number list) - &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our Table of Translation has three columns, and many blank rows.
Rather than storing the length of the range, we store
&lt;code&gt;source start + range length - 1&lt;/code&gt; (the inclusive end of the source range),
which will aid in searching through the map.&lt;/p&gt;
&lt;p&gt;When we see "seed-to-soil map" or "soil-to-fertilizer map" (or similar),
we understand that as the "defining a map" action, which clears the table
of its previous values.&lt;/p&gt;
&lt;p&gt;Each map row will be three numbers, but there’s no word at the start we can
use to help with parsing. Fortunately though, our overall format is not overly
complex, so we can understand &lt;em&gt;anything&lt;/em&gt; (which doesn’t match another action)
as the "entering a map row" action.
The arithmetic in this action is using &lt;code&gt;[text] + [text]&lt;/code&gt; and &lt;code&gt;[text] - [text]&lt;/code&gt;
operators (which we haven’t implemented yet, but will define later).&lt;/p&gt;
&lt;h3&gt;Transforming the seeds list&lt;/h3&gt;
&lt;p&gt;Once the map has been entered, the player will enter a blank line,
and we should apply the mapping to the seeds list
(which then should be called the soil list, but we won’t bother tracking the
name changes).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 3 - On the Application of Maps to Seeds&lt;/span&gt;

To decide which text is (input - text) after being mapped (this is mapping):
    repeat through the Table of Translation:
        if input is numerically at least source start entry and input is numerically at most source end entry:
            let offset be input - source start entry;
            decide on the destination start entry + offset;
    decide on input.

Finishing a map is an action applying to nothing.
&lt;span class="cm"&gt;[Understand &amp;quot;&amp;quot; as finishing a map.]&lt;/span&gt;

Carry out finishing a map:
    now the seed list is mapping applied to the seed list.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We define a &lt;a href="https://ganelson.github.io/inform-website/book/WI_11_17.html"&gt;&lt;em&gt;phrase&lt;/em&gt;&lt;/a&gt;
(a &lt;a href="https://ganelson.github.io/inform-website/book/WI_22_3.html"&gt;first-class&lt;/a&gt; function)
that takes a string (decimal number) and remaps it according to the current
entries in the Table of Translation.
The remapping is pretty simple: we loop through all the entries in our map, and
if our input is in the source interval, we figure out its offset to determine
its place in the destination interval. If it doesn’t match anything, it stays
as-is.&lt;/p&gt;
&lt;p&gt;Then when the player enters a blank line, we carry out the "finishing a map"
action, which &lt;a href="https://ganelson.github.io/inform-website/book/WI_22_5.html"&gt;applies the "mapping" phrase to every
entry&lt;/a&gt; in the seed
list.
There is (of course) a complication: you’d think that
&lt;code&gt;Understand "" as finishing a map.&lt;/code&gt; would configure Inform to understand the
empty line, but it’s actually a parser error, so we need to hook into the
parser in a different way to get it to actually work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Rule for printing a parser error when the latest parser error is the I beg your pardon error:
    try finishing a map instead.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Computing the answer&lt;/h3&gt;
&lt;p&gt;The actions above don’t keep track of which maps is being entered, so we need
to figure out which one is the last and print out the answer after its mapping
has been applied. This turns out to be beautifully simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 4 - On the Production of the Answer&lt;/span&gt;

After finishing a map for the eighth time:
    say &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[the minimization reduction of the seed list]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inform keeps track of how many times the action is invoked for us,
so we just need to find the smallest entry in the seed list.
To do this, we rely on having a phrase for finding the smaller of two decimal
numbers, and then reduce over the list (this is more or less
&lt;a href="https://ganelson.github.io/inform-website/book/WI_22_5.html"&gt;the maximization example in the docs&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Interlude: decimal numbers&lt;/h2&gt;
&lt;p&gt;That application logic wasn’t too hard to implement, but that’s mostly because I
hand-waved over all those numerical operations. We can skip equality (assuming we
never have leading zeros in numbers), so let’s start by writing a phrase that can
decide if one number is smaller, equal, or larger than another:&lt;/p&gt;
&lt;h3&gt;Comparisons&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;[This is an enum. Maybe there&amp;#39;s an existing/better way of doing this, but I couldn&amp;#39;t find it.]&lt;/span&gt;
Comparison is a kind of value. The comparisons are smaller, equal and larger.

To decide what comparison is (N - text) compared to (M - text):
    if the number of characters in N &amp;lt; the number of characters in M, decide on smaller;
    if the number of characters in N &amp;gt; the number of characters in M, decide on larger;
    repeat with i running from 1 to the number of characters in N:
        if character number i in N &amp;lt; character number i in M, decide on smaller;
        if character number i in N &amp;gt; character number i in M, decide on larger;
    decide on equal.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If one number has more digits than the other, it’s the larger one. If we have
the same number of digits, then moving from the highest-value digit to lowest,
whichever number has a higher value digit is larger.
There is an assumption in this code: it compares the digits as strings, so it
relies on the lexicographic comparison being the same, which appears to be true
for the Z-machine and Glulx targets.
We could do the string to number conversion, but uh, you’ll see why I wanted to
avoid that soon enough.&lt;/p&gt;
&lt;p&gt;We can then build on top of this phrase to provide the &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt;
relations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;[&amp;lt;]&lt;/span&gt;
Numerically less than relates a text (called N) to a text (called M) when N compared to M is smaller.
The verb to be numerically less than means the numerically less than relation.
&lt;span class="cm"&gt;[&amp;lt;=]&lt;/span&gt;
Numerically at most relates a text (called N) to a text (called M) when N compared to M is not larger.
The verb to be numerically at most means the numerically at most relation.
&lt;span class="cm"&gt;[&amp;gt;=]&lt;/span&gt;
Numerically at least relates a text (called N) to a text (called M) when N compared to M is not smaller.
The verb to be numerically at least means the numerically at least relation.
&lt;span class="cm"&gt;[&amp;gt;]&lt;/span&gt;
Numerically greater than relates a text (called N) to a text (called M) when N compared to M is larger.
The verb to be numerically greater than means the numerically greater than relation.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is also one last thing to define: in order to use
&lt;code&gt;the minimization reduction&lt;/code&gt; when computing the answer, we need:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide what text is the smaller of (N - text) and (M - text) (this is minimization):
    if N compared to M is smaller, decide on N;
    decide on M.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Addition&lt;/h3&gt;
&lt;p&gt;Consider how you would perform &lt;code&gt;972 + 35&lt;/code&gt; by hand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add the last digit of each: &lt;code&gt;2 + 5 = 7&lt;/code&gt;, so write 7 (→ 7)&lt;/li&gt;
&lt;li&gt;add the second-last digit of each: &lt;code&gt;7 + 3 = 10&lt;/code&gt;, so write 0 and carry the 1 (→ 07)&lt;/li&gt;
&lt;li&gt;add the carry to the first digit of the left number: &lt;code&gt;9 + 1 = 10&lt;/code&gt;, so write 0 and carry the 1 (→ 007)&lt;/li&gt;
&lt;li&gt;write the carry (→ 1007)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We’re going to do something very similar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;convert "972" + "35" to lists: [9, 7, 2] + [3, 5]&lt;/li&gt;
&lt;li&gt;reverse each list: [2, 7, 9] + [5, 3]&lt;/li&gt;
&lt;li&gt;pad with zeros to make them the same length: [2, 7, 9] + [5, 3, 0]&lt;/li&gt;
&lt;li&gt;produce a new list adding the digits together, keeping track of the carry:&lt;ul&gt;
&lt;li&gt;[2, 7, 9] + [5, 3, 0] = []&lt;/li&gt;
&lt;li&gt;[7, 9] + [3, 0] = [7]&lt;/li&gt;
&lt;li&gt;[9] + [0] = [7, 0] (+carry)&lt;/li&gt;
&lt;li&gt;[] + [] = [7, 0, 0] (+carry)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;add the final carry (if there is one):&lt;ul&gt;
&lt;li&gt;= [7, 0, 0, 1]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;reverse the list: [1, 0, 0, 7]&lt;/li&gt;
&lt;li&gt;convert back into a string: "1007"&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s the Inform implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide what text is the (N - text) + (M - text):
    let N digits be the digits of N;
    let M digits be the digits of M;
    reverse N digits;
    reverse M digits;
    extend N digits to the number of entries in M digits entries;
    extend M digits to the number of entries in N digits entries;
    let result digits be a list of numbers;
    let carry be 0;
    repeat with i running from 1 to the number of entries in N digits:
        let digit sum be entry i of N digits + entry i of M digits + carry;
        add the remainder after dividing digit sum by 10 to result digits;
        now carry is digit sum / 10;
    if carry &amp;gt; 0, add carry to result digits;
    reverse result digits;
    decide on the concatenation reduction of str applied to result digits.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code has a suprising numbers of dependencies. The line
&lt;code&gt;let N digits be the digits of N&lt;/code&gt; uses this phrase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide what list of numbers is the digits of (N - text):
    let digits be a list of numbers;
    repeat with i running from 1 to the number of characters in N:
        add the digit value of character number i in N to digits;
    decide on digits.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which itself relies on this phrase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide what number is the digit value of (N - text):
    if N is &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;, decide on 1;
    if N is &lt;span class="s2"&gt;&amp;quot;2&amp;quot;&lt;/span&gt;, decide on 2;
    if N is &lt;span class="s2"&gt;&amp;quot;3&amp;quot;&lt;/span&gt;, decide on 3;
    if N is &lt;span class="s2"&gt;&amp;quot;4&amp;quot;&lt;/span&gt;, decide on 4;
    if N is &lt;span class="s2"&gt;&amp;quot;5&amp;quot;&lt;/span&gt;, decide on 5;
    if N is &lt;span class="s2"&gt;&amp;quot;6&amp;quot;&lt;/span&gt;, decide on 6;
    if N is &lt;span class="s2"&gt;&amp;quot;7&amp;quot;&lt;/span&gt;, decide on 7;
    if N is &lt;span class="s2"&gt;&amp;quot;8&amp;quot;&lt;/span&gt;, decide on 8;
    if N is &lt;span class="s2"&gt;&amp;quot;9&amp;quot;&lt;/span&gt;, decide on 9;
    decide on 0.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sigh.&lt;/p&gt;
&lt;p&gt;Moving swiftly along, the line &lt;code&gt;extend N digits to the number of entries in M digits entries&lt;/code&gt;
is also notable: it fills any extra values needed with the default value of the list, which
since we have a list of numbers will be zero.
The final point of interest is turning the list back into a string:
&lt;code&gt;the concatenation reduction of str applied to result digits&lt;/code&gt; says to apply the
&lt;code&gt;str&lt;/code&gt; phrase to each entry of the list (converting each number into a string)
and then reduce the list with the &lt;code&gt;concatenation&lt;/code&gt; phrase, which joins the strings together.
Of course, we need to define these phrases:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide what text is the concatenation of (X - text) and (Y - text) (this is concatenation):
    decide on &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[X][Y]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;.

To decide what text is the str of (X - number) (this is str):
    decide on &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[X]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Subtraction&lt;/h3&gt;
&lt;p&gt;You know, let’s just skip over this: it’s very similar to addition.
The only thing to call out is that I decided not to implement negative numbers,
so my subtraction operator only implements &lt;code&gt;N - M&lt;/code&gt; under the condition &lt;code&gt;N &amp;gt;= M&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Part 2&lt;/h2&gt;
&lt;p&gt;It turns out we’ve been reading the input wrong! The line &lt;code&gt;seeds: 79 14 55 13&lt;/code&gt;
actually specifies two intervals of seeds to plant: &lt;code&gt;[79, 79 + 14)&lt;/code&gt; and
&lt;code&gt;[55, 55 + 13)&lt;/code&gt;. This is a modest 27 seed numbers, but looking at the actual
input suggests we’re going to have billions of seeds, so expanding these
ranges would be rather ill-advised.&lt;/p&gt;
&lt;h3&gt;Reading the seed ranges&lt;/h3&gt;
&lt;p&gt;Instead, we’ll track each range as a list of [start, end], and adjust our
algorithms to work on lists of ranges.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 1 - On the Identification of Seeds&lt;/span&gt;

Defining the seed ranges is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;seeds &lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; as defining the seed ranges.
The seed ranges is a list of lists of text which varies.

Carry out defining the seed ranges:
    change the seed ranges to have 0 entries;
    repeat with pair running through the pairs of words of the topic understood:
        let range be a list of text;
        add entry 1 of pair to range;
        add (entry 1 of pair + entry 2 of pair) - &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt; to range;
        add range to the seed ranges.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the seed list is entered, we convert each pair of start/length into
start/end ranges. Of course, we also have to define what a pair of words is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Section - Text Manipulation&lt;/span&gt;

&lt;span class="cm"&gt;[This phrase assumes there are an even number of words.]&lt;/span&gt;
To decide which list of lists of text is the pairs of words of (strings - text):
    let result be a list of lists of text;
    repeat with i running from 1 to the number of words in strings:
        let pair be a list of text;
        add word number i in strings to pair;
        add word number i + 1 in strings to pair;
        add pair to result;
        now i is i + 1;  &lt;span class="cm"&gt;[skip the second word of the pair]&lt;/span&gt;
    decide on result.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Reading maps&lt;/h3&gt;
&lt;p&gt;This section is slightly different to Part 1: for reasons that will shortly
become apparent, we’re going to add a column for the number of digits in the
source start.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 2 - On the Reading of Various Kinds of Maps&lt;/span&gt;

Table of Translation
Destination start (text)    Source start (text) Source end (text)   Source start length (number)
with 100 blank rows

Defining a map is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt; map&amp;quot;&lt;/span&gt; as defining a map.

Carry out defining a map:
    blank out the whole of the Table of Translation.

Entering a map row is an action applying to one topic.
Understand &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;[text]&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; as entering a map row.

Carry out entering a map row:
    let the number list be the words of the topic understood;
    choose a blank row in the Table of Translation;
    now destination start entry is entry 1 of the number list;
    now source start entry is entry 2 of the number list;
    now source start length entry is the number of characters in source start entry;
    now source end entry is (source start entry + entry 3 of the number list) - &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Transforming the seed ranges&lt;/h3&gt;
&lt;p&gt;In Part 1, we just needed to find the mapping for a single number, but now we
need to find the mapping for intervals. Let’s work through each step of that
process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gh"&gt;Chapter 3 - On the Application of Maps to Seeds&lt;/span&gt;

Carry out finishing a map:
    sort the Table of Translation in source start order;
    sort the Table of Translation in source start length order;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first thing we do is to sort the Table of Translation by the source start
order. Unfortunately, this is using lexicographical order, so &lt;code&gt;"10" &amp;lt; "2"&lt;/code&gt;.
But fear not: sorting tables is defined to be stable, so if we next sort by the
&lt;em&gt;length&lt;/em&gt; of the source start column (helpfully in its own explicit column),
the rows within each length will remain sorted lexicographically, which gives
us a numerical sort.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    now the seed ranges is the sorted interval union of the seed ranges;
    let the destination ranges be a list of lists of text;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we get the "sorted interval union" of the seed ranges.
We’ll define this later, but this takes the seed ranges, sorts them by their
interval start and then merges any adjacent/overlapping intervals
together, so we have a canonical, minimal representation of the intervals.&lt;/p&gt;
&lt;p&gt;We also define the destination ranges to store the mapped intervals as we go.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    let the starting row be 1;
    repeat with seed range running through the seed ranges:
        repeat with N running from the starting row to the number of filled rows in the Table of Translation:
            now the starting row is N;
            choose row N in the Table of Translation;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nested loops, in my interactive fiction?
But fear not: both the seed ranges and the Table of Translation are sorted,
so for each seed range, we scan through the Table of Translation to find out
how it should be mapped, but then we know that the rows &lt;em&gt;before&lt;/em&gt; that point
cannot possibly be useful for processing future seed ranges, so we keep
&lt;code&gt;starting row&lt;/code&gt; updated to note where we can start from (so this is linear time).&lt;/p&gt;
&lt;p&gt;Now we need to match a few cases for how the seed range intervals correspond to
the intervals in the Table of Translation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;            if entry 1 of seed range is numerically less than source start entry:
                &lt;span class="cm"&gt;[seed range start &amp;lt; source start, so we have an unmapped subrange]&lt;/span&gt;
                let unmapped subrange be a list of text;
                add entry 1 of seed range to unmapped subrange;
                let last possible unmapped number be the source start entry - &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;;
                add the smaller of entry 2 of seed range and the last possible unmapped number to unmapped subrange;
                let new source end be entry 2 of unmapped subrange + &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;;
                now entry 1 of seed range is new source end;
                add unmapped subrange to the destination ranges;
                &lt;span class="cm"&gt;[if seed range is now empty, there&amp;#39;s no point looking at any more translations]&lt;/span&gt;
                if seed range is an empty interval, break;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we have a seed range like &lt;code&gt;[2, 20]&lt;/code&gt; and the first row of the Table of
Translation is for the source range &lt;code&gt;[10, 30]&lt;/code&gt;, then we need to first split
off the &lt;code&gt;[2, 9]&lt;/code&gt; part, as it is unmapped and should just be added to the
destination ranges as-is. We’d then continue processing with the interval
&lt;code&gt;[10, 20]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we also could have had a seed range &lt;code&gt;[0, 5]&lt;/code&gt;: in that case, we add
it entirely to the destination ranges, and then update the seed range to be
&lt;code&gt;[6, 5]&lt;/code&gt;. The &lt;code&gt;if seed range is an empty interval&lt;/code&gt; condition catches this
case and breaks out of the loop: we entirely processed this seed range and
can move onto the next one. Note that &lt;code&gt;starting row&lt;/code&gt; remains at this row,
as our next seed range could also hit this case (e.g. &lt;code&gt;[7, 9]&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;            &lt;span class="cm"&gt;[seed range start &amp;gt;= source start, but we also need seed range start &amp;lt;= source end for overlap]&lt;/span&gt;
            if entry 1 of seed range is numerically at most source end entry:
                let mapped subrange be a list of text;
                let destination start be the mapping of entry 1 of seed range on row N;
                let subrange source end be the smaller of entry 2 of the seed range and source end entry;
                add destination start to mapped subrange;
                add the mapping of subrange source end on row N to mapped subrange;
                add mapped subrange to destination ranges;
                now entry 1 of seed range is subrange source end + &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;;
                &lt;span class="cm"&gt;[if seed range is now empty, there&amp;#39;s no point looking at any more translations]&lt;/span&gt;
                if seed range is an empty interval, break;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next case is for when the seed range interval overlaps the source range
in the Table of Translation. The previous case ensures that the seed range start
is now at least the source range start&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;, but our interval might be entirely
&lt;em&gt;after&lt;/em&gt; this source range, so we need to check our start is before the source
range end.&lt;/p&gt;
&lt;p&gt;If it is, then we need to find the subinterval which overlaps and apply the
mapping to the destination range. The destination start is simple: we know the
seed range start is in the source range, so we just need to map it, which we do
with &lt;code&gt;the mapping of entry 1 of seed range on row N&lt;/code&gt;. The arithmetic here is a
little messy inlined, so I’ve split off the source → destination mapping for a
single number to a separate phrase.&lt;/p&gt;
&lt;p&gt;The end of the interval is a little trickier, since it could extend outside the
source range of the current table row: so we find the &lt;code&gt;subrange source end&lt;/code&gt; as
the smaller of the seed range and and the source range end, and then map it
(using the source → destination mapping phrase again) to find the destination end.&lt;/p&gt;
&lt;p&gt;In case the seed range does extend past the source range, we adjust the seed range
start to source range end + 1. If it didn’t, then this will make the interval empty
and we’ll break out of the loop. If it did, then we’ll continue on to the next
table row with the rest of the interval to be mapped.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;        &lt;span class="cm"&gt;[we reached the end of the (possibly-empty) table]&lt;/span&gt;
        unless seed range is an empty interval:
            &lt;span class="cm"&gt;[with a non-empty range, so it must be unmapped]&lt;/span&gt;
            add seed range to destination ranges;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dropping out of the Table loop, if we reached the end of the table
and our seed range is still not empty, then we have another unmapped range.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;    now the seed ranges is the destination ranges.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally, once we’ve mapped all the seed ranges into destination mappings,
it replaces seed ranges. Phew.&lt;/p&gt;
&lt;p&gt;And let’s tidy up a couple of loose threads:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide which text is the mapping of (input - text) on row (N - number):
    choose row N in the Table of Translation;
    decide on destination start entry + (input - source start entry);

To decide if (X - list of text) is an empty interval:
    if entry 1 of X is numerically greater than entry 2 of X, decide yes;
    decide no.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Finding the sorted interval union&lt;/h3&gt;
&lt;p&gt;We tidied up the loose threads, now let’s tidy up a loose rope:
we need a phrase to find the "sorted interval union" of the seed ranges,
which I defined as sorting by the interval start and merging any adjacent
or overlapping intervals together.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide if (R1 - list of text) is before (R2 - list of text) (this is interval start order):
    if entry 1 of R1 is numerically less than entry 1 of R2, decide yes;
    decide no.

To decide which list of lists of text is the sorted interval union of (ranges - list of lists of text):
    let result be a list of lists of text;
    now ranges is ranges sorted by interval start order;
    repeat with range running through ranges:
        &lt;span class="cm"&gt;[The first entry cannot be merged with a previous interval.]&lt;/span&gt;
        if the number of entries in result is 0:
            add range to result;
            next;
        &lt;span class="cm"&gt;[We can merge if the start of this range &amp;lt;= the end of the previous range + 1]&lt;/span&gt;
        let this interval start be entry 1 of range;
        let the previous interval end be entry 2 of the last entry of result;
        if this interval start is numerically at most the previous interval end + &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;:
            let this interval end be entry 2 of range;
            now entry 2 of the last entry of result is the larger of the previous interval end and this interval end;
        otherwise:
            add range to result;
    decide on result.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we sort the ranges by &lt;code&gt;interval start order&lt;/code&gt;, which is defined
above as comparing the first entries (interval start) numerically.
Then we go through each interval. The first gets added to &lt;code&gt;result&lt;/code&gt;
unconditionally. For every interval after that though, we look at the previous
interval in &lt;code&gt;result&lt;/code&gt; and see if we can merge it: if this interval’s start
is at most the previous interval’s end + 1, it is either adjacent or overlapping,
and instead of adding the interval to the list, we just update the previous
interval’s end (if this interval’s end is larger).
If they’re not adjacent/overlapping, we just add the range to the list.&lt;/p&gt;
&lt;p&gt;This is all well and good, but there’s another slight hiccup.
Inform’s built-in &lt;a href="https://ganelson.github.io/inform-website/book/WI_21_8.html"&gt;sorting&lt;/a&gt;
will sort a list of values by their natural ordering, but does not let us
specify a custom comparator (our lists of intervals are lists of lists of text,
and so the natural ordering would sort by the interval starts lexicographically,
not numerically).&lt;/p&gt;
&lt;p&gt;That’s fine, let’s just bang out a quick and terrible implementation of quicksort:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;To decide which list of lists of text is (ranges - list of lists of text) sorted by (cmp - phrase (list of text, list of text) -&amp;gt; truth state):
    if the number of entries in ranges &amp;lt;= 1, decide on ranges;
    let pivot be entry 1 of ranges;
    let left be a list of lists of text;
    let right be a list of lists of text;
    repeat with i running from 2 to the number of entries in ranges:
        let range be entry i of ranges;
        if cmp applied to range and pivot is true:
            add range to left;
        otherwise:
            add range to right;
    now left is left sorted by cmp;
    now right is right sorted by cmp;
    add pivot to left;
    add right to left;
    decide on left;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While implementing this, I discovered two bugs: firstly because I initially tried to
remove the pivot from the list, which &lt;a href="https://inform7.atlassian.net/jira/software/c/projects/I7/issues/I7-2470"&gt;corrupts the list&lt;/a&gt;,
and then when I tried to use &lt;a href="https://ganelson.github.io/inform-website/book/WI_22_7.html"&gt;kind variables&lt;/a&gt; (generic types, more or less),
as &lt;code&gt;To decide what list of K is the (input - list of values of kind K) sorted by (cmp - phrase (K, K) -&amp;gt; truth state)&lt;/code&gt; &lt;a href="https://inform7.atlassian.net/browse/I7-2471"&gt;does not compile&lt;/a&gt; (this is apparently a regression).&lt;/p&gt;
&lt;h3&gt;Computing the answer&lt;/h3&gt;
&lt;p&gt;The answer will then be the smallest start of any interval,
which we could find in linear time, but instead of writing more code,
let’s just sort the intervals and look at the first one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;After finishing a map for the eighth time:
    now the seed ranges is the sorted interval union of the seed ranges;
    let the answer be entry 1 of entry 1 of the seed ranges;
    say the answer.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Transcript&lt;/h2&gt;
&lt;p&gt;The sections above focus on the programming elements of Inform, but what’s
the point of using an Interactive Fiction tool if you’re not going to tell a
story? So my actual code adds descriptions and some flavour text as the story
goes through the process of computing the answer.&lt;/p&gt;
&lt;p&gt;Here’s a transcript for the example for Part 2:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Advent of Code 2023 Day 5.2&lt;br&gt;
An Interactive Fiction by flowblok&lt;br&gt;
Release 1 / Serial number 240316 / Inform 7 v10.1.2&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Garden&lt;br&gt;
A giant garden that looks more to you like a farm.&lt;/p&gt;
&lt;p&gt;The latest Island Island almanac, containing an overwhelmingly amount of information on seeds, soils, fertilizers, water, lighting, temperature and
humidity.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;You can also see the gardener here.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; seeds: 79 14 55 13&lt;/strong&gt;&lt;br&gt;
You open the almanac and read out the long list of seeds, as the gardener looks on in appreciation.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
You flip to the next page of the almanac.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; seed-to-soil map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 50 98 2&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 52 50 48&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
One by one, you slowly find the right type of soil for each kind of seed. The gardener looks fascinated to understand how you're figuring everything out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; soil-to-fertilizer map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 0 15 37&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 37 52 2&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 39 0 15&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
As you find the right type of fertilizer for each kind of soil, the gardener looks on with fading interest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; fertilizer-to-water map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 49 53 8&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 0 11 42&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 42 0 7&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 57 7 4&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
The gardener is looking very bored now and wanders off to tend to other tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; water-to-light map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 88 18 7&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 18 25 70&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
Without an audience, you're starting to tire of this onerous task. You wonder how there could possibly be this many types of water.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; light-to-temperature map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 45 77 23&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 81 45 19&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 68 64 13&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
Some of these temperature numbers look a bit concerning, but there's no time to stop and think, you have to keep translating these numbers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; temperature-to-humidity map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 0 69 1&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 1 0 69&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
As you flip to the next page of the almanac, you realise you're almost at the end. A wave of determination washes over you, and you set yourself to the task anew.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;gt; humidity-to-location map:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 60 56 37&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; 56 93 4&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&amp;gt; &lt;/strong&gt;&lt;br&gt;
The gardener wanders over to you. "You found it already?" he exclaims. "It would have taken me ages to figure out it was 46!"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source code is in my
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/05"&gt;repository&lt;/a&gt; here, as always.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Unless you’re the kind of person who asserts the identity function requires
  implementation.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Well, not necessarily, it depends on what understanding sentences have
  been written. But colons are pesky in general.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;This may not be obvious, but if we’re here, we have a non-empty interval
  which does not have any part before the source range start (as we removed
  it as unmapped).&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Day 4</title><link href="2024-02/advent-of-code-2023-day-4.html" rel="alternate"/><published>2024-02-04T00:00:00+11:00</published><updated>2024-02-04T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2024-02-04:2024-02/advent-of-code-2023-day-4.html</id><summary type="html">&lt;p&gt;Making a scratchcard in Piet.&lt;/p&gt;</summary><content type="html">&lt;style&gt;
.piet-block { font-size: 200%; }
.piet-block.light-red { color: #FFC0C0; }
.piet-block.red { color: #FF0000; }
.piet-block.dark-red { color: #C00000; }
.piet-block.light-yellow { color: #FFFFC0; }
.piet-block.yellow { color: #FFFF00; }
.piet-block.dark-yellow { color: #C0C000; }
.piet-block.light-green { color: #C0FFC0; }
.piet-block.green { color: #00FF00; }
.piet-block.dark-green { color: #00C000; }
.piet-block.light-cyan { color: #C0FFFF; }
.piet-block.cyan { color: #00FFFF; }
.piet-block.dark-cyan { color: #00C0C0; }
.piet-block.light-blue { color: #C0C0FF; }
.piet-block.blue { color: #0000FF; }
.piet-block.dark-blue { color: #0000C0; }
.piet-block.light-magenta { color: #FFC0FF; }
.piet-block.magenta { color: #FF00FF; }
.piet-block.dark-magenta { color: #C000C0; }
&lt;/style&gt;

&lt;p&gt;On &lt;a href="https://adventofcode.com/2023/day/4"&gt;Day 4&lt;/a&gt;, we journey up the gondola lift to
&lt;em&gt;Island Island&lt;/em&gt; and meet an elf with a pile of colourful square scratchcards.
They look just like this one, which also happens to be a valid Piet program that
solves Part 2 of today’s problem:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part2-decorated-1x.png"&gt;&lt;img alt="" class="well original" src="static/images/aoc2023-04-part2-decorated-1x.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s only 48x48 pixels, so here it is with 16x zoom:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part2-decorated.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part2-decorated.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In 2023, my solution for Day 2 was in Piet
(&lt;a href="https://www.dangermouse.net/esoteric/piet.html"&gt;David Morgan-Mar’s website&lt;/a&gt;,
&lt;a href="https://esolangs.org/wiki/Piet"&gt;esolang&lt;/a&gt;), a stack-based language where the
source code is an image, and instructions are encoded by transitions in colours
as execution moves around the image.
At the time, I wrote the instructions as text, and used a Piet assembler to
construct an image. I don’t think I would have finished within 24h without that,
but it left me wanting to construct a Piet program by hand, and so I was
delighted when I realised this day’s problem was suitable.&lt;/p&gt;
&lt;h2&gt;Part 1&lt;/h2&gt;
&lt;p&gt;Our input is a sequence of cards, where each card has a set of winning numbers
(which I will call “card numbers”) and a list of numbers you have
(which I will call “test numbers”).
We need to find out how many of our test numbers are in the set of card
numbers to determine how many points each card is worth (one point for
the first match, and then doubling for each subsequent match) and output
the sum of all cards’ points.&lt;/p&gt;
&lt;p&gt;Piet is a stack-based language, and it provides operators for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;basic stack manipulation&lt;/em&gt;:
   &lt;strong&gt;push&lt;/strong&gt; a positive number on top of the stack,
   &lt;strong&gt;pop&lt;/strong&gt; off the top of the stack,
   &lt;strong&gt;duplicate&lt;/strong&gt; the top of the stack&lt;/li&gt;
&lt;li&gt;&lt;em&gt;arithmetic&lt;/em&gt;: &lt;strong&gt;add&lt;/strong&gt;, &lt;strong&gt;subtract&lt;/strong&gt;, &lt;strong&gt;multiply&lt;/strong&gt;, &lt;strong&gt;divide&lt;/strong&gt; and
   &lt;strong&gt;mod&lt;/strong&gt; for taking the remainder, which all pop the top two numbers on the
   stack and push their result&lt;/li&gt;
&lt;li&gt;&lt;em&gt;comparison&lt;/em&gt;:
   &lt;strong&gt;not&lt;/strong&gt; (replaces the top of the stack with 1 if nonzero, else 0), and
   &lt;strong&gt;greater&lt;/strong&gt; (replaces the top two values of the stack with 1 if the second is
   greater than the top, else 0)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;data-driven movement&lt;/em&gt;:
   &lt;strong&gt;pointer&lt;/strong&gt; pops the top value of the stack and rotates the direction pointer
   by that amount, and &lt;strong&gt;switch&lt;/strong&gt; does the same for the codel chooser&lt;/li&gt;
&lt;li&gt;&lt;em&gt;data-driven stack manipulation&lt;/em&gt;: &lt;strong&gt;roll&lt;/strong&gt; pops the top two values of the stack
   and uses them to rotate a section of the stack by a certain amount&lt;/li&gt;
&lt;li&gt;&lt;em&gt;I/O&lt;/em&gt;: &lt;strong&gt;in&lt;/strong&gt; and &lt;strong&gt;out&lt;/strong&gt; do what you expect, but there are variants for
   characters (specified as Unicode, but with no encoding) or numbers (but
   &lt;a href="https://dice.camp/@dmmaus/111763395988338938"&gt;exactly what is consumed from STDIN is undefined&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My approach to constructing a Piet program is structurally similar to what I did
for &lt;a href="2023-12/advent-of-code-2023-day-1.html"&gt;Brainfuck in Day 1&lt;/a&gt;: write the
algorithm, decompose that into a lower-level representation and then implement
in the low-level language. For Piet, I’ll write the algorithm, decompose it into
&lt;a href="https://en.wikipedia.org/wiki/Basic_block"&gt;basic blocks&lt;/a&gt; and then lay out those
basic blocks as an image.&lt;/p&gt;
&lt;p&gt;Here’s the high-level algorithm (in pseudo-Python):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Card &lt;/span&gt;&lt;span class="si"&gt;{N}&lt;/span&gt;&lt;span class="s2"&gt;: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# read the winning numbers&lt;/span&gt;
    &lt;span class="n"&gt;card_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;|&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;card_numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# test how many numbers match&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;test_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;card_number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card_numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;test_numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="c1"&gt;# convert the number of matches to points&lt;/span&gt;
    &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
            &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next step is to convert this into basic blocks, where each block contains a
sequence of Piet instructions that is run in sequence and ends with an
unconditional jump to another block, a conditional jump (zero or nonzero) to
two&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; other blocks, or by terminating the program.&lt;/p&gt;
&lt;p&gt;This representation is too verbose for this blog post, so &lt;a href="https://hg.flowblok.id.au/advent-of-code/-/blob/branch/default/2023/04/part1.py"&gt;I’ll leave the link
for interested readers&lt;/a&gt;,
and just call out a few interesting&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; aspects.
I wrote this representation in Python, where each basic
block is a function that calls functions implementing the Piet instructions and
ends in an unconditional jump (calling another basic block function), a
conditional jump (calling a &lt;code&gt;jnz&lt;/code&gt; function with the functions to call if the top
value is zero or nonzero) or doing nothing (which results in termination).
Implementing the framework for this is less than 100 lines of code, but does
require a &lt;code&gt;sys.setrecursionlimit&lt;/code&gt; call for non-trivial inputs, so not the kind
of code you want to put into production, but I think we’d already passed that
point by the second paragraph of this blog post.&lt;/p&gt;
&lt;p&gt;Anyhow, we start by setting &lt;code&gt;total&lt;/code&gt; to zero:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bb0_init_total&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;not_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;bb1_check_eof&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can’t just &lt;code&gt;push(0)&lt;/code&gt; because the value pushed is the size (number of
&lt;del&gt;pixels&lt;/del&gt;codels) of the previous colour block, so instead we push a
positive number (any will do, but one takes the least space) onto the stack
and then use &lt;strong&gt;not&lt;/strong&gt; to turn it into a zero. We then jump to &lt;code&gt;bb1_check_eof&lt;/code&gt;,
which implements the looping behaviour of &lt;code&gt;for each line in STDIN:&lt;/code&gt; by consuming
a character from STDIN and testing if we reached the end. This also consumes
the &lt;code&gt;'C'&lt;/code&gt; from &lt;code&gt;"Card {N}: "&lt;/code&gt;, but that’s okay.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bb1_check_eof&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# inchr: If no input is waiting on STDIN, this is an error and the command&lt;/span&gt;
    &lt;span class="c1"&gt;# is ignored.&lt;/span&gt;
    &lt;span class="c1"&gt;# To detect EOF, we push 0 onto the stack, then call inchr.&lt;/span&gt;
    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;not_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;inchr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="c1"&gt;# If we read nothing, we will have a zero on the stack.&lt;/span&gt;
    &lt;span class="n"&gt;not_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;jnz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb23_exit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bb2_not_eof&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Piet, any instructions that cannot be performed (including attempting to read
from STDIN when no input is available) are ignored and do nothing.
So if &lt;strong&gt;inchr&lt;/strong&gt; reads any character (except &lt;code&gt;NUL&lt;/code&gt;),
we’ll have a non-zero value on top of the stack,
which &lt;strong&gt;not&lt;/strong&gt; will turn into a zero, and &lt;code&gt;jnz&lt;/code&gt; will jump to &lt;code&gt;bb2_not_eof&lt;/code&gt;.
If it does nothing, &lt;strong&gt;not&lt;/strong&gt; will turn the zero we put into the stack into a one,
and &lt;code&gt;jnz&lt;/code&gt; will jump to &lt;code&gt;bb23_exit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That’s all well and good, but let’s skip ahead to the parsing code. We need to
implement &lt;code&gt;STDIN.discard("Card {N}: ")&lt;/code&gt;, where &lt;code&gt;{N}&lt;/code&gt; can be any (space-prefixed)
integer. To achieve this without excessive instructions, we’re going to &lt;em&gt;assume
our input is well-formed&lt;/em&gt;. This is not an assumption one should usually make,
but in this situation, we only have two inputs our program is expected to handle
(the example and the actual input), which are well-formed, so we can make this
assumption.&lt;/p&gt;
&lt;p&gt;Having assumed our input is well-formed, let’s move the goalposts a little
closer: rather than discarding &lt;code&gt;"Card {N}: "&lt;/code&gt; specifically, we can discard
any character that appears in &lt;code&gt;"Card 0123456789"&lt;/code&gt; and is not &lt;code&gt;":"&lt;/code&gt;, and then
discard two characters.
That may not seem like a huge improvement, but it allows us to disregard any
character not in those sets, which lets us ask: are there integers &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt;
such that the expression &lt;code&gt;(ord(character) + x) % m&lt;/code&gt; (where &lt;code&gt;ord&lt;/code&gt; is the
Unicode value of a character) is zero for &lt;code&gt;':'&lt;/code&gt; and non-zero for each of
&lt;code&gt;"Card 0123456789"&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;A tiny amount brute-force later, we find the answer is yes (&lt;code&gt;x = 0&lt;/code&gt;, &lt;code&gt;m = 29&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Card 0123456789&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means we can construct a basic block which discards characters until it
reaches a colon (and then calls &lt;code&gt;bb4_read_card_numbers&lt;/code&gt; which needs to consume
the space):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;bb3_read_until_colon&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;inchr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;not_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;jnz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bb4_read_card_numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bb3_read_until_colon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Parsing numbers&lt;/h3&gt;
&lt;p&gt;Our next interesting challenge is to parse a one or two digit integer, where
one digit integers are space-prefixed. It would be lovely to use the
instruction that reads numbers, but as noted above, the specification doesn’t
define what state STDIN should be left in, so I am reluctant to write any
program that would need to call any input instruction after using it.
But fear not, we have made a bold assumption that lets us use a dirty trick,
so let’s just do that again. Can we find &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt; such that each of the
Unicode characters &lt;code&gt;"0123456789"&lt;/code&gt; map to their integer values and &lt;code&gt;' '&lt;/code&gt; maps
to 0?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0123456789&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Huh. That’s a power of two, so we’re just bit-masking to the four
least-significant bits. That feels awfully convenient, very similar to the
case-conversion trick in ASCII (&lt;code&gt;'a-z'&lt;/code&gt; can be converted to &lt;code&gt;'A-Z'&lt;/code&gt; and
vice-versa by XORing with &lt;code&gt;0x20&lt;/code&gt;), and seems like the kind of thing my
predecessors would have known about, but I’ve never seen written down
(and nobody pointed me to references when I
&lt;a href="https://tech.lgbt/@flowblok/111713972810621474"&gt;asked about it&lt;/a&gt;).
If you’ve seen this before, please leave a comment, I’d love to know!&lt;/p&gt;
&lt;p&gt;Unfortunately, if we read three characters at a time (space, first maybe-digit,
second digit), the terminating &lt;code&gt;'|'&lt;/code&gt; will be in the first maybe-digit space,
which means we actually need to handle the character set &lt;code&gt;" 0123456789|"&lt;/code&gt;.
Fortunately, &lt;code&gt;ord('|') % 16 == 12&lt;/code&gt;, so we can use &lt;code&gt;(ord(c) + 4) % 16&lt;/code&gt; to get 0
when &lt;code&gt;c == '|'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the case where the first maybe-digit isn’t &lt;code&gt;'|'&lt;/code&gt;, we subtract 4 to get its
value, multiply by 10 (to move it to the tens place), read the next character,
modulo by 16 and add to put the second digit in the ones place.&lt;/p&gt;
&lt;h3&gt;Lists&lt;/h3&gt;
&lt;p&gt;The next point of interest is how to store &lt;code&gt;card_numbers&lt;/code&gt; in our stack.
We will need the length of the list to do various manipulations, so that should
be at the top of the stack, and then we can just put each element of the list
in order: &lt;code&gt;B| list[0] … list[n-1] list_length |T&lt;/code&gt;
(where &lt;code&gt;B|&lt;/code&gt; and &lt;code&gt;|T&lt;/code&gt; indicate the bottom and top of the stack, respectively).
An empty list would just have the list length of zero: &lt;code&gt;B| 0 |T&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As we read each card number, we will need to append it to the &lt;code&gt;card_numbers&lt;/code&gt;
list. If we have parsed the card number to the top of the stack, then we merely
need to do some stack manipulation to move the card number into the list
representation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] n new_card_number |T&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] new_card_number n |T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, we’re using the &lt;strong&gt;roll&lt;/strong&gt; instruction to take the top two values
of the stack and rotate them by one so they get swapped.
Then we just need to increment &lt;code&gt;n&lt;/code&gt; so we include &lt;code&gt;new_card_number&lt;/code&gt; in the list:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] new_card_number n |T&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] new_card_number n+1 |T&lt;/span&gt;
&lt;span class="c1"&gt;# which is equivalent to&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-2] card_numbers[n-1] n |T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We will also need to be able to iterate over each element of the list.
If we wanted the first element of the list to be at the top of the stack,
we can achieve it with the sequence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] n |T&lt;/span&gt;
&lt;span class="n"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] n n+1 n |T&lt;/span&gt;
&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] n card_numbers[0] |T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once we are done with this element, we can return the list representation to its
canonical state:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] n card_numbers[0] |T&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] card_numbers[0] n |T&lt;/span&gt;
&lt;span class="n"&gt;dup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] card_numbers[0] n n |T&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] card_numbers[0] n n 3 1 |T&lt;/span&gt;
&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] n card_numbers[0] n |T&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[1] … card_numbers[n-1] n card_numbers[0] n+1 1 |T&lt;/span&gt;
&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# STACK: B| card_numbers[0] … card_numbers[n-1] n |T&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are still reading this, then either you skipped over that low-level
nonsense (fair), read through it and learnt something new (good for you!), or
have already dealt with this level of bullshit (my condolences) and thought this
was rather ho-hum.
If you were in the latter two groups, you’ll be &lt;em&gt;delighted&lt;/em&gt; to know that
it’s not this simple, as you may have other variables on top of your stack
(the number of matches so far, and the current test number), and will need to
keep a counter of how far through the list you are. But if you did follow that
sequence, those are merely a matter of perseverance.&lt;/p&gt;
&lt;h3&gt;Structure&lt;/h3&gt;
&lt;p&gt;Many hours later, I had the high-level algorithm decomposed into basic blocks in
my Python representation, and I produced this Graphviz representation:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-blocks-dot.svg"&gt;&lt;img alt="Graph of 24 nodes representing basic blocks, each containing Piet instructions and with edges representing the control flow to other nodes" class="well" src="static/images/aoc2023-04-part1-blocks-dot.svg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are no crossing edges in this layout, which means we could assemble this
into an image fairly mechanically: draw the instructions for each basic block
vertically, leaving enough space around it for the edges to route the control
flow correctly, and then we just need a little extra logic for conditional
edges.&lt;/p&gt;
&lt;p&gt;One of the things I disliked about using the Piet assembler was the output was
sparse and not very artistic, so we’re not going to do that: instead we’re
going to try to keep the basic blocks next to each other and keep the image
as dense as possible.&lt;/p&gt;
&lt;h3&gt;Assembly&lt;/h3&gt;
&lt;p&gt;I used a combination of programs for assembly and debugging:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.gimp.org/"&gt;GNU Image Manipulation Program&lt;/a&gt; for drawing pixels.
   I created a layer for each basic block: toggling visibility of layers
   made it easy to match sections of the image to the graph structure,
   and was useful for isolating sections for debugging.&lt;/li&gt;
&lt;li&gt;Piet Studio, an unreleased Piet debugger I wrote.
   Like many Piet interpreters, it lets you step through the program showing the
   current cursor position, direction pointer, codel chooser and stack,
   but it also lets you manually edit the first three of those (stack editing is
   still on the backlog 😉).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bertnase.de/npiet/"&gt;npietedit&lt;/a&gt; just for figuring out what the
   colour transitions were for the instructions I needed.
   &lt;a href="https://piet.bubbler.one/"&gt;These&lt;/a&gt;
   &lt;a href="https://sanguinel.github.io/PietPlus/"&gt;two&lt;/a&gt;
   web-based editors also provide this functionality and may be useful for
   following along with this section.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bertnase.de/npiet/"&gt;npiet&lt;/a&gt; as an independent Piet implementation to
   verify my program doesn’t rely on bugs in my Piet implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s have a look at how I assembled out the four basic blocks at the top of
my graph: &lt;code&gt;bb0_init_total&lt;/code&gt;, &lt;code&gt;bb1_check_eof&lt;/code&gt;, &lt;code&gt;bb23_exit&lt;/code&gt;, and &lt;code&gt;bb2_not_eof&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A section from the top-left of my Part 1 solution. It looks like random pixel
art, except it’s somewhat structured as it represents the control flow.
Alt text for Piet programs is hard." class="well" src="static/images/aoc2023-04-part1-filter1.png"&gt;&lt;/p&gt;
&lt;p&gt;We start at the top-left, and the first three codels are &lt;code&gt;bb0_init_total&lt;/code&gt;:
we can choose the first arbitrarily (&lt;span class="piet-block light-magenta"&gt;■&lt;/span&gt;),
but then we need to &lt;strong&gt;push 1&lt;/strong&gt;, which means our first colour block must only have
one codel (which it does), and we need to shift the colour one darker
(&lt;span class="piet-block light-magenta"&gt;■&lt;/span&gt; → &lt;span class="piet-block magenta"&gt;■&lt;/span&gt;).
We next need &lt;code&gt;not&lt;/code&gt;, which means we need to shift the colour two darker
(i.e. one lighter) and two hue steps 
(&lt;span class="piet-block magenta"&gt;■&lt;/span&gt; → &lt;span class="piet-block light-yellow"&gt;■&lt;/span&gt;).
This isn’t our only choice: we could interrupt the flow of colours with a white
codel, which would not be treated as an instruction, but would let us start
again at any colour. This does add two extra codels to the program, so I’ve
only used white colour blocks when they’re needed for the control flow.&lt;/p&gt;
&lt;p&gt;The control flow continues on into &lt;code&gt;bb1_check_eof&lt;/code&gt;, which ends with &lt;strong&gt;not&lt;/strong&gt; on a
dark blue colour block (&lt;span class="piet-block dark-blue"&gt;■&lt;/span&gt;).
We then need to branch: to &lt;code&gt;bb23_exit&lt;/code&gt; if the top of the stack is nonzero, or
&lt;code&gt;bb2_not_eof&lt;/code&gt; if it is zero. Except I have actually made this easier, and
ensured that whenever we call &lt;code&gt;jnz&lt;/code&gt; the top of the stack will always be either
0 or 1 (which is why so many blocks end with &lt;code&gt;not&lt;/code&gt;).
There are two instructions for data-driven movement: in this case, we’ll use
&lt;strong&gt;pointer&lt;/strong&gt; (which is one darker and three hue shifts,
so &lt;span class="piet-block dark-blue"&gt;■&lt;/span&gt; → &lt;span class="piet-block light-yellow"&gt;■&lt;/span&gt;):
which will rotate the direction pointer by the top value on the stack.
If that’s a zero, we’ll keep moving right
(&lt;span class="piet-block light-yellow"&gt;■&lt;/span&gt; → &lt;span class="piet-block dark-yellow"&gt;■&lt;/span&gt; is the &lt;strong&gt;pop&lt;/strong&gt; of &lt;code&gt;bb2_not_eof&lt;/code&gt;), otherwise it will be a one, which will start moving us downwards.&lt;/p&gt;
&lt;p&gt;I made a few mistakes in Part 1, and one of the big ones was forgetting to
actually output the answer. The program in the graph structure above computes
the answer and leaves it on top of the stack, but &lt;code&gt;bb23_exit&lt;/code&gt; should have
included an &lt;strong&gt;out&lt;/strong&gt; instruction.
Unfortunately I had assembled &lt;code&gt;bb23_exit&lt;/code&gt; to just exit and put other blocks
around it before I realised this, so I was left the unpleasant task of figuring
out how to retrofit it in. So the instructions in &lt;code&gt;bb23_exit&lt;/code&gt;
(&lt;span class="piet-block light-yellow"&gt;■&lt;/span&gt; → &lt;span class="piet-block light-magenta"&gt;■&lt;/span&gt; → &lt;span class="piet-block blue"&gt;■&lt;/span&gt;)
are &lt;strong&gt;out (number)&lt;/strong&gt; and (uselessly) &lt;strong&gt;dup&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The other thing &lt;code&gt;bb23_exit&lt;/code&gt; needs to do is terminate the program. From the Piet
specification:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Black colour blocks and the edges of the program restrict program flow.
If the Piet interpreter attempts to move into a black block or off an edge,
it is stopped and the CC is toggled. The interpreter then attempts to move
from its current block again. If it fails a second time, the DP is moved
clockwise one step. These attempts are repeated, with the CC and DP being
changed between alternate attempts. If after eight attempts the interpreter
cannot leave its current colour block, there is no way out and the program
terminates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this trace from &lt;code&gt;npiet&lt;/code&gt; (edited to improve legibility), we can see how that
works in our program: when we enter the final blue block, the direction pointer
is down and the codel chooser is left, so we attempt to move from the codel
marked with &lt;code&gt;9.0&lt;/code&gt; to the codel below it. That’s black, so we toggle the codel
chooser to right and try again from the codel marked with &lt;code&gt;9.1&lt;/code&gt;. It’s also black,
so we rotate the direction pointer and try again. When the eighth attempt
(&lt;code&gt;9.7&lt;/code&gt;) also fails, we realise there’s no way out and the program terminates.&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-filter1-trace.png"&gt;&lt;img alt="A program trace of the start of the above section of Part 1, showing the
control flow move through bb0_init_total and bb1_check_eof then into bb23_exit,
where the cursor gets trapped in the last colour block." class="well" src="static/images/aoc2023-04-part1-filter1-trace.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Having dealt with the end of file, let’s take a look at the assembly of
&lt;code&gt;bb3_read_until_colon&lt;/code&gt; (&lt;code&gt;bb2_not_eof&lt;/code&gt; and &lt;code&gt;bb4_read_card_numbers&lt;/code&gt; are also
included to give context):&lt;/p&gt;
&lt;p&gt;&lt;img alt="A section from the top-center of my Part 1 solution." class="well" src="static/images/aoc2023-04-part1-filter2.png"&gt;&lt;/p&gt;
&lt;p&gt;That’s a big block of dark red. If you recall, we found 29 was the magic number
for distinguishing colon/non-colon characters, so we need to &lt;strong&gt;push 29&lt;/strong&gt; in this
block, and you’ll be unsurprised that there are 29 dark red codels in that block.
This is not really a mistake, but there are more efficient ways we could have
achived the same thing. For example, I &lt;em&gt;could have&lt;/em&gt; split this into a sequence
of instructions using only 10 codels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;push 3&lt;/strong&gt; (stack: &lt;code&gt;[3]&lt;/code&gt;, +3 codels)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dup&lt;/strong&gt; (stack: &lt;code&gt;[3 3]&lt;/code&gt;, +1 codel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dup&lt;/strong&gt; (stack: &lt;code&gt;[3 3 3]&lt;/code&gt;, +1 codel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mul&lt;/strong&gt; (stack: &lt;code&gt;[3 9]&lt;/code&gt;, +1 codel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;mul&lt;/strong&gt; (stack: &lt;code&gt;[27]&lt;/code&gt;, +1 codel)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;push 2&lt;/strong&gt; (stack: &lt;code&gt;[27 2]&lt;/code&gt;, +2 codels)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;add&lt;/strong&gt; (stack: &lt;code&gt;[29]&lt;/code&gt;, +1 codel)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anyhow, let’s plow on with the straightforward approach and take a look at how
we loop back to the start of the block. Here’s a trace:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-filter2-trace1.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part1-filter2-trace1.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We perform the sequence &lt;strong&gt;in (character)&lt;/strong&gt;, &lt;strong&gt;push 29&lt;/strong&gt;, &lt;strong&gt;mod&lt;/strong&gt;, &lt;strong&gt;not&lt;/strong&gt;
(&lt;span class="piet-block dark-yellow"&gt;■&lt;/span&gt; →
&lt;span class="piet-block dark-red"&gt;■&lt;/span&gt; →
&lt;span class="piet-block light-red"&gt;■&lt;/span&gt; →
&lt;span class="piet-block green"&gt;■&lt;/span&gt; →
&lt;span class="piet-block light-blue"&gt;■&lt;/span&gt;),
and then we need to go back to the start of &lt;code&gt;bb3_read_until_colon&lt;/code&gt; if the top of
the stack is zero, or on to &lt;code&gt;bb4_read_card_numbers&lt;/code&gt; if it’s one.
But it makes more sense for the layout if we turn right to get back to the start
of &lt;code&gt;bb3_read_until_colon&lt;/code&gt; and do nothing for &lt;code&gt;bb4_read_card_numbers&lt;/code&gt;, so we add
another &lt;strong&gt;not&lt;/strong&gt; before we use &lt;strong&gt;pointer&lt;/strong&gt; to change our direction
(&lt;span class="piet-block light-blue"&gt;■&lt;/span&gt; →
&lt;span class="piet-block dark-red"&gt;■&lt;/span&gt; →
&lt;span class="piet-block light-cyan"&gt;■&lt;/span&gt;).&lt;/p&gt;
&lt;p&gt;But there are two more instructions before we go back to the start:
&lt;strong&gt;push 1&lt;/strong&gt; and &lt;strong&gt;switch&lt;/strong&gt;
(&lt;span class="piet-block light-cyan"&gt;■&lt;/span&gt; →
&lt;span class="piet-block cyan"&gt;■&lt;/span&gt; →
&lt;span class="piet-block light-red"&gt;■&lt;/span&gt;), what’s with that?
Well, when we were on the light red codel at the top right,
we were moving right and then hit the black codel to the right,
which rotated our direction pointer so we started moving down,
but it &lt;em&gt;also&lt;/em&gt; flipped our codel chooser from left to right
(following the rules quoted above).
But it also gets flipped in the white colour block (changing from moving
left to up) and the dark yellow colour block (changing from moving up to
moving right), which means on the second iteration our codel chooser would
be right: then when we exit the big dark red block, we’d be choosing the
&lt;em&gt;rightmost codel&lt;/em&gt; on the right edge to exit from, which would put us on
the light-blue codel, skipping the light red and green codels!
So we just flip the codel chooser once more to get us back into the correct
state.&lt;/p&gt;
&lt;p&gt;This is probably a good time to bring up another mistake I made in constructing
Part 1: I used a black background, which means it’s not clear which black
codels are load-bearing for the program’s control flow and which are unused.
This is good for speed, since I didn’t have to draw in the black codels
whenever I need them, but bad for knowing whether I can use adjacent codels for
other parts of the program flow, or use for decoration at the end.&lt;/p&gt;
&lt;p&gt;There’s not too many things to talk about for Part 1, so here’s my solution:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-decorated-1x.png"&gt;&lt;img alt="" class="well original" src="static/images/aoc2023-04-part1-decorated-1x.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-decorated.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part1-decorated.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To help with understanding the high-level control flow, here’s a version where
I’ve coloured each basic block a distinct colour and overlaid the npiet trace
from running with the example input:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part1-gradient-trace.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part1-gradient-trace.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You’ll notice that some parts of the image are coloured but unvisited: these
are either the handling for empty lists (which don’t appear in the input) or
just decoration.&lt;/p&gt;
&lt;h2&gt;Part 2&lt;/h2&gt;
&lt;p&gt;In Part 2, we discover there is no such thing as points. We’re actually
winning copies of subsequent scratchcards, and need to count how many we end up
with.
Here’s the high-level algorithm:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Number of instances of each scratchcard we&amp;#39;ve processed so far.&lt;/span&gt;
&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="c1"&gt;# Number of copies for scratchcards yet to be processed.&lt;/span&gt;
&lt;span class="c1"&gt;# (i.e. copies[0] + 1 will be the number of instances for the next scratchcard)&lt;/span&gt;
&lt;span class="n"&gt;copies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Card &lt;/span&gt;&lt;span class="si"&gt;{N}&lt;/span&gt;&lt;span class="s2"&gt;: &amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# read the winning numbers&lt;/span&gt;
    &lt;span class="n"&gt;card_numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;|&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;card_numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# test how many numbers match&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;test_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;card_number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card_numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;card_number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;test_numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;

    &lt;span class="c1"&gt;# record the number of instances of this scratchcard in the seen list&lt;/span&gt;
    &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;copies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;copies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# make sure copies is long enough&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;copies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;copies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# add `instances` copies of the next `matches` scratchcards&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;where&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;copies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt;

&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;instances&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first half of the loop is the same as Part 1, so while I did go through
everything again (to be certain of correctness, and add comments about the stack
state to aid in debugging), I was able to copy many of the basic blocks.
You may also notice that the &lt;code&gt;seen&lt;/code&gt; list doesn’t need to be a list:
we could just keep a total and add to it instead of appending.
This choice was also made in aid of debugging: if something goes off the rails
and I only notice later, I can look through the list to figure out at which
point it happened.&lt;/p&gt;
&lt;p&gt;Decomposing this algorithm into basic blocks was still many hours of effort, but
with the aid of rigorous commenting, being able to copy bits from Part 1 and
just having more experience, it was a much smoother process than Part 1.
Here’s &lt;a href="https://hg.flowblok.id.au/advent-of-code/-/blob/branch/default/2023/04/part2.py"&gt;a link to the Python representation&lt;/a&gt;,
and the Graphviz representation:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part2-blocks-dot.svg"&gt;&lt;img alt="Graph of 28 nodes representing basic blocks, with structural similarity to the graph for Part 1, but longer" class="well" src="static/images/aoc2023-04-part2-blocks-dot.svg"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, I started with a grey background, and marked load-bearing black
codels explicitly in their basic block layers.
I also wanted to have more room for decoration, so I tried to lay things out so
I would get a border around the image, and then put basic blocks inside that.
I initially aimed to just leave the “scratch!” box free, but then it became
clear I’d have some more space, and so I tried to line up some cells for
numbers, which worked out pretty well.&lt;/p&gt;
&lt;p&gt;If I turn off the decoration layers, I can get a bare version of the program at
the top of this article and get a trace using the example input to show the
control flow:&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part2-bare.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part2-bare.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last step is decoration, and anything can go in the grey areas, so long as
it doesn’t change the existing colour blocks by having the same colour adjacent&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;.
I drew in the text and numbers, and then just filled the rest with random greens
and blues to be thematic.&lt;/p&gt;
&lt;p&gt;&lt;a href="static/images/aoc2023-04-part2-decorated.png"&gt;&lt;img alt="" class="well" src="static/images/aoc2023-04-part2-decorated.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As always, code and intermediate stages are in my
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/04"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;There are eight possible combinations of the direction pointer and codel
  chooser, so more sophisticated control flow mechanisms are possible!
  But we’re going to keep things simple, at least for now…&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;So long as you share my definition of interesting, which… well, I’m not
  sure how good an idea that is.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I was too lazy to do this manually, so all the greens and blues I added
  are one step away from the correct Piet colours in RGB space.&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Day 3</title><link href="2023-12/advent-of-code-2023-day-3.html" rel="alternate"/><published>2023-12-22T00:00:00+11:00</published><updated>2023-12-22T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2023-12-22:2023-12/advent-of-code-2023-day-3.html</id><summary type="html">&lt;p&gt;Repairing an engine with COBOL.&lt;/p&gt;</summary><content type="html">&lt;p&gt;On Day 3 we reach the gondola lift station, but the engine is broken and for
some inexplicable reason the only computer we have is a mainframe running COBOL
(which appears to be the COmmon Bauble Ornamentation Language on this machine).&lt;/p&gt;
&lt;p&gt;To repair the engine, we’re going to need a program:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;IDENTIFICATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PROGRAM-ID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;aoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And we’re going to need it to be able to read the engine schematic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kr"&gt;INPUT-OUTPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="kr"&gt;FILE-CONTROL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="kp"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STDIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;ASSIGN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KEYBOARD&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="kp"&gt;ORGANIZATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;LINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;SEQUENTIAL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DATA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;FILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;FD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STDIN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;STDIN-LINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC X(200)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s a lot of verbosity here, but the key points are we declare a file
called &lt;code&gt;STDIN&lt;/code&gt; which comes from the keyboard input and is a sequence of lines,
with each line containing 200 characters (this is what &lt;code&gt;PIC X(200)&lt;/code&gt; indicates:
&lt;code&gt;X&lt;/code&gt; means any character).
Our actual input is only 140 characters wide, I’m just rounding up.&lt;/p&gt;
&lt;p&gt;We’re going to want to read the input into memory so we can manipulate it,
so we’ll define a bunch of variables for doing this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;WORKING-STORAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;02 &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-ROW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;03 &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first four variables (&lt;code&gt;WIDTH&lt;/code&gt;, &lt;code&gt;HEIGHT&lt;/code&gt;, &lt;code&gt;X&lt;/code&gt; and &lt;code&gt;Y&lt;/code&gt;) are all defined with
&lt;code&gt;PIC 9(3)&lt;/code&gt;, meaning they can store three-digit numbers (000–999), which is all
we need. The definition of &lt;code&gt;CHAR-GRID&lt;/code&gt; will be a little less obvious to COBOL
newcomers. I’d say that it’s effectively &lt;code&gt;char[200][200]&lt;/code&gt;, but that’s not
exactly true, since we give names to each level of the array, so it’s more like
this C++ code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CharGridRowRecord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CHAR_GRID_CELL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;CharGridRecord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;CharGridRowRecord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CHAR_GRID_ROW&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;CharGridRecord&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CHAR_GRID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Anyhow, let’s read into this structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PROCEDURE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * Read the grid.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;OPEN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;INPUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STDIN&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;EXIT&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;READ&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STDIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kp"&gt;AT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;EXIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;AT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;END-READ&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;CLOSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;STDIN&lt;/span&gt;
&lt;span class="c"&gt;      * Calculate width and height.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;           &lt;/span&gt;&lt;span class="kr"&gt;INSPECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TALLYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;ALL &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; &amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Reading the grid is pretty straightforward: &lt;code&gt;PERFORM UNTIL EXIT&lt;/code&gt; is what
most other languages call &lt;code&gt;while (true)&lt;/code&gt;, and &lt;code&gt;EXIT PERFORM&lt;/code&gt; is &lt;code&gt;break&lt;/code&gt;.
Computing the width is messier than I’d like: I’m counting the number of
spaces (which occur at the end of the line, after the read characters) in
the first line, and then subtracting from 200 (the size I picked above).
I’d love to know if someone has a better way to do this.&lt;/p&gt;
&lt;h2&gt;Part 1&lt;/h2&gt;
&lt;p&gt;Now we’ve got that boilerplate out of the way, let’s get onto the main part
of the problem.
We’re given a grid of characters, and are asked for the sum of numbers which
are adjacent (including diagonals) to symbols (excluding periods).
Here’s what an example input looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The way I’m going to approach this is to build a grid of booleans
(i.e. a bitmap or 2D bitmask), each indicating whether the corresponding cell
in the input grid can reach a symbol.
The first step is to construct the bitmap with &lt;code&gt;1&lt;/code&gt;s where there are symbols in
the input, and &lt;code&gt;0&lt;/code&gt;s everywhere else:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;0000000000
0001000000
0000000000
0000001000
0001000000
0000010000
0000000000
0000000000
0001010000
0000000000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’ll need some more data structures to represent this in our working storage
section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;WORKING-STORAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;FILTER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;02 &lt;/span&gt;&lt;span class="nv"&gt;FILTER-ROW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;03 &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;USAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;BIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FILTER&lt;/code&gt; is a 2D array like &lt;code&gt;CHAR-GRID&lt;/code&gt;, but this time of booleans
(&lt;code&gt;PIC 1 USAGE BIT&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;).
We can construct the bitmap for the first step with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PROCEDURE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above)&lt;/span&gt;
&lt;span class="c"&gt;      * Construct the filter grid.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMERIC&lt;/span&gt;
&lt;span class="kp"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;ELSE&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then expand this bitmap: for each cell, we can set it to a &lt;code&gt;1&lt;/code&gt; if the
corresponding character in the input is a digit and one of the eight
neighbours in the bitmap is a &lt;code&gt;1&lt;/code&gt;.
The first application of this rule produces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;0010000000
0001000000
0011001000
0000001000
0011000000
0000010000
0000100000
0000001000
0001010000
0011011000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Rinse and repeat: once we stop making changes, we end up with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;1110000000
0001000000
0011001110
0000001000
1111000000
0000010000
0011100000
0000001110
0001010000
0111011100
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To implement this, we need a few more bits of storage: a boolean indicating
whether applying the rule resulted in any changes (&lt;code&gt;FILTER-CHANGED&lt;/code&gt;),
some integers tracking the delta to each neighbour (&lt;code&gt;DX&lt;/code&gt;, &lt;code&gt;DY&lt;/code&gt;)
and each neighbour’s position (&lt;code&gt;X2&lt;/code&gt;, &lt;code&gt;Y2&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;WORKING-STORAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CHANGED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;USAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;BIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC S9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC S9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The additions to the procedure division are a mess of nested loops:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PROCEDURE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DIVISION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above)&lt;/span&gt;
&lt;span class="c"&gt;      * Expand the bitmap.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CHANGED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;               &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMERIC&lt;/span&gt;
&lt;span class="kp"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;                       &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;                           &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;                           &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The structure here is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Outermost loop: keep running until we do an iteration without changing
   any cells (indicated by setting &lt;code&gt;FILTER-CHANGED&lt;/code&gt; to 1)&lt;/li&gt;
&lt;li&gt;Second/third loops: look at each cell (X,Y) in the bitmap,
   check if the corresponding input character is a digit (&lt;code&gt;IS NUMERIC&lt;/code&gt;),
   and the bitmap cell is not already a 1.&lt;/li&gt;
&lt;li&gt;Fourth/fifth loops: loop over each of the relative offsets to the eight
   neighbouring cells: (-1, -1), (0, -1), (1, -1), (-1, 0), ..., (1, 1).
   This does also include (0, 0), but in that case we already know that
   &lt;code&gt;FILTER-CELL(Y2,X2)&lt;/code&gt; will be 0, so it doesn’t matter.&lt;/li&gt;
&lt;li&gt;Innermost body: compute (X2, Y2) as the absolute position of the neighbour,
   check if it’s in bounds and the neighbour’s cell is a 1.
   If so, we can set this cell to a 1 and set &lt;code&gt;FILTER-CHANGED&lt;/code&gt; to 1 to indicate
   we’ve made a change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can then apply this bitmask back to the original input to hide the numbers
which aren’t touching a symbol, leaving us with the simpler problem of adding
the remaining numbers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;FILTER-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After this, our example input is now missing the numbers which weren’t
touching a non-period symbol:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;467.......
...*......
..35..633.
......#...
617*......
.....+....
..592.....
......755.
...$.*....
.664.598..
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only thing left is to add up the remaining numbers and print the total.
We need two last variables in our working storage section:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;WORKING-STORAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(9)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To add up the numbers, we first need to assemble each number out of its digits.
We loop over each cell in the grid: when we see a digit, we multiply the
existing digits in &lt;code&gt;PARSED-NUMBER&lt;/code&gt; by 10 to shift them up a place, then add on
the digit. Unfortunately while we know that &lt;code&gt;CHAR-GRID-CELL(Y,X)&lt;/code&gt; is a digit,
it’s still a &lt;code&gt;PIC X&lt;/code&gt;, so we need &lt;code&gt;FUNCTION NUMVAL(…)&lt;/code&gt; to turn it into a &lt;code&gt;PIC 9&lt;/code&gt;
so we can add it.&lt;/p&gt;
&lt;p&gt;Whenever we reach a non-numeric character or the end of the row, we add
&lt;code&gt;PARSED-NUMBER&lt;/code&gt; to the total and reset it to zero. It doesn’t matter if
we hadn’t actually seen a number: in that case, &lt;code&gt;PARSED-NUMBER&lt;/code&gt; will be
zero and adding it to &lt;code&gt;TOTAL&lt;/code&gt; will have no effect.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      * Find numbers in the grid, add them up.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMERIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMBER &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMBER &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10 &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kp"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NUMVAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;ELSE&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;      &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Print&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;DISPLAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;STOP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Part 2&lt;/h2&gt;
&lt;p&gt;Having found the missing part, we need to take things up a gear.
We now only care about the numbers which are touching a &lt;code&gt;*&lt;/code&gt; symbol:
when there are two of these, we need to multiply them together and
add them to the total.&lt;/p&gt;
&lt;p&gt;Let’s give each gear (a &lt;code&gt;*&lt;/code&gt; symbol) a number, starting from 1.
Then we can turn our bitmap into something a bit more detailed:
rather than just storing 0 or 1, it’ll store 0 or the gear number
that digit is connected to.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;WORKING-STORAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;SECTION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      * … (as above, but without FILTER / FILTER-CHANGED)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;02 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-ROW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="mi"&gt;03 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;GEARS-CHANGED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;USAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;BIT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-INFO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;OCCURS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000 &lt;/span&gt;&lt;span class="kp"&gt;TIMES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;02 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-RATIO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(6)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="mi"&gt;02 &lt;/span&gt;&lt;span class="nv"&gt;GEAR-COUNT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="mi"&gt;01 &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;PIC 9(3)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also have the number of gears (&lt;code&gt;GEARS-COUNT&lt;/code&gt;) and for each gear, a
record (&lt;code&gt;GEAR-INFO&lt;/code&gt;) which will have the result of multiplying the touching
numbers (&lt;code&gt;GEAR-RATIO&lt;/code&gt;) and the count of how many touching numbers there are
(&lt;code&gt;GEAR-COUNT&lt;/code&gt;&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;). There’s also &lt;code&gt;CURRENT-GEAR&lt;/code&gt;, which is an index into
&lt;code&gt;GEAR-INFO&lt;/code&gt; we’ll need later.&lt;/p&gt;
&lt;p&gt;To construct the gear grid, we loop over each cell: whenever we see a &lt;code&gt;*&lt;/code&gt;
we increment the gear count to get the current gear number, put it in the
gear grid, and initialize the corresponding entries in &lt;code&gt;GEAR-INFO&lt;/code&gt;.
We initialize &lt;code&gt;GEAR-RATIO&lt;/code&gt; to 1, which will come in handy later, when we’re
multiplying numbers into it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      * (replacing the &amp;quot;Construct the filter grid&amp;quot; section)&lt;/span&gt;
&lt;span class="c"&gt;      * Construct the gears grid.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;INITIALIZE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;*&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-RATIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;ELSE&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For our example input, this gives us (pretending that &lt;code&gt;GEAR-GRID-CELL&lt;/code&gt; is a single-digit number):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;0000000000
0001000000
0000000000
0000000000
0002000000
0000000000
0000000000
0000000000
0000030000
0000000000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Iteratively expanding the gears grid is very similar to the filter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      * (replacing the &amp;quot;Expand the bitmap&amp;quot; section)&lt;/span&gt;
&lt;span class="c"&gt;      * Expand the gears grid.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-CHANGED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;               &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMERIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;                       &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;                           &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DX&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DY&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;AND&lt;/span&gt;
&lt;span class="ow"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;                           &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                             &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-CHANGED&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Aside from a bunch of renaming, pretty much the only change is that we now need
to copy the gear number from the neighbour (&lt;code&gt;GEAR-GRID-CELL(Y2,X2)&lt;/code&gt;) when
setting each updated cell’s gear number (&lt;code&gt;GEAR-GRID-CELL(Y,X)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Once expanded, we get (again pretending that &lt;code&gt;GEAR-GRID-CELL&lt;/code&gt; is a single-digit number):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;1110000000
0001000000
0011000000
0000000000
2222000000
0000000000
0000000000
0000003330
0000030000
0000033300
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like in Part 1, we filter the &lt;code&gt;CHAR-GRID&lt;/code&gt; using &lt;code&gt;GEAR-GRID&lt;/code&gt; to remove anything
which isn’t touching a gear, but now our final step is a bit more complicated.
When we find a number, we need to figure out which gear it corresponds to,
multiply the number into &lt;code&gt;GEAR-RATIO&lt;/code&gt; and increment &lt;code&gt;GEAR-COUNT&lt;/code&gt; (so we’ll be
able to tell if there’s exactly two touching numbers we multiplied):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      * (replacing the &amp;quot;Find numbers in the grid, add them up&amp;quot; section)&lt;/span&gt;
&lt;span class="c"&gt;      * Find numbers in the grid, multiply them into their gears.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HEIGHT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;WIDTH&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;NUMERIC&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;COMPUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10 &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kp"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;NUMVAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CHAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-GRID-CELL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;ELSE&lt;/span&gt;
&lt;span class="kr"&gt;                      &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MULTIPLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                         &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-RATIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;              &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;MULTIPLY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-RATIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PARSED-NUMBER&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this part, we need to track which gear the number belongs to, so when we find
a digit, we store &lt;code&gt;GEAR-GRID-CELL(Y,X)&lt;/code&gt; (which has the gear number) in
&lt;code&gt;CURRENT-GEAR&lt;/code&gt;. Unlike addition, multiplying by zero does have an effect,
so we also need to check when we actually have a number to do something with,
which is provided by &lt;code&gt;CURRENT-GEAR&lt;/code&gt; being non-zero.&lt;/p&gt;
&lt;p&gt;All that’s left is to add up the gear ratios for any gear where we saw exactly
two touching numbers and print it out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;      * Add up the gear ratios.&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;MOVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0 &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;PERFORM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;VARYING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kp"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1 &lt;/span&gt;&lt;span class="kp"&gt;UNTIL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEARS-COUNT&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2 &lt;/span&gt;&lt;span class="kr"&gt;THEN&lt;/span&gt;
&lt;span class="kr"&gt;                  &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;ADD&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GEAR-RATIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CURRENT-GEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="kr"&gt;END-IF&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;END-PERFORM&lt;/span&gt;
&lt;span class="kr"&gt;          &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;DISPLAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TOTAL&lt;/span&gt;
&lt;span class="c"&gt;      &lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="kr"&gt;STOP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kp"&gt;RUN&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As always, the code is in my
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/03"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Unfortunately, my COBOL implementation doesn’t implement this efficiently
  and currently my blog’s syntax highlighter treats this as an error.
  But it’s totally a thing!&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I regret this is very similar to &lt;code&gt;GEARS-COUNT&lt;/code&gt;, but I couldn’t find an
  alternative I liked.&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Day 2</title><link href="2023-12/advent-of-code-2023-day-2.html" rel="alternate"/><published>2023-12-12T00:00:00+11:00</published><updated>2023-12-12T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2023-12-12:2023-12/advent-of-code-2023-day-2.html</id><summary type="html">&lt;p&gt;Solving number-based problems with sed.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I don’t know about you, but when I see a problem that needs some basic
numeric operations like min, max, addition and multiplication, I think to
myself: “this is a great opportunity to pull out &lt;code&gt;sed&lt;/code&gt;”.
Oh, just me then?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; is a line-oriented text processor: you give it a sequence of commands,
and it applies those commands to each line of the input.
The &lt;code&gt;s&lt;/code&gt;ubstitution command is the most well-known, and many folks will only have
ever used &lt;code&gt;sed 's/regex/replacement/g'&lt;/code&gt; on the command line, opting for other
tools for more complex problems.
But &lt;code&gt;sed&lt;/code&gt; has plenty more functionality to offer!&lt;/p&gt;
&lt;h2&gt;The problem input&lt;/h2&gt;
&lt;p&gt;We have a game where we’re pulling marbles from a bag, and we’re given a
record of what happened for these games. The input format is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It turns out we don’t need to care about the commas and semicolons: neither
of the parts have any kind of dependency on the sequence or conjunction of
cubes.&lt;/p&gt;
&lt;h2&gt;Part 1&lt;/h2&gt;
&lt;p&gt;To work with numbers in &lt;code&gt;sed&lt;/code&gt;, we’re going to convert our numbers between
numeral systems. The three forms we will have are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Decimal: 43 (the coefficients of 4×10¹ + 3×10⁰)&lt;/li&gt;
&lt;li&gt;Tally: BBBBAAA (B = 10¹, A = 10⁰, add together the values)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Unary_numeral_system"&gt;Unary&lt;/a&gt;: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (count the symbols)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our input, the largest number is three digits (Game 100), so we can convert
from decimal to tally format like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Convert all numbers to tally format.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;9([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C8\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;8([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C7\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;7([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C6\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;6([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C5\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;5([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C4\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;4([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C3\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;3([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C2\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;2([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C1\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;1([0-9]{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;9([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B8\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;8([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B7\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;7([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B6\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;6([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B5\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;5([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B4\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;4([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B3\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;3([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B2\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;2([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B1\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;1([0-9])&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;9&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A8&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;8&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A7&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;7&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A6&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;6&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A5&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;5&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A4&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;4&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;3&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;A&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;0&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first line turns any 9xx decimal number into C8xx, which then gets turned by
the second line into CC7xx, and so on, until the coefficient of 10² is entirely
in tally format.
The next ten lines do the same for the coefficient of 10¹, and the last ten
lines for 10⁰.&lt;/p&gt;
&lt;p&gt;Our example now looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Game A: AAA blue, AAAA red; A red, AA green, AAAAAA blue; AA green
Game AA: A blue, AA green; AAA green, AAAA blue, A red; A green, A blue
Game AAA: AAAAAAAA green, AAAAAA blue, BB red; AAAAA blue, AAAA red, BAAA green; AAAAA green, A red
Game AAAA: A green, AAA red, AAAAAA blue; AAA green, AAAAAA red; AAA green, BAAAAA blue, BAAAA red
Game AAAAA: AAAAAA red, A blue, AAA green; AA blue, A red, AA green
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can then turn this into unary format with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Convert tally format to unary.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;C&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;BBBBBBBBBB&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;B&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;AAAAAAAAAA&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which turns our example into:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Game A: AAA blue, AAAA red; A red, AA green, AAAAAA blue; AA green
Game AA: A blue, AA green; AAA green, AAAA blue, A red; A green, A blue
Game AAA: AAAAAAAA green, AAAAAA blue, AAAAAAAAAAAAAAAAAAAA red; AAAAA blue, AAAA red, AAAAAAAAAAAAA green; AAAAA green, A red
Game AAAA: A green, AAA red, AAAAAA blue; AAA green, AAAAAA red; AAA green, AAAAAAAAAAAAAAA blue, AAAAAAAAAAAAAA red
Game AAAAA: AAAAAA red, A blue, AAA green; AA blue, A red, AA green
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we need to discard any game which has more than 12 red cubes, 13 green cubes
or 14 blue cubes. Consider the red cubes: if we see &lt;code&gt;"AAAAAAAAAAAAA red"&lt;/code&gt;
(that’s 13 &lt;code&gt;A&lt;/code&gt;s) somewhere in the line, then it means that one of the draws had
&lt;em&gt;at least&lt;/em&gt; 13 red cubes. So, we can discard the games with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# 13+ red cubes -&amp;gt; impossible&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^.*AAAAAAAAAAAAA red.*$&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;# 14+ green cubes -&amp;gt; impossible&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^.*AAAAAAAAAAAAAA green.*$&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;# 15+ blue cubes -&amp;gt; impossible&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^.*AAAAAAAAAAAAAAA blue.*$&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Typically, you’d discard lines using the &lt;code&gt;d&lt;/code&gt; command (e.g. &lt;code&gt;/AAAAAAAAAAAAA red/d&lt;/code&gt;),
but I couldn’t figure out how to make that work with the next part, so we’re just
emptying the line.
Our example is now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Game A: AAA blue, AAAA red; A red, AA green, AAAAAA blue; AA green
Game AA: A blue, AA green; AAA green, AAAA blue, A red; A green, A blue


Game AAAAA: AAAAAA red, A blue, AAA green; AA blue, A red, AA green
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We next need only the game ids, so we filter out everything else:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Get just the ids.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^Game (A+):.*$&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which gives us:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;A
AA


AAAAA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we need to merge all the lines together to add our numbers,
which obviously is just:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Merge the lines together.&lt;/span&gt;
&lt;span class="k"&gt;H&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="k"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;x&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Obviously?&lt;/em&gt; Not in the slightest, so let’s break this down.
Remember how &lt;code&gt;sed&lt;/code&gt; processes text line-by-line?
In order to do multi-line operations, we need to make use of the &lt;em&gt;hold buffer&lt;/em&gt;.
The &lt;code&gt;H&lt;/code&gt; command appends the current line to the hold buffer, which is great,
we can now accumulate all our lines into one place.&lt;/p&gt;
&lt;p&gt;But we only want to do anything with what we put into the hold buffer once we
reach the ends, so what &lt;code&gt;$!d&lt;/code&gt; says is “if it’s not (&lt;code&gt;!&lt;/code&gt;) the last line (&lt;code&gt;$&lt;/code&gt;),
then &lt;code&gt;d&lt;/code&gt;elete the line (and go back to the start of the program to process
the next line)”.&lt;/p&gt;
&lt;p&gt;On the last line&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; however, we get to continue with &lt;code&gt;x&lt;/code&gt;, which (to skip some
detail) says that our hold buffer (of all the lines we accumulated) is now
the line we’re processing. This “line” actually contains multiple lines with
newline characters between, which the &lt;code&gt;s/\n//g&lt;/code&gt; removes to give us a single
line.&lt;/p&gt;
&lt;p&gt;Anyhow, this mess does what we need, and we get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;AAAAAAAA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then just need to turn this back into a decimal number, which we do by first
turning it into the tally format:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Convert the tally back to a number.&lt;/span&gt;
&lt;span class="c1"&gt;# We have 100 input lines, so at worst we can have 1 + 2 + ... + 100 = 5050&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;AAAAAAAAAA&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;BBBBBBBBBB&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;CCCCCCCCCC&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;D&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then decimal:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Insert zeros.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;D([^CD]|$)&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;D0\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;C([^BC]|$)&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;C0\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;B([^AB]|$)&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;B0\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;

&lt;span class="c1"&gt;# Convert back to digits.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{8})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;9&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{7})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;8&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{6})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;7&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{5})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;6&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{4})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{3})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{2})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;3&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;([ABCD])(\1{1})&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;[ABCD]&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Part 2&lt;/h2&gt;
&lt;p&gt;For the second part, we don’t care about the game ids, and the commas and
semicolons are going to get in the way, so we start by removing them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Remove unnecessary parts.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^.*:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;[,;]&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which turns our example into:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; 3 blue 4 red 1 red 2 green 6 blue 2 green
 1 blue 2 green 3 green 4 blue 1 red 1 green 1 blue
 8 green 6 blue 20 red 5 blue 4 red 13 green 5 green 1 red
 1 green 3 red 6 blue 3 green 6 red 3 green 15 blue 14 red
 6 red 1 blue 3 green 2 blue 1 red 2 green
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We then convert our numbers to unary format as before (though this time we only
need to do two digits), and then turn the colours into a single-letter suffix on
our numbers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# (decimal-&amp;gt;tally-&amp;gt;unary from above)&lt;/span&gt;

&lt;span class="c1"&gt;# Turn the colour into a suffix.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt; red&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;r&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt; green&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;g&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt; blue&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our example is now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; AAAb AAAAr Ar AAg AAAAAAb AAg
 Ab AAg AAAg AAAAb Ar Ag Ab
 AAAAAAAAg AAAAAAb AAAAAAAAAAAAAAAAAAAAr AAAAAb AAAAr AAAAAAAAAAAAAg AAAAAg Ar
 Ag AAAr AAAAAAb AAAg AAAAAAr AAAg AAAAAAAAAAAAAAAb AAAAAAAAAAAAAAr
 AAAAAAr Ab AAAg AAb Ar AAg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to find the minimum number of cubes for any game, we need to find the
maximum of each colour. We can do this by repeatedly removing any number where
there is a larger number in the same line (e.g. in the first line, we should
remove &lt;code&gt;AAAb&lt;/code&gt; because there is also &lt;code&gt;AAAAAAb&lt;/code&gt; which is bigger).
The implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Find the minimum number of cubes of each colour required.&lt;/span&gt;
&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;min1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt; (A+[rgb])(.*)\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\2\1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;t&lt;/span&gt;&lt;span class="s1"&gt;min1&lt;/span&gt;

&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;min2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;(A+[rgb])(.*) \1&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\1\2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;t&lt;/span&gt;&lt;span class="s1"&gt;min2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are two new features shown here: &lt;code&gt;:min1&lt;/code&gt; defines a &lt;em&gt;label&lt;/em&gt; named &lt;code&gt;min1&lt;/code&gt;,
and &lt;code&gt;tmin1&lt;/code&gt; says to jump to &lt;code&gt;min1&lt;/code&gt; if an &lt;code&gt;s&lt;/code&gt; command has succeeded since the
last input line was read or a conditional branch (which includes &lt;code&gt;t&lt;/code&gt;) was taken.
Combined, this just says “keep running the &lt;code&gt;s&lt;/code&gt; command in between until it
doesn’t do anything”.&lt;/p&gt;
&lt;p&gt;So what does the &lt;code&gt;s&lt;/code&gt; command do? It matches a space, then a number with a
colour-suffix (which is the first capture group &lt;code&gt;\1&lt;/code&gt;), then anything (the
second capture group &lt;code&gt;\2&lt;/code&gt;), then a repetition of the first capture group.
Consider the first line of our example, where we can find this match:
&lt;code&gt;(AAAb)( AAAAr Ar AAg AAA)AAAb&lt;/code&gt;. This finds an example where the first
capture group is a number which is &lt;em&gt;less than or equal to&lt;/em&gt; a number (of
the same colour) appearing later in the line.
This gets substituted with &lt;code&gt;\2\1&lt;/code&gt;, which would be
&lt;code&gt;( AAAAr Ar AAg AAA)(AAAb)&lt;/code&gt; (without the parentheses), which removes the
left number.&lt;/p&gt;
&lt;p&gt;Unsurprisingly, &lt;code&gt;min2&lt;/code&gt; does the same thing, but for the cases where the
larger number appears earlier in the line.
Once these have both run to completion, we’ll be left with one number per
colour, though in no particular order.
Here’s what the result looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt; AAAAr AAAAAAb AAg
 AAAg AAAAb Ar
 AAAAAAb AAAAAAAAAAAAAAAAAAAAr AAAAAAAAAAAAAg
 AAAg AAAAAAAAAAAAAAAb AAAAAAAAAAAAAAr
 AAAAAAr AAAg AAb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We don’t need the order though, we just need to multiply them together,
which we’ll do with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Discard colour suffixes.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;[rgb]&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="k"&gt;g&lt;/span&gt;

&lt;span class="c1"&gt;# Multiply the first two numbers together.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;mul1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;= (A+) AA&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\1= \1 A&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;t&lt;/span&gt;&lt;span class="s1"&gt;mul1&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;(A*)= (A+) A &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt; \1\2 &lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;

&lt;span class="c1"&gt;# Multiply the third number in.&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;^&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="k"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;mul2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;= (A+) AA&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\1= \1 A&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;t&lt;/span&gt;&lt;span class="s1"&gt;mul2&lt;/span&gt;
&lt;span class="k"&gt;s&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="sr"&gt;(A*)= (A+) A$&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="s1"&gt;\1\2&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Consider the first line of our example, &lt;code&gt;" AAAAr AAAAAAb AAg"&lt;/code&gt;. We start by
discarding the colour suffixes and adding a &lt;code&gt;=&lt;/code&gt; at the start of the line,
giving &lt;code&gt;"= AAAA AAAAAA AA"&lt;/code&gt;.
The &lt;code&gt;mul1&lt;/code&gt; loop then performs naïve multiplication of &lt;code&gt;AAAA&lt;/code&gt; and &lt;code&gt;AAAAAA&lt;/code&gt;,
by adding &lt;code&gt;AAAA&lt;/code&gt; (4) to a total &lt;code&gt;AAAAAA&lt;/code&gt; (6) times.
Here’s each substitution (parentheses indicate capture groups, square
brackets indicate the entire match):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[= (AAAA) AA]AAAA AA&lt;/code&gt; → &lt;code&gt;[AAAA= AAAA A]AAAA AA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AAAA[= AAAA AA]AAA AA&lt;/code&gt; → &lt;code&gt;AAAA[AAAA= AAAA A]AAA AA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AAAAAAAA[= AAAA AA]AA AA&lt;/code&gt; → &lt;code&gt;AAAAAAAA[AAAA= AAAA A]AA AA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AAAAAAAAAAAA[= AAAA AA]A AA&lt;/code&gt; → &lt;code&gt;AAAAAAAAAAAA[AAAA= AAAA A]A AA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AAAAAAAAAAAAAAAA[= AAAA AA] AA&lt;/code&gt; → &lt;code&gt;AAAAAAAAAAAAAAAA[AAAA= AAAA A] AA&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And then post-loop, we make the substitution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[(AAAAAAAAAAAAAAAAAAAA)= AAAA A ]AA&lt;/code&gt; → &lt;code&gt;[AAAAAAAAAAAAAAAAAAAAAAAA] AA&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which leaves us with 24×2 as the multiplication for &lt;code&gt;mul2&lt;/code&gt;, which operates
almost exactly the same.&lt;/p&gt;
&lt;p&gt;Once we’ve done that, it’s just a matter of merging the lines together to
add all the numbers, then converting back to the tally format and then back
to decimal. Simple.&lt;/p&gt;
&lt;p&gt;As always, the code is in my
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/02"&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This is why I couldn’t use the &lt;code&gt;d&lt;/code&gt;elete command earlier: if the last
  line of the input was an impossible game, we’d delete it and the
  program would terminate without getting to the processing of the hold
  buffer. Perhaps there’s a nicer solution? I’d love to know.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Day 1</title><link href="2023-12/advent-of-code-2023-day-1.html" rel="alternate"/><published>2023-12-10T00:00:00+11:00</published><updated>2023-12-10T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2023-12-10:2023-12/advent-of-code-2023-day-1.html</id><summary type="html">&lt;p&gt;Solving Day 1 of Advent of Code 2023 in Brainfuck.&lt;/p&gt;</summary><content type="html">&lt;p&gt;We’re kicking things off the same way as last year: taking the (hopefully)
easiest problem and solving it in one of the more difficult languages to
code in: &lt;a href="https://esolangs.org/wiki/Brainfuck"&gt;Brainfuck&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A brief introduction to Brainfuck&lt;/h2&gt;
&lt;p&gt;As a brief reminder (or introduction if you’ve never had the pleasure),
Brainfuck programs run using an array of memory cells, each storing an integer.
There is a pointer into this array, initially pointing at the first cell.&lt;/p&gt;
&lt;p&gt;There are only eight instructions for Brainfuck programs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Command&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Move the pointer one cell to the right.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Move the pointer one cell to the left.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Increment the integer in the pointed-at cell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Decrement the integer in the pointed-at cell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;,&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Read from the input into the pointed-at cell.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Write the pointed-at cell to the output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;[&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Jump past the matching &lt;code&gt;]&lt;/code&gt; if the pointed-at cell is 0.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;&lt;code&gt;]&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;Jump back to the matching &lt;code&gt;[&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With these instructions, we can start to build higher-level constructs.
The simplest of these is &lt;code&gt;[-]&lt;/code&gt;. This repeatedly decrements the pointed-at
cell until it is equal to zero, or more simply, it sets the pointed-at cell
to zero. Add some &lt;code&gt;+&lt;/code&gt;s after that, and we now can &lt;strong&gt;set the pointed-at cell
to any number&lt;/strong&gt;, regardless of its prior state.&lt;/p&gt;
&lt;p&gt;We also will want to be able to copy a value from one cell to another.
Let’s first look at how we’d &lt;strong&gt;move a value from one cell to another&lt;/strong&gt;:
&lt;code&gt;[-&amp;gt;+&amp;lt;]&lt;/code&gt;.
This decrements the first cell, moves to the next cell, increments it,
and then moves back to the first cell. The loop will repeat until the
first cell is zero, at which point we will have finished moving the value.&lt;/p&gt;
&lt;p&gt;To &lt;strong&gt;copy a cell&lt;/strong&gt; then, we need a temporary cell: we’ll start by moving from
the first cell to the second and third cells with &lt;code&gt;[-&amp;gt;+&amp;gt;+&amp;lt;&amp;lt;]&lt;/code&gt;,
and then follow up by moving the third cell back to the first cell with
&lt;code&gt;&amp;gt;&amp;gt;[-&amp;lt;&amp;lt;+&amp;gt;&amp;gt;]&lt;/code&gt;.
Note that in the constructs we’ve seen so far, the &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; within &lt;code&gt;[…]&lt;/code&gt;
loops are all balanced: this is usually the case, until you start wanting to
use the memory cells as an array.&lt;/p&gt;
&lt;p&gt;Another useful high-level construct is to be able to &lt;strong&gt;read the input one
character at a time until you encounter some specific value&lt;/strong&gt;. Here’s how you’d
read a single line of input: &lt;code&gt;,----------[++++++++++ $inner ,----------]&lt;/code&gt;.
This is pretty straightforward, we read a character and subtract 10 so the
newline character (&lt;code&gt;'\n'&lt;/code&gt; = 10) will become a zero that terminates the loop.
The &lt;code&gt;++++++++++&lt;/code&gt; restores the original character value, though this is optional
if you’re happy to process a shifted character value.&lt;/p&gt;
&lt;p&gt;We’ll also want &lt;strong&gt;conditionals (if/else statements)&lt;/strong&gt;: we can use
&lt;code&gt;[[-] $ifbody]&lt;/code&gt; to run &lt;code&gt;$ifbody&lt;/code&gt; once if the pointed-at cell is nonzero,
though this also sets the pointed-at cell to zero, so we may wish to use a
temporary and then move the value back with &lt;code&gt;[[-&amp;gt;+&amp;lt;] $ifbody]&amp;gt;[-&amp;lt;+&amp;gt;]&amp;lt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To implement an else statement, we need to use another memory cell as a flag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# set the &amp;quot;else flag&amp;quot; to one&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;# if the initial memory cell is nonzero (destructive)&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    $ifbody&lt;/span&gt;
&lt;span class="c"&gt;    # clear the &amp;quot;else flag&amp;quot;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# if the &amp;quot;else flag&amp;quot; is nonzero&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; we didn&amp;#39;t run $ifbody&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    $elsebody&lt;/span&gt;
&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some conventions I will continue to use: &lt;code&gt;#&lt;/code&gt; indicates a comment
(there is a way to do comments natively in Brainfuck, but this should be
familiar to readers),
and &lt;code&gt;$&lt;/code&gt; indicates that the supplied code is merely a template for a Brainfuck
program: in the case above, &lt;code&gt;$ifbody&lt;/code&gt; and &lt;code&gt;$elsebody&lt;/code&gt; would need to be replaced
with some actual Brainfuck instructions.&lt;/p&gt;
&lt;h2&gt;Part 1: the problem&lt;/h2&gt;
&lt;p&gt;Heavens, all that and we haven’t even looked at the problem yet?!
As expected, the problem is pretty straightforward: we want the sum of
&lt;em&gt;calibration values&lt;/em&gt; for each line, and each calibration value is formed
by concatenating the first and last digits found in the line.
The last line of the example input helpfully points out the edge case where
there is only one digit in the line, so the first and last digits will be the
same.&lt;/p&gt;
&lt;p&gt;We’ll start by designing an algorithm for this that works character-by-character
(since that’s how we’ll need to implement it):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Until the end of the input:&lt;ol&gt;
&lt;li&gt;Read characters until we find a digit&lt;/li&gt;
&lt;li&gt;Add the digit 10 times to the total&lt;/li&gt;
&lt;li&gt;Store the digit as the last-seen digit&lt;/li&gt;
&lt;li&gt;Read characters until a newline, updating the last-seen digit as we go&lt;/li&gt;
&lt;li&gt;Add the last-seen digit to the total&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Print the total&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Is it a digit?&lt;/h2&gt;
&lt;p&gt;We have most of the pieces we need already, but there is one tricky part:
how do we tell if a character is a digit or not?
The digits &lt;code&gt;'0123456789'&lt;/code&gt; have ASCII/Unicode values 48, 49, …, 57, so if we
knew we had a digit, we could get its value by subtracting 48.
But if we have another character like &lt;code&gt;'a'&lt;/code&gt; (value 97), then subtracting 48
will give us 49. So we could subtract 48 and then test if the value is in the
range 0-9… but we don’t have a construct for doing that in Brainfuck
(we have "if nonzero", and that can be combined with subtraction to get "if
equal to", but we don’t have an "if less than" or "if greater than").&lt;/p&gt;
&lt;p&gt;Rather than being clever and coming up for a construct for that, let’s do
something simpler and just test each of the 10 different digits.
Here’s some pseudo-code to explain the approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;in c, out is_digit, temp c_copy, temp else_flag&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;is_digit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;unroll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;48.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;.57&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;digit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temporary&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;else_flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;else_flag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;else_flag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;else_flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;else_flag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;is_digit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We need four memory cells:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;c&lt;/code&gt;: the character to test, which we pinky-promise will be unchanged at
   the end of the function&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_digit&lt;/code&gt;: a flag indicating if &lt;code&gt;c&lt;/code&gt; is a digit (1) or not (0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c_copy&lt;/code&gt;: a temporary variable which will hold &lt;code&gt;c - '0'&lt;/code&gt;, &lt;code&gt;c - '1'&lt;/code&gt;, etc.
   If this is ever zero, it means we’ve found a digit.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;else_flag&lt;/code&gt;: a temporary variable used for copying &lt;code&gt;c&lt;/code&gt; to &lt;code&gt;c_copy&lt;/code&gt; and
   indicating if the &lt;code&gt;else&lt;/code&gt; conditional needs to be run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s now turn that into Brainfuck:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# is_digit = 0&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;# $digit = &amp;#39;0&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    # c_copy = c&lt;/span&gt;
&lt;span class="c"&gt;    # else_flag = c&lt;/span&gt;
&lt;span class="c"&gt;    # c = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;    # c = else_flag&lt;/span&gt;
&lt;span class="c"&gt;    # else_flag = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;    # c_copy &lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;0&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;    # else_flag = 1&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;
&lt;span class="c"&gt;    # if c_copy != 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # c_copy = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # else_flag = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # else&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # else_flag = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # is_digit = 1&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;# $digit = &amp;#39;1&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;    # c_copy &lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;1&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;

&lt;span class="c"&gt;# $digit = &amp;#39;2&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;--------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;3&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;---------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;4&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;----------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;5&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-----------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;6&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;7&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;8&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;--------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $digit = &amp;#39;9&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;---------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Gorgeous.&lt;/p&gt;
&lt;p&gt;For the "read characters until we find a digit" step, we’re going to want to
have a loop that terminates when we’ve found a digit. Since loops terminate
on zero, it’s convenient to have a variant of this which returns 0 if the
character is a digit, else 1, which can be constructed by swapping all
&lt;code&gt;is_digit = 0&lt;/code&gt; with &lt;code&gt;is_digit = 1&lt;/code&gt; (and vice versa).&lt;/p&gt;
&lt;h2&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;We start with our memory layout:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;total&lt;/code&gt;: The total of all calibration values.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;last_seen&lt;/code&gt;: The last-seen digit (as a value 0–9).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c&lt;/code&gt;: The current character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_digit&lt;/code&gt;: Whether the current character is a digit or not (though the assignment of 0/1 to true/false is contextual).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c_copy&lt;/code&gt;: Temporary variable, primarily for copying &lt;code&gt;c&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;else_flag&lt;/code&gt;: Temporary variable, primarily for else statements.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The overall program structure then looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# Read a character until EOF&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;    # Check if that character was a digit&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;    $is_digit(false→1&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; true→0)&lt;/span&gt;

&lt;span class="c"&gt;    # Until we find a digit:&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # Read the next character&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;
&lt;span class="c"&gt;        $is_digit(false→1&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; true→0)&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;    # Set last_seen to the first digit&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; and add it ten times to the total&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;    # Read characters until a newline&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;----------&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;
&lt;span class="c"&gt;        # If this character is a digit&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; store it in last_seen&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;        $is_digit(false→0&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; true→1)&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;            &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;            &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;        # Read the next character&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;----------&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;    # Add last_seen to total&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;    # Read the next character (start of the next line)&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# Print total&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;++++++++&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;++++++&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;# then a newline&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;++++++++++&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running this is a bit of a challenge: the input is 1000 lines long, and each
line can add at most 99 to the total, so our &lt;code&gt;total&lt;/code&gt; cell needs to be able to
hold the number 99000, which rules out 8-bit and 16-bit cell widths.
Fortunately, there is an &lt;a href="https://www.nayuki.io/page/optimizing-brainfuck-compiler"&gt;optimizing brainfuck compiler&lt;/a&gt;
which can recognise some of the constructs in our program and produce equivalent
code that runs much faster.&lt;/p&gt;
&lt;h2&gt;Onwards to Part 2&lt;/h2&gt;
&lt;p&gt;That wasn’t too tricky, was it? Unfortunately, Part 2 is here to ruin our fun.
Now our digits can also be spelled out with letters, so we need to recognise
&lt;code&gt;one&lt;/code&gt;, &lt;code&gt;two&lt;/code&gt;, …, &lt;code&gt;nine&lt;/code&gt;.
Uh oh, that’s going to be a lot of conditionals…&lt;/p&gt;
&lt;p&gt;Here’s the new memory layout:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;total&lt;/code&gt;: The total of all calibration values.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;last_seen&lt;/code&gt;: The last-seen digit (as a value 0–9).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multiplier&lt;/code&gt;: How many times to add a digit to the total.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c4&lt;/code&gt;: The fifth-last character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c3&lt;/code&gt;: The fourth-last character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c2&lt;/code&gt;: The third-last character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c1&lt;/code&gt;: The second-last character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c0&lt;/code&gt;: The last (current) character.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;match&lt;/code&gt;: does the current word match so far? /
   &lt;code&gt;else_flag&lt;/code&gt;: temporary variable for &lt;code&gt;else&lt;/code&gt; statements.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c_copy&lt;/code&gt;: Temporary variable, primarily for copying &lt;code&gt;cN&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s really only two changes here: we’ve introduced &lt;code&gt;multiplier&lt;/code&gt; and a
sliding window of the last five input characters.
One of the problems of the Part 1 solution is that &lt;code&gt;$is_digit&lt;/code&gt; had to be
repeated three times. With the addition of the words for digits, we’re going
to have a lot of extra code for recognising digits, so we really want to
restructure the code to avoid that repetition.&lt;/p&gt;
&lt;p&gt;This is where the &lt;code&gt;multiplier&lt;/code&gt; cell comes in. We’re going to simplify the
algorithm so we just have one loop reading input characters:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set &lt;code&gt;multiplier&lt;/code&gt; to 10&lt;/li&gt;
&lt;li&gt;Until the end of the input:&lt;ol&gt;
&lt;li&gt;Shift the sliding window (&lt;code&gt;c0&lt;/code&gt; goes to &lt;code&gt;c1&lt;/code&gt;, … &lt;code&gt;c3&lt;/code&gt; goes to &lt;code&gt;c4&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Read a character into &lt;code&gt;c0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If that character was a newline:&lt;ol&gt;
&lt;li&gt;Add the last-seen digit to the total&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;multiplier&lt;/code&gt; to 10&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;For each digit word (including the actual digits),
   if the word appears at the end of the sliding window:&lt;ol&gt;
&lt;li&gt;Set the last-seen digit to the digit’s value&lt;/li&gt;
&lt;li&gt;Add the digit’s value &lt;code&gt;multiplier&lt;/code&gt; times to total&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;multiplier&lt;/code&gt; to 0&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Print the total&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first time we see a digit, &lt;code&gt;multiplier&lt;/code&gt; will be 10, so we’ll add it to the
total ten times. But for every digit after that, &lt;code&gt;multiplier&lt;/code&gt; will be 0, so
although the same code path will be run, we’ll add it to the total zero times,
having no effect.
Then once we see the newline, we’ll add the last digit to the total, and reset
&lt;code&gt;multiplier&lt;/code&gt; for the first digit of the next line.&lt;/p&gt;
&lt;h2&gt;Finding words in the sliding window&lt;/h2&gt;
&lt;p&gt;The phrase "if the word appears at the end of the sliding window" in the
algorithm above is suspiciously high-level for a Brainfuck program.
Implementing it turns out to be not too difficult, albeit extremely verbose.&lt;/p&gt;
&lt;p&gt;Here’s some pseudo-code to explain the approach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$letter, in c, out match, temp c_copy&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;letter&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;letter&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$word, in c4, in c3, in c2, in c1, in c0, out match, temp c_copy&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;$&lt;span class="n"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;$&lt;span class="n"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;$&lt;span class="n"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;$&lt;span class="n"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;$&lt;span class="n"&gt;test_character&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;'two'&lt;/code&gt;, that turns into the following Brainfuck code
(the last-seen digit and multiplier adjustments are included):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# match = 1&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;

&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# $index = 2&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $letter = &amp;#39;t&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    # c2 &lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;t&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;--------------------------------------------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    # if c2 != 0:&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # c_copy = c2; c2 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # match = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # c2 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c_copy; c_copy = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    # c2 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;t&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;# $index = 1&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $letter = &amp;#39;w&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    # c1 &lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;w&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;-----------------------------------------------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    # if c1 != 0:&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # c_copy = c1; c1 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # match = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # c1 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c_copy; c_copy = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    # c1 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;w&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;# $index = 0&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $letter = &amp;#39;o&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    # c0 &lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;o&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;---------------------------------------------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;    # if c0 != 0:&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # c_copy = c0; c0 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # match = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # c0 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c_copy; c_copy = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    # c0 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;o&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Putting it together (again)&lt;/h2&gt;
&lt;p&gt;Once we’ve tested a word, &lt;code&gt;match&lt;/code&gt; will be 1 if we found the digit word at the
end of the sliding window, so we’re nicely set up to make the adjustments to
&lt;code&gt;last_seen&lt;/code&gt;, &lt;code&gt;multiplier&lt;/code&gt; and &lt;code&gt;total&lt;/code&gt; from our algorithm.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;check_digit_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$word, $value, in c4, in c3, in c2, in c1, in c0, temp match, temp c_copy&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;$&lt;span class="n"&gt;test_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c_copy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;value&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;$&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiplier&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;multiplier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;'two'&lt;/code&gt;, &lt;code&gt;$value = 2&lt;/code&gt; and will turn into (omitting &lt;code&gt;$test_word&lt;/code&gt;) this Brainfuck code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# if match != 0&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;    # match = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # last_seen = 2&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;++&lt;/span&gt;
&lt;span class="c"&gt;    # total &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= 2 * multiplier; multiplier = 0&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;++&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We now have everything we need to implement the entire algorithm:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c"&gt;# set multiplier to 10&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;
&lt;span class="c"&gt;# c0 = read()&lt;/span&gt;
&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;
&lt;span class="c"&gt;# while c0 != eof:&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;    # else_flag = 1&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    # if c0 != &amp;#39;\n&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;----------&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # c_copy = c0; c0 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # else_flag = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;    # c0 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c_copy&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;    # c0 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= &amp;#39;\n&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;
&lt;span class="c"&gt;    # else / if else_flag == 1 / if c0 == &amp;#39;\n&amp;#39;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;
&lt;span class="c"&gt;        # else_flag = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # add last&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="c"&gt;seen digit to total&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # multiplier = 10&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;    &lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;one&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=1)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;two&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=2)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;three&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=3)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;four&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=4)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;five&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=5)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;six&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=6)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;seven&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=7)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;eight&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=8)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;nine&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=9)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=0)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=1)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;2&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=2)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;3&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=3)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;4&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=4)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;5&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=5)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;6&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=6)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;7&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=7)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;8&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=8)&lt;/span&gt;
&lt;span class="c"&gt;    $check_digit_word($word=&amp;quot;9&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;&lt;span class="c"&gt; $value=9)&lt;/span&gt;

&lt;span class="c"&gt;    # update the character window&lt;/span&gt;
&lt;span class="c"&gt;        # c4 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # c4 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c3; c3 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # c3 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c2; c2 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # c2 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c1; c1 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # c1 &lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="c"&gt;= c0; c0 = 0&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;        # c0 = read()&lt;/span&gt;
&lt;span class="c"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;,&lt;/span&gt;
&lt;span class="k"&gt;]&lt;/span&gt;

&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;
&lt;span class="c"&gt;# Print total&lt;/span&gt;
&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;++++++++++&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nb"&gt;++++++++&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;++++++&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="nv"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;]&lt;/span&gt;
&lt;span class="c"&gt;# Print &amp;#39;\n&amp;#39;&lt;/span&gt;
&lt;span class="nb"&gt;++++++++++&lt;/span&gt;&lt;span class="nt"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A complete code listing, debugging interpreter and a tool to strip
comments/whitespace are available in the
&lt;a href="https://heptapod.host/flowblok/advent-of-code/-/tree/branch/default/2023/01"&gt;repository&lt;/a&gt;.&lt;/p&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Advent of Code 2023: Introduction</title><link href="2023-12/advent-of-code-2023-introduction.html" rel="alternate"/><published>2023-12-10T00:00:00+11:00</published><updated>2023-12-10T00:00:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2023-12-10:2023-12/advent-of-code-2023-introduction.html</id><summary type="html">&lt;p&gt;The introduction to a series of posts for Advent of Code,
explaining my self-imposed restrictions.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; is a set of Christmas-themed
programming problems. The problems are released daily from December 1–25,
and each problem consists of two parts, with the second part not visible
until the first part has been solved (usually, this is an extension that
makes the problem slightly harder).&lt;/p&gt;
&lt;p&gt;In previous years
(&lt;a href="https://twitter.com/flowblok/status/1466383158429949952"&gt;2021&lt;/a&gt;,
&lt;a href="https://tech.lgbt/@flowblok/109438322711775692"&gt;2022&lt;/a&gt;), I’ve had the
self-imposed restriction of using a different language for each day’s puzzle.
I’ve enjoyed this restriction: it has prompted me to use all kinds of
different langauges that I wouldn’t normally use.
I view this restriction as encouraging me to create art with my code:
rather than engineering the best solution (however you want to define that)
to the problem, I’m trying to create interesting and beautiful solutions.&lt;/p&gt;
&lt;p&gt;Unfortunately, I was busy at the start of December, so was unable to do the
problems as they were released. But I’ve chosen to view this as a blessing in
disguise: rather than pushing myself to solve each day’s problem before the
next one is released, I can take my time to produce refined solutions.
Hopefully, this will allow me to use interesting languages for more days.&lt;/p&gt;
&lt;p&gt;I’m also going to use the lack of time pressure to write up some more detailed
commentary on my solutions, which I’ll post here.&lt;/p&gt;</content><category term="adventofcode"/><category term="aoc2023"/></entry><entry><title>Ringsteady Sorting in Linear Time</title><link href="2023-08/ringsteady-sorting-in-linear-time.html" rel="alternate"/><published>2023-08-23T00:00:00+10:00</published><updated>2023-08-23T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2023-08-23:2023-08/ringsteady-sorting-in-linear-time.html</id><summary type="html">&lt;p&gt;An optimization for implementing Ringsteady Subsetting in linear time.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Last year, I wrote
&lt;a href="https://queue.acm.org/detail.cfm?id=3570937"&gt;an article for ACM Queue&lt;/a&gt;
describing an algorithm I co-invented for backend subsetting (and recently
&lt;a href="https://netflixtechblog.com/curbing-connection-churn-in-zuul-2feb273a3598"&gt;Netflix published that they’d adopted parts of it&lt;/a&gt;).
This post is really just a footnote to that article, something which was
originally included but had to be dropped due to word limits.&lt;/p&gt;
&lt;p&gt;As a quick recap, to build our algorithm we start with
&lt;a href="https://en.wikipedia.org/wiki/Consistent_hashing"&gt;Consistent Hashing&lt;/a&gt;
and extend it to return a subset of backend tasks rather than just a single task
(coined Consistent Subsetting). Ringsteady Subsetting is then a specific case
of Consistent Subsetting that improves connection balance, by replacing the
randomly chosen positions with positions from a low-discrepancy sequence.&lt;/p&gt;
&lt;p&gt;The particular sequence we chose was the binary
&lt;a href="https://en.wikipedia.org/wiki/Van_der_Corput_sequence"&gt;van der Corput sequence&lt;/a&gt;,
and the article notes that one of the reasons is the ease of computing any
individual element, but another reason is that it enables a particular
optimisation.&lt;/p&gt;
&lt;p&gt;The most straightforward way to implement this is to generate pairs of each
backend’s index and position, and sort them by position. Here’s a Rust
implementation&lt;sup id="fnref:readability"&gt;&lt;a class="footnote-ref" href="#fn:readability"&gt;1&lt;/a&gt;&lt;/sup&gt; (this uses backend scaling&lt;sup id="fnref:scaling"&gt;&lt;a class="footnote-ref" href="#fn:scaling"&gt;2&lt;/a&gt;&lt;/sup&gt;, so
corresponds to Figure 4c in the article):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Copyright 2023 Google LLC.&lt;/span&gt;
&lt;span class="c1"&gt;// SPDX-License-Identifier: Apache-2.0&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// The backend&amp;#39;s index in the range [0, n)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// Its position on the circle, as a fixed-point number&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;/// (i.e. usize::MAX / 2 is halfway around the circle)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Self&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ringsteady_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frontend_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Generate a vector of [index, position] for each backend.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse_bits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_position&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Sort the vector by each backend&amp;#39;s position.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Get position of this frontend on the circle as a float in the range [0, 1).&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frontend_unit_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frontend_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse_bits&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="k"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;powi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Figure out how many backends this frontend needs to rotate by.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frontend_unit_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Output the backend indices.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;subset_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// OUTPUT:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #0: [0, 4]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #1: [1, 5]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #2: [2, 1]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #3: [3, 0]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #4: [4, 2]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ringsteady_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Frontend #{}: {:?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sorting these pairs is the most expensive part of this algorithm.
Wouldn’t it be neat if we could side-step that and just write out the backends
in the correct order?&lt;/p&gt;
&lt;p&gt;The key insight is that if we have &lt;em&gt;2&lt;sup&gt;w&lt;/sup&gt;&lt;/em&gt; backends (for some integer
&lt;em&gt;w&lt;/em&gt;), then the positions of our backends will be a
&lt;a href="https://en.wikipedia.org/wiki/Bit-reversal_permutation"&gt;bit-reversal permutation&lt;/a&gt;,
which is an involution (i.e. reversing the bits of a number twice gives the same
number).
This means that not only does reversing the bits give us the position for a
backend index, it also gives us the backend index for a position.
For example, here’s &lt;em&gt;w = 3&lt;/em&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center;"&gt;Backend Index&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Position&lt;/th&gt;
&lt;th style="text-align: center;"&gt;&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Position&lt;/th&gt;
&lt;th style="text-align: center;"&gt;Backend Index&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;0&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0&lt;/td&gt;
&lt;td style="text-align: center;"&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;1&lt;/td&gt;
&lt;td style="text-align: center;"&gt;4&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1&lt;/td&gt;
&lt;td style="text-align: center;"&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;2&lt;/td&gt;
&lt;td style="text-align: center;"&gt;2&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;2&lt;/td&gt;
&lt;td style="text-align: center;"&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;3&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;3&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;4&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;4&lt;/td&gt;
&lt;td style="text-align: center;"&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;5&lt;/td&gt;
&lt;td style="text-align: center;"&gt;5&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;5&lt;/td&gt;
&lt;td style="text-align: center;"&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;6&lt;/td&gt;
&lt;td style="text-align: center;"&gt;3&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;6&lt;/td&gt;
&lt;td style="text-align: center;"&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center;"&gt;7&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7&lt;/td&gt;
&lt;td style="text-align: center;"&gt;&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7&lt;/td&gt;
&lt;td style="text-align: center;"&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To extend this to any number of backends &lt;em&gt;N&lt;/em&gt;, we just need to find the smallest
integer &lt;em&gt;w&lt;/em&gt; such that &lt;em&gt;N ≤ 2&lt;sup&gt;w&lt;/sup&gt;&lt;/em&gt;, iterate over the positions
&lt;em&gt;[0, 2&lt;sup&gt;w&lt;/sup&gt;)&lt;/em&gt;, reversing each position (as a &lt;em&gt;w&lt;/em&gt; bit number) to get the
backend index at that position, skipping any backend index that isn’t in &lt;em&gt;[0, N)&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what that looks like in Rust:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// Copyright 2023 Google LLC.&lt;/span&gt;
&lt;span class="c1"&gt;// SPDX-License-Identifier: Apache-2.0&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;ringsteady_linear_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frontend_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Generate a vector of backend indices in the correct order.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_positions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_power_of_two&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_positions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trailing_zeros&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;num_positions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse_bits&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BITS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend_index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Get position of this frontend on the circle as a float in the range [0, 1).&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frontend_unit_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frontend_index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse_bits&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="k"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;powi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Figure out how many backends this frontend needs to rotate by.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frontend_unit_position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Output the backend indices.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;subset_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num_backends&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// OUTPUT:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #0: [0, 4]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #1: [1, 5]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #2: [2, 1]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #3: [3, 0]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Frontend #4: [4, 2]&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ringsteady_linear_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Frontend #{}: {:?}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It’s fairly straightforward to show that &lt;em&gt;2&lt;sup&gt;w&lt;/sup&gt; &amp;lt; 2N&lt;/em&gt; (if it wasn’t,
there would be a smaller valid choice of &lt;em&gt;w&lt;/em&gt;), so the time complexity is linear
in &lt;em&gt;N&lt;/em&gt;.
It’s also much faster in practice (even for small &lt;em&gt;N&lt;/em&gt;), since we avoid the
memory read/writes from swapping items in the sort algorithm.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: Any opinions stated here are my own, and not necessarily shared by
my employer (Google).&lt;/em&gt;&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:readability"&gt;
&lt;p&gt;I have preferred readability over performance or idiomatic code
in these snippets, in the hope that it will be understandable to readers
unfamiliar with Rust. For instance, there is no need for floating-point
when calculating the frontend rotation, but I couldn’t figure out how to
do that without distracting amounts of boilerplate.&amp;#160;&lt;a class="footnote-backref" href="#fnref:readability" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:scaling"&gt;
&lt;p&gt;It’s possible to use this optimization without backend scaling,
but it made the sample code too verbose for this blog post.&amp;#160;&lt;a class="footnote-backref" href="#fnref:scaling" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="algorithms"/><category term="optimisation"/><category term="software"/></entry><entry><title>Galactic Puzzle Hunt 2019: Reflection</title><link href="2019-04/galactic-puzzle-hunt-2019-reflection.html" rel="alternate"/><published>2019-04-08T00:00:00+10:00</published><updated>2019-04-08T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2019-04-08:2019-04/galactic-puzzle-hunt-2019-reflection.html</id><summary type="html">&lt;p class="first last"&gt;A reflection on organizing a puzzle hunt team.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I recently organized and participated in a team for the 2019 edition of the
&lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/"&gt;Galactic Puzzle Hunt&lt;/a&gt;. This post is my reflections on that process.&lt;/p&gt;
&lt;div class="section" id="organizing-a-team"&gt;
&lt;h2&gt;Organizing a team&lt;/h2&gt;
&lt;p&gt;Teams for the puzzle hunt can be up to 10 people &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;, so the first step was to
attempt to find other people who are at least as insane as I am. I put out a
&lt;a class="reference external" href="https://twitter.com/flowblok/status/1100166796785504256"&gt;tweet&lt;/a&gt; linking to a Google Form (and also messaged a few friends), asking
(amongst a few other things) for a time commitment, with the intent that I’d
select team members most happy to spend time on it.&lt;/p&gt;
&lt;p&gt;As it happened, I only got seven signups (including myself), so that was my
team formed. I thought that was a reasonable number, but it did have a lot of
people only able to make a small time commitment, and I should have kept
searching for more people. My perception was we had ~2.5 daytime solvers on
average.&lt;/p&gt;
&lt;p&gt;There were a few people who gave us some help during the hunt, so I think that
means I need to promote signups more next year.&lt;/p&gt;
&lt;p&gt;Having brought together some people, we needed a team name. A bunch of ideas we
had (some of which followed the Antarctica theme for the puzzle hunt):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Production Freeze&lt;/li&gt;
&lt;li&gt;By accepting this team name you agree to our EULA&lt;/li&gt;
&lt;li&gt;Fizz puzz&lt;/li&gt;
&lt;li&gt;Ice climbers&lt;/li&gt;
&lt;li&gt;TE-901&lt;/li&gt;
&lt;li&gt;UNTITL~1.GPH&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But “How about ☃ repeated as many times as the registration form will allow?”
was most liked. Confession time: I didn’t actually do this. Instead, I went for
three rows of snowmen (as displayed on my laptop). So while we didn’t have the
longest team name, we did have the most vertical space in the team list.&lt;/p&gt;
&lt;p&gt;Perhaps next year we’ll figure out how to consume even more vertical space with
our team name?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="team-coordination"&gt;
&lt;h2&gt;Team coordination&lt;/h2&gt;
&lt;p&gt;We used Slack and Google Docs to coordinate. In the Slack workspace, there was a
&lt;tt class="docutils literal"&gt;#coordination&lt;/tt&gt; channel which had login details, a periodically updated “best
puzzles to work on” post, celebrations of solved puzzles, and occasional calls
for help. Puzzle solving didn’t happen in that channel: instead, we created a
channel per puzzle.&lt;/p&gt;
&lt;p&gt;It’s hard to predict what technology will be best to solve a puzzle, but
generally we always want a document to describing what we’ve tried and with
partial progress (which eventually morphs into a “how to solve the puzzle” doc).
All of these documents got put into a shared Google Drive folder, and each
puzzle channel in Slack had a topic linking to the relevant doc for easy access.&lt;/p&gt;
&lt;p&gt;A few of the puzzles needed more than a doc. We also used:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Photos shared in Slack (for a physical jigsaw-like puzzle)&lt;/li&gt;
&lt;li&gt;Images shared in Slack&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.google.com/mymaps"&gt;Google My Maps&lt;/a&gt; in Drive (super-useful for visualising latitude/longitude
pairs and looking for patterns)&lt;/li&gt;
&lt;li&gt;Google Spreadsheets in Drive (although a few times I started to create one,
and then replaced it with a table in a Google Doc)&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://colab.research.google.com"&gt;Google Colaboratory&lt;/a&gt; in Drive (omg sharable Python code with notes!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was generally happy with the technology choices, although I need to figure out
how to use Colaboratory in a way that involves less clicking and makes better
use of my vim muscle memory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="puzzle-solving"&gt;
&lt;h2&gt;Puzzle solving&lt;/h2&gt;
&lt;p&gt;Something I keep saying about puzzle hunts is that it’s important to have lots
of people on the team, even if they aren’t spending gross amounts of time
solving puzzles. What’s really important is that there’s a continuous stream of
“have you tried &amp;lt;thing&amp;gt;?” ideas and “that looks kinda like &amp;lt;other thing&amp;gt;”
observations: otherwise, you get stuck trying to solve a puzzle and then get
frustrated at not having ideas, which isn’t a great mental state to be producing
ideas with.&lt;/p&gt;
&lt;p&gt;I’m very grateful to my team members for contributing their time: I think I
spent less time in that stuck state than I have in previous years, but there’s
definitely still room for improvement there.&lt;/p&gt;
&lt;p&gt;There were also a few times where I think we had trouble because we didn’t quite
know what “rules” the puzzle authors were playing by, so to speak. In one case,
I wrote a bunch of Python code looking for patterns in some data because of a
perceived hint in the puzzle title, which turned out to be a wild goose chase.
There was also at least one hint spent on something where we should have just
been able to get it out by being more rigorous.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="results"&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/teams?team=%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83%E2%98%83"&gt;Our team&lt;/a&gt; solved the first 12 puzzles, which put us in &lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/teams"&gt;239th&lt;/a&gt; place.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Out of 717 registered teams: top 33%&lt;/li&gt;
&lt;li&gt;Out of 505 teams who solved at least one puzzle: top 47%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’m pretty happy with how we went: I only wish we’d managed to finish our
half-solved metapuzzle in time!&lt;/p&gt;
&lt;p&gt;For next year’s Galactic Puzzle Hunt (or any other ones for that matter), I’ve
been thinking lately about doing some training sessions (where you solve a
puzzle, but there’s zero-cost hints as soon as you get stuck). If this sounds
interesting, hit me up?&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Although the &lt;a class="reference external" href="https://2019.galacticpuzzlehunt.com/rules"&gt;rules&lt;/a&gt; also say “You may use any external sources for
help, including other people, as long as they aren’t helping other
teams and aren’t actively participating in the hunt.”&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="puzzles"/><category term="gph"/></entry><entry><title>Python C Extension Module Corruption</title><link href="2018-06/python-c-extension-module-corruption.html" rel="alternate"/><published>2018-06-23T00:00:00+10:00</published><updated>2018-06-23T00:00:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2018-06-23:2018-06/python-c-extension-module-corruption.html</id><summary type="html">&lt;p class="first last"&gt;In which a segfault in cPython is caused by an interesting interaction with shared modules on Linux.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;If you follow me on Twitter, you might have seen this &lt;a class="reference external" href="https://twitter.com/flowblok/status/1009765246750187520"&gt;recent thread&lt;/a&gt;, in
which I spent a few days metaphorically banging my head against a wall trying to
figure out why my Python program would occasionally segfault (with no apparent
trigger).&lt;/p&gt;
&lt;p&gt;This post is an attempt to write up the cause (and my workaround) in a clearer
form, and hopefully make it Google-able for the next poor soul to encounter
this (if it's not fixed before then).&lt;/p&gt;
&lt;div class="section" id="dramatis-personae"&gt;
&lt;h2&gt;Dramatis Personæ&lt;/h2&gt;
&lt;p&gt;The program in question here is a Python web application, written in &lt;a class="reference external" href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;,
and run using &lt;a class="reference external" href="http://gunicorn.org/"&gt;gunicorn&lt;/a&gt; (using a few sync workers). It also uses
&lt;a class="reference external" href="https://sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; (via &lt;a class="reference external" href="http://flask-sqlalchemy.pocoo.org/"&gt;Flask-SQLAlchemy&lt;/a&gt;) to access a &lt;a class="reference external" href="https://sqlite.org/"&gt;sqlite3&lt;/a&gt; database.
It’s all running on a &lt;a class="reference external" href="https://cloud.google.com/compute/"&gt;Google Compute Engine&lt;/a&gt; VM running &lt;a class="reference external" href="https://debian.org/"&gt;Debian Linux&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most of those details aren’t relevant, but I mention them just to give a sense
of the scope: each of them was a suspect at some stage or another during my
investigation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-evidence"&gt;
&lt;h2&gt;The Evidence&lt;/h2&gt;
&lt;p&gt;Aside from seeing 5xx responses from my web server, the only information I had
was in syslog:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
kernel: [1234.567890] gunicorn[1234]: segfault at ad0 ip 0000000000000ad0 sp 00007fffc0401448
                                      error 14 in python3.5[55f0889ee000+3ef000]
&lt;/pre&gt;
&lt;p&gt;The gunicorn logs noted that it had started a new worker to replace the
segfaulted one, but nothing more. The syslog entry is really quite odd: the
&lt;tt class="docutils literal"&gt;at ad0&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;ip 0000000000000ad0&lt;/tt&gt; parts indicate that the program tried to
jump to (and hence execute) code at the address 0xad0, which is &lt;em&gt;quite&lt;/em&gt;
suspicious: I don't know offhand what's at that address, but it’s not usually
program code.&lt;/p&gt;
&lt;p&gt;Clearly I needed more information to debug this, so I tried to get a
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Core_dump"&gt;core dump&lt;/a&gt;. For reasons I’m still not entirely sure of, getting a core dump
didn’t just work out-of-the-box—I believe Linux is meant to write a file named
&lt;tt class="docutils literal"&gt;core&lt;/tt&gt; to the process’s working directory (provided ulimit is set correctly).&lt;/p&gt;
&lt;p&gt;Some quick Googling revealed that &lt;a class="reference external" href="https://www.freedesktop.org/software/systemd/man/systemd-coredump.html"&gt;systemd-coredump&lt;/a&gt; is a thing, so I installed
that, and then my program didn’t segfault for an hour, so I tweeted thinking
that this was a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Heisenbug"&gt;Heisenbug&lt;/a&gt;. It turned out not to be the case: the segfault
only happened a few times an hour, and I was just unlucky.&lt;/p&gt;
&lt;p&gt;Anyhow, I later came back and used &lt;tt class="docutils literal"&gt;coredumpctl list&lt;/tt&gt; to see when the program
had segfaulted, and &lt;tt class="docutils literal"&gt;coredumpctl gdb&lt;/tt&gt; to launch &lt;a class="reference external" href="https://www.gnu.org/software/gdb/"&gt;gdb&lt;/a&gt; with the core dump
loaded. For all the hate systemd seems to get for reinventing the wheel, I will
say that systemd-coredump seems quite good at its job.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ coredumpctl gdb
...
Core was generated by `/usr/bin/python3'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000000ad0 in ?? ()
(gdb)
&lt;/pre&gt;
&lt;p&gt;Great: there's the 0xad0 address from my syslog entry, so the crash is the same
as the one I’m seeing! This might seem like a minor point, but fervently
verifying the world is as you expect it to be is critically important in
debugging. Let’s look at a backtrace to see what the program was doing when it
segfaulted:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
(gdb) bt
#0  0x0000000000000ad0 in ?? ()
#1  0x0000556dce8116df in PyCFunction_Call () at ../Objects/methodobject.c:109
#2  0x0000556dce7d16e9 in call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e1e0) at ../Python/ceval.c:4720
#3  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#4  0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e310, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#5  call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e310) at ../Python/ceval.c:4745
#6  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#7  0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e440, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#8  call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e440) at ../Python/ceval.c:4745
#9  PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#10 0x0000556dce7d6286 in _PyEval_EvalCodeWithName () at ../Python/ceval.c:4033
#11 0x0000556dce7d24c9 in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e650, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4828
#12 call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e650) at ../Python/ceval.c:4745
#13 PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#14 0x0000556dce7d193f in fast_function (nk=&amp;lt;optimized out&amp;gt;, na=&amp;lt;optimized out&amp;gt;, n=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e780, func=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4818
#15 call_function (oparg=&amp;lt;optimized out&amp;gt;, pp_stack=0x7ffd2fc7e780) at ../Python/ceval.c:4745
#16 PyEval_EvalFrameEx () at ../Python/ceval.c:3251
#17 0x0000556dce7d70df in _PyEval_EvalCodeWithName (qualname=0x0, name=&amp;lt;optimized out&amp;gt;, closure=0x0, kwdefs=0x0, defcount=0, defs=0x0, kwcount=0, kws=&amp;lt;optimized out&amp;gt;, argcount=&amp;lt;optimized out&amp;gt;,
    args=&amp;lt;optimized out&amp;gt;, locals=&amp;lt;optimized out&amp;gt;, globals=&amp;lt;optimized out&amp;gt;, _co=&amp;lt;optimized out&amp;gt;) at ../Python/ceval.c:4033
#18 PyEval_EvalCodeEx () at ../Python/ceval.c:4054
#19 0x0000556dce8135d3 in function_call.lto_priv () at ../Objects/funcobject.c:627
#20 0x0000556dce85a647 in PyObject_Call () at ../Objects/abstract.c:2166
#21 0x0000556dce77a94e in method_call.lto_priv () at ../Objects/classobject.c:330
#22 0x0000556dce85a647 in PyObject_Call () at ../Objects/abstract.c:2166
#23 0x0000556dce81aebf in slot_tp_iter () at ../Objects/typeobject.c:6214
#24 0x0000556dce85a4a3 in PyObject_GetIter () at ../Objects/abstract.c:2755
#25 0x0000556dce84cd56 in listextend.lto_priv.683 (
    b=&amp;lt;BaseQuery(_group_by=False, _params={&amp;lt;_anonymous_label at remote 0x7f26c3adbd48&amp;gt;: 'flowblok.id.au'}, _entities=[&amp;lt;_MapperEntity(entity_zero=&amp;lt;Mapper(self_and_descendants=&amp;lt;WeakSequence(_storage=[&amp;lt;weakref at remote 0x7f26c3a9bf98&amp;gt;]) at remote 0x7f26c3aae898&amp;gt;, inherits=None, version_id_generator=&amp;lt;function at remote 0x7f26c3e837b8&amp;gt;, confirm_deleted_rows=True, _readonly_props=set(), local_table=&amp;lt;Table(name=&amp;lt;quoted_name at remote 0x7f26c3e8c1c8&amp;gt;, description=&amp;lt;...&amp;gt;, _cloned_set={&amp;lt;...&amp;gt;}, constraints={&amp;lt;ForeignKeyConstraint(name='domain_admin_group_constraint', initially=None, dialect_kwargs=&amp;lt;_DialectArgView(obj=&amp;lt;...&amp;gt;) at remote 0x7f26c82b5160&amp;gt;, parent=&amp;lt;...&amp;gt;, _referred_table=&amp;lt;Table(name=&amp;lt;quoted_name at remote 0x7f26c3e8cbc8&amp;gt;, description=&amp;lt;...&amp;gt;, _cloned_set={&amp;lt;...&amp;gt;}, constraints={&amp;lt;ForeignKeyConstraint(name=None, initially=None, parent=&amp;lt;...&amp;gt;, _referred_table=&amp;lt;...&amp;gt;, columns=&amp;lt;ColumnCollection at remote 0x7f26c3e22cc0&amp;gt;, ondelete=None, elements=[&amp;lt;ForeignKey(name=None, column=&amp;lt;Column(_cloned_set={&amp;lt;...&amp;gt;}, constraints=set(), forei...(truncated),
    self=0x7f26c3433608) at ../Objects/listobject.c:831
...
(truncated for your sanity)
&lt;/pre&gt;
&lt;p&gt;This isn’t terribly useful: it’s full of Python interpreter stuff. Knowing the
title of the blog, you &lt;em&gt;might&lt;/em&gt; notice that the calling function is
&lt;tt class="docutils literal"&gt;PyCFunction_Call&lt;/tt&gt;, which you might start to get ideas from: but that could be
from anything. Fortunately, gdb has &lt;em&gt;amazing&lt;/em&gt; Python integration, so I can do
this instead:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
(gdb) py-bt
Traceback (most recent call first):
  &amp;lt;error reading variable: Cannot access memory at address 0xe90&amp;gt;
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/engine/base.py&amp;quot;, line 1024, in _execute_clauseelement
    distilled_params = _distill_params(multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/sql/elements.py&amp;quot;, line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/engine/base.py&amp;quot;, line 948, in execute
    return meth(self, multiparams, params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2958, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2935, in __iter__
    return self._execute_and_instances(context)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2864, in one_or_none
    ret = list(self)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 2894, in one
    ret = self.one_or_none()
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/loading.py&amp;quot;, line 250, in load_on_pk_identity
    return q.one()
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 962, in _get_impl
    return db_load_fn(self, primary_key_identity)
  File &amp;quot;/…/lib/python3.5/site-packages/sqlalchemy/orm/query.py&amp;quot;, line 887, in get
    ident, loading.load_on_pk_identity)
  File &amp;quot;/…/app.py&amp;quot;, line 47, in setup_context
...
(truncated for your sanity)
&lt;/pre&gt;
&lt;p&gt;The last frame of that backtrace is my application code, trying to run a
SQLAlchemy query. The rest of the backtrace is SQLAlchemy trying to execute it.
But the first frame is most interesting: SQLAlchemy is calling a function
&lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt;, but we don't have the source for that call.&lt;/p&gt;
&lt;p&gt;If you look at the source for &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/engine/base.py"&gt;sqlalchemy/engine/base.py&lt;/a&gt;, you’ll see that
&lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt; gets imported from &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/engine/util.py"&gt;sqlalchemy/engine/util.py&lt;/a&gt;, which has
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;71&lt;/span&gt;
&lt;span class="normal"&gt;72&lt;/span&gt;
&lt;span class="normal"&gt;73&lt;/span&gt;
&lt;span class="normal"&gt;74&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sqlalchemy.cutils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;_distill_params&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ImportError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;py_fallback&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;That is, &lt;tt class="docutils literal"&gt;_distill_params&lt;/tt&gt; comes from a C extension module, unless it’s not
importable, in which case there’s a pure Python version as a fallback.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="c-extension-modules"&gt;
&lt;h2&gt;C Extension Modules&lt;/h2&gt;
&lt;p&gt;At this point, I’ve figured out the first part of what’s gone wrong: something
is causing Python to think that the address of the _distill_params function is
0xad0, instead of whatever it’s meant to actually be.&lt;/p&gt;
&lt;p&gt;For the next part, we need to delve a little into how C extension modules work.
Here’s part of the source for &lt;a class="reference external" href="https://bitbucket.org/zzzeek/sqlalchemy/src/rel_1_2_8/lib/sqlalchemy/cextension/utils.c"&gt;sqlalchemy/cextension/utils.c&lt;/a&gt;, which provides
the &lt;tt class="docutils literal"&gt;sqlalchemy.cutils&lt;/tt&gt; module imported above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;178&lt;/span&gt;
&lt;span class="normal"&gt;179&lt;/span&gt;
&lt;span class="normal"&gt;180&lt;/span&gt;
&lt;span class="normal"&gt;181&lt;/span&gt;
&lt;span class="normal"&gt;182&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PyMethodDef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;module_methods&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;_distill_params&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distill_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;METH_VARARGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Distill an execute() parameter structure.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="cm"&gt;/* Sentinel */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;This C file gets compiled into a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries"&gt;shared library&lt;/a&gt;: on my system it’s a file
named &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;cutils.cpython-35m-x86_64-linux-gnu.so&lt;/span&gt;&lt;/tt&gt;. The CPython interpreter loads
extension modules in &lt;a class="reference external" href="https://hg.python.org/cpython/file/3.5/Python/importdl.c"&gt;Python/importdl.c&lt;/a&gt;, which uses the
&lt;tt class="docutils literal"&gt;_PyImport_FindSharedFuncptr&lt;/tt&gt; function in &lt;a class="reference external" href="https://hg.python.org/cpython/file/3.5/Python/dynload_shlib.c"&gt;Python/dynload_shlib.c&lt;/a&gt;, which
uses &lt;a class="reference external" href="http://man7.org/linux/man-pages/man3/dlopen.3.html"&gt;dlopen&lt;/a&gt; to load that &lt;tt class="docutils literal"&gt;.so&lt;/tt&gt; file into memory and get a function pointer.&lt;/p&gt;
&lt;p&gt;Most of those details weren’t important: the key part is that we have some C
code, being loaded into the process’s memory at &lt;em&gt;runtime&lt;/em&gt;. So, let’s attach GDB
to a running process, and have at look at that &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; array:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ gdb -p 1234
...
(gdb) p 'sqlalchemy/cextension/utils.c'::module_methods
$1 = {{ml_name = 0x7f66abdfde90 &amp;quot;_distill_params&amp;quot;, ml_meth = 0x7f66abdfdad0 &amp;lt;distill_params&amp;gt;,
       ml_flags = 1, ml_doc = 0x7f66abdfdef8 &amp;quot;Distill an execute() parameter structure.&amp;quot;},
      {ml_name = 0x0, ml_meth = 0x0, ml_flags = 0, ml_doc = 0x0}}
&lt;/pre&gt;
&lt;p&gt;That’s more or less what I’d expect to see: &lt;tt class="docutils literal"&gt;ml_name&lt;/tt&gt; has the Python name of
the function, and &lt;tt class="docutils literal"&gt;ml_meth&lt;/tt&gt; is a pointer to the &lt;tt class="docutils literal"&gt;distill_params&lt;/tt&gt; C function.
What happens if we try the same thing in our core dump?&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ coredumpctl gdb
...
(gdb) p 'sqlalchemy/cextension/utils.c'::module_methods
$1 = {{ml_name = 0xe90 &amp;lt;error: Cannot access memory at address 0xe90&amp;gt;, ml_meth = 0xad0,
       ml_flags = 1, ml_doc = 0xef8 &amp;lt;error: Cannot access memory at address 0xef8&amp;gt;},
      {ml_name = 0x0, ml_meth = 0x0, ml_flags = 0, ml_doc = 0x0}}
&lt;/pre&gt;
&lt;p&gt;Well. That’s not right.&lt;/p&gt;
&lt;p&gt;But wait, those hex numbers look kinda similar to the correct numbers…&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{:48b}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x7f66abdfde90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt; 11111110110011010101011110111111101111010010000&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{:48b}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xe90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;                                    111010010000&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{:48b}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x7f66abdfdad0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt; 11111110110011010101011110111111101101011010000&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{:48b}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xad0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="go"&gt;                                    101011010000&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Huh? Sometime between being a perfectly happy process chugging along, and it
segfaulting, the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct in a C extension module gets all its
numbers truncated to 12 &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[*]&lt;/a&gt; bits‽&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="time-and-space"&gt;
&lt;h2&gt;Time and Space&lt;/h2&gt;
&lt;p&gt;Thinking that the problem was something &lt;em&gt;writing&lt;/em&gt; to the memory to corrupt it, I
spent quite some time looking through &lt;a class="reference external" href="https://strace.io/"&gt;strace&lt;/a&gt; and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ltrace"&gt;ltrace&lt;/a&gt; outputs to try and
find something touching the relevant addresses in memory, but to no avail.&lt;/p&gt;
&lt;p&gt;My breakthrough came when I compared &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/proc/$PID/smaps&lt;/span&gt;&lt;/tt&gt; from a running
process and a almost-segfaulted process. To capture the latter, I attached GDB,
and then typed in &lt;tt class="docutils literal"&gt;shell cat /proc/1234/smaps &amp;gt; 1234.smaps&lt;/tt&gt; and hit enter, so
it would get run when the program had segfaulted (but before Linux forgot the
process state). I also attempted using a breakpoint, but in hindsight I don’t
think that worked. Regardless, I found the section of memory in which
the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct resided, and found this difference:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
7f26c44e2000-7f26c44e3000 rw-p 00002000 08:10 666070 /…/cutils.cpython-35m-x86_64-linux-gnu.so
Size:                  4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         4 kB
Referenced:            4 kB
Anonymous:             4 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac sd
&lt;/pre&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
7f26c44e2000-7f26c44e3000 rw-p 00002000 08:10 666070 /…/cutils.cpython-35m-x86_64-linux-gnu.so
Size:                  4 kB
Rss:                   4 kB
Pss:                   4 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         4 kB
Private_Dirty:         0 kB
Referenced:            4 kB
Anonymous:             0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
&lt;/pre&gt;
&lt;p&gt;The key difference here is that this section of memory went from having 4 kB of
&lt;tt class="docutils literal"&gt;Private_Dirty&lt;/tt&gt; data to having 4 kB of &lt;tt class="docutils literal"&gt;Private_Clean&lt;/tt&gt; data.&lt;/p&gt;
&lt;p&gt;Combined with the knowledge that the numbers inside that section of memory
seemed to be being “truncated”, I realised what was happening:
&lt;em&gt;this section of memory was being reloaded from the shared library file&lt;/em&gt;.
I’m glossing over some details here, but on disk the shared library has various
pointers to code and data within that shared library, but when it gets loaded
into a process’s memory, it’ll be loaded at some offset, and all the pointers
within the shared library need to be adjusted in order to work.&lt;/p&gt;
&lt;p&gt;So on disk, inside the &lt;tt class="docutils literal"&gt;module_methods&lt;/tt&gt; struct, the &lt;tt class="docutils literal"&gt;ml_name&lt;/tt&gt; pointer is
actually 0xe90, but when it initially gets loaded into memory, it gets adjusted
to 0x7f66abdfde90 so it’s pointing at the location where the string
&lt;tt class="docutils literal"&gt;&amp;quot;_distill_params&amp;quot;&lt;/tt&gt; got loaded into memory.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-piping-gun"&gt;
&lt;h2&gt;The Piping Gun&lt;/h2&gt;
&lt;p&gt;Knowing that I was looking for something which was causing the shared library
file to be reloaded from disk, I remembered that I had a bug in my automation
which was unnecessarily reinstalling all Python libraries using &lt;a class="reference external" href="https://pip.pypa.io/"&gt;pip&lt;/a&gt;.
I didn’t think it was a huge problem: the downloads are cached locally, and
reinstalling a package with the same version should just be a no-op, right?&lt;/p&gt;
&lt;p&gt;Turns out not so much. When pip installs a wheel, it &lt;a class="reference external" href="https://github.com/pypa/pip/blob/10.0.1/src/pip/_internal/wheel.py#L292"&gt;replaces all the files
using shutil.copyfile&lt;/a&gt;. This opens the file for writing, truncates it, and
then copies the content in.&lt;/p&gt;
&lt;p&gt;Because this is changing the &lt;em&gt;same file&lt;/em&gt; which is also memory mapped into a
running process, Linux notices that section of data has been re-written (even
with exactly the same bytes), and invalidates all unwritten changes in active
processes (this is what makes the memory mapping go from &lt;tt class="docutils literal"&gt;Private_Dirty&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;Private_Clean&lt;/tt&gt;), thus undoing the offset adjustments the process made when
loading the shared library.&lt;/p&gt;
&lt;p&gt;Honestly, I’m not sure where the bug is here. Probably after loading a shared
library, Python should unassociate the data section from the file it was loaded
from: though I’m not sure if the C functions to do that even exist.
I’d also say pip should do atomic file replacements to avoid the problem, but
this is more of a bandaid than an actual fix.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="my-workaround"&gt;
&lt;h2&gt;My Workaround&lt;/h2&gt;
&lt;p&gt;After figuring out what was happening, I had a realisation that this problem
could be mitigated in the application, with a pure Python solution.&lt;/p&gt;
&lt;p&gt;Instead of trying to unassociate the memory mapping, I can atomically replace
the file it loaded with a copy of itself, thus making the memory mapping in the
process point at data which no longer exists in the filesystem.&lt;/p&gt;
&lt;p&gt;So here is a Python module which you can import, which will periodically find C
extension modules loaded into the Python process and perform shenanigans on them
to make them resilient against this failure mode. This potentially costs memory,
but I doubt it’ll ever be an amount you care about.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Copyright 2018 Google LLC&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;
&lt;span class="c1"&gt;# you may not use this file except in compliance with the License.&lt;/span&gt;
&lt;span class="c1"&gt;# You may obtain a copy of the License at&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#     https://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Unless required by applicable law or agreed to in writing, software&lt;/span&gt;
&lt;span class="c1"&gt;# distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;
&lt;span class="c1"&gt;# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;
&lt;span class="c1"&gt;# See the License for the specific language governing permissions and&lt;/span&gt;
&lt;span class="c1"&gt;# limitations under the License.&lt;/span&gt;
&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="sd"&gt;Module sopin implements &amp;quot;pinning&amp;quot; of shared libraries.&lt;/span&gt;

&lt;span class="sd"&gt;This is a workaround for the problem where rewriting a Python C extension module&lt;/span&gt;
&lt;span class="sd"&gt;on disk corrupts the state of any Python process which has previously imported&lt;/span&gt;
&lt;span class="sd"&gt;it: for more details, see&lt;/span&gt;
&lt;span class="sd"&gt;https://blog.flowblok.id.au/2018-06/python-c-extension-module-corruption.html&lt;/span&gt;

&lt;span class="sd"&gt;Usage:&lt;/span&gt;
&lt;span class="sd"&gt;    import sopin&lt;/span&gt;
&lt;span class="sd"&gt;    sopin.start()&lt;/span&gt;
&lt;span class="sd"&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;shutil&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tempfile&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;threading&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;warnings&lt;/span&gt;

&lt;span class="n"&gt;SUFFIXES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.so&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;

&lt;span class="c1"&gt;# Modules previously pinned.&lt;/span&gt;
&lt;span class="n"&gt;_pinned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Modules which we couldn&amp;#39;t pin (so shouldn&amp;#39;t reattempt).&lt;/span&gt;
&lt;span class="n"&gt;_blacklisted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_pinned&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_blacklisted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Atomically create a unique tempfile.&lt;/span&gt;
        &lt;span class="c1"&gt;# Don&amp;#39;t delete, because we&amp;#39;ll move it over the old file.&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tempfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NamedTemporaryFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Copy the content+permissions of the old file into our new file.&lt;/span&gt;
            &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Move the new file over the old file.&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;warnings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;could not pin &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_blacklisted&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;_pinned&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;pin_all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__file__&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;AttributeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SUFFIXES&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period_seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pin_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[*]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Twitter followers may note this is not the 13 or 14 that I tweeted: I was
actually debugging &lt;tt class="docutils literal"&gt;processors.c&lt;/tt&gt; at that point, so these details are
slightly different.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="c"/><category term="debugging"/><category term="linux"/><category term="python"/><category term="software"/></entry><entry><title>Synchronized Music on micro:bits</title><link href="2018-02/synchronized-music-on-microbits.html" rel="alternate"/><published>2018-02-24T22:02:00+11:00</published><updated>2018-02-24T22:02:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2018-02-24:2018-02/synchronized-music-on-microbits.html</id><summary type="html">&lt;p class="first last"&gt;How I made a mesh network of BBC microbits play music.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It’s been a while since I last posted. So, uh, sorry about that.
I’m going to make it up to y’all with this post. It’s in two parts: the video
demonstrates the project in action, and the rest of this blog post details all
the technical details. I’d recommend starting with the video to see the cool
demo, and if you make it to the end, read the rest of the blog post.&lt;/p&gt;
&lt;p class="ratio-16-9"&gt;&lt;iframe src="https://www.youtube.com/embed/KhXfi021K3c"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;For many years now, I’ve tutored at the &lt;a class="reference external" href="http://ncss.edu.au/summer-school"&gt;National Computer Science School&lt;/a&gt;,
which teaches computer science and programming to Australian high school kids.
The school is divided into two parts: a web stream, which builds a website using
Python, and an embedded stream, which (nowadays) uses &lt;a class="reference external" href="https://micropython.org/"&gt;MicroPython&lt;/a&gt; on the
&lt;a class="reference external" href="https://microbit.org/"&gt;micro:bit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Last year I tutored the web stream, and so it was only during the closing
ceremony while watching videos demonstrating all the embedded students’ projects
that I started wondering about the potential of the micro:bit, and whether I
could build a mesh network so they could play music synchronized across a large
area.
So when work started getting quieter over the Christmas / New Year break, and
this year’s NCSS started approaching, I decided to buy a couple of micro:bits
and start hacking.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="breaking-it-down"&gt;
&lt;h2&gt;Breaking it down&lt;/h2&gt;
&lt;p&gt;I broke down the project into a number of distinct components:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;network protocol&lt;/strong&gt;: for sending structured data as micro:bit radio packets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;time sync&lt;/strong&gt;: getting all micro:bits on the network to share a common time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;triggers&lt;/strong&gt;: getting all micro:bits to do an action at a given time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;music&lt;/strong&gt;: making the micro:bits play notes on a piezo buzzer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Writing micro:bits all the time is too hard, so I’m going to refer to them from
here on as &lt;em&gt;nodes&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="time-sync"&gt;
&lt;h2&gt;Time Sync&lt;/h2&gt;
&lt;p&gt;Time is hard. It’s a little easier in this application, because we don’t care
what the &lt;em&gt;actual&lt;/em&gt; wall-clock time is, just that all the nodes have the same
time.&lt;/p&gt;
&lt;p&gt;The first step in figuring out how to synchronize the nodes was to head off to
Wikipedia and do some reading on the way I already knew computers synchronized
time, &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Network_Time_Protocol"&gt;Network Time Protocol (NTP)&lt;/a&gt;. After very little reading, it seemed
fairly obvious that NTP was far too complicated for this application: I didn’t
need to deal with leap seconds, and polling multiple servers and doing filtering
on the answers seemed like too much effort.&lt;/p&gt;
&lt;p&gt;Fortunately it took little effort to find the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Precision_Time_Protocol"&gt;Precision Time Protocol (PTP)&lt;/a&gt;.
PTP is a really nice simple protocol, though the Wikipedia page does have much
more detail than is really needed. It’s designed to work on multiple network
segments (hey, that sounds &lt;em&gt;kinda&lt;/em&gt; like the network topology we have), and on
each segment, there’s a master which all the other nodes synchronize to.
Each node periodically pings (called &lt;tt class="docutils literal"&gt;Delay_Req&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Delay_Resp&lt;/tt&gt;, but I
like ping better) the master to approximate their Round Trip Time (RTT), and the
master periodically sends out a &lt;tt class="docutils literal"&gt;Sync&lt;/tt&gt; message with its current time.
Add in some maths, and hey presto, you have synchronized time.&lt;/p&gt;
&lt;p&gt;In order to adapt the algorithm for a mesh network, there were a few changes I
needed to make. PTP has a &lt;em&gt;best master clock algorithm&lt;/em&gt; for selecting the
master, but since my nodes might drop in and out of network connectivity all the
time, my system needed to continuously adapt which nodes were sending timestamps
to synchronize to, based on which nodes could talk to them. I decided to term
those nodes &lt;em&gt;thought leaders&lt;/em&gt; (partially because I really disliked the &amp;quot;master&amp;quot;
terminology, partially because I like being stupid 😛).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="network-protocol"&gt;
&lt;h2&gt;Network Protocol&lt;/h2&gt;
&lt;p&gt;All nodes in my network are assigned an &lt;em&gt;id&lt;/em&gt; and &lt;em&gt;level&lt;/em&gt;. Given the scale of my
network, both of these are unsigned bytes (i.e. 0–255). The id is chosen
randomly at startup. Astute readers will note this falls victim to the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Birthday_problem"&gt;Birthday
Problem&lt;/a&gt;, so after 20 nodes there’s a 50% chance two nodes will share an id.
This turns out not to actually be a problem in practice: the presence of
duplicate ids on the network is only an issue if another node can see both of
them (&lt;em&gt;cough&lt;/em&gt; and the nodes are sufficiently unreliable that they restart
occasionally anyway &lt;em&gt;cough&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Levels:&lt;/strong&gt;
One node is designated as the &amp;quot;root&amp;quot; (by pressing buttons on the micro:bit),
and is assigned level 0. All other nodes start with level 31 (chosen because it
can be easily displayed in binary on the micro:bit LEDs).
The level is an estimate of how close the node is to the root, and nodes will
only synchronize with nodes closer to the root than it.
As nodes do so, they decrease their own level (but never more than one more than
the level of the node it’s synchronizing with).
If the node hasn’t managed to synchronize for a few seconds, its level increases
by one, ensuring that nodes always choose good nodes to synchronize with.&lt;/p&gt;
&lt;p&gt;Here’s an example network:&lt;/p&gt;
&lt;object data="static/images/tlt-levels.dot.svg" type="image/svg+xml"&gt;Diagram showing several nodes with levels, with arrows indicating
which nodes synchronize from each other.&lt;/object&gt;
&lt;p&gt;The level 1 and 4 nodes can both synchronize from the level 0 node (since it’s
less than 1 and 4). The level 4 doesn’t synchronize from the level 2 node in
this example, just because it can’t see it over the radio network (but it would
if it could). As the level 4 node becomes more in sync, it will reduce to a
level 3 node, then a level 2 node, and finally a level 1 node—at which point it
would stop synchronizing from the other level 1 node.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pings:&lt;/strong&gt;
All nodes periodically send &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; messages on the network, which
contain:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_node&lt;/tt&gt;: the id of the node requesting the ping&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ping_id&lt;/tt&gt;: a unique (16 bit) id for this ping request&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;votes&lt;/tt&gt;: a list of node ids this node would like to sync from.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;votes&lt;/tt&gt; list is just the ids of nodes which the node has seen &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;
messages from, where &lt;tt class="docutils literal"&gt;req_level&lt;/tt&gt; was less than its own: this may be out-of-date,
but if so, the node can just ignore the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message later (and it won't send
that node id next time).&lt;/p&gt;
&lt;p&gt;Any node which sees a &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; message from another node responds back
with a &lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt; message, which contains:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_node&lt;/tt&gt;: copied from the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;resp_node&lt;/tt&gt;: the id of the node responding to the ping&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;resp_level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;ping_id&lt;/tt&gt;: copied from the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;req_end_timestamp&lt;/tt&gt;: the time at which the &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; was received,
according to the responding node’s clock&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Syncs:&lt;/strong&gt;
Periodically, nodes use the &lt;tt class="docutils literal"&gt;votes&lt;/tt&gt; it has seen from other nodes to see if
it’s the best node to send a &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message. If it is, it does, which
contains:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;node&lt;/tt&gt;: the node sending the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;level&lt;/tt&gt;: the node’s level&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt;: the node’s timestamp at time of sending&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;triggers&lt;/tt&gt;: a list of trigger ids and timestamps (sent as deltas relative to
&lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a node receives a &lt;tt class="docutils literal"&gt;PING&lt;/tt&gt; from a node with a lower level, it uses the same
formula from PTP to adjust its own clock. Assuming:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T_1\)&lt;/span&gt; is the time the node sent a &lt;tt class="docutils literal"&gt;PING_REQUEST&lt;/tt&gt; to the node sending a
&lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T'_1\)&lt;/span&gt; is the &lt;tt class="docutils literal"&gt;req_end_timestamp&lt;/tt&gt; from the corresponding
&lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T_2\)&lt;/span&gt; is the &lt;tt class="docutils literal"&gt;timestamp&lt;/tt&gt; in the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message&lt;/li&gt;
&lt;li&gt;&lt;span class="math"&gt;\(T'_2\)&lt;/span&gt; is the time the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message was received&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the offset &lt;span class="math"&gt;\(õ\)&lt;/span&gt; between the two nodes’ clocks is:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
õ = \frac{T'_1 - T_1 - T'_2 + T_2}{2}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;In practice, I use &lt;a class="reference external" href="//en.wikipedia.org/wiki/Exponential_smoothing"&gt;exponential smoothing&lt;/a&gt; on this process, which reduces the
impact of outliers.
Earlier I mentioned that nodes reduce their level as they become &amp;quot;more in sync&amp;quot;:
nodes actually reduce their level every time they do an offset adjustment where
the adjustment is small enough (&lt;span class="math"&gt;\(|õ| &amp;lt; \varepsilon \)&lt;/span&gt;, where &lt;span class="math"&gt;\(\varepsilon  =\)&lt;/span&gt; 10ms).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; Originally, I intended to use MicroPython on the micro:bit to
implement this project. However, after just trying to implement something to
decode the network protocol messages into in-memory data structures, I ran out
of memory using MicroPython. I briefly toyed with the idea of having some code
to &lt;em&gt;generate&lt;/em&gt; the MicroPython code (so I could write sensible high-level code,
and have it spit out stupid code), but that was far too much effort, so I
switched to using the &lt;a class="reference external" href="https://lancaster-university.github.io/microbit-docs/"&gt;micro:bit C++ API&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="simulation"&gt;
&lt;h2&gt;Simulation&lt;/h2&gt;
&lt;p&gt;There’s a lot of detail in the previous section, without much explanation of why
I designed it that way. This section isn’t going to help much, but it will
explain the process a little better.
When I started, I realised that attempting to develop the time synchronization
protocol on the actual micro:bits would be very slow and tedious, so I started
by writing a Python simulation.&lt;/p&gt;
&lt;p&gt;The simulation let me quickly iterate on the design of the protocol, testing
various number of nodes with different network connection topologies, and meant
that when the network didn’t converge to a common time, I could debug the
algorithm by adding things into my Python code, rather than messing around with
micro:bit serial cables.&lt;/p&gt;
&lt;p&gt;Unfortunately, I didn’t save all the diagrams along the way, but here’s the last
one I generated, which shows 13 nodes all converging at different times to the
same time (within an accuracy of 20ms).&lt;/p&gt;
&lt;object data="static/images/tlt-simulation.svg" type="image/svg+xml"&gt;A diagram showing latencies of various nodes, and lots of coloured
circles. It’s not entirely clear what it represents.&lt;/object&gt;
&lt;p&gt;The 0.1ms line in the diagram is a lie: it should be 0, but that doesn’t work so
well with the logarithmic y-axis.
The coloured dots on the lines indicate the &lt;em&gt;level&lt;/em&gt; of the node at that point in
time. Well. That’s what my notes claim, at least.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="triggers"&gt;
&lt;h2&gt;Triggers&lt;/h2&gt;
&lt;p&gt;So far, we’ve managed to get a mesh network of nodes synchronized to the same
time, but we haven’t managed to get them to do anything &lt;em&gt;useful&lt;/em&gt; yet.
This is where &lt;em&gt;triggers&lt;/em&gt; come in.&lt;/p&gt;
&lt;p&gt;A trigger is just an event with an id (saying what you’d like to happen), and a
timestamp in the future (when you’d like it to happen).
Every &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; message contains a list of triggers: the root node can schedule
triggers, and every node in the network propagates on that list (while the
triggers’ timestamps are still in the future) to all the other nodes.&lt;/p&gt;
&lt;p&gt;In order to get triggers into the network, the root node listens on the serial
port for a six-character hex number: the first two characters are a trigger id,
and the remaining four are in how many milliseconds from now to schedule the
trigger (for example, &lt;tt class="docutils literal"&gt;2a0fa0&lt;/tt&gt; schedules trigger #42 to happen in 4 seconds).&lt;/p&gt;
&lt;p&gt;Just before a trigger is scheduled to happen, the node checks if it’s reasonably
in sync (the last adjustment &lt;span class="math"&gt;\(õ &amp;lt; \varepsilon \)&lt;/span&gt;): if not, it skips the trigger (on the
assumption that not playing some music is better than playing out of time).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="music"&gt;
&lt;h2&gt;Music&lt;/h2&gt;
&lt;p&gt;When a trigger fires, I want to play a &lt;em&gt;segment&lt;/em&gt; (roughly five seconds long)
of music. This means that if the node gets out of sync, it’ll only be silent for
five seconds. Also, if the time synchronization is off by a noticable amount,
it’ll only sound weird for five seconds.&lt;/p&gt;
&lt;p&gt;Since I want the micro:bits to play music, I need some way to input music into
the program. I started with &lt;a class="reference external" href="http://lilypond.org/"&gt;Lilypond&lt;/a&gt;, an excellent music engraving program
that I've used before (it’s kinda like LaTeX for music). As an example for this
section, I’ve written Beethoven’s &lt;em&gt;Ode to Joy&lt;/em&gt; in Lilypond:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\version&lt;/span&gt; &amp;quot;2.18.2&amp;quot;

&lt;span class="k"&gt;\include&lt;/span&gt; &amp;quot;articulate.ly&amp;quot;

music = &lt;span class="k"&gt;\new&lt;/span&gt; Staff &lt;span class="k"&gt;\relative&lt;/span&gt; c&amp;#39;&amp;#39; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\tempo&lt;/span&gt; 4=160
  &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
  &lt;span class="k"&gt;\key&lt;/span&gt; c &lt;span class="k"&gt;\major&lt;/span&gt;
  e4 e f g |
  g f e d |
  c c d e |
  e4. d8 d2 |
  &lt;span class="k"&gt;\bar&lt;/span&gt; &amp;quot;|.&amp;quot;
&lt;span class="nb"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;\score&lt;/span&gt; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\articulate&lt;/span&gt; &lt;span class="k"&gt;\music&lt;/span&gt;
  &lt;span class="k"&gt;\midi&lt;/span&gt; &lt;span class="nb"&gt;{}&lt;/span&gt;
&lt;span class="nb"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;\score&lt;/span&gt; &lt;span class="nb"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;\music&lt;/span&gt;
  &lt;span class="k"&gt;\layout&lt;/span&gt; &lt;span class="nb"&gt;{}&lt;/span&gt;
&lt;span class="nb"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This Lilypond file results in this PDF output:&lt;/p&gt;
&lt;img alt="Musical score for Ode to Joy." src="static/images/tlt-ode-to-joy.png" /&gt;
&lt;p&gt;It also generates a MIDI file (in case you’re wondering: the &lt;tt class="docutils literal"&gt;\articulate&lt;/tt&gt;
command just makes the MIDI output sound a lot nicer), which I can conveniently
read using in Python using the &lt;a class="reference external" href="https://mido.readthedocs.io/"&gt;Mido library&lt;/a&gt;, and thus generate some C++
arrays representing the data. These look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_event_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_segment_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_event_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_segment_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n_segments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_event_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1276&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1276&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1911&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1911&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;328&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1517&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;492&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;164&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;period_us&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1703&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;656&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_segment_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;start_event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;start_event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;music_data_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;song_data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_events&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_events&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;n_segments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_song_segments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The resulting &lt;tt class="docutils literal"&gt;song_data&lt;/tt&gt; has two segments, which index into the events array.
The intention there was that if music has repeating sections, the segments could
reference the same events, saving program space: as it turned out, I had plenty
of program space and finding repeating sections is hard, so this was a bit of a
premature optimization.&lt;/p&gt;
&lt;p&gt;Then all that remained was to hook up trigger &lt;em&gt;n&lt;/em&gt; to play segment &lt;em&gt;n&lt;/em&gt; of the
music, using &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pulse-width_modulation"&gt;PWM&lt;/a&gt; on the piezo buzzer I’d attached to pin 0 of my micro:bits.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reliability"&gt;
&lt;h2&gt;Reliability&lt;/h2&gt;
&lt;p&gt;Having written all this code, I found that on the micro:bits, the program would
occasionally get stuck: either the CPU would freeze up entirely, or the radio
module would get into a bad state and not receive / transmit messages, or other
weirdness would happen.&lt;/p&gt;
&lt;p&gt;Instead of trying to fix these, I used the watchdog timer of the nRF51 (the
CPU which runs the program): this lets me configure up to 8 &amp;quot;reload requests&amp;quot;,
and a period. If my application code doesn’t trigger all the enabled reload
requests within the period, the CPU will reset itself.&lt;/p&gt;
&lt;p&gt;I configured the period to two seconds, and set up two reload requests:
one in the main loop that I use for display (as a &amp;quot;the program has just stopped&amp;quot;
check), and one inside the time sync logic, which triggers the reload request
iff the time has synced (again, &lt;span class="math"&gt;\(|õ| &amp;lt; \varepsilon \)&lt;/span&gt;) within the last 30 seconds.
This second check is really useful: if the network gets into a weird state where
none of the nodes can sync, the nodes will all be obviously displaying this by
resetting themselves every 30 seconds.&lt;/p&gt;
&lt;p&gt;The other huge reliability concern was the root node in the network: if that
dies, all the other nodes lose their sync. As a way to reduce this impact, I
made it so any node could become the root node (by holding down some buttons):
that way, if I noticed the root node die, I could promote a standby node, and
since it already had almost the same time, the other nodes wouldn’t lose their
synchronization.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="epilogue"&gt;
&lt;h2&gt;Epilogue&lt;/h2&gt;
&lt;p&gt;So while this project was really cool and fun to build, I didn’t really have a
use for it until NCSS started. We run a programming competition at the camp,
which this year was Hitchhiker’s Guide to the Galaxy themed.
I did the theming for the scoreboard, and had a little intro animation (the
words “Don’t Panic” zooming in over a background of starts), and had the
micro:bits play &lt;em&gt;Journey of a Sorcerer&lt;/em&gt; (the theme music for HG2G) during that.&lt;/p&gt;
&lt;p&gt;I think it went reasonably well, though the big unforeseen issue was that the
piezo buzzers on most of the micro:bits weren’t as loud as the ones I had been
testing with, so the effect was somewhat, uh, muted. 😛&lt;/p&gt;
&lt;p&gt;If I was to do more on this project, there’s a few things I would improve:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The time sync algorithm doesn’t &lt;em&gt;actually&lt;/em&gt; need the &lt;tt class="docutils literal"&gt;SYNC&lt;/tt&gt; packets: it could
be done as part of the &lt;tt class="docutils literal"&gt;PING_RESPONSE&lt;/tt&gt; packets, which would simplify it
somewhat.&lt;/li&gt;
&lt;li&gt;Send the music data across the network: compiling and flashing all the nodes
every time I want to play a different song is very tedious.&lt;/li&gt;
&lt;li&gt;Relatedly, move the song data to flash storage: that way the music data from
the network would be persisted across reboots.&lt;/li&gt;
&lt;li&gt;Use something other than piezo buzzers. I was watching the &lt;a class="reference external" href="https://youtu.be/IvUU8joBb1Q"&gt;Wintergatan marble
machine&lt;/a&gt; (well, actually the videos for the construction of its
replacement), and it’s made me really want to do something where the micro:bit
triggers physical actions to make noise (maybe plucking a violin string)?&lt;/li&gt;
&lt;li&gt;Support multiple channels of MIDI. There’s actually very little stopping the
micro:bits from playing polyphonic sounds: if I had compiled the nodes with
different song data (but matching segment lengths), they could already do it
pretty easily.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; is in &lt;a class="reference external" href="http://hg.flowblok.id.au/thought-leadering-time"&gt;this Mercurial repository&lt;/a&gt;.
It’s open source, under the Apache 2.0 license.&lt;/p&gt;
&lt;/div&gt;
&lt;script type='text/javascript'&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="embedded"/><category term="microbit"/><category term="music"/><category term="ncss"/><category term="software"/></entry><entry><title>Finding Good Answers Quickly</title><link href="2015-10/finding-good-answers-quickly.html" rel="alternate"/><published>2015-10-08T00:05:00+11:00</published><updated>2015-10-08T00:05:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2015-10-08:2015-10/finding-good-answers-quickly.html</id><summary type="html">&lt;p class="first last"&gt;An introduction to Hufflepuff, a beam search library for Python.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I recently had a difficult problem to solve: assigning 94 students into teams of
four. The first reason it’s difficult is that you end up with 23.5 teams, and
since we can’t chop students in half, we have to go with 22 teams of four, and
two teams of three.&lt;/p&gt;
&lt;p&gt;But we’re not done yet: the competition should be fair, in the sense that teams
should be roughly equally matched: we don’t want to have one really good team
that easily wins. So we get some estimations of students’ abilities—
let’s say they get a score of 1 (worst), 2 or 3 (best).
Then we can define a team’s strength as the sum of the students’ ability scores,
and we want teams strengths to be roughly equal.&lt;/p&gt;
&lt;p&gt;But we might even still have further requirements: maybe we want students to not
be in teams with students they’ve already worked with (so they meet more
people), or not put students who have done the competition together (as
experience isn’t quite the same thing as ability).&lt;/p&gt;
&lt;p&gt;Now this problem isn’t difficult because it’s hard to solve, but because it’s
difficult to figure out exactly what the requirements are.
The way I’d like to solve this problem is by specifying a small part of the
problem, having something give me a possible solution, and then iterating to
further define the problem (to avoid the bad cases I see).&lt;/p&gt;
&lt;p&gt;I’ve had to solve a few problems of this nature, and after writing &lt;em&gt;almost&lt;/em&gt; the
same Python script each time, I refactored out the common code into
&lt;a class="reference external" href="http://hg.flowblok.id.au/hufflepuff"&gt;Hufflepuff&lt;/a&gt;, an open-source project I released today.
Like any good UNIX tool, it’s not especially interesting or complex itself,
but lets you glue together other simple tools to produce something useful.&lt;/p&gt;
&lt;p&gt;Let’s see how it works with a cut-down version of the example above.
First, create a working directory, install a Python virtualenv and install
Hufflepuff from pypi, and test it works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;~/tmp/teams&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/tmp/teams
$&lt;span class="w"&gt; &lt;/span&gt;virtualenv&lt;span class="w"&gt; &lt;/span&gt;.
$&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;bin/activate
&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;hufflepuff
&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;hufflepuff&lt;span class="w"&gt; &lt;/span&gt;-h
usage:&lt;span class="w"&gt; &lt;/span&gt;hufflepuff&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;-h&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;CONFIG_FILE&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--initial-binary&lt;span class="w"&gt; &lt;/span&gt;INITIAL_BINARY&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--initial-file&lt;span class="w"&gt; &lt;/span&gt;INITIAL_FILE&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--mutate-binary&lt;span class="w"&gt; &lt;/span&gt;MUTATE_BINARY&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--score-binary&lt;span class="w"&gt; &lt;/span&gt;SCORE_BINARY&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--beam-width&lt;span class="w"&gt; &lt;/span&gt;BEAM_WIDTH&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--expand-mantissa&lt;span class="w"&gt; &lt;/span&gt;EXPAND_MANTISSA&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--expand-multiplier&lt;span class="w"&gt; &lt;/span&gt;EXPAND_MULTIPLIER&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--target&lt;span class="w"&gt; &lt;/span&gt;TARGET&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--num-iterations&lt;span class="w"&gt; &lt;/span&gt;NUM_ITERATIONS&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;--parallelism&lt;span class="w"&gt; &lt;/span&gt;PARALLELISM&lt;span class="o"&gt;]&lt;/span&gt;
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We’ll also need some test data to play with, and for simplicity, we’ll just have
a few students:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python
&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;import&lt;span class="w"&gt; &lt;/span&gt;json,&lt;span class="w"&gt; &lt;/span&gt;random
&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;open&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;f:
...&lt;span class="w"&gt;   &lt;/span&gt;json.dump&lt;span class="o"&gt;([&lt;/span&gt;
...&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
...&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;name,
...&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;skill&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;random.randint&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;,
...&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;experience&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;random.random&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.3,
...&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
...&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ABCDEFGHIJKLMN&amp;#39;&lt;/span&gt;
...&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="o"&gt;)&lt;/span&gt;
...
&amp;gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;Ctrl-D&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should now have a &lt;tt class="docutils literal"&gt;students.json&lt;/tt&gt; file with some students, each with a
unique name, skill level, and experience indicating whether they’ve done the
competition before or not.&lt;/p&gt;
&lt;p&gt;Now we need to provide three things to Hufflepuff: an initial assignment of
students to teams, a program to mutate those teams, and a program to score a
team assignment.&lt;/p&gt;
&lt;p&gt;For the initial assignment, we need to specify the team sizes, and provide a
valid assignment: so let’s just put the students in order. Here’s
&lt;tt class="docutils literal"&gt;initial.py&lt;/tt&gt;, which goes into your working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="c1"&gt;# For 14 students, two teams of four, and two of three.&lt;/span&gt;
&lt;span class="n"&gt;team_sizes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_sizes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_size&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team_size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_sizes&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now when you run it, you should get the following output.
It should be fairly obvious: the first team has four people (A, B, C, and D).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;initial.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next thing to provide to Hufflepuff is a way of mutating the state.
In this example, the easiest thing to do is to swap a pair of students in
different teams. We need to be carefuly to only produce valid states
(every student should be in exactly one team), and ideally we shouldn’t
duplicate states— so since teams are a set of students, let’s keep them in
sorted order within the team.&lt;/p&gt;
&lt;p&gt;Here’s &lt;cite&gt;mutate.py&lt;/cite&gt;, also to be placed in the working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# Read lines from stdin (ending cleanly on EOF):&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Swap between 1-3 pairs.&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="c1"&gt;# Select two teams to swap between.&lt;/span&gt;
        &lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;team_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Choose a person from each team.&lt;/span&gt;
        &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;team_b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Swap them.&lt;/span&gt;
        &lt;span class="n"&gt;team_a&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_a&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;team_b&lt;/span&gt;&lt;span class="p"&gt;[:]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;person_a&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;person_b&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team_b&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be able to run it as below.
The first JSON list is input, the second is output, and it should continue
accepting input until you send EOF (by pressing Ctrl-D).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;mutate.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You’ll have noticed that it swaps a few pairs of people around.
The initial state will be pretty bad, so we want to make a lot of swaps, to
quickly head towards the better state.
Towards the end of the search, lots of swaps will usually be bad,
and we want single swaps to settle on the local optimum.
There are more sophisticated things you could do, but I’ve found this is easy
and works well enough in practice.&lt;/p&gt;
&lt;p&gt;The last thing we need to provide is some way of scoring the state—
nicely congregating all your experimental decisions of what to optimise into a
simple problem: giving a score for a team assignment.&lt;/p&gt;
&lt;p&gt;In this case, it’s easiest to specify the score as a “cost”, and say how bad an
assignment is. We’ll inflict penalties for teams with more than one experienced
student, and for the difference between a team’s strength and the average team
strength. Here’s &lt;tt class="docutils literal"&gt;score.py&lt;/tt&gt;, also to be placed in the working directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;students.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;students_by_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;experience_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;penalty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;n_experienced&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;experience&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n_experienced&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;penalty&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;skill_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;strengths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;skill&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;average_strength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strengths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strengths&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;average_strength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;strength&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;strengths&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Replace students&amp;#39; names with their full info.&lt;/span&gt;
    &lt;span class="n"&gt;teams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;students_by_name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;team&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;teams&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;experience_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;skill_penalty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;teams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run it, and provide it the initial team JSON, it should print out some
score (which will vary based on your &lt;tt class="docutils literal"&gt;students.json&lt;/tt&gt; file):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;score.py
&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="m"&gt;104&lt;/span&gt;.0
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A side note: it’s handy to put a “debug mode” into the scoring program, so you
can give it a solution, and quickly see how well it does on each of the
conditions. This can be useful for distinguishing between multiple
equally-scoring solutions, and also for telling if the search found the optimum.&lt;/p&gt;
&lt;p&gt;Finally, we’ll write a &lt;tt class="docutils literal"&gt;search.cfg&lt;/tt&gt; file to specify what we want Hufflepuff to
do (we could also just pass the parameters as command line args, but this is
nicer):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="na"&gt;initial-binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python initial.py&lt;/span&gt;
&lt;span class="na"&gt;mutate-binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python mutate.py&lt;/span&gt;
&lt;span class="na"&gt;score-binary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python score.py&lt;/span&gt;
&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, all we need to do is to run Hufflepuff, which will take all those things,
and run a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Beam_search"&gt;beam search&lt;/a&gt;. As it runs, it prints out the scores of the items in
it’s current search beam to stderr, and finally prints out &lt;tt class="docutils literal"&gt;[score, state]&lt;/tt&gt;
JSON-encoded lists to stdout of the best states it ever saw.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;teams&lt;span class="o"&gt;)&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;hufflepuff&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;search.cfg
...
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.0,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;C&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;L&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;N&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;I&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;J&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;K&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;D&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;F&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;G&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;B&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;E&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, it found a bunch of possible solutions, all of which had score
2.0. I can then manually inspect those states, determine if there’s any major
problems with them, and refine the scoring program if necessary.&lt;/p&gt;
&lt;p&gt;A cute little trick is that you can take those states (without the score), put
them into &lt;tt class="docutils literal"&gt;best.json&lt;/tt&gt;, and then run
&lt;tt class="docutils literal"&gt;hufflepuff &lt;span class="pre"&gt;-c&lt;/span&gt; search.cfg &lt;span class="pre"&gt;--initial-binary='cat&lt;/span&gt; &lt;span class="pre"&gt;$initial-file'&lt;/span&gt;
&lt;span class="pre"&gt;--initial-file=best.json&lt;/span&gt;&lt;/tt&gt;
to start the search from those states (and you can make it less ugly by defining
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;initial-binary&lt;/span&gt;&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;search.cfg&lt;/tt&gt; since that’s the default).&lt;/p&gt;
&lt;p&gt;Pull requests, &lt;a class="reference external" href="http://hg.flowblok.id.au/hufflepuff/issues"&gt;bugs and feature requests&lt;/a&gt; are welcome, and I’m also happy to
answer questions in the comments below.
And if case you were wondering, &lt;a class="reference external" href="https://youtu.be/5NANaYemJdg?t=53s"&gt;here’s the inspiration for the name&lt;/a&gt;.&lt;/p&gt;
</content><category term="software"/></entry><entry><title>Solving Problems Badly</title><link href="2015-10/solving-problems-badly.html" rel="alternate"/><published>2015-10-03T14:02:00+10:00</published><updated>2015-10-03T14:02:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2015-10-03:2015-10/solving-problems-badly.html</id><summary type="html">&lt;p class="first last"&gt;A story from the NCSS Challenge of how sometimes the simple, “terrible” option is best.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Perfect is the enemy of good.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have an interesting story to tell today, and I’m sure the title of this post
probably surprised you. Well, it’s not &lt;a class="reference external" href="http://writebadlywell.blogspot.com.au/"&gt;satire&lt;/a&gt; or &lt;a class="reference external" href="http://thedailywtf.com/"&gt;schadenfreude&lt;/a&gt;, but
actually serious advice.
This is the story of some code I wrote (back in 2013) to solve a problem, where
I deliberately chose a “bad” solution.&lt;/p&gt;
&lt;p&gt;One of the things I’ve done for many years now is tutor high school students to
learn programming as part of the &lt;a class="reference external" href="https://groklearning.com/challenge/"&gt;NCSS Challenge&lt;/a&gt;. Each week, for five weeks,
students are given a set of problems to solve, accompanied by notes explaining
all the Python features and concepts they need to solve them.
Many high school computing teachers sign up their class (sometimes using it as
an assessment task), but many students also sign up themselves.
In addition to the notes, students can also chat and discuss the problems (but
not share code) on forums, or seek help privately from our volunteer tutors.&lt;/p&gt;
&lt;p&gt;The challenge uses &lt;a class="reference external" href="https://www.discourse.org/"&gt;Discourse&lt;/a&gt; for the forums, which works well, but also for
the private messaging (with some &lt;a class="reference external" href="https://github.com/groklearning/discourse-public"&gt;custom modifications&lt;/a&gt;), which works less
well. There are two main problems, which the following sequence illustrates:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Student 1 sends a message (sending a notification to all tutors).&lt;/li&gt;
&lt;li&gt;Tutor A starts writing a response.&lt;/li&gt;
&lt;li&gt;Tutor B starts writing a response&lt;/li&gt;
&lt;li&gt;Tutor A posts their response.&lt;/li&gt;
&lt;li&gt;Tutor B either doesn’t notice (meaning the student gets two responses,
which might attempt to solve the problem in different ways), or does notice
and is annoyed that their time has been wasted.&lt;/li&gt;
&lt;li&gt;Tutor A goes back to the notifications, and starts clicking through the
unread posts, but they’ve all been answered by other tutors.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To solve the problem of multiple tutors answering the same post, we set up an
IRC channel that tutors joined, so they could announce which threads they were
going to work on. Various tutors wrote IRC bots to automate bits of this, and
then I combined all the functionality into a single bot for keeping track of
what state (unreplied/replied) threads were in, and which tutor was currently
assigned to answer it.&lt;/p&gt;
&lt;p&gt;And now you have enough background to get onto what my bad solution was.
One of the parts of this IRC bot was the “threads backend”, which stores thread
metadata: author, title, unreplied/replied state, and the tutor assigned to it.
Now the obvious answer to this is to use a database: &lt;a class="reference external" href="https://www.sqlite.org/"&gt;SQLite&lt;/a&gt; if it’s a toy
problem, and &lt;a class="reference external" href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; if it’s big enough.&lt;/p&gt;
&lt;p&gt;Instead of doing that, however, I went with something far more crude:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load_threads&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;save_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json.new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# If this crashes, we end up with an empty threads.json.new file,&lt;/span&gt;
        &lt;span class="c1"&gt;# but threads.json is still intact.&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;threads.json.new&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;threads.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;request_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_threads&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;get_post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;get_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;new_post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;save_threads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s terrible! On every request, the entire database is read from disk, and
whenever a thread gets modified, the entire database has to be written back to
disk (not just the parts which changed).
There’s some other problems too: it doesn’t let you handle requests
concurrently, and it doesn’t let you create secondary indices to speed up
queries. It’s also missing an &lt;cite&gt;os.fsync()&lt;/cite&gt; call to ensure the data is &lt;em&gt;actually&lt;/em&gt;
written to disk, but let’s not talk about that one. 😉&lt;/p&gt;
&lt;p&gt;The secondary indices isn’t just a throwaway comment either: one of the
operations this backend supports is filtering threads by various attributes.
Obviously, the only way this backend can support that is by doing a linear scan
over all threads, and returning the ones which match.&lt;/p&gt;
&lt;p&gt;But actually, despite the number of raised eyebrows, it’s not actually quite as
terrible as it first seems. Firstly, it’s a backend to an IRC bot, which means
that any response it gives is going to be seen on an IRC channel, which is (sort
of) a serialised output: so not handling concurrent requests isn’t a big deal.
It also doesn’t have to handle a lot of traffic (much less than one request per
second), and even a 1s latency wouldn’t be obviously bad on IRC.&lt;/p&gt;
&lt;p&gt;The other thing to consider is that the database isn’t actually that big!
In the first year, there were ~500 threads: even with a generous 1KiB of
metadata per thread, that’s still less than a mebibyte, which is easily written
to disk in less than a second. It’s also small enough to fit in the OS buffer
cache, so reading the database from “disk” isn’t expensive either.
And you can also see that those linear scans aren’t that expensive either.&lt;/p&gt;
&lt;p&gt;Futhermore, there’s some advantages to the code.
It’s &lt;strong&gt;simple&lt;/strong&gt;: there’s full visibility into the internal state, serialisation and
deserialisation processes (unlike many ORMs, e.g. &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt;).
It’s &lt;strong&gt;debuggable&lt;/strong&gt;: it’s just JSON, so it’s incredibly easy to manually inspect
(after pretty-printing with &lt;tt class="docutils literal"&gt;python &lt;span class="pre"&gt;-m&lt;/span&gt; json.tool&lt;/tt&gt;) or write custom tools to
analyse it.
&lt;strong&gt;Schema migrations are easy&lt;/strong&gt;: tell &lt;tt class="docutils literal"&gt;Thread.__init__&lt;/tt&gt; about the new key
(providing a default value), and have &lt;tt class="docutils literal"&gt;Thread.to_json&lt;/tt&gt; emit whatever schema
you want stored: on the next database write, all record will be rewritten to the
new schema.&lt;/p&gt;
&lt;p&gt;This system performed adequately for the first year it was used.
There were many schema changes, but it had no data corruption or was the cause
of any outages. It’s performance was good enough: by the end, some filtering
requests took just over a second to respond to, but it was never bad enough
for users to notice.&lt;/p&gt;
&lt;p&gt;The next year, I rewrote it to use &lt;a class="reference external" href="http://www.sqlalchemy.org/"&gt;SQLAlchemy&lt;/a&gt; backed by &lt;a class="reference external" href="https://www.sqlite.org/"&gt;SQLite&lt;/a&gt; and added
indices to optimise the most frequent filter requests.
But I didn’t see that as admitting defeat: on the contrary: by starting with a
terrible implementation, I learnt exactly what the bottlenecks were, and could
then make informed decisions when designing the next version.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;Perhaps you weren’t surprised I went with a “bad” solution first: the idea is
&lt;a class="reference external" href="http://c2.com/cgi/wiki?PrematureOptimization"&gt;certainly&lt;/a&gt; &lt;a class="reference external" href="http://c2.com/cgi/wiki?PlanToThrowOneAway"&gt;not&lt;/a&gt; &lt;a class="reference external" href="http://c2.com/cgi/wiki?YouArentGonnaNeedIt"&gt;new&lt;/a&gt;. It’s an interesting strategy, but you need to
be careful with it: if you don’t make it bad enough, you won’t be motivated
enough to rewrite it in the future, and if you make it too bad, then it’ll crash
and burn before you’re ready to rewrite.&lt;/p&gt;
&lt;p&gt;Even if this story hasn’t fully convinced you that deliberately choosing
imperfect solutions is a good idea, I hope you can see it’s a viable strategy in
some cases.&lt;/p&gt;
</content><category term="random"/></entry><entry><title>Introducing vipdf</title><link href="2014-03/introducing-vipdf.html" rel="alternate"/><published>2014-03-09T12:41:00+11:00</published><updated>2014-03-09T12:41:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2014-03-09:2014-03/introducing-vipdf.html</id><summary type="html">&lt;p class="first last"&gt;A tool for PDF presentations with vi-inspired keybindings.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I mentioned in an earlier post (almost a year ago now, eep!) that I had some
more software to release to the world, and as promised, this is the next one.
The word “introducing” in the title is perhaps a bit misleading— I’ve written
(but not released) several similar programs to this, and I’ve been using this
particular program for a few years now, so it’s not really new.&lt;/p&gt;
&lt;p&gt;But enough of that, let’s get on to what this thing is.&lt;/p&gt;
&lt;div class="section" id="rationale"&gt;
&lt;h2&gt;Rationale&lt;/h2&gt;
&lt;p&gt;The rationale for vipdf is that I’m unhappy with a lot of existing presentation
software, for a variety of reasons, and I realised I could solve a lot of these
problems with a vi-style keyboard mapping for navigating presentations.&lt;/p&gt;
&lt;p&gt;The things which I wanted specifically were:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;strong&gt;Moving to an arbitrary slide.&lt;/strong&gt;
If you get to the end of a presentation and then someone in your audience asks
you a question about something on slide 15, you should be able to go directly
to that slide— you shouldn’t have to move through all the intermediate slides.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skipping over slides.&lt;/strong&gt;
Suppose you’re giving a talk, and you realise that you haven’t got time to
talk about something in depth. No matter, your software should let you just
skip over the next slide (or two), without flashing those slides at your
audience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keyboard controls.&lt;/strong&gt;
If I have to use a mouse while giving a presentation, I’m going to
accidentally click things I didn’t mean to and miss things that I meant to
click on. Since I can touch-type, it makes sense to control everything by the
keyboard, and by using familiar vi-style keybindings, I can reduce the
cognitive overhead of remembering shortcuts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the moment, vipdf only supports PDFs (just in case the name didn’t make that
obvious), which makes it great for pairing with beamer (as much as I dislike
TeX, I haven’t found anything better yet), but you can easily export PDFs from
most other presentation software (Microsoft Powerpoint, Keynote and LibreOffice
Impress all have this functionality).&lt;/p&gt;
&lt;p&gt;Yes, this will remove the gratuitous animations you had carefully wasted time
setting up.
No, there’s no support for embedded videos (in case you didn’t know, yes, you
can do that with PDFs).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="controls"&gt;
&lt;h2&gt;Controls&lt;/h2&gt;
&lt;p&gt;As I mentioned, there are vi-style keyboard controls:&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&amp;lt;Left&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Move to the previous slide&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;&amp;lt;Left&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Go back &lt;strong&gt;N&lt;/strong&gt; slides
(e.g. &lt;tt class="docutils literal"&gt;3&amp;lt;Left&amp;gt;&lt;/tt&gt; will move you back 3 slides)&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;&amp;lt;Right&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Move to the next slide&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;&amp;lt;Right&amp;gt;&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Go forward &lt;strong&gt;N&lt;/strong&gt; slides
(e.g. &lt;tt class="docutils literal"&gt;2&amp;lt;Right&amp;gt;&lt;/tt&gt; will skip forward over a slide)&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;g&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Goto slide &lt;strong&gt;N&lt;/strong&gt;
(e.g. &lt;tt class="docutils literal"&gt;33g&lt;/tt&gt; takes you to slide 33)&lt;/dd&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;b&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Toggle blackout&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Yeah, I didn’t map &lt;tt class="docutils literal"&gt;hjkl&lt;/tt&gt;. If you feel strongly about that, you should let me
know in the comments what the mapping should be (should both &lt;tt class="docutils literal"&gt;h&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;j&lt;/tt&gt;
move back, or only &lt;tt class="docutils literal"&gt;h&lt;/tt&gt;?)&lt;/p&gt;
&lt;p&gt;There’s also controls for auto-advancing slides
(&lt;tt class="docutils literal"&gt;t&lt;/tt&gt; is for timer, btw):&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;&lt;tt class="docutils literal"&gt;t&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Toggle auto-advance on or off.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;t&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Start auto-advance (if not already started), and set the time interval to
advance every &lt;strong&gt;N&lt;/strong&gt; seconds.
The default time interval is 5 seconds.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;[&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Set the start slide to slide &lt;strong&gt;N&lt;/strong&gt;, or unset if &lt;strong&gt;N&lt;/strong&gt; is 0.&lt;/dd&gt;
&lt;dt&gt;N &lt;tt class="docutils literal"&gt;]&lt;/tt&gt;&lt;/dt&gt;
&lt;dd&gt;Set the end slide to slide &lt;strong&gt;N&lt;/strong&gt;, or unset if &lt;strong&gt;N&lt;/strong&gt; is 0.
If auto-advance reaches the end slide, it will loop back to the start slide
if one is set, otherwise it will turn off that auto-advance.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div class="section" id="api"&gt;
&lt;h2&gt;API&lt;/h2&gt;
&lt;p&gt;There’s also a DBUS api available for use, and there’s also a sample control
script provided so you can write your own scripts to control your presentation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;vipdf-control&lt;span class="w"&gt; &lt;/span&gt;list
&lt;span class="c1"&gt;# will give you a list of viewer ids, though at the moment there&amp;#39;s just one&lt;/span&gt;
&lt;span class="c1"&gt;# you should take that and pass it into the following commands&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;vipdf-control&lt;span class="w"&gt; &lt;/span&gt;next&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$viewerid&lt;/span&gt;
&lt;span class="c1"&gt;# advances by 1 slide&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;vipdf-control&lt;span class="w"&gt; &lt;/span&gt;next&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$viewerid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="c1"&gt;# advances by 2 slides&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;vipdf-control&lt;span class="w"&gt; &lt;/span&gt;prev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$viewerid&lt;/span&gt;
&lt;span class="c1"&gt;# goes back 1 slide&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;vipdf-control&lt;span class="w"&gt; &lt;/span&gt;goto&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$viewerid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="c1"&gt;# goto slide 10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, the API doesn’t make a lot of sense at the moment, as you can’t
have more than one presentation open in a vipdf instance at a time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="implementation"&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;vipdf is written in Vala, using Poppler and Clutter.
You can find &lt;a class="reference external" href="http://hg.flowblok.id.au/vipdf"&gt;the code&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;Quick build instructions for Debian:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;aptitude&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;valac&lt;span class="w"&gt; &lt;/span&gt;libgee-dev&lt;span class="w"&gt; &lt;/span&gt;libpoppler-glib-dev&lt;span class="w"&gt; &lt;/span&gt;libclutter-1.0-dev
$&lt;span class="w"&gt; &lt;/span&gt;make
...
$&lt;span class="w"&gt; &lt;/span&gt;bin/vipdf&lt;span class="w"&gt; &lt;/span&gt;/path/to/file.pdf
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I’ve also written a &lt;a class="reference external" href="http://hg.flowblok.id.au/vipdf-web"&gt;web interface&lt;/a&gt; for it
(which also integrates with &lt;a class="reference external" href="2012-11/controlling-projectors-with-pjlink.html"&gt;pjlink&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"/></entry><entry><title>Python: Wat</title><link href="2014-03/python-wat.html" rel="alternate"/><published>2014-03-08T19:18:00+11:00</published><updated>2014-03-08T19:18:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2014-03-08:2014-03/python-wat.html</id><summary type="html">&lt;p class="first last"&gt;Some strange-at-first-glance Python code.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;You’ve all seen &lt;a class="reference external" href="https://www.destroyallsoftware.com/talks/wat"&gt;wat&lt;/a&gt;, right?
Of course you have.&lt;/p&gt;
&lt;p&gt;Of course, Python doesn’t have any Wats, right?
Well, not so much.&lt;/p&gt;
&lt;div class="section" id="lets-talk-about-python"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;toys&amp;quot;r&amp;quot;us&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Makes sense.
So if we change those outer quotes to double quotes, we should get a syntax
error, right?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;toys&amp;quot;&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;us&amp;quot;&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;toysus&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="lets-talk-about-python-1"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is because before True and False were available as builtins, people would
write &lt;tt class="docutils literal"&gt;True = 1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;False = 0&lt;/tt&gt; at the top of their Python files.
When &lt;a class="reference external" href="http://python.org/dev/peps/pep-0285/"&gt;they were introduced&lt;/a&gt;, they made &lt;tt class="docutils literal"&gt;bool&lt;/tt&gt; a subclass of &lt;tt class="docutils literal"&gt;int&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;True == 1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;False == 0&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;So of course, you can do arithmetic with booleans.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="lets-talk-about-python-2"&gt;
&lt;h2&gt;Let’s talk about Python!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;one&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Right, it’s a dictionary with three items: the keys have different types, but
the basic idea is that we map objects to some string representation of them.
Right?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are three behaviours of dictionaries which come into play here.
The first is that keys in dictionaries compare equal if they have the same hash
value (&lt;tt class="docutils literal"&gt;__hash__&lt;/tt&gt;), &lt;em&gt;and&lt;/em&gt; compare equal (&lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt;).
The second is that dictionary literals are evaluated by starting with an empty
dictionary, and inserting each item in order (left to right).
The third is that if you are inserting an item into a dictionary and the key is
already there, the value will be replaced, but not the key.&lt;/p&gt;
&lt;p&gt;Which means that in the example above, this is what happens:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;one&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;one&amp;#39;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;one&amp;#39;, &amp;#39;1&amp;#39;: &amp;#39;won&amp;#39;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;# {1: &amp;#39;true&amp;#39;, &amp;#39;1&amp;#39;: &amp;#39;won&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wait, why did the last insertion replace the value for 1?
Well because &lt;tt class="docutils literal"&gt;hash(True) == hash(1)&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;True == 1&lt;/tt&gt;, of course.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="enough-making-fun-of-languages-that-suck-lets-talk-about-json"&gt;
&lt;h2&gt;Enough making fun of languages that suck, let’s talk about JSON!&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;won&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;1&amp;quot;: &amp;quot;won&amp;quot;, &amp;quot;1&amp;quot;: &amp;quot;true&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This &lt;a class="reference external" href="http://stackoverflow.com/a/19927061"&gt;is valid JSON&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="lets-talk-about-python-3"&gt;
&lt;h2&gt;Let’s talk about Python.&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Uh, wait, but 1 &amp;gt; 2 isn’t true, so it should be False, right?
Like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hmmm, that works, maybe it’s just a precedence thing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nope (and note the re-occurence of &lt;tt class="docutils literal"&gt;False == 0&lt;/tt&gt; in this).
This is actually a case of Python’s chained comparisons:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Chained comparisons are actually awesome…&lt;/p&gt;
&lt;img alt="a t-rex playing" src="static/images/awesome.png" /&gt;
&lt;p&gt;…but if you ever use it with disparate operators like this, Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="and-lets-finish-up-with-some-python"&gt;
&lt;h2&gt;And let’s finish up with some Python!&lt;/h2&gt;
&lt;p&gt;This was fixed in Python 3, but there’s still plenty of Python 2 programmers
just waiting to bump into this one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;GiantSpiders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="n"&gt;legs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;        &lt;span class="s1"&gt;&amp;#39;leg &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GiantSpiders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Arrrghh! Help, spiders!&lt;/p&gt;
&lt;p&gt;Wat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="also-the-one-which-didnt-qualify"&gt;
&lt;h2&gt;Also, the one which didn’t qualify&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-March/026446.html"&gt;Time objects representing midnight are falsey&lt;/a&gt;.
Except, it's &lt;a class="reference external" href="https://mail.python.org/pipermail/python-ideas/2014-March/026647.html"&gt;not quite as simple&lt;/a&gt; as that.
Unfortunately, this only qualifies for an honourable mention: there’s an
&lt;a class="reference external" href="http://bugs.python.org/issue13936"&gt;open bug&lt;/a&gt; for this issue, so it might be fixed.&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"/></entry><entry><title>Introducing gst-launch-dynamic</title><link href="2013-04/introducing-gst-launch-dynamic.html" rel="alternate"/><published>2013-04-05T23:15:00+11:00</published><updated>2013-04-05T23:15:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-04-05:2013-04/introducing-gst-launch-dynamic.html</id><summary type="html">&lt;p class="first last"&gt;A tool which allows modifying GStreamer pipelines at runtime.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I thought it would probably be a good idea to write posts about the random
projects I do. As it happens, this particular post is about a tool which still
could do with more work, but is in a reasonable enough state to present to the
world. I’ve got another tool which I’ll write a post about after doing a little
clean up.&lt;/p&gt;
&lt;p&gt;So, on to the entertainment.
GStreamer is a multimedia framework, which lets you build applications which do
all kinds of strange and wonderful things with multimedia (audio / video /
anything you can stream).&lt;/p&gt;
&lt;p&gt;I like to think of it as UNIX pipelines for multimedia:
a GStreamer pipeline is built out of many elements (processes),
and each element has a number of pads (stdin / stdout / stderr / other fds)
which can be joined to other elements.
Indeed, there’s even a nice tool called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt; which lets you express
the pipeline in a UNIX-like syntax— in fact, it’s better, since it lets you
connect any element to any other element, which is not possible (as far as I can
tell) in the POSIX shell language.&lt;/p&gt;
&lt;div class="section" id="what-is-it"&gt;
&lt;h2&gt;What is it?&lt;/h2&gt;
&lt;p&gt;Great, so that’s what GStreamer is, what does my tool do?
Well, it’s almost the same as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;, except for one little thing:
&lt;em&gt;it lets you modify the pipeline at runtime&lt;/em&gt;.
I think this is both amazing and slightly insane: it greatly lowers the barrier
for ad-hoc experimentation with pipelines, and allows for easy scripting of
pipelines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="how-does-it-work"&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;It’s very simple. To run it, call it in the same way you would normally call
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gst-launch-dynamic&lt;span class="w"&gt; &lt;/span&gt;videotestsrc&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;autovideosink
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It will construct the pipeline, and start playing it, in this case showing the
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/SMPTE_color_bars"&gt;SMPTE colour bars&lt;/a&gt;. But, to let you change the pipeline, it also watches
standard input for commands, so we can type this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;videotestsrc0.pattern = 1
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This sets the &lt;tt class="docutils literal"&gt;pattern&lt;/tt&gt; property on the &lt;tt class="docutils literal"&gt;videotestsrc0&lt;/tt&gt; element
(since we didn’t give it a name, it gets automatically numbered) to 1,
which corresponds to &amp;quot;snow&amp;quot; (i.e., black &amp;amp; white noise).
As soon as you press enter, the property is set, and the video displayed on your
screen will change.&lt;/p&gt;
&lt;p&gt;It’s probably worth noting a bug (well, more of an inconsistency) here: the
thing on the right hand side of the equals sign is interpreted as a Python
object (using &lt;tt class="docutils literal"&gt;ast.literal_eval&lt;/tt&gt;), which isn’t exactly the same syntax that
the command-line arguments (and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gst-launch&lt;/span&gt;&lt;/tt&gt;) uses.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="other-commands"&gt;
&lt;h2&gt;Other commands&lt;/h2&gt;
&lt;p&gt;Aside from changing properties, you can control the state of the pipeline with
these commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;play
pause
stop
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can rewire the pipeline by adding, removing, linking and unlinking elements.
This is a little unstable at the moment, since it’s just translating commands
into GStreamer API calls without any sanity checking.
It is possible to replace an element with another at runtime, but you need to be
careful about unlinking and relinking the pads.&lt;/p&gt;
&lt;p&gt;Link an element to another: &lt;tt class="docutils literal"&gt;element.sink &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; element.src&lt;/tt&gt;
(in this case, both pad names are optional)&lt;/p&gt;
&lt;p&gt;Unlink an element from another: &lt;tt class="docutils literal"&gt;element.sink x&amp;gt; element.src&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Add an element: &lt;tt class="docutils literal"&gt;+ type key=value …&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Remove an element: &lt;tt class="docutils literal"&gt;- element&lt;/tt&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="scripting"&gt;
&lt;h2&gt;Scripting&lt;/h2&gt;
&lt;p&gt;One immediate possibility is to write a program which sends these commands.
A side note: during development, FIFO files are great for keeping the pipeline
open while you modify your script. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkfifo&lt;span class="w"&gt; &lt;/span&gt;commands.fifo
$&lt;span class="w"&gt; &lt;/span&gt;gst-launch-dynamic&lt;span class="w"&gt; &lt;/span&gt;…&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;commands.fifo
$&lt;span class="w"&gt; &lt;/span&gt;./my-random-script&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;commands.fifo
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As an example of how this can be completely useless, but &lt;em&gt;very entertaining&lt;/em&gt;,
here’s how to make any video psychedelic.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;psychedelic.py&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;STEP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;
&lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;drange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;drange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STEP&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vb.hue =&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DELAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Usage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-u&lt;span class="w"&gt; &lt;/span&gt;psychedelic.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;gst-launch-dynamic&lt;span class="w"&gt; &lt;/span&gt;filesrc&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gitannex.ogv&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;decodebin2&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;videobalance&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vb&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="w"&gt; &lt;/span&gt;autovideosink
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="finale"&gt;
&lt;h2&gt;Finale&lt;/h2&gt;
&lt;p&gt;After I wrote this tool, I realised that nothing I had built so far really
utilised its potential, so I really hope that someone will find this and build
something cool with it.&lt;/p&gt;
&lt;p&gt;Of course, you can’t do that if I don’t point you at &lt;a class="reference external" href="http://hg.flowblok.id.au/gst-launch-dynamic"&gt;the code&lt;/a&gt;!&lt;/p&gt;
&lt;/div&gt;
</content><category term="software"/></entry><entry><title>Blackmagic DeckLink, ConsoleKit and TTYs</title><link href="2013-02/blackmagic-decklink-consolekit-and-ttys.html" rel="alternate"/><published>2013-02-19T07:11:00+11:00</published><updated>2013-02-19T07:11:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-02-19:2013-02/blackmagic-decklink-consolekit-and-ttys.html</id><summary type="html">&lt;p class="first last"&gt;In which a statically-sized buffer is too small.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Regular readers may skip this blog post, it’s being posted so people find the
solution when the google it, not because it’s interesting.&lt;/p&gt;
&lt;p&gt;I had a computer with a Blackmagic capture card, and discovered that it wouldn’t
shut down properly.
After tracing my way through PolicyKit, I discovered that it thought that my
login session wasn’t local, but was a remote session.&lt;/p&gt;
&lt;p&gt;Looking at the output of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ck-list-sessions&lt;/span&gt;&lt;/tt&gt;, ConsoleKit didn’t have a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;x11-display-device&lt;/span&gt;&lt;/tt&gt; entry for my session
(it’s normally something like &lt;tt class="docutils literal"&gt;/dev/tty7&lt;/tt&gt;).
Confusingly though, it did have an &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;x11-display&lt;/span&gt;&lt;/tt&gt; (which was &lt;tt class="docutils literal"&gt;:0&lt;/tt&gt; as expected).&lt;/p&gt;
&lt;p&gt;ConsoleKit determines the x11-display-device using a helper program, which on my
system is located at &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/lib/ConsoleKit/ck-get-x11-display-device&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This helper program parses &lt;tt class="docutils literal"&gt;/proc/tty/drivers&lt;/tt&gt;, into a struct in
&lt;a class="reference external" href="http://cgit.freedesktop.org/ConsoleKit/tree/src/ck-sysdeps-linux.c?id=b8a961f91105df661957f6b86922f744bac8b91c#n91"&gt;src/ck-sysdeps-linux.c:92&lt;/a&gt;.
I discovered the name in &lt;tt class="docutils literal"&gt;/proc/tty/drivers&lt;/tt&gt; was more than 16 characters, and
hence overflowed the &lt;tt class="docutils literal"&gt;name&lt;/tt&gt; array, causing the program to segfault.&lt;/p&gt;
&lt;p&gt;Since ConsoleKit’s project page indicates that there’s no longer any active
development on it, I decided on the hacky solution to solve my problem, rather
than fixing the actual problem and bumped the size of the name array up by a
couple of bytes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;Index: consolekit-0.4.5/src/ck-sysdeps-linux.c&lt;/span&gt;
&lt;span class="gh"&gt;===================================================================&lt;/span&gt;
&lt;span class="gd"&gt;--- consolekit-0.4.5.orig/src/ck-sysdeps-linux.c    2012-07-14 14:08:02.298395776 +1000&lt;/span&gt;
&lt;span class="gi"&gt;+++ consolekit-0.4.5/src/ck-sysdeps-linux.c 2012-07-14 14:11:07.282389984 +1000&lt;/span&gt;
&lt;span class="gu"&gt;@@ -93,7 +93,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        guint major_number;
&lt;span class="w"&gt; &lt;/span&gt;        guint minor_first;
&lt;span class="w"&gt; &lt;/span&gt;        guint minor_last;
&lt;span class="gd"&gt;-        char name[16];&lt;/span&gt;
&lt;span class="gi"&gt;+        char name[24];&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        char devfs_type;
&lt;span class="w"&gt; &lt;/span&gt;} tty_map_node;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="software"/></entry><entry><title>Shell startup scripts</title><link href="2013-02/shell-startup-scripts.html" rel="alternate"/><published>2013-02-17T05:50:00+11:00</published><updated>2013-02-17T05:50:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2013-02-17:2013-02/shell-startup-scripts.html</id><summary type="html">&lt;p class="first last"&gt;A description of the startup behaviour of several shells, with an instructive diagram.&lt;/p&gt;
</summary><content type="html">&lt;div class="admonition admonition-update-2020-03-29"&gt;
&lt;p class="first admonition-title"&gt;Update (2020-03-29)&lt;/p&gt;
&lt;p class="last"&gt;When I originally wrote this blog post, my intent was to describe how things
worked on a typical system at the time.
Unfortunately, the operation of bash startup scripts is dependent on
patches added by OS distributions, compile-time options and if bash thinks
it was invoked by sshd or rshd.
I have no desire to add complexity to this post, so instead please
read this as a rough approximation of the truth.
If you want to know for certain how it works on your system, the only answer
is &lt;em&gt;test it yourself&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you’re a regular shell user, you’ve almost certainly got a
&lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt; script in your home folder,
which usually contains various tweaks, such as setting environment variables
(adding that directory to &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt;), telling your shell to do clever things
(like &lt;tt class="docutils literal"&gt;set &lt;span class="pre"&gt;-o&lt;/span&gt; noclobber&lt;/tt&gt;) and adding various aliases to commands
(like &lt;tt class="docutils literal"&gt;alias please=sudo&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;(If you’re really organised, you’ll have all your dotfiles in a repository
somewhere so that you can keep your settings synchronised across all the
machines you work on.)&lt;/p&gt;
&lt;p&gt;Anyhow, I suspect that few people know when things like &lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt; actually get executed. When I started, I just followed people’s
advice of putting stuff in &lt;tt class="docutils literal"&gt;.bashrc&lt;/tt&gt;, and then when it didn’t work, into
&lt;tt class="docutils literal"&gt;.bash_profile&lt;/tt&gt;.
I could stop here and describe just the bash startup process (as silly as it
is), but there’s a complication in that I switched to zsh a few years ago
(and haven’t looked back), but occasionally use bash on machines which don’t
have zsh installed.&lt;/p&gt;
&lt;p&gt;In order to handle this nicely then, I need to be able to specify things which
are specific to bash or zsh in their own files, and then to specify things
which any POSIX-compliant shell (like aliases and environment variables) can
understand in a common startup file.&lt;/p&gt;
&lt;p&gt;My solution to this problem is to define some new dotfile folders,
one for each shell (&lt;tt class="docutils literal"&gt;.bash/&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;.zsh/&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.sh/&lt;/tt&gt;), and one for the
shell-independent files (&lt;tt class="docutils literal"&gt;.shell/&lt;/tt&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.bash/
    env
    interactive
    login
    logout
.sh/
    env
    interactive
    login
.shell/
    env
    interactive
    login
    logout
.zsh/
    env
    interactive
    login
    logout
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;“But!”, you say, &lt;a class="footnote-reference" href="#since" id="footnote-reference-1"&gt;[1]&lt;/a&gt; “what do the different files in here do?”
Ah, well I’m glad you asked.
There are two kinds of shells:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;[non-]interactive shells (you type into them / shell scripts)&lt;/li&gt;
&lt;li&gt;[non-]login shells (the shell run when you first login / subshells)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All shells will first run &lt;tt class="docutils literal"&gt;env&lt;/tt&gt;, then login shells will run &lt;tt class="docutils literal"&gt;login&lt;/tt&gt;,
then interactive shells will run &lt;tt class="docutils literal"&gt;interactive&lt;/tt&gt;.
Once finished, login shells will run &lt;tt class="docutils literal"&gt;logout&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="section" id="where-to-put-stuff"&gt;
&lt;h2&gt;Where to put stuff&lt;/h2&gt;
&lt;p&gt;It all depends on when it needs to be run.&lt;/p&gt;
&lt;p&gt;If it’s setting / modifying environment variables, it should go in &lt;tt class="docutils literal"&gt;login&lt;/tt&gt;.
If it’s alias or a terminal-specific environment variable (e.g., GREP_COLOR),
it should go in &lt;tt class="docutils literal"&gt;interactive&lt;/tt&gt;.
In my &lt;tt class="docutils literal"&gt;.shell/env&lt;/tt&gt; file, I have my umask set, and also define some useful
functions for modifying colon-separated path environment variables (like
$PATH).&lt;/p&gt;
&lt;p&gt;Even if you don’t adopt anything else from my scheme, I’d recommend you take a
look at the what my functions are doing which differs from something like
&lt;tt class="docutils literal"&gt;export &lt;span class="pre"&gt;PATH=$PATH:/path/to/dir&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;That particular pattern is way too common, and is very dangerous if you
consider the case when &lt;tt class="docutils literal"&gt;$PATH&lt;/tt&gt; (or whatever your variable is, like
&lt;tt class="docutils literal"&gt;$LD_LIBRARY_PATH&lt;/tt&gt;) isn’t set.
Then, the value will be &lt;tt class="docutils literal"&gt;:/path/to/dir&lt;/tt&gt;, which usually means both
&lt;tt class="docutils literal"&gt;/path/to/dir&lt;/tt&gt; &lt;em&gt;and the current directory&lt;/em&gt;, which is usually both unexpected
behaviour and a security concern.&lt;/p&gt;
&lt;p&gt;With my implementation (see &lt;tt class="docutils literal"&gt;.shell/env_functions&lt;/tt&gt;), you can append, prepend
and remove directories from any colon-separated environment variable, and when
appending or prepending, you are guaranteed that directory will only then
appear in that variable once.&lt;/p&gt;
&lt;p&gt;As a side note, I’m very disappointed in my &lt;tt class="docutils literal"&gt;indirect_expand()&lt;/tt&gt; function,
so if you have a better solution, please let me know (or send me a pull request).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="implementation"&gt;
&lt;h2&gt;Implementation&lt;/h2&gt;
&lt;p&gt;In order to implement this, you first need an understanding of which startup
files are run in each case.
Of course, this isn’t hard, since all the shells have the same, sensible
system. Ahahaha, no.&lt;/p&gt;
&lt;p&gt;Fortunately, I’ve read the man pages for you, and drawn a pretty diagram.
To read it, pick your shell, whether it's a login shell, whether it's
interactive, and follow the same colour through the diagram.
When the arrows split out to multiple files, it means that the shell will try
to read each one in turn (working left to right), and will use the first one it
can read.&lt;/p&gt;
&lt;img alt="" src="static/images/shell-startup.png" /&gt;
&lt;p&gt;An important point here is that if you have a non-login, non-interactive shell
running a plain POSIX shell (dash, bash in sh compatibility mode), it &lt;em&gt;won’t
fulfil&lt;/em&gt; our contract in that it won’t read &lt;tt class="docutils literal"&gt;.sh/env&lt;/tt&gt;
(and hence &lt;tt class="docutils literal"&gt;.shell/env&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;The other special case which exists is Bash’s remote shell mode (which is
rather warped), where it tries to detect if it’s running under ssh or rsh (I
assume by looking at the process name of its parent), and if so, follows a
different path, which is indicated on the diagram.&lt;/p&gt;
&lt;p&gt;Except that diagram shows what happens according to the man page, and not what
happens when you actually try it out in real life.
This second diagram more accurately captures the insanity of bash:&lt;/p&gt;
&lt;img alt="" src="static/images/shell-startup-actual.png" /&gt;
&lt;p&gt;See how remote interactive login shells read /etc/bash.bashrc, but normal
interactive login shells don’t? Sigh.&lt;/p&gt;
&lt;p&gt;Finally, &lt;a class="reference external" href="http://hg.flowblok.id.au/shell-startup"&gt;here’s a repository&lt;/a&gt; containing my implementation and the graphviz
files for the above diagram.
If your POSIX-compliant shell isn’t listed here, or if I’ve made a horrible
mistake (or just a tiny one), please send me a pull request or make a comment
below, and I’ll update this post accordingly.&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="since" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;and since I’m writing this, I can make you say whatever I want for the purposes of narrative.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="software"/></entry><entry><title>Controlling Projectors with PJLink</title><link href="2012-11/controlling-projectors-with-pjlink.html" rel="alternate"/><published>2012-11-30T22:41:00+11:00</published><updated>2012-11-30T22:41:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-30:2012-11/controlling-projectors-with-pjlink.html</id><summary type="html">&lt;p class="first last"&gt;A Python tool for turning it off and back on again.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I regularly need to control a data projector, and previously the only way to do
this has been using a web interface created by the manufacturer.
Of course the interface is, while usable, poorly designed, and somewhat
sluggish.&lt;/p&gt;
&lt;p&gt;Fortunately, there’s a protocol for controlling projectors, &lt;a class="reference external" href="http://pjlink.jbmia.or.jp/english/"&gt;PJLink&lt;/a&gt;,
but unfortunately, there aren’t many implementations
(I did find a &lt;a class="reference external" href="http://search.cpan.org/dist/Net-PJLink/lib/Net/PJLink.pm"&gt;perl module&lt;/a&gt; in my search),
so I decided to write my own implementation in Python.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://hg.flowblok.id.au/pjlink"&gt;So I did&lt;/a&gt;, and it took me just a single morning, which I was quite impressed
with. The specification is reasonable simple, and aside from one or two stupid
mistakes, implementing it was straight forward.
In addition to the Python API, I also wrote a simple command line utility for
communicating with the projector, which is probably more useful than the API
(since you can throw it into your own shell scripts).&lt;/p&gt;
&lt;p&gt;If you want to try it out, you can install it from &lt;a class="reference external" href="http://pypi.python.org/pypi/pjlink"&gt;PyPI&lt;/a&gt; using pip.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--user&lt;span class="w"&gt; &lt;/span&gt;pjlink
&lt;span class="c1"&gt;# or if you&amp;#39;re in a virtualenv&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pjlink
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The location of projectors can be specified in ~/.config/pjlink/pjlink.conf
(on Linux, see &lt;a class="reference external" href="http://pypi.python.org/pypi/appdirs"&gt;appdirs&lt;/a&gt; for your platform):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[default]&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;myprojector&lt;/span&gt;
&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;4212&lt;/span&gt;
&lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;JBMIAProjectorLink&lt;/span&gt;

&lt;span class="k"&gt;[other]&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;bob&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note this stores your passwords in plain text: this is an indication of what I
think of the authentication protocol which PJLink uses. I couldn’t find a
specific vulnerability, but I still wouldn’t trust it.&lt;/p&gt;
&lt;p&gt;Then, you can use -p to reference the projector in the config file.
You can also just specify a host and port manually (but not a password).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# get power state of myprojector:4212 (using password)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;default&lt;span class="w"&gt; &lt;/span&gt;power
&lt;span class="c1"&gt;# get power state of bob:4352 (default port, no password)&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;power

&lt;span class="c1"&gt;# for the &amp;quot;default&amp;quot; projector, you can omit -p&lt;/span&gt;
&lt;span class="c1"&gt;# this is the same as the first command&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;power

&lt;span class="c1"&gt;# get available inputs&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;inputs

&lt;span class="c1"&gt;# get current input&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;input

&lt;span class="c1"&gt;# switch to input RGB-1&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pjlink&lt;span class="w"&gt; &lt;/span&gt;input&lt;span class="w"&gt; &lt;/span&gt;RGB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there are many more commands available: everything in the specification is
implemented as plainly as possible, so if the projector can do it, my tool
supports it.&lt;/p&gt;
&lt;p&gt;And for the record, I disclaim any responsibility for inappropriate use of this
tool. (because I can think of a few!)&lt;/p&gt;
</content><category term="software"/></entry><entry><title>Awesome + GNOME Configuration</title><link href="2012-11/awesome-gnome-configuration.html" rel="alternate"/><published>2012-11-28T10:31:00+11:00</published><updated>2012-11-28T10:31:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-28:2012-11/awesome-gnome-configuration.html</id><summary type="html">&lt;p class="first last"&gt;Configuration files which let Awesome and GNOME play well together.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I was recently prompted to publish my configuration for integrating GNOME and
awesome. At the time I wrote this setup, the awesome wiki was recommending a
configuration which didn’t correctly autostart applications,
nor integrate well with anything expecting to find a session manager.
However, it now contains &lt;a class="reference external" href="http://awesome.naquadah.org/wiki/Quickly_Setting_up_Awesome_with_Gnome#Configuration:_3.0_.3C.3D_gnome_.3C_3.4"&gt;instructions for a similar setup&lt;/a&gt;, so you should have a
look at that too.&lt;/p&gt;
&lt;p&gt;The first thing you need is an entry to tell your display manager about a new
session type, so put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/xsessions/awesome-gnome.desktop&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;This session logs you into GNOME using the Awesome WM&lt;/span&gt;
&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-session --session=awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;TryExec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-session&lt;/span&gt;
&lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you need to tell gnome-session about the &amp;quot;awesome-gnome&amp;quot; session.
You might want to modify the notification daemon here.
Put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.config/gnome-session/sessions/awesome-gnome.session&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[GNOME Session]&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome-gnome&lt;/span&gt;
&lt;span class="na"&gt;RequiredComponents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-settings-daemon&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="na"&gt;RequiredProviders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;windowmanager&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-windowmanager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome&lt;/span&gt;
&lt;span class="na"&gt;DefaultProvider-notifications&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;notification-daemon&lt;/span&gt;
&lt;span class="na"&gt;FallbackSession&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gnome-fallback&lt;/span&gt;
&lt;span class="na"&gt;DesktopName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;GNOME&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, GNOME expects to find a XDG application entry for &amp;quot;awesome&amp;quot;,
so put this in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.local/share/applications/awesome.desktop&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Awesome&lt;/span&gt;
&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Window manager&lt;/span&gt;
&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;sh -c &amp;#39;/usr/bin/awesome &amp;amp;&amp;#39;&lt;/span&gt;
&lt;span class="na"&gt;NoDisplay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-WMName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;awesome&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Autostart-Phase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;WindowManager&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Provides&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;windowmanager&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-Autostart-Notify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;X-GNOME-AutoRestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you should be able to logout, and immediately see the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;awesome-gnome&lt;/span&gt;&lt;/tt&gt;
session which you can log into.
Your display manager usually remembers the last session you logged into,
but using this method, you can easily switch between GNOME shell,
GNOME fallback and awesome-gnome.&lt;/p&gt;
</content><category term="software"/></entry><entry><title>Prettify script for PC^2</title><link href="2012-11/prettify-script-for-pc2.html" rel="alternate"/><published>2012-11-17T17:23:00+11:00</published><updated>2012-11-17T17:23:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-17:2012-11/prettify-script-for-pc2.html</id><summary type="html">&lt;p class="first last"&gt;Adding styling to programming competition software.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;When I was doing programming competitions at uni, I wrote this script, which
takes the default HTML output from PC^2 and add some colour to it to indicate
passed / failed problems. Since it was written / enhanced on the day of the
competition, there’s definitely some hacky code in there, but I’m publishing it
so future generations can steal it for their own nefarious purposes.&lt;/p&gt;
&lt;p&gt;This code is released into the &lt;a class="reference external" href="http://creativecommons.org/licenses/CC0/1.0/"&gt;public domain&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# prettify.py&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;string&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;lxml&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;prettify.xsl&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rU&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;xslt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;etree&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XSLT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^[0-9]*/[1-9][0-9]*$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^.*[1-9]/[-0]*$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;//td&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrib&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;passed&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrib&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;failed&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;An Industrial Spy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Simple Polygon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;High Score&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Rankings&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Wormholes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Fractal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Rewards for Math!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;Divisible Subsequences&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xpath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;//th/strong/u&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xslt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;#body = doc.xpath(&amp;#39;//html/body&amp;#39;)&lt;/span&gt;
&lt;span class="c1"&gt;#d = etree.Element(&amp;#39;div&amp;#39;)&lt;/span&gt;
&lt;span class="c1"&gt;#d.attrib[&amp;#39;class&amp;#39;] = &amp;#39;timer&amp;#39;&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#import sys&lt;/span&gt;
&lt;span class="c1"&gt;#from datetime import datetime&lt;/span&gt;
&lt;span class="c1"&gt;#n = datetime.now()&lt;/span&gt;
&lt;span class="c1"&gt;#t = datetime(2011, 9, 3, 17, 0, 0, 0)&lt;/span&gt;
&lt;span class="c1"&gt;#print&amp;gt;&amp;gt;sys.stderr, n - t&lt;/span&gt;

&lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tostring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- prettify.xsl --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;xsl:stylesheet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;1.0&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;xmlns:xsl=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:variable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stage&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;xml&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;indent=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;yes&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;doctype-system=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;doctype-public=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;-//W3C//DTD XHTML 1.0 Transitional//EN&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- Document structure --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- Head for styling, etc. --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:copy-of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/head/node()&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://cornify.com/js/cornify.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!--&amp;lt;meta http-equiv=&amp;quot;refresh&amp;quot; content=&amp;quot;5&amp;quot; /&amp;gt;--&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://fonts.googleapis.com/css?family=Questrial&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;style&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;html,&lt;span class="w"&gt; &lt;/span&gt;body&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;margin:&lt;span class="w"&gt; &lt;/span&gt;0;&lt;span class="w"&gt; &lt;/span&gt;padding:&lt;span class="w"&gt; &lt;/span&gt;0;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;color:&lt;span class="w"&gt; &lt;/span&gt;#729fcf;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;body&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;                    &lt;/span&gt;font-family:&lt;span class="w"&gt; &lt;/span&gt;&amp;#39;Questrial&amp;#39;,&lt;span class="w"&gt; &lt;/span&gt;sans-serif;
&lt;span class="w"&gt;                    &lt;/span&gt;color:&lt;span class="w"&gt; &lt;/span&gt;#eeeeec;
&lt;span class="w"&gt;                    &lt;/span&gt;background:&lt;span class="w"&gt; &lt;/span&gt;#2e3436;
&lt;span class="w"&gt;                &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;font-size:&lt;span class="w"&gt; &lt;/span&gt;20pt;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;.passed,&lt;span class="w"&gt; &lt;/span&gt;.failed&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;font-weight:&lt;span class="w"&gt; &lt;/span&gt;bold;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;.passed&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;                    &lt;/span&gt;background:&lt;span class="w"&gt; &lt;/span&gt;#8ae234;
&lt;span class="w"&gt;                    &lt;/span&gt;color:&lt;span class="w"&gt; &lt;/span&gt;black;
&lt;span class="w"&gt;                &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;.failed&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;                    &lt;/span&gt;background:&lt;span class="w"&gt; &lt;/span&gt;#ef2929;
&lt;span class="w"&gt;                    &lt;/span&gt;color:&lt;span class="w"&gt; &lt;/span&gt;white;
&lt;span class="w"&gt;                &lt;/span&gt;}

&lt;span class="w"&gt;                &lt;/span&gt;h1&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;text-align:&lt;span class="w"&gt; &lt;/span&gt;center;&lt;span class="w"&gt; &lt;/span&gt;margin:&lt;span class="w"&gt; &lt;/span&gt;0;&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt;                &lt;/span&gt;thead&lt;span class="w"&gt; &lt;/span&gt;th&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;font-size:&lt;span class="w"&gt; &lt;/span&gt;12pt;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;th.team-name&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt;                    &lt;/span&gt;font-weight:&lt;span class="w"&gt; &lt;/span&gt;normal;
&lt;span class="w"&gt;                    &lt;/span&gt;text-align:&lt;span class="w"&gt; &lt;/span&gt;left;
&lt;span class="w"&gt;                &lt;/span&gt;}

&lt;span class="w"&gt;                &lt;/span&gt;th:nth-child(2)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;width:&lt;span class="w"&gt; &lt;/span&gt;50px;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;td:nth-child(4)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;width:&lt;span class="w"&gt; &lt;/span&gt;3.5em;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;                &lt;/span&gt;td:nth-child(12)&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;width:&lt;span class="w"&gt; &lt;/span&gt;3.5em;&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- Body --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body/table[1]&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;scoreboard&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:for-each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tr[1]/th&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;scope=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;col&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;strong/u&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:for-each&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:for-each&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;tr[position() &amp;gt; 2]&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- Rank --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;td&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;rank&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[1]&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="cm"&gt;&amp;lt;!-- Name --&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;th&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;team-name&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;scope=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;row&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;xsl:value-of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[2]&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:copy-of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;td[position() &amp;gt; 2]&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:for-each&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;match=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/html/body&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$stage &amp;gt;= 1&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Competition&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;over!&lt;span class="w"&gt; &lt;/span&gt;Congratulations&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;all!!!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:if&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:apply-templates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;select=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;table[1]&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Prettify&lt;span class="w"&gt; &lt;/span&gt;script&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;Peter&lt;span class="w"&gt; &lt;/span&gt;Ward&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;xsl:if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;test=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$stage &amp;gt;= 2&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;![CDATA[&lt;/span&gt;
&lt;span class="cp"&gt;            var count = 0;&lt;/span&gt;
&lt;span class="cp"&gt;            var delay = 10000;&lt;/span&gt;
&lt;span class="cp"&gt;            function magical() {&lt;/span&gt;
&lt;span class="cp"&gt;                cornify_add();&lt;/span&gt;
&lt;span class="cp"&gt;                count++;&lt;/span&gt;
&lt;span class="cp"&gt;                if (count &amp;gt; 60) {&lt;/span&gt;
&lt;span class="cp"&gt;                    var child = null;&lt;/span&gt;
&lt;span class="cp"&gt;                    var nodes = document.body.children;&lt;/span&gt;
&lt;span class="cp"&gt;                    for (var i = 0; i &amp;lt; nodes.length; i++) {&lt;/span&gt;
&lt;span class="cp"&gt;                        if (nodes[i].tagName == &amp;quot;DIV&amp;quot;) {&lt;/span&gt;
&lt;span class="cp"&gt;                            child = nodes[i];&lt;/span&gt;
&lt;span class="cp"&gt;                            break;&lt;/span&gt;
&lt;span class="cp"&gt;                        }&lt;/span&gt;
&lt;span class="cp"&gt;                    }&lt;/span&gt;
&lt;span class="cp"&gt;                    document.body.removeChild(child);&lt;/span&gt;
&lt;span class="cp"&gt;                }&lt;/span&gt;
&lt;span class="cp"&gt;                window.setTimeout(magical, delay * (Math.random() / 2.0 + 0.75));&lt;/span&gt;
&lt;span class="cp"&gt;                if (delay &amp;gt; 10)&lt;/span&gt;
&lt;span class="cp"&gt;                    delay *= 0.9;&lt;/span&gt;
&lt;span class="cp"&gt;            }&lt;/span&gt;
&lt;span class="cp"&gt;            window.onload = magical;&lt;/span&gt;
&lt;span class="cp"&gt;            ]]&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:if&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/xsl:template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
</content><category term="pc2"/><category term="progcomp"/><category term="acm"/><category term="icpc"/><category term="script"/><category term="xslt"/><category term="university"/></entry><entry><title>End of Honours</title><link href="2012-11/end-of-honours.html" rel="alternate"/><published>2012-11-15T09:57:00+11:00</published><updated>2012-11-15T09:57:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2012-11-15:2012-11/end-of-honours.html</id><summary type="html">&lt;p class="first last"&gt;A life update.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Well, I’ve handed in my thesis
(Providing Useful Feedback to Beginner Programmers),
and done a presentation for it (twice, for logistical reasons),
so I’ve now finished everything I need to do for uni!
I’m very happy about this, and I’ve been enjoying the little bubble of
happiness about its completion the past few days.&lt;/p&gt;
&lt;img alt="Me and my thesis by flowblok" class="well" src="static/images/me-and-my-thesis.jpg" /&gt;
&lt;p&gt;We also continued the tradition of doing a prank on my supervisor’s office,
by turning it into a fish tank / sea themed room.
Photos from the setup and the final office are in &lt;a class="reference external" href="https://photos.app.goo.gl/WecD3L8DjFX380BT2"&gt;this photo album&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And for the few people who haven’t already caught up with this news,
I start working full-time at Google on Monday.&lt;/p&gt;
</content><category term="honours"/><category term="google"/><category term="prank"/><category term="fish"/><category term="office"/><category term="university"/></entry><entry><title>Box specific bar numbers in Lilypond</title><link href="2010-11/box-specific-bar-numbers-in-lilypond.html" rel="alternate"/><published>2010-11-05T04:30:00+11:00</published><updated>2010-11-05T04:30:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-11-05:2010-11/box-specific-bar-numbers-in-lilypond.html</id><summary type="html">&lt;p class="first last"&gt;A music typesetting snippet.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Just a bit of Lilypond / Scheme I wrote last night — if you want to have
boxed bar numbers at certain bar numbers (which don't fit every nth bar
- see the docs for that), you can use this code to do it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;break-visibility = #end-of-line-invisible
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;self-alignment-X = #0
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;font-size = #2
&lt;span class="k"&gt;\override&lt;/span&gt; Score.BarNumber #&amp;#39;stencil = #(make-stencil-boxer 0.1 0.25 ly:text-interface::print)
&lt;span class="k"&gt;\set&lt;/span&gt; Score.barNumberVisibility = #(lambda (x) (not (eq? (member x &amp;#39;(4 9 16 25)) #f)))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will put boxed numbers at bars 4, 9, 16 and 25.&lt;/p&gt;
</content><category term="software"/></entry><entry><title>Mouse activity</title><link href="2010-05/mouse-activity.html" rel="alternate"/><published>2010-05-12T00:41:00+10:00</published><updated>2010-05-12T00:41:00+10:00</updated><author><name>Peter Ward</name></author><id>tag:None,2010-05-12:2010-05/mouse-activity.html</id><summary type="html">&lt;p class="first last"&gt;A neat tool.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.sourceguru.net/hour-life-mouse/"&gt;As seen on&lt;/a&gt; &lt;a class="reference external" href="http://planet.debian.org/"&gt;Planet Debian&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
“&lt;a class="reference external" href="http://iographica.com/"&gt;IOGraph&lt;/a&gt;” is a small Java program that tracks and draws the
status of your mouse over a period of time. Lines are movement,
circles are places where the mouse has stopped… The bigger the
circle, the longer it was there.&lt;/blockquote&gt;
&lt;img alt="" src="static/images/mouse_activity.png" /&gt;
&lt;p&gt;I suspect the large density of horizontal lines in the center is due to
my habit of always selecting the text I'm currently reading. It's
interesting to see the areas of the screen which I don't really use
(yes, this only tracks mouse activity, but my eyes usually follow my
mouse).&lt;/p&gt;
</content><category term="random"/></entry><entry><title>Productivity</title><link href="2009-12/productivity.html" rel="alternate"/><published>2009-12-10T10:20:00+11:00</published><updated>2009-12-10T10:20:00+11:00</updated><author><name>Peter Ward</name></author><id>tag:None,2009-12-10:2009-12/productivity.html</id><summary type="html">&lt;p class="first last"&gt;Any resemblance to reality is purely coincidental.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Any resemblance to reality is purely coincidental.&lt;/p&gt;
&lt;img alt="because you can never have enough Comic Sans" src="static/images/productivity1.png" /&gt;
</content><category term="university"/></entry></feed>