Jekyll2019-04-14T09:39:38+00:00https://davecturner.github.io/feed.xmlDavid Turner says…Github PagesTimezone-sensitive rounding2019-04-14T00:00:00+00:002019-04-14T00:00:00+00:00https://davecturner.github.io/2019/04/14/timezone-rounding<p>Here’s an example of the sort of thing you can do with <a href="/2018/08/12/working-with-timezones.html">local-universal time
diagrams</a>. Suppose you have
some data about events that occurred at particular times in a system, such as
log messages. You want to calculate some aggregate statistics about its
behaviour over time. For instance you might want to aggregate all the events
from <code class="highlighter-rouge">00:00</code> to <code class="highlighter-rouge">03:00</code> together, and then all the events from <code class="highlighter-rouge">03:00</code> until
<code class="highlighter-rouge">06:00</code>, and so on for every 3-hour window in the day.</p>
<p>The tricky bit is that you want to do this in <em>local</em> time, so when the clocks
change for daylight savings time you want some windows to be shorter or longer
than normal so that the times continue to line up with the local times that are
a multiple of 3 hours past midnight.</p>
<p>Because the data contains machine-generated timestamps and is all about events
that have occurred in the past, <a href="/2018/08/12/working-with-timezones.html#future-transitions">you can store the actual event times in
UTC</a>. The
question is then how to identify the window in which any given data point lies.
A good way to do this is to truncate each time, rounding it down to the nearest
multiple of <code class="highlighter-rouge">03:00</code>, and then use this truncated time to identify events that
occur in the same window.</p>
<p>On a local-universal time graph the equally-spaced local times can be marked on
the vertical axis:</p>
<p><img src="/assets/2019-04-timezone-rounding/01-rounded-local-times.png" alt="" /></p>
<p>Of course if the clocks are not changing then this means that the “rounded”
times are also equally spaced on the horizontal axis, although they might not
be nice round times if the timezone’s offset from UTC is not a multiple of 3
hours:</p>
<p><img src="/assets/2019-04-timezone-rounding/02-rounded-local-times-conversions.png" alt="" /></p>
<p>The basic idea for truncating a time is to convert it to local time, round it
down, and then convert it back to universal time again:</p>
<p><img src="/assets/2019-04-timezone-rounding/03-fixed-offset.png" alt="" /></p>
<p>Of course, with time calculations nothing is ever so simple.</p>
<h3 id="when-the-clocks-go-back">When the clocks go back</h3>
<p>When rounding down a time just before the clocks go back, this process still
works correctly:</p>
<p><img src="/assets/2019-04-timezone-rounding/04-clocks-go-back-ok-before-transition.png" alt="" /></p>
<p>The process might also work correctly when rounding down a time just after the
clocks go back:</p>
<p><img src="/assets/2019-04-timezone-rounding/05-clocks-go-back-ok-after-transition.png" alt="" /></p>
<p>Of course you have to be careful with the “convert back to universal time”
step, because in both of the cases above there are two universal times
corresponding with the rounded local time. Fortunately it’s not too hard to
compute them both and choose the right one.</p>
<p>There are other cases just after a clock change where the process doesn’t work.
Rounding the local time down would give a result that’s too early. Instead it
looks like you have to round the local time up:</p>
<p><img src="/assets/2019-04-timezone-rounding/06-clocks-go-back-needs-rounding-up.png" alt="" /></p>
<p>Notice that this is nothing to do with the ambiguity in the “convert back to
universal time” step, because if we had rounded the local time down here then
there would have been no ambiguity.</p>
<p>Futhermore it’s not enough to try both rounding up and down, because sometimes
you might even need to skip a rounded local time when rounding it up:</p>
<p><img src="/assets/2019-04-timezone-rounding/07-clocks-go-back-needs-rounding-up-and-skip.png" alt="" /></p>
<p>The problem in these cases is really that the input time and the result are on
opposite sides of the time when the clocks change. We’re looking for the
greatest possible rounded time, but the naive process might yield a time that’s
far too early. The error is bounded by the amount by which the clocks have
changed. Fortunately there’s no need to do any kind of expensive search for a
better time to correct for this, because we know that there’s no “rounded”
universal time in between the input time and the transition (inclusive) and
this means that it’s sufficient to go back to just before the transition and
try and round that down instead:</p>
<p><img src="/assets/2019-04-timezone-rounding/08-clocks-go-back-retry-at-transition.png" alt="" /></p>
<p>Another problem that can arise when the clocks go back is that sometimes you do
not want to duplicate a rounded time. For instance, it’s common for windows to
be some number of days long, so that typically each window will start at
midnight. However if the <a href="/2018/08/12/working-with-timezones.html#effects-on-midnight-and-other-special-times">clocks are set back across midnight</a> then some days will contain two
midnights and yet you might not want to consider the second such midnight to be
“rounded”:</p>
<p><img src="/assets/2019-04-timezone-rounding/09-clocks-go-back-duplicated-midnight.png" alt="" /></p>
<p>There’s no technical reason for choosing one way or another: it’s purely a
business decision as to which duplicated times should be considered to be
rounded. Sometimes it’s ok to use both, but in other situations you might want
only the earlier or the later of the two.</p>
<h3 id="when-the-clocks-go-forwards">When the clocks go forwards</h3>
<p>When the clocks are set forwards there are some local times with no
corresponding universal time, which raises the question of what one should do
if a rounded local time falls in this gap:</p>
<p><img src="/assets/2019-04-timezone-rounding/08-clocks-go-forward.png" alt="" /></p>
<p>The solution depends on whether or not you want to consider a transition time
to be rounded if it contains a rounded local time. If you do want to do this
then the transition time is the correct result:</p>
<p><img src="/assets/2019-04-timezone-rounding/09-clocks-go-forward-use-transition.png" alt="" /></p>
<p>However if you do not want to consider the transition time to be rounded then
you know that there’s no other rounded time in between the input time and the
transition (inclusive) which means that it’s sufficient to go back to just
before the transition and try and round that down instead:</p>
<p><img src="/assets/2019-04-timezone-rounding/10-clocks-go-forward-retry-at-transition.png" alt="" /></p>
<p>As above, there’s no technical reason for choosing one way or another: it’s
purely down to your requirements.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Rounding a time is a little tricky when you need to work in local time and
account for clock changes. A local-universal time diagram helps to visualise
some of the problems you might face, to identify some of the decisions you need
to make, and to design a robust solution.</p>Here’s an example of the sort of thing you can do with local-universal time diagrams. Suppose you have some data about events that occurred at particular times in a system, such as log messages. You want to calculate some aggregate statistics about its behaviour over time. For instance you might want to aggregate all the events from 00:00 to 03:00 together, and then all the events from 03:00 until 06:00, and so on for every 3-hour window in the day.Programming an ATMega328P with a Raspberry Pi2019-02-23T01:23:45+00:002019-02-23T01:23:45+00:00https://davecturner.github.io/2019/02/23/programming-avr-microcontrollers<p>The <a href="https://en.wikipedia.org/wiki/AVR_microcontrollers">AVR</a> series of
microcontrollers are wonderful little devices with surprisingly many features
built-in. The ATMega328P model sits at the heart of the famous <a href="https://en.wikipedia.org/wiki/Arduino">Arduino
Uno</a> boards, and much of the useful
functionality available on an Arduino actually comes directly from the
microcontroller. The Arduino board adds handy things like USB support and
compatibility with lots of add-on peripherals, but they’re expensive enough to
make me think twice before hard-wiring them into something that just makes some
lights flash on and off or something equally daft. The bare microcontrollers,
on the other hand, are a fraction of the price, so I’m happy to use them fairly
freely.</p>
<p>One of the features that the Arduino adds is a development environment that
lets you program the microcontroller over the USB connection. “Programming”
here means writing the compiled firmware into the on-chip flash memory so the
microprocessor can execute it. Programming the microcontroller is obviously
essential in any project, and this article is all about how I do that without
needing the whole Arduino board around it.</p>
<h2 id="connecting-a-programmer">Connecting a programmer</h2>
<p>AVR microcontrollers support <em>in-system programming</em> (ISP): you can reset them
by pulling the <code class="highlighter-rouge">!RESET</code> pin low and then releasing it, and while the <code class="highlighter-rouge">!RESET</code>
pin is low you can talk to them over their Serial Peripheral Interface (SPI)
and give them instructions to overwrite the program stored in their Flash
memory. The SPI is three lines (called <code class="highlighter-rouge">SCLK</code>, <code class="highlighter-rouge">MISO</code> and <code class="highlighter-rouge">MOSI</code>), and this
means that you need at least 5 lines for ISP (3 lines for SPI, the <code class="highlighter-rouge">!RESET</code>
line and a common ground). Programmers normally add a power line so you don’t
need to provide power from elsewhere during programming, and the standard
connector for ISP is a 3x2 array of pins:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> MISO ○ ○ Power
SCLK ○ ○ MOSI
!RESET ○ ○ GND
</code></pre></div></div>
<p>You can buy a USB-connected programmer for a few pounds (dollars, euros, etc.)
that will attach your computer to this ISP connector and upload programs
directly to the microcontroller. However, this connector is a royal pain if you
want to build your circuit on
<a href="https://en.wikipedia.org/wiki/Stripboard">stripboard</a> because it means you
have to break the tracks <em>between</em> holes, and also add quite a lot of wiring to
join the connector’s pins up with the appropriate pins on the chip. Here’s what
you’re aiming for on an ATMega328P:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> !RESET 1 ˘ ○
○ ○
○ ○
○ ○
○ ○
○ ○
Power ○ ○ GND
○ ○
○ ○ Power
○ ○ SCLK
○ ○ MISO
○ ○ MOSI
○ ○
○ ○
</code></pre></div></div>
<p>As you can see, five of the six lines are <em>right next to each other</em>, so it
makes a bunch of sense to use this arrangement instead:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ○ GND
○ !RESET
○ Power
○ SCLK
○ MISO
○ MOSI
</code></pre></div></div>
<p>This layout means that on stripboard you can line it up with the pins on the
chip and save a bunch of effort: just break the track from the <code class="highlighter-rouge">!RESET</code> line to
the pin on the chip and route that, and the power line, round to the other
side. Simples. I typically use a 6x1 <a href="https://en.wikipedia.org/wiki/Pin_header">pin
header</a> for the physical connector.
Here is a photo of what it looks like in situ on a recent project:</p>
<p><img src="/assets/2019-02-23-programming-avr-microcontrollers/01-circuit.jpg" alt="stripboard circuit including pin header for programming" /></p>
<p>The ISP connector is the bottom 6 pins of the 8-pin header on the right, and
the orange and red wires route things round to the far side of the chip.</p>
<h2 id="el-cheapo-multiway-connectors">El cheapo multiway connectors</h2>
<p>I normally connect to the pin header using a ribbon cable. I like the
multicoloured ribbons that are sold fairly cheaply as “DuPont” wires on eBay.
These are labelled with the same <a href="https://en.wikipedia.org/wiki/Electronic_color_code">colour code as
resistors</a>, and I use
these consecutive colours:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ○ GND = Black
○ !RESET = Brown
○ Power = Red
○ SCLK = Orange
○ MISO = Yellow
○ MOSI = Green
</code></pre></div></div>
<p>It is a very happy coincidence that this lets <code class="highlighter-rouge">GND</code> be black and the power line
be red. I initially thought it’d be more elegant to have <code class="highlighter-rouge">GND</code> next to the
power line, but I was wrong.</p>
<p>The ribbon cables I use come with a separate connector for each pin. After a
while it gets boring connecting the programmer pin-by-pin, so I normally add a
blob of epoxy resin to stick them all together in the right order. Here’s the
one that goes with the circuit pictured above, using a white and a grey wire
for the extra two pins.</p>
<p><img src="/assets/2019-02-23-programming-avr-microcontrollers/02-uc-connector.jpg" alt="ribbon cable configured for programming" /></p>
<h2 id="raspberry-pi-as-programmer">Raspberry Pi as programmer</h2>
<p>As I mentioned above, you can get a USB-connected programmer quite cheaply. But
for a little bit more money you can get a <a href="https://en.wikipedia.org/wiki/Raspberry_Pi">Raspberry
Pi</a>, a fully-fledged
general-purpose computer running Linux that connects to your network, but which
nonetheless lets you natively talk directly to hardware in ways that I fondly
remember first doing with a <a href="https://en.wikipedia.org/wiki/BBC_Micro">BBC
Micro</a> back in the day.</p>
<p>In particular, Raspberry Pis have a SPI which can be used to program an AVR
microcontroller. The most convenient pins to use are here on the standard
40-pin connector, using GPIO pin <code class="highlighter-rouge">22</code> for the <code class="highlighter-rouge">!RESET</code> line:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 1 ○
○ ○
○ ○
○ ○
○ ○
○ ○
○ ○
GPIO 22 ○ ○
+3.3V ○ ○
MOSI ○ ○
MISO ○ ○
SCLK ○ ○
GND ○ ○
○ ○
○ ○
○ ○
○ ○
○ ○
○ ○
○ ○
</code></pre></div></div>
<p>Note that the IO pins on a Pi run at 3.3V whereas Arduinos run at 5V. You will
apparently blow up your Pi if you expose it to a 5V signal on an IO pin, and
conversely 3.3V isn’t a big enough voltage to reliably register as a logically
<code class="highlighter-rouge">HIGH</code> signal in a 5V circuit, so they’re not trivially compatible. However,
AVR microcontrollers themselves run just fine at 3.3V, so you can connect them
together using 3.3V power as long as you’re sure there’s no 5V power anywhere
in the circuit.</p>
<p>Of course these pins are in a different order from the ones on the
microcontroller, but it’s great that they’re all adjacent: just like at the
other end, it’s easy enough to make up a suitable connector by rearranging the
wires and adding a blob of epoxy to keep them there:</p>
<p><img src="/assets/2019-02-23-programming-avr-microcontrollers/03-pi-connector.jpg" alt="ribbon cable configured for Raspberry Pi" /></p>
<h2 id="uploading-firmware">Uploading firmware</h2>
<p>That’s about it for the hardware side of things. Everything else needed to
program the microcontroller is software. The usual workflow is to write the
firmware for your project in some programming language (often C or C++) and
then to compile it down to the raw bytes that need to be written to the
microcontroller. The result of this process is a <code class="highlighter-rouge">.hex</code> file. If you are using
the Arduino development environment then you can compile your sketch to a
<code class="highlighter-rouge">.hex</code> file with the <em>Sketch > Export Compiled Binary</em> option, and find it with
<em>Sketch > Show Sketch Folder</em>.</p>
<p>Once you have a <code class="highlighter-rouge">.hex</code> file you can copy it into the on-chip flash memory on
the microcontroller using the <a href="http://savannah.nongnu.org/projects/avrdude"><em>AVR
Downloader/UploaDEr</em></a>, a.k.a.
<code class="highlighter-rouge">avrdude</code>. The operating system of choice on a Pi is
<a href="https://www.raspberrypi.org/downloads/raspbian/">Raspbian</a>, a <a href="https://www.debian.org">Debian
Linux</a> derivative, which uses <code class="highlighter-rouge">apt</code> for package
management, so you can install <code class="highlighter-rouge">avrdude</code> with <code class="highlighter-rouge">sudo apt-get install avrdude</code>.
It supports a wide variety of different programmers, all configured in
<code class="highlighter-rouge">/etc/avrdude.conf</code>. The one we want to use is called <code class="highlighter-rouge">linuxspi</code> and by default
it uses GPIO pin <code class="highlighter-rouge">25</code> for the reset line, but I prefer to use pin <code class="highlighter-rouge">22</code> so this
needs adjusting:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#This programmer uses the built in linux SPI bus devices to program an
#attached AVR. A GPIO accessed through the sysfs GPIO interface needs to
#be specified for a reset pin since the linux SPI userspace functions do
#not allow for control over the slave select/chip select signal.
#
programmer
id = "linuxspi";
desc = "Use Linux SPI device in /dev/spidev*";
type = "linuxspi";
reset = 22;
baudrate=200000;
;
</code></pre></div></div>
<p>You also need to enable the SPI interface by running <code class="highlighter-rouge">sudo raspi-config</code> and
selecting the appropriate options. Then you can write <code class="highlighter-rouge">firmware.hex</code> to an
ATMega328P with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo avrdude -P /dev/spidev0.0 -c linuxspi -p m328p -U flash:w:firmware.hex
</code></pre></div></div>
<h2 id="developing-firmware-on-the-pi">Developing firmware on the Pi</h2>
<p>You can write the firmware and compile it directly on the Raspberry Pi too,
using the AVR toolchain including the <code class="highlighter-rouge">gcc-avr</code> cross-compiler and the
<code class="highlighter-rouge">avr-libc</code> library.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo apt-get install gcc-avr avr-libc
</code></pre></div></div>
<p>A <a href="https://en.wikipedia.org/wiki/%22Hello,_World!%22_program">good first
program</a> for a
microcontroller is one that makes an LED flash. Connect an LED and a 330Ω
current-limiting resistor to <code class="highlighter-rouge">PB0</code> (the bottom-left pin) and save the following
program to a file called <code class="highlighter-rouge">blink.c</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include <avr/io.h>
#include <util/delay_basic.h>
int main (void)
{
DDRB |= _BV(PORTB0); // set pin 0 of port B as an output pin
for (;;) {
PORTB |= _BV(PORTB0); // set pin 0 of port B high
_delay_loop_2(62500); // loop for 62500 iterations * 4 cycles / 1MHz clock ~= 250ms
PORTB &= ~_BV(PORTB0); // set pin 0 of port B low
_delay_loop_2(62500); // loop for 62500 iterations * 4 cycles / 1MHz clock ~= 250ms
}
}
</code></pre></div></div>
<p>Compile this to <code class="highlighter-rouge">blink.o</code> with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avr-gcc -Os -mmcu=atmega328p -I/usr/lib/avr/include -c blink.c
</code></pre></div></div>
<p>Link it to <code class="highlighter-rouge">blink.elf</code> with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avr-gcc -mmcu=atmega328p -o blink.elf blink.o
</code></pre></div></div>
<p>Create the firmware image <code class="highlighter-rouge">blink.hex</code> with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
</code></pre></div></div>
<p>Finally upload the firmware to your microprocessor with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo avrdude -P /dev/spidev0.0 -c linuxspi -p m328p -U flash:w:blink.hex
</code></pre></div></div>
<p>You can also dump the contents of <code class="highlighter-rouge">blink.elf</code> with</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>avr-objdump -h -S blink.elf > blink.dump
</code></pre></div></div>
<p>This makes it possible to look into how the compiler works. The basic AVR
instruction set is 8-bit (with a few 16-bit operations on certain registers)
and has no floating-point instructions, so it’s interesting to see how
operations on <code class="highlighter-rouge">uint32_t</code> and <code class="highlighter-rouge">float</code> variables are implemented. You can also
see how much extra code is needed to use everyday functions like <code class="highlighter-rouge">snprintf()</code>
or <code class="highlighter-rouge">malloc()</code> on a microcontroller.</p>The AVR series of microcontrollers are wonderful little devices with surprisingly many features built-in. The ATMega328P model sits at the heart of the famous Arduino Uno boards, and much of the useful functionality available on an Arduino actually comes directly from the microcontroller. The Arduino board adds handy things like USB support and compatibility with lots of add-on peripherals, but they’re expensive enough to make me think twice before hard-wiring them into something that just makes some lights flash on and off or something equally daft. The bare microcontrollers, on the other hand, are a fraction of the price, so I’m happy to use them fairly freely.Kitty Grundman’s self referential puzzle2018-10-22T00:00:00+00:002018-10-22T00:00:00+00:00https://davecturner.github.io/2018/10/22/kitty-grundman-self-referential-puzzle<p>Linked from <a href="/2018/10/21/self-referential-aptitude-test.html">James Propp’s self-referential aptitude test</a> is <a href="http://faculty.uml.edu/jpropp/kitty.txt">another delightfully
self-referential puzzle attributed to Kitty
Grundman</a>, <a href="https://www.brynmawr.edu/people/helen-g-grundman">Helen
Grundman</a>’s cat:</p>
<hr />
<p>Below are ten statements concerning X, a whole number between 1 and 10
(inclusive). Not all of the statements are true, but not all of them are false
either. What number is X?</p>
<ol>
<li>
<p>X equals the sum of the statement numbers of the false statements in this
list.</p>
</li>
<li>
<p>X is less than the number of false statements in this list, and statement 10
is true.</p>
</li>
<li>
<p>There are exactly three true statements in this list, or statement 1 is
false, but not both.</p>
</li>
<li>
<p>The previous three statements are all false, or statement 9 is true, or
both.</p>
</li>
<li>
<p>Either X is odd, or statement 7 is true, but not both.</p>
</li>
<li>
<p>Exactly two of the odd-numbered statements are false.</p>
</li>
<li>
<p>X is the number of a true statement.</p>
</li>
<li>
<p>The even-numbered statements are either all true or all false.</p>
</li>
<li>
<p>X equals three times the statement number of the first true statement in
this list, or statement 4 is false, or both.</p>
</li>
<li>
<p>X is even, or statement 6 is true, or both.</p>
</li>
</ol>
<hr />
<p><strong>There’s spoilers throughout this article</strong>, so you should stop reading now if
you want to try this puzzle yourself instead.</p>
<p>The killer feature here is the fact that we don’t even know which statements
are true up front, and this gets deeply confusing and makes it very difficult
to see what the next step might be. A perfect opportunity for formalisation.
Although it doesn’t say so, it turns out that the truth or falsehood of each
statement is uniquely determined too.</p>
<h2 id="modelling-the-puzzle">Modelling the puzzle</h2>
<p>We start by defining the set of statement numbers.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition statementNumber :: "nat set" where "statementNumber ≡ {1..10}"
</code></pre></div></div>
<p>It’s occasionally handy to be able to do a case split on a statement number,
which this lemma enables.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma statementNumber_cases[consumes 1]:
assumes q: "n ∈ statementNumber"
assumes "n = 1 ⟹ P"
assumes "n = 2 ⟹ P"
assumes "n = 3 ⟹ P"
assumes "n = 4 ⟹ P"
assumes "n = 5 ⟹ P"
assumes "n = 6 ⟹ P"
assumes "n = 7 ⟹ P"
assumes "n = 8 ⟹ P"
assumes "n = 9 ⟹ P"
assumes "n = 10 ⟹ P"
shows P
proof -
note q
also have "statementNumber = set [1..<11]" unfolding atLeastAtMost_upt statementNumber_def by simp
also have "… = set [1,2,3,4,5,6,7,8,9,10]"
apply (intro cong [OF refl, where f = set])
by (simp add: upt_rec)
finally show P using assms by auto
qed
</code></pre></div></div>
<p>It’s also useful later on to have a type that comprises just the valid
statement numbers.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typedef Statement = statementNumber unfolding statementNumber_def by auto
</code></pre></div></div>
<p>The puzzle itself is defined in a locale, which starts by introducing the two
free variables in the problem:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale Kitty =
fixes X :: nat
assumes X_bounds: "1 ≤ X" "X ≤ 10"
fixes isTrueStatement :: "Statement ⇒ bool"
</code></pre></div></div>
<p>It’s a lot more useful to be able to talk about the truth of statements
directly in terms of their numbers so we also define some helper functions:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes isTrue :: "nat ⇒ bool"
defines "isTrue ≡ isTrueStatement ∘ Abs_Statement"
fixes isFalse :: "nat ⇒ bool"
defines "isFalse ≡ (op = False) ∘ isTrue"
fixes trueStatements :: "nat set"
defines "trueStatements ≡ {n ∈ statementNumber. isTrue n}"
fixes falseStatements :: "nat set"
defines "falseStatements ≡ {n ∈ statementNumber. isFalse n}"
</code></pre></div></div>
<p>The preamble to the puzzle tells us that <code class="highlighter-rouge">trueStatements</code> and <code class="highlighter-rouge">falseStatements</code>
are nonempty but in fact we do not need this information so it’s omitted from
the model. The statements themselves follow.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (* 1. X equals the sum of the statement numbers of the false statements in this list. *)
assumes s1: "isTrue 1 = (X = ∑falseStatements)"
(* 2. X is less than the number of false statements in this list, and statement 10 is true. *)
assumes s2: "isTrue 2 = (X < card falseStatements ∧ isTrue 10)"
(* 3. There are exactly three true statements in this list, or statement 1 is false, but not both. *)
assumes s3: "isTrue 3 = ((card trueStatements = 3) ≠ (isFalse 1))"
(* 4. The previous three statements are all false, or statement 9 is true, or both. *)
assumes s4: "isTrue 4 = ({1,2,3} ⊆ falseStatements ∨ isTrue 9)"
(* 5. Either X is odd, or statement 7 is true, but not both. *)
assumes s5: "isTrue 5 = (odd X ≠ isTrue 7)"
(* 6. Exactly two of the odd-numbered statements are false. *)
assumes s6: "isTrue 6 = (card (falseStatements ∩ Collect odd) = 2)"
(* 7. X is the number of a true statement. *)
assumes s7: "isTrue 7 = isTrue X"
(* 8. The even-numbered statements are either all true or all false. *)
assumes s8: "isTrue 8 = (falseStatements ∩ Collect even = {} ∨ trueStatements ∩ Collect even = {})"
(* 9. X equals three times the statement number of the first true statement in this list, or statement 4 is false, or both. *)
assumes s9: "isTrue 9 = (X = 3 * (LEAST n. n ∈ trueStatements) ∨ isFalse 4)"
(* 10. X is even, or statement 6 is true, or both. *)
assumes s10: "isTrue 10 = (even X ∨ isTrue 6)"
</code></pre></div></div>
<p>This is mostly straightforward but there are a couple of things worth pointing
out. Firstly, two of the statements are of the form “A or B but not both”, i.e.
an exclusive disjunction, which is written <code class="highlighter-rouge">≠</code>. This wasn’t obvious to me at
first but it makes sense if you draw some truth tables. Secondly, <code class="highlighter-rouge">Collect
even</code> means the same as <code class="highlighter-rouge">{n. even n}</code>; writing <code class="highlighter-rouge">falseStatements ∩ Collect even</code>
instead of <code class="highlighter-rouge">{n. isFalse n ∧ even n}</code> seemed to play more nicely with the proof
automation.</p>
<h2 id="solving-the-puzzle">Solving the puzzle</h2>
<p>We start with some basic statements about cardinalities:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma card_statementNumber: "card statementNumber = 10"
proof -
have p: "statementNumber = {1,2,3,4,5,6,7,8,9,10}"
proof (intro cong [OF refl, where f = card] subsetI equalityI)
fix x assume "x ∈ statementNumber" thus "x ∈ {1,2,3,4,5,6,7,8,9,10}" by (cases rule: statementNumber_cases, auto)
qed (auto simp add: statementNumber_def)
show ?thesis unfolding p by simp
qed
lemma card_trueFalse: "card trueStatements + card falseStatements = 10"
proof -
have "card statementNumber = card (trueStatements ∪ falseStatements)"
by (intro cong [OF refl, where f = card], auto simp add: falseStatements_def trueStatements_def isFalse_def)
also have "… = card trueStatements + card falseStatements"
by (intro card_Un_disjoint, auto simp add: trueStatements_def falseStatements_def isFalse_def statementNumber_def)
finally show ?thesis using card_statementNumber by simp
qed
</code></pre></div></div>
<p>The first step of the puzzle is quite straightforward: if statement 4 is false
then statement 9 is true, which implies that statement 4 is true, a
contradiction. Therefore statement 4 must be true:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma true4: "isTrue 4"
proof (cases "isTrue 4")
case False
hence "isFalse 4" by (simp add: isFalse_def)
with s9 have "isTrue 9" by simp
with s4 show "isTrue 4" by simp
qed simp
</code></pre></div></div>
<p>Statement 9 discusses the lowest-numbered true statement, and since we have
found a true statement we know that the lowest-numbered one is well-defined:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma leastTrue: "(LEAST n. n ∈ trueStatements) ∈ trueStatements"
proof -
from true4 have "4 ∈ trueStatements" by (auto simp add: trueStatements_def statementNumber_def)
thus ?thesis by (intro LeastI)
qed
</code></pre></div></div>
<p>The next step is a bit of a pig, but it turns out that we can now show that
statement 2 is false. The proof is by contradiction and runs as follows.
Suppose statement 2 were true. Then:</p>
<ul>
<li>statement 10 is true.</li>
<li>statement 4 (already known to be true) tells us that statement 9 is true.</li>
<li>statement 9 tells us that X is 3 or 6 since the first true statement is
either 1 or 2.</li>
<li>if X were 6 then according to statement 2 there are ≥ 7 false statements in
the list; however we have found 4 true statements (2, 4, 9 and 10) so this
cannot be; therefore X is 3 and the first true statement is 1.</li>
<li>statement 1 tells us that X (i.e. 3) is the sum of the numbers of the false
statements; statements 1 and 2 are true which means that the only false
statement can be number 3.</li>
<li>this means that in particular statement 7 is true, which says that statement
3 is true, which is a contradiction 🎉</li>
</ul>
<p>Here’s the formal version of this argument:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma false2: "isFalse 2"
proof (rule ccontr)
assume "¬ ?thesis"
hence true2: "isTrue 2" by (auto simp add: isFalse_def)
with s4 true2 true4 have true9: "isTrue 9" by (auto simp add: falseStatements_def isFalse_def)
with s9 true4 have X_3_min: "X = 3 * (LEAST n. n ∈ trueStatements)" by (simp add: isFalse_def)
from s2 true2 have true10: "isTrue 10" by simp
from true2 have "(LEAST n. n ∈ trueStatements) ≤ 2"
by (intro Least_le, simp add: trueStatements_def statementNumber_def)
with leastTrue have "(LEAST n. n ∈ trueStatements) ∈ {1,2}" by (auto simp add: trueStatements_def statementNumber_def)
hence least1: "(LEAST n. n ∈ trueStatements) = 1"
proof (elim insertE)
assume 2: "(LEAST n. n ∈ trueStatements) = 2"
with X_3_min true2 s2 have "6 < card falseStatements" by simp
also have "… = 10 - card trueStatements" using card_trueFalse by simp
also have "… ≤ 10 - card {2,4,9,10::nat}"
using true2 true4 true9 true10 by (intro diff_le_mono2 card_mono, auto simp add: trueStatements_def statementNumber_def)
also have "… = 6" by simp
finally show ?thesis by simp
qed simp_all
from X_3_min least1 have X3: "X = 3" by simp
from least1 leastTrue have true1: "isTrue 1" by (simp add: trueStatements_def)
have falseStatements_3: "falseStatements ⊆ {3}"
proof (intro subsetI)
fix f assume f: "f ∈ falseStatements"
from true1 s1 X3 have "3 = sum id falseStatements" by simp
also from f have "… = sum id (insert f falseStatements)" by (intro cong [OF refl, where f = "sum id"], auto)
also have "… = id f + sum id (falseStatements - {f})" by (intro sum.insert_remove, auto simp add: falseStatements_def statementNumber_def)
also have "… ≥ f" by simp
finally have f3: "f ≤ 3" .
from f have "f ∈ statementNumber" "isFalse f" unfolding falseStatements_def by auto
from this f3 true1 true2 show "f ∈ {3}" by (cases rule: statementNumber_cases, auto simp add: isFalse_def)
qed
hence trueNot3: "⋀n. n ∈ statementNumber ⟹ n ≠ 3 ⟹ isTrue n"
by (auto simp add: falseStatements_def isFalse_def statementNumber_def)
have "isTrue 7" by (intro trueNot3, auto simp add: statementNumber_def)
with s7 X3 have true3: "isTrue 3" by simp
from s2 true2 X_bounds have "card falseStatements ≠ 0" by simp
then obtain f where f: "f ∈ falseStatements" by (cases "falseStatements = {}", auto)
with falseStatements_3 have "f = 3" by auto
with f true3 show False by (auto simp add: falseStatements_def isFalse_def)
qed
</code></pre></div></div>
<p>Phew. After this we get a few easy wins. Firstly, statement 8 is false because
we’ve found even-numbered statements that are both true and false:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma false8: "isFalse 8"
proof (rule ccontr)
assume "¬ isFalse 8" hence true8: "isTrue 8" by (simp add: isFalse_def)
moreover from false2 have "2 ∈ falseStatements ∩ Collect even" by (auto simp add: falseStatements_def statementNumber_def)
moreover from true4 have "4 ∈ trueStatements ∩ Collect even" by (auto simp add: trueStatements_def statementNumber_def)
ultimately show False using s8 by auto
qed
</code></pre></div></div>
<p>This lets us deduce that statement 1 cannot be true, because if it were true
then X ≤ 10 would be equal to the sum of the numbers of the false statements; 2
and 8 are both false and add up to 10, so all the other statements are true.
This contradicts all sorts of things, for instance statement 6 (also statements
3 and 9) so we know that the first statement is false:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma false1: "isFalse 1"
proof (rule ccontr)
assume "¬ isFalse 1" hence true1: "isTrue 1" by (simp add: isFalse_def)
with s1 have "X = sum id falseStatements" by simp
also from false8 have "… = sum id (insert 8 falseStatements)"
by (intro cong [OF refl, where f = "sum id"], auto simp add: falseStatements_def statementNumber_def)
also have "… = id 8 + sum id (falseStatements - {8})"
by (intro sum.insert_remove, auto simp add: falseStatements_def statementNumber_def)
finally have lt2: "∑ (falseStatements - {8}) ≤ 2" using X_bounds by simp
have finf: "finite falseStatements" by (auto simp add: falseStatements_def statementNumber_def)
note true1
with s1 have "X = sum id falseStatements" by simp
also from false2 false8 have "… = sum id (insert 2 (insert 8 falseStatements))"
by (intro cong [OF refl, where f = "sum id"], auto simp add: falseStatements_def statementNumber_def)
also have "… = id 2 + sum id (insert 8 falseStatements - {2})"
using finf by (intro sum.insert_remove, auto)
also have "… = id 2 + sum id (insert 8 (falseStatements - {2}))"
by (intro cong [OF refl, where f = "op + (id 2)"] cong [OF refl, where f = "sum id"], auto)
also have "… = id 2 + (id 8 + sum id (falseStatements - {2} - {8}))"
using finf by (intro cong [OF refl sum.insert_remove, where f = "op + (id 2)"], auto)
also have "… = 10 + sum id (falseStatements - {2} - {8})" by simp
finally have "sum id (falseStatements - {2} - {8}) = 0" using X_bounds by simp
with finf have "∀n ∈ (falseStatements - {2} - {8}). id n = 0"
by (intro iffD1 [OF sum_eq_0_iff], auto)
with false2 false8 have onlyFalse: "falseStatements = {2,8}" by (auto simp add: falseStatements_def statementNumber_def)
have "isFalse 6" by (simp add: isFalse_def s6 onlyFalse)
hence "6 ∈ falseStatements" by (simp add: falseStatements_def statementNumber_def)
with onlyFalse show False by simp
qed
</code></pre></div></div>
<p>The next step took quite some exploration, but it now turns out we can work out
the truth of statement 3 as follows.</p>
<p>Suppose for a contradiction that statement 3 were false. Since we know that
statement 1 is also false this means that (with a bit of boolean algebra) there
are exactly three true statements in the list. Also statement 9 must be false
since the first true statement in the list is at least statement 4, which is
too large. This means there are at least three odd-numbered false statements
(1, 3 and 9) which means that statement 6 is also false. The remaining
statements are numbers 5, 7 and 10:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (* 5. Either X is odd, or statement 7 is true, but not both. *)
assumes s5: "isTrue 5 = (odd X ≠ isTrue 7)"
(* 7. X is the number of a true statement. *)
assumes s7: "isTrue 7 = isTrue X"
(* 10. X is even, or statement 6 is true, or both. *)
assumes s10: "isTrue 10 = (even X ∨ isTrue 6)"
</code></pre></div></div>
<p>We can simplify these a bit: since statement 6 is false, statement 10 is just
“X is even”, and this means that</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isTrue 5 = (even X = isTrue 7)
= (isTrue 10 = isTrue 7).
</code></pre></div></div>
<p>The equality signs here are really bi-implications, which is an associative and
commutative operation, so we could just as well write this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>isTrue 5 = isTrue 7 = isTrue 10
</code></pre></div></div>
<p>However a chain of bi-implications like this is a pretty weird operation: in
general an expression like</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A = B = C = ... = Z
</code></pre></div></div>
<p>is true iff it has an even number of false components. Since in this case we
have three components, this means that an odd number of them must be true. The
only other true statement is number 4, so all told this means that there is an
even number of true statements in the list. But we already knew that there are
three true statements in the list, which is odd, giving us a contradiction.
Hurrah! 🎉 Formally:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma true3: "isTrue 3"
proof (rule ccontr)
assume "¬ isTrue 3" hence false3: "isFalse 3" by (simp add: isFalse_def)
from false3 false1 s3 have threeTruths: "card trueStatements = 3" by (simp add: isFalse_def)
from leastTrue have
"(LEAST n. n ∈ trueStatements) ∈ statementNumber"
"isTrue (LEAST n. n ∈ trueStatements)"
by (auto simp add: trueStatements_def)
from this false1 false2 false3 have "4 ≤ (LEAST n. n ∈ trueStatements)"
by (cases rule: statementNumber_cases, auto simp add: isFalse_def)
with s9 true4 X_bounds have false9: "isFalse 9" by (auto simp add: isFalse_def)
have "3 = card {1,3,9::nat}" by simp
also from false1 false3 false9 have "… ≤ card (falseStatements ∩ {a. odd a})"
by (intro card_mono, auto simp add: falseStatements_def statementNumber_def)
finally have false6: "isFalse 6" using s6 by (auto simp add: isFalse_def)
note known = false1 false2 false3 true4 false6 false8 false9
define whenTrue :: "nat ⇒ nat set" where "⋀n. whenTrue n ≡ {n} ∩ (if isTrue n then UNIV else {})"
have ts: "trueStatements = (whenTrue 5) ∪ (whenTrue 7) ∪ (whenTrue 10) ∪ {4}"
(is "?LHS = ?RHS")
proof (intro equalityI subsetI)
fix n assume "n ∈ ?LHS" hence "n ∈ statementNumber" "isTrue n" by (auto simp add: trueStatements_def)
from this known show "n ∈ ?RHS"
by (cases rule: statementNumber_cases, auto simp add: trueStatements_def statementNumber_def isFalse_def whenTrue_def)
next
fix n assume "n ∈ ?RHS" with true4 show "n ∈ ?LHS"
apply (cases "isTrue 5", auto simp add: trueStatements_def statementNumber_def whenTrue_def)
by (meson empty_iff)+
qed
from s5 s10 false6 have "isTrue 5 = isTrue 7 = isTrue 10" by (auto simp add: isFalse_def)
then consider
(5) "isTrue 5" "isFalse 7" "isFalse 10"
| (7) "isFalse 5" "isTrue 7" "isFalse 10"
| (10) "isFalse 5" "isFalse 7" "isTrue 10"
| (all) "isTrue 5" "isTrue 7" "isTrue 10" by (auto simp add: isFalse_def)
hence "even (card trueStatements)"
unfolding ts by (cases, auto simp add: whenTrue_def isFalse_def)
with threeTruths show False by simp
qed
</code></pre></div></div>
<p>This means that the first disjunct of statement 4 is false, so the second is
true:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma true9: "isTrue 9"
using true3 true4 false1 false2 s4 by (auto simp add: falseStatements_def isFalse_def)
</code></pre></div></div>
<p>And statement 9 tells us what X is:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma X9: "X = 9"
proof -
from true9 true4 s9 have X_3_least: "X = 3 * (LEAST n. n ∈ trueStatements)" by (simp add: isFalse_def)
from leastTrue have
"(LEAST n. n ∈ trueStatements) ∈ statementNumber"
"isTrue (LEAST n. n ∈ trueStatements)"
by (auto simp add: trueStatements_def)
moreover from true3 have "(LEAST n. n ∈ trueStatements) ≤ 3"
by (intro Least_le, auto simp add: trueStatements_def statementNumber_def)
ultimately have "(LEAST n. n ∈ trueStatements) = 3" using false1 false2
by (cases rule: statementNumber_cases, auto simp add: isFalse_def)
with X_3_least show X9: "X = 9" by simp
qed
</code></pre></div></div>
<p>From this point on it’s quite simple to resolve the truths of the remaining
statements:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma true7: "isTrue 7" using X9 true9 s7 by simp
lemma false5: "isFalse 5" using X9 true7 s5 by (simp add: isFalse_def)
lemma true6: "isTrue 6"
proof -
have "falseStatements ∩ Collect odd = {1,5}"
proof (intro equalityI subsetI)
fix n::nat assume "n ∈ {1,5}"
with false1 false5 show "n ∈ falseStatements ∩ {a. odd a}"
by (auto simp add: falseStatements_def statementNumber_def)
next
fix n assume "n ∈ falseStatements ∩ {a. odd a}"
hence "n ∈ statementNumber" "isFalse n" "odd n" by (auto simp add: falseStatements_def)
from this true3 true7 true9 show "n ∈ {1,5}"
by (cases rule: statementNumber_cases, auto simp add: isFalse_def)
qed
with s6 show "isTrue 6" by auto
qed
lemma true10: "isTrue 10" using s10 true6 by simp
</code></pre></div></div>
<p>It remains to validate that this solution really does satisfy the problem
statement, and that the whole puzzle didn’t contain a contradiction. We do this
by showing that we have really found a model for this locale:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition solvedTrueStatement :: "Statement ⇒ bool"
where "solvedTrueStatement s ≡ Rep_Statement s ∈ {3,4,6,7,9,10}"
lemma "Kitty 9 solvedTrueStatement"
proof -
have "(LEAST n. n ∈ {3, 4, 6, 7, 9, 10}) = (3::nat)" by (intro Least_equality, auto)
moreover have p: "⋀n. 1 ≤ n ⟹ n ≤ 10 ⟹ (solvedTrueStatement ∘ Abs_Statement) n = (n ∈ {3,4,6,7,9,10})"
proof -
fix n::nat assume "1 ≤ n" "n ≤ 10" hence "n ∈ statementNumber" by (simp add: statementNumber_def)
from Abs_Statement_inverse [OF this] show "(solvedTrueStatement ∘ Abs_Statement) n = (n ∈ {3,4,6,7,9,10})"
by (simp add: solvedTrueStatement_def)
qed
hence
"(solvedTrueStatement ∘ Abs_Statement) 8 = False"
"{n ∈ statementNumber. (solvedTrueStatement ∘ Abs_Statement) n} = {3,4,6,7,9,10}"
by (auto simp add: statementNumber_def)
moreover have "{n ∈ statementNumber. (op = False ∘ (solvedTrueStatement ∘ Abs_Statement)) n} = {1,2,5,8}"
using p
proof (intro equalityI subsetI)
fix x assume a: "x ∈ {n ∈ statementNumber. (op = False ∘ (solvedTrueStatement ∘ Abs_Statement)) n}"
hence "x ∈ statementNumber" by simp
from this a p show "x ∈ {1,2,5,8}" by (cases rule: statementNumber_cases, auto)
qed (auto simp add: statementNumber_def)
ultimately show ?thesis apply unfold_locales by auto
qed
</code></pre></div></div>
<p>Finally, we can show that the solution is unique:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma (in Kitty) "isTrueStatement = solvedTrueStatement"
proof (intro ext)
fix x
have "isTrueStatement x = isTrue (Rep_Statement x)"
by (simp add: isTrue_def Rep_Statement_inverse)
also from Rep_Statement [of x] false1 false2 true3 true4 false5 true6 true7 false8 true9 true10
have "isTrue (Rep_Statement x) = solvedTrueStatement x"
by (cases rule: statementNumber_cases, auto simp add: solvedTrueStatement_def isFalse_def)
finally show "isTrueStatement x = solvedTrueStatement x".
qed
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>Somewhat surprisingly, we didn’t need to know that there was at least one true
and at least one false statement in the list; moreover we didn’t need to work
out the truth of every statement in order to find X. Despite this, there is in
fact only one way to assign the truths of the statements. I found there to be a
couple of very tricky steps along the way, in which you had to follow a chain
of reasoning for quite some distance before it yielded a contradiction. It was
often hard to see which path to take next, and it was fun to explore all the
possibilities.</p>
<p>I’m impressed by how well-crafted this puzzle is. It all fits together very
elegantly.</p>Linked from James Propp’s self-referential aptitude test is another delightfully self-referential puzzle attributed to Kitty Grundman, Helen Grundman’s cat:The self-referential aptitude test2018-10-21T00:00:00+00:002018-10-21T00:00:00+00:00https://davecturner.github.io/2018/10/21/self-referential-aptitude-test<p>I stumbled across <a href="http://faculty.uml.edu/jpropp/srat-Q.txt">James Propp’s self-referential aptitude
test</a> the other day and thought it’d
be fun to formalise it and its solution in
<a href="https://isabelle.in.tum.de">Isabelle/HOL</a> to make sure I wasn’t making any
invalid logical leaps.</p>
<p>The first half of this post models the test itself (without many spoilers) and
the second half develops the solution (which necessarily contains a lot of
spoilers.) However if you want to experience the test in its full glory it’s
probably best to stop reading now and go and <a href="http://faculty.uml.edu/jpropp/srat-Q.txt">read the
original</a>.</p>
<h2 id="modelling-the-test">Modelling the test</h2>
<p>The test comprises twenty multiple-choice questions, many of which refer to the
other questions by number in some way:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition questionNumber :: "nat set" where "questionNumber ≡ {1..20}"
</code></pre></div></div>
<p>My first attempt defined this as an algebraic datatype:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>datatype Question = Q1 | Q2 | ...
</code></pre></div></div>
<p>This gave me some useful properties, like the ability to prove things by simply
enumerating all the cases and checking them one-by-one, but also lost the
ability to easily talk about “odd-numbered questions” or “questions preceding
11”. We could develop the theory needed for that, of course, but here it’s
simpler to model them as a subset of the natural numbers. We can regain the
ability to enumerate all the cases using this lemma:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma questionNumber_cases[consumes 1]:
assumes q: "n ∈ questionNumber"
assumes "n = 1 ⟹ P"
assumes "n = 2 ⟹ P"
assumes "n = 3 ⟹ P"
assumes "n = 4 ⟹ P"
assumes "n = 5 ⟹ P"
assumes "n = 6 ⟹ P"
assumes "n = 7 ⟹ P"
assumes "n = 8 ⟹ P"
assumes "n = 9 ⟹ P"
assumes "n = 10 ⟹ P"
assumes "n = 11 ⟹ P"
assumes "n = 12 ⟹ P"
assumes "n = 13 ⟹ P"
assumes "n = 14 ⟹ P"
assumes "n = 15 ⟹ P"
assumes "n = 16 ⟹ P"
assumes "n = 17 ⟹ P"
assumes "n = 18 ⟹ P"
assumes "n = 19 ⟹ P"
assumes "n = 20 ⟹ P"
shows P
proof -
note q
also have "questionNumber = set [1..<21]" unfolding atLeastAtMost_upt questionNumber_def by simp
also have "… = set [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]"
apply (intro cong [OF refl, where f = set])
by (simp add: upt_rec)
finally show P using assms by auto
qed
</code></pre></div></div>
<p>It’s also useful to have a datatype that comprises <em>just</em> the question numbers:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typedef Question = "questionNumber" unfolding questionNumber_def by auto
</code></pre></div></div>
<p>Each question has 5 answers, which is appropriate to model as an algebraic
datatype:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>datatype Answer = A | B | C | D | E
</code></pre></div></div>
<p>We model the questions themselves as logical statements in a locale, with a
single free variable <code class="highlighter-rouge">f</code> which assigns an answer to each question.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale Test =
fixes f :: "Question ⇒ Answer"
</code></pre></div></div>
<p>In fact it’s way more useful to consider the question numbers as numbers, so we
define an auxiliary function <code class="highlighter-rouge">nat ⇒ Answer</code> in terms of <code class="highlighter-rouge">f</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes answer :: "nat ⇒ Answer"
defines "answer n ≡ f (Abs_Question n)"
</code></pre></div></div>
<h3 id="question-1">Question 1</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
1. The first question whose answer is B is question
(A) 1
(B) 2
(C) 3
(D) 4
(E) 5
*)
fixes firstQuestionWithAnswerB :: nat
defines "firstQuestionWithAnswerB ≡ LEAST n. 1 ≤ n ∧ answer n = B"
assumes q1a: "(answer 1 = A) = (firstQuestionWithAnswerB = 1)"
assumes q1b: "(answer 1 = B) = (firstQuestionWithAnswerB = 2)"
assumes q1c: "(answer 1 = C) = (firstQuestionWithAnswerB = 3)"
assumes q1d: "(answer 1 = D) = (firstQuestionWithAnswerB = 4)"
assumes q1e: "(answer 1 = E) = (firstQuestionWithAnswerB = 5)"
</code></pre></div></div>
<p>This isn’t quite the full story for this question, because of <a href="/2018/04/09/partial-functions-isabelle.html">how Isabelle
models ill-defined things</a>. The problem is that if there is no <code class="highlighter-rouge">n ≥ 1</code> with <code class="highlighter-rouge">answer n = B</code> then
<code class="highlighter-rouge">firstQuestionWithAnswerB</code> is not well-defined, so it could be anything, and
strictly speaking the question also tells us there is such a question so that
we can exclude this situation. It turns out not to be necessary to model this
extra fact here.</p>
<h3 id="question-2">Question 2</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
2. The only two consecutive questions with identical answers are questions
(A) 6 and 7
(B) 7 and 8
(C) 8 and 9
(D) 9 and 10
(E) 10 and 11
*)
fixes questionsWithSameAnswerAsNext :: "nat set"
defines "questionsWithSameAnswerAsNext ≡ { n. n ∈ questionNumber ∧ Suc n ∈ questionNumber ∧ answer n = answer (Suc n) }"
assumes q2a: "(answer 2 = A) = (questionsWithSameAnswerAsNext = {6})"
assumes q2b: "(answer 2 = B) = (questionsWithSameAnswerAsNext = {7})"
assumes q2c: "(answer 2 = C) = (questionsWithSameAnswerAsNext = {8})"
assumes q2d: "(answer 2 = D) = (questionsWithSameAnswerAsNext = {9})"
assumes q2e: "(answer 2 = E) = (questionsWithSameAnswerAsNext = {10})"
</code></pre></div></div>
<p>This is quite a useful question: even without knowing its answer, it tells us
that many of the questions definitely have different answers from their
neighbours.</p>
<h3 id="question-3">Question 3</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
3. The number of questions with the answer E is
(A) 0
(B) 1
(C) 2
(D) 3
(E) 4
*)
fixes numberOfQuestionsWithAnswersIn :: "Answer set ⇒ nat"
defines "numberOfQuestionsWithAnswersIn S ≡ card { n ∈ questionNumber. answer n ∈ S }"
assumes q3a: "(answer 3 = A) = (numberOfQuestionsWithAnswersIn {E} = 0)"
assumes q3b: "(answer 3 = B) = (numberOfQuestionsWithAnswersIn {E} = 1)"
assumes q3c: "(answer 3 = C) = (numberOfQuestionsWithAnswersIn {E} = 2)"
assumes q3d: "(answer 3 = D) = (numberOfQuestionsWithAnswersIn {E} = 3)"
assumes q3e: "(answer 3 = E) = (numberOfQuestionsWithAnswersIn {E} = 4)"
</code></pre></div></div>
<p>There are quite a few questions that talk about the number of questions with
particular answers, so we introduce the <code class="highlighter-rouge">numberOfQuestionsWithAnswersIn</code>
function.</p>
<h3 id="question-4">Question 4</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
4. The number of questions with the answer A is
(A) 4
(B) 5
(C) 6
(D) 7
(E) 8
*)
assumes q4a: "(answer 4 = A) = (numberOfQuestionsWithAnswersIn {A} = 4)"
assumes q4b: "(answer 4 = B) = (numberOfQuestionsWithAnswersIn {A} = 5)"
assumes q4c: "(answer 4 = C) = (numberOfQuestionsWithAnswersIn {A} = 6)"
assumes q4d: "(answer 4 = D) = (numberOfQuestionsWithAnswersIn {A} = 7)"
assumes q4e: "(answer 4 = E) = (numberOfQuestionsWithAnswersIn {A} = 8)"
</code></pre></div></div>
<h3 id="question-5">Question 5</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
5. The answer to this question is the same as the answer to question
(A) 1
(B) 2
(C) 3
(D) 4
(E) 5
*)
assumes q5a: "(answer 5 = A) = (answer 5 = answer 1)"
assumes q5b: "(answer 5 = B) = (answer 5 = answer 2)"
assumes q5c: "(answer 5 = C) = (answer 5 = answer 3)"
assumes q5d: "(answer 5 = D) = (answer 5 = answer 4)"
assumes q5e: "(answer 5 = E) = (answer 5 = answer 5)"
</code></pre></div></div>
<p>This question is one of the more mindbending ones, at least partly because
unlike the preceding questions the answers are not inherently mutually
exclusive. However, the nature of this kind of puzzle is that the answers <em>are</em>
all mutually exclusive, which this model captures: for instance if <code class="highlighter-rouge">answer 5 =
answer 1</code> then <code class="highlighter-rouge">answer 5 = A</code> and therefore <code class="highlighter-rouge">answer 5 ≠ B</code> so <code class="highlighter-rouge">answer 5 ≠
answer 2</code> as well.</p>
<h3 id="question-6">Question 6</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
6. The answer to question 17 is
(A) C
(B) D
(C) E
(D) none of the above
(E) all of the above
*)
assumes q6a: "(answer 6 = A) = (answer 17 = C)"
assumes q6b: "(answer 6 = B) = (answer 17 = D)"
assumes q6c: "(answer 6 = C) = (answer 17 = E)"
assumes q6d: "(answer 6 = D) = (answer 17 ∉ {C,D,E})"
assumes q6e: "(answer 6 = E) = (answer 17 = C ∧ answer 17 = D ∧ answer 17 = E ∧ answer 17 ∉ {C,D,E})"
</code></pre></div></div>
<p>I like that there’s a bit of self-reference in the options to this question
here, although it’s quite easy to untangle it.</p>
<h3 id="question-7">Question 7</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
7. Alphabetically, the answer to this question and the answer to the
following question are
(A) 4 apart
(B) 3 apart
(C) 2 apart
(D) 1 apart
(E) the same
*)
assumes q7a: "(answer 7 = A) = ({answer 7, answer 8} ∈ { {A,E} })"
assumes q7b: "(answer 7 = B) = ({answer 7, answer 8} ∈ { {A,D},{B,E} })"
assumes q7c: "(answer 7 = C) = ({answer 7, answer 8} ∈ { {A,C},{B,D},{C,E} })"
assumes q7d: "(answer 7 = D) = ({answer 7, answer 8} ∈ { {A,B},{B,C},{C,D},{D,E} })"
assumes q7e: "(answer 7 = E) = (answer 7 = answer 8)"
</code></pre></div></div>
<p>I couldn’t think of a slicker way to model this question than simply
enumerating all the possibilities. If there were many questions of this form
then perhaps it’d make more sense to develop more machinery on top of the
<code class="highlighter-rouge">Answer</code> type, but there aren’t so I didn’t.</p>
<h3 id="question-8">Question 8</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
8. The number of questions whose answers are vowels is
(A) 4
(B) 5
(C) 6
(D) 7
(E) 8
*)
assumes q8a: "(answer 8 = A) = (numberOfQuestionsWithAnswersIn {A,E} = 4)"
assumes q8b: "(answer 8 = B) = (numberOfQuestionsWithAnswersIn {A,E} = 5)"
assumes q8c: "(answer 8 = C) = (numberOfQuestionsWithAnswersIn {A,E} = 6)"
assumes q8d: "(answer 8 = D) = (numberOfQuestionsWithAnswersIn {A,E} = 7)"
assumes q8e: "(answer 8 = E) = (numberOfQuestionsWithAnswersIn {A,E} = 8)"
</code></pre></div></div>
<p>Similarly to question 7, it’s simplest to just enumerate the vowels.</p>
<h3 id="question-9">Question 9</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
9. The next question with the same answer as this one is question
(A) 10
(B) 11
(C) 12
(D) 13
(E) 14
*)
assumes q9a: "(answer 9 = A) = ((LEAST n. 9 < n ∧ answer n = answer 9) = 10)"
assumes q9b: "(answer 9 = B) = ((LEAST n. 9 < n ∧ answer n = answer 9) = 11)"
assumes q9c: "(answer 9 = C) = ((LEAST n. 9 < n ∧ answer n = answer 9) = 12)"
assumes q9d: "(answer 9 = D) = ((LEAST n. 9 < n ∧ answer n = answer 9) = 13)"
assumes q9e: "(answer 9 = E) = ((LEAST n. 9 < n ∧ answer n = answer 9) = 14)"
</code></pre></div></div>
<p>Similarly to question 1, this model omits the fact that there <em>is</em> a later
question with the same answer, but this turns out not to be necessary.</p>
<h3 id="question-10">Question 10</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
10. The answer to question 16 is
(A) D
(B) A
(C) E
(D) B
(E) C
*)
assumes q10a: "(answer 10 = A) = (answer 16 = D)"
assumes q10b: "(answer 10 = B) = (answer 16 = A)"
assumes q10c: "(answer 10 = C) = (answer 16 = E)"
assumes q10d: "(answer 10 = D) = (answer 16 = B)"
assumes q10e: "(answer 10 = E) = (answer 16 = C)"
</code></pre></div></div>
<h3 id="question-11">Question 11</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
11. The number of questions preceding this one with the answer B is
(A) 0
(B) 1
(C) 2
(D) 3
(E) 4
*)
assumes q11a: "(answer 11 = A) = (card { n ∈ questionNumber. n < 11 ∧ answer n = B } = 0)"
assumes q11b: "(answer 11 = B) = (card { n ∈ questionNumber. n < 11 ∧ answer n = B } = 1)"
assumes q11c: "(answer 11 = C) = (card { n ∈ questionNumber. n < 11 ∧ answer n = B } = 2)"
assumes q11d: "(answer 11 = D) = (card { n ∈ questionNumber. n < 11 ∧ answer n = B } = 3)"
assumes q11e: "(answer 11 = E) = (card { n ∈ questionNumber. n < 11 ∧ answer n = B } = 4)"
</code></pre></div></div>
<h3 id="question-12">Question 12</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
12. The number of questions whose answer is a consonant is
(A) an even number
(B) an odd number
(C) a perfect square
(D) a prime
(E) divisible by 5
*)
assumes q12a: "(answer 12 = A) = (even (numberOfQuestionsWithAnswersIn {B,C,D}))"
assumes q12b: "(answer 12 = B) = (odd (numberOfQuestionsWithAnswersIn {B,C,D}))"
assumes q12c: "(answer 12 = C) = (numberOfQuestionsWithAnswersIn {B,C,D} ∈ {1,4,9,16})"
assumes q12d: "(answer 12 = D) = (numberOfQuestionsWithAnswersIn {B,C,D} ∈ {2,3,5,7,11,13,17,19})"
assumes q12e: "(answer 12 = E) = (numberOfQuestionsWithAnswersIn {B,C,D} ∈ {5,10,15,20})"
</code></pre></div></div>
<p>I could have encoded the properties “a perfect square”, “prime” and “divisible
by 5” more faithfully, but there’s only 20 questions so enumerating the
possibilities by hand was much quicker.</p>
<h3 id="question-13">Question 13</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
13. The only odd-numbered problem with answer A is
(A) 9
(B) 11
(C) 13
(D) 15
(E) 17
*)
assumes q13a: "(answer 13 = A) = ({n ∈ questionNumber. odd n ∧ answer n = A} = {9})"
assumes q13b: "(answer 13 = B) = ({n ∈ questionNumber. odd n ∧ answer n = A} = {11})"
assumes q13c: "(answer 13 = C) = ({n ∈ questionNumber. odd n ∧ answer n = A} = {13})"
assumes q13d: "(answer 13 = D) = ({n ∈ questionNumber. odd n ∧ answer n = A} = {15})"
assumes q13e: "(answer 13 = E) = ({n ∈ questionNumber. odd n ∧ answer n = A} = {17})"
</code></pre></div></div>
<p>This immediately tells us that many odd-numbered problems do <em>not</em> have the
answer A, which could be quite useful.</p>
<h3 id="question-14">Question 14</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
14. The number of questions with answer D is
(A) 6
(B) 7
(C) 8
(D) 9
(E) 10
*)
assumes q14a: "(answer 14 = A) = (numberOfQuestionsWithAnswersIn {D} = 6)"
assumes q14b: "(answer 14 = B) = (numberOfQuestionsWithAnswersIn {D} = 7)"
assumes q14c: "(answer 14 = C) = (numberOfQuestionsWithAnswersIn {D} = 8)"
assumes q14d: "(answer 14 = D) = (numberOfQuestionsWithAnswersIn {D} = 9)"
assumes q14e: "(answer 14 = E) = (numberOfQuestionsWithAnswersIn {D} = 10)"
</code></pre></div></div>
<h3 id="question-15">Question 15</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
15. The answer to question 12 is
(A) A
(B) B
(C) C
(D) D
(E) E
*)
assumes q15a: "(answer 15 = A) = (answer 12 = A)"
assumes q15b: "(answer 15 = B) = (answer 12 = B)"
assumes q15c: "(answer 15 = C) = (answer 12 = C)"
assumes q15d: "(answer 15 = D) = (answer 12 = D)"
assumes q15e: "(answer 15 = E) = (answer 12 = E)"
</code></pre></div></div>
<h3 id="question-16">Question 16</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
16. The answer to question 10 is
(A) D
(B) C
(C) B
(D) A
(E) E
*)
assumes q16a: "(answer 16 = A) = (answer 10 = D)"
assumes q16b: "(answer 16 = B) = (answer 10 = C)"
assumes q16c: "(answer 16 = C) = (answer 10 = B)"
assumes q16d: "(answer 16 = D) = (answer 10 = A)"
assumes q16e: "(answer 16 = E) = (answer 10 = E)"
</code></pre></div></div>
<h3 id="question-17">Question 17</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
17. The answer to question 6 is
(A) C
(B) D
(C) E
(D) none of the above
(E) all of the above
*)
assumes q17a: "(answer 17 = A) = (answer 6 = C)"
assumes q17b: "(answer 17 = B) = (answer 6 = D)"
assumes q17c: "(answer 17 = C) = (answer 6 = E)"
assumes q17d: "(answer 17 = D) = (answer 6 ∉ {C,D,E})"
assumes q17e: "(answer 17 = E) = (answer 6 = C ∧ answer 6 = D ∧ answer 6 = E ∧ answer 6 ∉ {C,D,E})"
</code></pre></div></div>
<h3 id="question-18">Question 18</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
18. The number of questions with answer A equals the number of questions
with answer
(A) B
(B) C
(C) D
(D) E
(E) none of the above
*)
assumes q18a: "(answer 18 = A) = (numberOfQuestionsWithAnswersIn {A} = numberOfQuestionsWithAnswersIn {B})"
assumes q18b: "(answer 18 = B) = (numberOfQuestionsWithAnswersIn {A} = numberOfQuestionsWithAnswersIn {C})"
assumes q18c: "(answer 18 = C) = (numberOfQuestionsWithAnswersIn {A} = numberOfQuestionsWithAnswersIn {D})"
assumes q18d: "(answer 18 = D) = (numberOfQuestionsWithAnswersIn {A} = numberOfQuestionsWithAnswersIn {E})"
assumes q18e: "(answer 18 = E) = (numberOfQuestionsWithAnswersIn {A} ∉ numberOfQuestionsWithAnswersIn `{ {B},{C},{D},{E} })"
</code></pre></div></div>
<h3 id="question-19">Question 19</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
19. The answer to this question is:
(A) A
(B) B
(C) C
(D) D
(E) E
*)
assumes q19a: "(answer 19 = A) = (answer 19 = A)"
assumes q19b: "(answer 19 = B) = (answer 19 = B)"
assumes q19c: "(answer 19 = C) = (answer 19 = C)"
assumes q19d: "(answer 19 = D) = (answer 19 = D)"
assumes q19e: "(answer 19 = E) = (answer 19 = E)"
</code></pre></div></div>
<p>All of the options here are tautologies, but I wrote them out anyway.</p>
<h3 id="question-20">Question 20</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (*
20. Standardized test is to intelligence as barometer is to
(A) temperature (only)
(B) wind-velocity (only)
(C) latitude (only)
(D) longitude (only)
(E) temperature, wind-velocity, latitude, and longitude
*)
assumes q20: "answer 20 = E"
</code></pre></div></div>
<p>This didn’t fit into the model so I just wrote down the answer.</p>
<h2 id="the-solution-spoilers-below">The solution (spoilers below)</h2>
<p>Now we can start to answer the questions by proving lemmas within the locale
we’ve just defined:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>context Test
begin
</code></pre></div></div>
<p>First up, an easy one:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q5: "answer 5 = E" using q5e by auto
</code></pre></div></div>
<p>Next, questions 10 and 16 refer to each other’s answers, and there are some
obviously inconsistent answers (e.g. <code class="highlighter-rouge">10C ⟹ 16E ⟹ 10E</code>). I wondered if these
two questions alone determined their answers so I tried simply throwing all the
facts into the pot and doing a case split and it worked:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q10: "answer 10 = A" using q10c q10d q10e q16b q16c q16d q16e by (cases "answer 10", metis+)
lemma q16: "answer 16 = D" using q10a q10 by simp
</code></pre></div></div>
<p>There’s a similar relationship between questions 6 and 17. We can easily rule
out either of these being <code class="highlighter-rouge">A</code>, <code class="highlighter-rouge">C</code> and <code class="highlighter-rouge">E</code>, so either 6 is <code class="highlighter-rouge">D</code> and 17 is <code class="highlighter-rouge">B</code> or
vice versa. Question 2 tells us that neither question should have the same
answer as either of its neighbours, so since we know that the answer to
question 16 is <code class="highlighter-rouge">D</code> it can’t be that 17 is also <code class="highlighter-rouge">D</code>, so it must be the following:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q6: "answer 6 = D"
using q6e q17b q6c q17e q6b q6a q17c
proof (cases "answer 6")
case B
with q17d have "answer (Suc 16) = D" by simp
with q16 have "16 ∈ questionsWithSameAnswerAsNext"
unfolding questionsWithSameAnswerAsNext_def questionNumber_def by auto
with q2a q2b q2c q2d q2e show ?thesis by (cases "answer 2", auto)
qed auto
lemma q17: "answer 17 = B" using q17b q6 by simp
</code></pre></div></div>
<p>Next up is question 12, and again we can use the fact that the answers are all
mutually exclusive to good effect. Firstly, every number is either even or odd
so the answer must be one of the first two, and therefore none of the remaining
three, so we need to think about the numbers which are not prime or square or
multiples of five, and this only leaves a few possibilities:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma nqs_BCD: "numberOfQuestionsWithAnswersIn {B,C,D} ∈ {6,8,12,14,18}"
proof -
have nz: "0 < numberOfQuestionsWithAnswersIn {B,C,D}"
unfolding numberOfQuestionsWithAnswersIn_def card_gt_0_iff
proof (intro conjI)
have "6 ∈ questionNumber" by (simp add: questionNumber_def)
with q6 show "{n ∈ questionNumber. answer n ∈ {B, C, D}} ≠ {}" by auto
have fqs: "finite questionNumber" unfolding questionNumber_def by simp
show "finite {n ∈ questionNumber. answer n ∈ {B, C, D}}" by (intro finite_subset [OF _ fqs], auto)
qed
have "numberOfQuestionsWithAnswersIn {B,C,D} ≤ card questionNumber"
unfolding numberOfQuestionsWithAnswersIn_def by (intro card_mono, auto simp add: questionNumber_def)
also have "… = 20" unfolding questionNumber_def card_atLeastAtMost by simp
finally have "numberOfQuestionsWithAnswersIn {B,C,D} ∈ set [0..<21]" by simp
also have "… = set [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]"
apply (intro cong [OF refl, where f = set]) by (simp add: upt_rec)
finally show ?thesis
using nz q12a q12b q12c q12d q12e by (cases "answer 12", auto)
qed
</code></pre></div></div>
<p>The trick here was simply to enumerate the possibilities, which forced a case
split that eliminated all the unwanted ones in the last line. All of the
possibilities are even so this gives us two more answers:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q12: "answer 12 = A" using nqs_BCD q12a by auto
lemma q15: "answer 15 = A" using q15a q12 by auto
</code></pre></div></div>
<p>We’ve found an odd-numbered question with answer <code class="highlighter-rouge">A</code> so that resolves question
13:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q13: "answer 13 = D"
proof -
from q13a q13b q13c q13d q13e
obtain q where q: "{n ∈ questionNumber. odd n ∧ answer n = A} = {q}" by (cases "answer 13", auto)
from q15 have "15 ∈ {n ∈ questionNumber. odd n ∧ answer n = A}" by (auto simp add: questionNumber_def)
with q q13d show ?thesis by simp
qed
</code></pre></div></div>
<p>Since we have narrowed down the possibilities for the number of questions with
an answer that is a consonant, we also know the possibilities for questions
whose answer is a vowel, which affects questions 3, 4 and 8. Firstly a couple
of results about doing calculations with <code class="highlighter-rouge">numberOfQuestionsWithAnswersIn</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma nqs_disjoint:
assumes "S1 ∩ S2 = {}"
shows "numberOfQuestionsWithAnswersIn (S1 ∪ S2) = numberOfQuestionsWithAnswersIn S1 + numberOfQuestionsWithAnswersIn S2"
proof -
have "card {n ∈ questionNumber. answer n ∈ S1 ∪ S2}
= card ({n ∈ questionNumber. answer n ∈ S1} ∪ {n ∈ questionNumber. answer n ∈ S2})"
by (intro cong [OF refl, where f = card], auto)
also have "… = card {n ∈ questionNumber. answer n ∈ S1} + card {n ∈ questionNumber. answer n ∈ S2}"
proof (intro card_Un_disjoint)
from assms show "{n ∈ questionNumber. answer n ∈ S1} ∩ {n ∈ questionNumber. answer n ∈ S2} = {}" by auto
have "finite questionNumber" unfolding questionNumber_def by simp
thus "finite {n ∈ questionNumber. answer n ∈ S1}" "finite {n ∈ questionNumber. answer n ∈ S2}" by auto
qed
finally show ?thesis
unfolding numberOfQuestionsWithAnswersIn_def by simp
qed
lemma nqs_insert:
assumes "ans ∉ S"
shows "numberOfQuestionsWithAnswersIn (insert ans S) = numberOfQuestionsWithAnswersIn {ans} + numberOfQuestionsWithAnswersIn S"
proof -
have "numberOfQuestionsWithAnswersIn (insert ans S) = numberOfQuestionsWithAnswersIn ({ans} ∪ S)"
by (intro cong [OF refl, where f = numberOfQuestionsWithAnswersIn], auto)
also have "… = numberOfQuestionsWithAnswersIn {ans} + numberOfQuestionsWithAnswersIn S"
using assms by (intro nqs_disjoint, auto)
finally show ?thesis .
qed
</code></pre></div></div>
<p>We can use this to divide the 20 questions into those with vowel answers and
those with consonant answers:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma nqs_AE_BCD: "numberOfQuestionsWithAnswersIn {A,E} + numberOfQuestionsWithAnswersIn {B,C,D} = 20"
proof -
have "20 = card questionNumber" unfolding questionNumber_def by simp
also have "… = numberOfQuestionsWithAnswersIn UNIV" unfolding numberOfQuestionsWithAnswersIn_def by auto
also have "… = numberOfQuestionsWithAnswersIn ({A,E} ∪ {B,C,D})"
apply (intro cong [OF refl, where f = numberOfQuestionsWithAnswersIn]) using Answer.exhaust by blast
also have "… = numberOfQuestionsWithAnswersIn {A,E} + numberOfQuestionsWithAnswersIn {B,C,D}"
by (intro nqs_disjoint, auto)
finally show ?thesis by simp
qed
</code></pre></div></div>
<p>Question 8 and <code class="highlighter-rouge">nqs_BCD</code> above together narrow down the possibilities a lot:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma nqs_AE: "numberOfQuestionsWithAnswersIn {A,E} ∈ {6,8}"
proof -
have "numberOfQuestionsWithAnswersIn {A,E} ∈ {2,6,8,12,14}" using nqs_BCD nqs_AE_BCD by auto
with q8a q8b q8c q8d q8e show ?thesis by (cases "answer 8", auto)
qed
</code></pre></div></div>
<p>This means that the answers to question 3 and 4 must add up to either 6 or 8:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma nqs_AE_sum: "numberOfQuestionsWithAnswersIn {A,E} = numberOfQuestionsWithAnswersIn {A} + numberOfQuestionsWithAnswersIn {E}"
by (intro nqs_insert, simp)
</code></pre></div></div>
<p>Question 1 tells us more about questions 3 and 4 too. With a bit of effort we
can narrow down question 1 to either <code class="highlighter-rouge">C</code> or <code class="highlighter-rouge">D</code>: <code class="highlighter-rouge">A</code> and <code class="highlighter-rouge">B</code> are both
contradictory, and question 5 rules out <code class="highlighter-rouge">E</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma firstQuestionWithAnswerB_properties:
"1 ≤ firstQuestionWithAnswerB" "answer firstQuestionWithAnswerB = B"
"⋀n. ⟦ 1 ≤ n; n < firstQuestionWithAnswerB ⟧ ⟹ answer n ≠ B"
proof -
define P where "⋀n. P n ≡ 1 ≤ n ∧ answer n = B"
have 1: "firstQuestionWithAnswerB = Least P" unfolding P_def firstQuestionWithAnswerB_def by simp
have "P firstQuestionWithAnswerB"
unfolding 1
proof (intro LeastI)
from q17 show "P 17" unfolding P_def by auto
qed
thus "1 ≤ firstQuestionWithAnswerB" "answer firstQuestionWithAnswerB = B" unfolding P_def by auto
fix k
assume k1: "1 ≤ k" and k_lt: "k < firstQuestionWithAnswerB"
from k_lt have "¬ P k"
by (intro not_less_Least, simp add: P_def firstQuestionWithAnswerB_def)
with k1 show "answer k ≠ B" unfolding P_def by auto
qed
lemma q1_CD: "answer 1 ∈ {C,D}"
proof (cases "answer 1")
case A with q1a firstQuestionWithAnswerB_properties show ?thesis by simp
next
case B
with q1b have fq: "firstQuestionWithAnswerB = 2" by simp
hence "answer 1 ≠ B" by (intro firstQuestionWithAnswerB_properties(3), simp_all)
with B show ?thesis by simp
next
case E with q5 q5a show ?thesis by simp
qed auto
</code></pre></div></div>
<p>That means that either 3’s answer or 4’s answer is <code class="highlighter-rouge">B</code>. Moreover, the other one
of them is <code class="highlighter-rouge">D</code>: they must add up to either 6 or 8, and they cannot be equal
since they are neighbouring questions:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q34_BD: "{answer 3, answer 4} = {B,D}"
using q1_CD
proof (elim insertE)
from nqs_AE_sum nqs_AE
have nqs_A_E: "numberOfQuestionsWithAnswersIn {A} + numberOfQuestionsWithAnswersIn {E} ∈ {6,8}" by simp
have answer34_distinct: "answer 3 ≠ answer 4"
proof (intro notI)
assume "answer 3 = answer 4"
hence "3 ∈ questionsWithSameAnswerAsNext"
unfolding questionsWithSameAnswerAsNext_def questionNumber_def by auto
with q2a q2b q2c q2d q2e show False by (cases "answer 2", auto)
qed
{
assume "answer 1 = D"
with q1d have "firstQuestionWithAnswerB = 4" by simp
with firstQuestionWithAnswerB_properties have q4: "answer 4 = B" by simp
from q4 q4b nqs_A_E have "numberOfQuestionsWithAnswersIn {E} ∈ {1, 3}" by auto
with q3b q3d answer34_distinct q4 show ?thesis by auto
next
assume "answer 1 = C"
with q1c have "firstQuestionWithAnswerB = 3" by simp
with firstQuestionWithAnswerB_properties have q3: "answer 3 = B" by simp
from q3 q3b nqs_A_E have "numberOfQuestionsWithAnswersIn {A} ∈ {5, 7}" by auto
with q3 q4b q4d answer34_distinct show ?thesis by auto
}
qed simp
</code></pre></div></div>
<p>This means that the number of questions whose answers are a vowel is either 1+7
or 3+5 and is therefore 8:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q8: "answer 8 = E"
proof -
from nqs_AE_sum nqs_AE
have nqs_A_E: "numberOfQuestionsWithAnswersIn {A} + numberOfQuestionsWithAnswersIn {E} ∈ {6,8}" by simp
with q34_BD q8e q3b q3d q4b q4d nqs_AE_sum
show ?thesis unfolding doubleton_eq_iff by auto
qed
</code></pre></div></div>
<p>This means there are at least two questions whose answer is <code class="highlighter-rouge">E</code> (5 and 8) and
we already worked out that there are either one or three of them, so there must
be three, and therefore there’s five with answer <code class="highlighter-rouge">A</code>.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q3: "answer 3 = D"
using q34_BD unfolding doubleton_eq_iff
proof (elim disjE conjE)
assume "answer 3 = B"
with q3b have "card {n ∈ questionNumber. answer n ∈ {E}} = 1"
unfolding numberOfQuestionsWithAnswersIn_def by simp
then obtain q where q: "{n ∈ questionNumber. answer n ∈ {E}} = {q}" by (elim card_1_singletonE, auto)
moreover from q5 have "5 ∈ {n ∈ questionNumber. answer n ∈ {E}}" unfolding questionNumber_def by auto
moreover from q8 have "8 ∈ {n ∈ questionNumber. answer n ∈ {E}}" unfolding questionNumber_def by auto
ultimately show ?thesis by simp
qed
lemma q4: "answer 4 = B" using q34_BD q3 unfolding doubleton_eq_iff by simp
</code></pre></div></div>
<p>This finally resolves question 1:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q1: "answer 1 = D"
using q1_CD
proof (elim insertE)
assume "answer 1 = C" with q1c firstQuestionWithAnswerB_properties q3 show ?thesis by simp
qed auto
</code></pre></div></div>
<p>This was as far as I could get without using the answer to question 20, which
is also <code class="highlighter-rouge">E</code>, so that’s all three of them:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma Es: "{n ∈ questionNumber. answer n = E} = {5,8,20}"
proof (intro sym [OF card_seteq])
from q5 q8 q20 show "{5, 8, 20} ⊆ {n ∈ questionNumber. answer n = E}" by (auto simp add: questionNumber_def)
have "finite {8,20::nat}" by simp
from card_insert_if [OF this, where x = 5] have "card {5,8,20::nat} = Suc (card {8,20::nat})" by simp
also have "… = 3" by simp
also from q3 q3d have "3 = card {n ∈ questionNumber. answer n = E}" by (simp add: numberOfQuestionsWithAnswersIn_def)
finally show "card {n ∈ questionNumber. answer n = E} ≤ card {5, 8, 20::nat}" by simp
have "finite questionNumber" by (simp add: questionNumber_def)
thus "finite {n ∈ questionNumber. answer n = E}" by simp
qed
</code></pre></div></div>
<p>This resolves question 2: none of the other questions can have answer <code class="highlighter-rouge">E</code>, so
in particular neither of 8’s neighbours are <code class="highlighter-rouge">E</code>; moreover 9 and 10 cannot share
an answer since that answer would be <code class="highlighter-rouge">A</code> and we already found that 15 is an
odd-numbered question with answer <code class="highlighter-rouge">A</code>, which question 13 tells us to be unique:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q2: "answer 2 = A"
proof (cases "answer 2")
case B with q2b have "7 ∈ questionsWithSameAnswerAsNext" by simp
with q8 have "7 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionsWithSameAnswerAsNext_def by auto
with Es show ?thesis by simp
next
case C with q2c have "8 ∈ questionsWithSameAnswerAsNext" by simp
with q8 have "9 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionsWithSameAnswerAsNext_def by auto
with Es show ?thesis by simp
next
case D with q2d have "9 ∈ questionsWithSameAnswerAsNext" by simp
with q10 have "9 ∈ {n ∈ questionNumber. odd n ∧ answer n = A}" unfolding questionsWithSameAnswerAsNext_def by auto
with q13 q13d show ?thesis by simp
next
case E hence "2 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionNumber_def by auto
with Es show ?thesis by simp
qed
</code></pre></div></div>
<p>We already know the answer to question 6, so this tells us the answer to
question 7 too:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q7: "answer 7 = D"
proof -
from q2 q2a have "6 ∈ questionsWithSameAnswerAsNext" by simp
with q6 show ?thesis unfolding questionsWithSameAnswerAsNext_def by auto
qed
</code></pre></div></div>
<p>We can also resolve question 18. We know there are 5 questions with answer <code class="highlighter-rouge">A</code>.
Question 14 tells us that the number of questions with answer <code class="highlighter-rouge">D</code> is at least 6
so that rules out <code class="highlighter-rouge">C</code>. There are 3 questions with answer <code class="highlighter-rouge">E</code> so that rules out
<code class="highlighter-rouge">D</code>. We already know all the questions with answer <code class="highlighter-rouge">E</code>, and <code class="highlighter-rouge">B</code> is forbidden
since that’s the answer to neighbouring question 17, which leaves <code class="highlighter-rouge">A</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q18: "answer 18 = A"
proof (cases "answer 18")
case B
with q17 have "17 ∈ questionsWithSameAnswerAsNext" unfolding questionsWithSameAnswerAsNext_def questionNumber_def by auto
with q2 q2a show ?thesis by simp
next
case C
with q18c q4 q4b have "numberOfQuestionsWithAnswersIn {D} = 5" by simp
with q14a q14b q14c q14d q14e show ?thesis by (cases "answer 14", auto)
next
case D
with q18d q4 q4b q3 q3d show ?thesis by simp
next
case E hence "18 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionNumber_def by auto
with Es show ?thesis by simp
qed
</code></pre></div></div>
<p>In other words, there are 5 questions with answer <code class="highlighter-rouge">B</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma cardDs5: "numberOfQuestionsWithAnswersIn {B} = 5" using q18 q18a q4b q4 by simp
</code></pre></div></div>
<p>Since there are only 12 questions whose answer is a consonant, 5 of which are
<code class="highlighter-rouge">B</code>, and question 14 tells us that there are at least 6 with answer <code class="highlighter-rouge">D</code>, this
rules out a lot of possibilities. Also the answer to question 14 cannot be <code class="highlighter-rouge">A</code>
since that’s the answer to neighbouring question 15, so this means there must
be 7 questions with answer <code class="highlighter-rouge">D</code> and none at all with answer <code class="highlighter-rouge">C</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q14: "answer 14 = B" and no_C: "⋀n. n ∈ questionNumber ⟹ answer n ≠ C"
proof -
from nqs_AE_BCD q8 q8e have "12 = numberOfQuestionsWithAnswersIn {B,C,D}" by simp
also have "numberOfQuestionsWithAnswersIn {B,C,D} = numberOfQuestionsWithAnswersIn {B} + numberOfQuestionsWithAnswersIn {C,D}"
by (intro nqs_insert, simp)
also have "… = numberOfQuestionsWithAnswersIn {B} + (numberOfQuestionsWithAnswersIn {C} + numberOfQuestionsWithAnswersIn {D})"
by (intro cong [OF refl, where f = "(op +) (numberOfQuestionsWithAnswersIn {B})"] nqs_insert, simp)
also from cardDs5 have "… = 5 + numberOfQuestionsWithAnswersIn {C} + numberOfQuestionsWithAnswersIn {D}" by simp
finally have CD_7: "numberOfQuestionsWithAnswersIn {C} + numberOfQuestionsWithAnswersIn {D} = 7" by simp
from CD_7 have "numberOfQuestionsWithAnswersIn {D} ≤ 7" by auto
with q14c q14d q14e show "answer 14 = B"
proof (cases "answer 14")
case A with q15 have "14 ∈ questionsWithSameAnswerAsNext" unfolding questionsWithSameAnswerAsNext_def questionNumber_def by auto
with q2 q2a show ?thesis by simp
qed auto
with q14b CD_7 have "numberOfQuestionsWithAnswersIn {C} = 0" by simp
moreover have "finite questionNumber" unfolding questionNumber_def by simp
hence "finite {n ∈ questionNumber. answer n = C}" by simp
ultimately have "{n ∈ questionNumber. answer n = C} = {}"
unfolding numberOfQuestionsWithAnswersIn_def by (intro iffD1 [OF card_0_eq], simp_all)
thus "⋀n. n ∈ questionNumber ⟹ answer n ≠ C" by simp
qed
</code></pre></div></div>
<p>This narrows down the possibilities for question 9: it cannot be <code class="highlighter-rouge">A</code> (thanks to
question 13) or <code class="highlighter-rouge">C</code>, and we’ve found all the <code class="highlighter-rouge">E</code>s, so it must be <code class="highlighter-rouge">B</code> or <code class="highlighter-rouge">D</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q9_BD: "answer 9 ∈ {B,D}"
proof (cases "answer 9")
case A
hence "9 ∈ {n ∈ questionNumber. odd n ∧ answer n = A}" unfolding questionNumber_def by simp
with q13 q13d show ?thesis by simp
next
case C have "9 ∈ questionNumber" by (simp add: questionNumber_def) with no_C C show ?thesis by simp
next
case E hence "9 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionNumber_def by auto
with Es show ?thesis by simp
qed auto
</code></pre></div></div>
<p>In either case the answer to question 11 is <code class="highlighter-rouge">B</code>: if 9’s answer is <code class="highlighter-rouge">B</code> then this
is clear; if 9’s answer is <code class="highlighter-rouge">D</code> then 11’s answer cannot be <code class="highlighter-rouge">D</code>, but by the same
reasoning as for question 9 it also cannot be <code class="highlighter-rouge">A</code>, <code class="highlighter-rouge">C</code> or <code class="highlighter-rouge">E</code>, so it is <code class="highlighter-rouge">B</code> in
both cases.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q11: "answer 11 = B"
using q9_BD
proof (elim insertE)
assume "answer 9 = B"
with q9b have p: "(LEAST n. 9 < n ∧ answer n = B) = 11" by simp
define P where "P ≡ λn. 9 < n ∧ answer n = B"
have "P (Least P)"
proof (intro LeastI)
from q14 show "P 14" by (simp add: P_def)
qed
with p show ?thesis by (simp add: P_def)
next
assume "answer 9 = D"
with q9d have p: "(LEAST n. 9 < n ∧ answer n = D) = 13" by simp
define P where "P ≡ λn. 9 < n ∧ answer n = D"
have "¬ P 11" using p by (intro not_less_Least, auto simp add: P_def)
hence not_D: "answer 11 ≠ D" by (simp add: P_def)
thus ?thesis
proof (cases "answer 11")
case A
hence "11 ∈ {n ∈ questionNumber. odd n ∧ answer n = A}" unfolding questionNumber_def by simp
with q13 q13d show ?thesis by simp
next
case C have "11 ∈ questionNumber" by (simp add: questionNumber_def) with no_C C show ?thesis by simp
next
case E hence "11 ∈ {n ∈ questionNumber. answer n = E}" unfolding questionNumber_def by auto
with Es show ?thesis by simp
qed auto
qed simp
</code></pre></div></div>
<p>This means there is only one question with answer <code class="highlighter-rouge">B</code> below 11, which is
question 4, so question 9’s answer must be <code class="highlighter-rouge">D</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q9: "answer 9 = D"
using q9_BD
proof (elim insertE)
from q11 q11b have "is_singleton {n ∈ questionNumber. n < 11 ∧ answer n = B}" unfolding is_singleton_altdef by simp
then obtain q where "{n ∈ questionNumber. n < 11 ∧ answer n = B} = {q}" by (elim is_singletonE)
moreover assume "answer 9 = B" hence "9 ∈ {n ∈ questionNumber. n < 11 ∧ answer n = B}" by (simp add: questionNumber_def)
moreover from q4 have "4 ∈ {n ∈ questionNumber. n < 11 ∧ answer n = B}" by (simp add: questionNumber_def)
ultimately show ?thesis by simp
qed simp_all
</code></pre></div></div>
<p>We have only found four <code class="highlighter-rouge">B</code>s and we know there to be five, so the remaining
question, number 19, must also have answer <code class="highlighter-rouge">B</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma q19: "answer 19 = B"
proof -
have "{ n ∈ questionNumber. answer n = B } = {4,11,14,17,19}"
proof (intro card_seteq finite.insertI finite.emptyI subsetI, elim CollectE conjE)
have "card {4, 11, 14, 17, 19::nat} = 5" by simp
moreover from cardDs5 have "card {n ∈ questionNumber. answer n = B} = 5"
unfolding numberOfQuestionsWithAnswersIn_def by simp
ultimately show "card {4, 11, 14, 17, 19::nat} ≤ card {n ∈ questionNumber. answer n = B}" by simp
fix n assume "n ∈ questionNumber" "answer n = B"
thus "n ∈ {4, 11, 14, 17, 19}"
using q1 q2 q3 q5 q6 q7 q8 q9 q10 q12 q13 q15 q16 q18 q20
by (cases n rule: questionNumber_cases, simp_all)
qed
thus ?thesis by auto
qed
</code></pre></div></div>
<h2 id="validation">Validation</h2>
<p>At this point it seems we have found an answer to every question, but it
remains to show that these answers are all consistent with the questions. To do
this we can define a function <code class="highlighter-rouge">Question ⇒ Answer</code> with the answers we have
found and show that it satisfies all the assumptions of the locale.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition answers :: "Answer list" where "answers ≡ [D,A,D,B,E, D,D,E,D,A, B,A,D,B,A, D,B,A,B,E]"
definition ans :: "Question ⇒ Answer" where "ans q = answers ! (Rep_Question q - 1)"
</code></pre></div></div>
<p>It’s useful to spell out all the answers individually too for the benefit of
the simplifier:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma ans_simps:
"ans (Abs_Question 1) = D"
"ans (Abs_Question (Suc 0)) = D"
"ans (Abs_Question 2) = A"
"ans (Abs_Question (Suc (Suc 0))) = A"
"ans (Abs_Question 3) = D"
"ans (Abs_Question 4) = B"
"ans (Abs_Question 5) = E"
"ans (Abs_Question 6) = D"
"ans (Abs_Question 7) = D"
"ans (Abs_Question 8) = E"
"ans (Abs_Question 9) = D"
"ans (Abs_Question 10) = A"
"ans (Abs_Question 11) = B"
"ans (Abs_Question 12) = A"
"ans (Abs_Question 13) = D"
"ans (Abs_Question 14) = B"
"ans (Abs_Question 15) = A"
"ans (Abs_Question 16) = D"
"ans (Abs_Question 17) = B"
"ans (Abs_Question 18) = A"
"ans (Abs_Question 19) = B"
"ans (Abs_Question 20) = E"
using Abs_Question_inverse unfolding ans_def answers_def questionNumber_def by simp_all
</code></pre></div></div>
<p>This particular simplification is quite useful up-front:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma refl_eq_iff: "((a = a) = P) = P" by simp
</code></pre></div></div>
<p>Here’s the proof that <code class="highlighter-rouge">ans</code> satisfies all the assumptions of the locale:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma answer_valid: "Test ans"
proof (unfold_locales, unfold ans_simps refl_eq_iff)
</code></pre></div></div>
<p>At this point there are 96 subgoals, one for each assumption defined above (19
questions multiplied by 5 answers, plus the unique answer to question 20). The
proof just works through them all one-by-one.</p>
<h3 id="question-1-1">Question 1</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "(LEAST n. 1 ≤ n ∧ ans (Abs_Question n) = B) = 4"
proof (intro Least_equality conjI, simp_all add: ans_simps, elim conjE)
fix n assume n: "Suc 0 ≤ n" "ans (Abs_Question n) = B"
show "4 ≤ n"
proof (rule ccontr)
assume "¬ 4 ≤ n" with n have "n ∈ {1,2,3}" by auto
with n show False by (auto simp add: ans_simps)
qed
qed
thus "(D = A) = ((LEAST n. 1 ≤ n ∧ ans (Abs_Question n) = B) = 1)"
"(D = B) = ((LEAST n. 1 ≤ n ∧ ans (Abs_Question n) = B) = 2)"
"(D = C) = ((LEAST n. 1 ≤ n ∧ ans (Abs_Question n) = B) = 3)"
"(D = E) = ((LEAST n. 1 ≤ n ∧ ans (Abs_Question n) = B) = 5)" by simp_all
</code></pre></div></div>
<h3 id="question-2-1">Question 2</h3>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "{n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))} = {6}"
proof (intro equalityI subsetI)
fix n :: nat assume "n ∈ {6}" thus "n ∈ {n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))}"
by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))}"
hence n: "n ∈ questionNumber" "n ≤ 19" "ans (Abs_Question n) = ans (Abs_Question (Suc n))" by (auto simp add: questionNumber_def)
thus "n ∈ {6}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
thus "(A = B) = ({n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))} = {7})"
"(A = C) = ({n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))} = {8})"
"(A = D) = ({n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))} = {9})"
"(A = E) = ({n ∈ questionNumber. Suc n ∈ questionNumber ∧ ans (Abs_Question n) = ans (Abs_Question (Suc n))} = {10})" by simp_all
</code></pre></div></div>
<h3 id="question-3-1">Question 3</h3>
<p>There are quite a few properties for which we need to know exactly which
questions have a particular answer. Here’s the questions with answer <code class="highlighter-rouge">E</code>, which
answers question 3.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> have Es: "{n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = {5,8,20}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {5,8,20}" thus "n ∈ questionNumber" "ans (Abs_Question n) ∈ {E}" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}}"
hence n: "n ∈ questionNumber" "ans (Abs_Question n) = E" by (auto simp add: questionNumber_def)
thus "n ∈ {5,8,20}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
thus "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = 3" by auto
thus "(D = A) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = 0)"
"(D = B) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = 1)"
"(D = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = 2)"
"(D = E) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}} = 4)" by simp_all
</code></pre></div></div>
<h3 id="question-4-1">Question 4</h3>
<p>Similarly to question 3, we enumerate the questions have answer <code class="highlighter-rouge">A</code> using the
same proof, which answers question 4.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> have As: "{n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = {2,10,12,15,18}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {2,10,12,15,18}" thus "n ∈ questionNumber" "ans (Abs_Question n) ∈ {A}" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}}"
hence n: "n ∈ questionNumber" "ans (Abs_Question n) = A" by (auto simp add: questionNumber_def)
thus "n ∈ {2,10,12,15,18}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
thus "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = 5" by auto
thus "(B = A) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = 4)"
"(B = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = 6)"
"(B = D) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = 7)"
"(B = E) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = 8)" by simp_all
</code></pre></div></div>
<h3 id="questions-5-7">Questions 5-7</h3>
<p>The proof obligations from these questions are easy to discharge <code class="highlighter-rouge">by auto</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show
"(E = A) = (E = D)"
"(E = B) = (E = A)"
"(E = C) = (E = D)"
"(E = D) = (E = B)"
"(E = E)"
"(D = A) = (B = C)"
"(D = B) = (B = D)"
"(D = C) = (B = E)"
"B ∉ {C, D, E}"
"(D = E) = (B = C ∧ B = D ∧ B = E ∧ B ∉ {C, D, E})"
"(D = A) = ({D, E} ∈ { {A, E} })"
"(D = B) = ({D, E} ∈ { {A, D}, {B, E} })"
"(D = C) = ({D, E} ∈ { {A, C}, {B, D}, {C, E} })"
"({D, E} ∈ { {A, B}, {B, C}, {C, D}, {D, E} })"
"(D = E) = (D = E)" by auto
</code></pre></div></div>
<h3 id="question-8-1">Question 8</h3>
<p>We already enumerated the <code class="highlighter-rouge">A</code>s and the <code class="highlighter-rouge">E</code>s so this is easy to prove.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> have "{n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = {2,5,8,10,12,15,18,20}" using As Es by auto
thus "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = 8" by auto
thus "(E = A) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = 4)"
"(E = B) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = 5)"
"(E = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = 6)"
"(E = D) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A, E}} = 7)" by simp_all
</code></pre></div></div>
<h3 id="question-9-1">Question 9</h3>
<p>The tricky bit of this proof is showing that the 13 is the <em>first</em> question
after 9 with which it shares an answer. The trick was to use contradiction
(<code class="highlighter-rouge">rule ccontr</code>) to get the goal into a form where we can automatically reduce
it to the three cases <code class="highlighter-rouge">10</code>, <code class="highlighter-rouge">11</code> and <code class="highlighter-rouge">12</code>.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "(LEAST n. 9 < n ∧ ans (Abs_Question n) = D) = 13"
proof (intro Least_equality conjI, simp_all add: ans_simps, elim conjE, rule ccontr)
fix n assume "9 < n" "¬ 13 ≤ n" "ans (Abs_Question n) = D"
hence "n ∈ {10,11,12}" "ans (Abs_Question n) = D" by auto
thus False by (auto simp add: ans_simps)
qed
thus "(D = A) = ((LEAST n. 9 < n ∧ ans (Abs_Question n) = D) = 10)"
"(D = B) = ((LEAST n. 9 < n ∧ ans (Abs_Question n) = D) = 11)"
"(D = C) = ((LEAST n. 9 < n ∧ ans (Abs_Question n) = D) = 12)"
"(D = E) = ((LEAST n. 9 < n ∧ ans (Abs_Question n) = D) = 14)" by simp_all
</code></pre></div></div>
<h3 id="question-10-1">Question 10</h3>
<p>This question’s obligations are easy to discharge:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "D = D"
"(A = B) = (D = A)"
"(A = C) = (D = E)"
"(A = D) = (D = B)"
"(A = E) = (D = C)" by simp_all
</code></pre></div></div>
<h3 id="question-11-1">Question 11</h3>
<p>By enumerating all the questions below question 11 (<code class="highlighter-rouge">cases n rule:
questionNumber_cases</code>) it’s easy to check that only one of them has answer <code class="highlighter-rouge">B</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> have "{n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = {4}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {4}" thus "n ∈ questionNumber" "n < 11" "ans (Abs_Question n) = B" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B}"
hence n: "n ∈ questionNumber" "n < 11" "ans (Abs_Question n) = B" by (auto)
thus "n ∈ {4}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
thus "card {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = 1" by simp
thus "(B = A) = (card {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = 0)"
"(B = C) = (card {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = 2)"
"(B = D) = (card {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = 3)"
"(B = E) = (card {n ∈ questionNumber. n < 11 ∧ ans (Abs_Question n) = B} = 4)" by simp_all
</code></pre></div></div>
<h3 id="question-12-1">Question 12</h3>
<p>Similarly to in questions 3 and 4, we start by enumerating all the questions
with answer <code class="highlighter-rouge">B</code>, <code class="highlighter-rouge">C</code> and <code class="highlighter-rouge">D</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> have Bs: "{n ∈ questionNumber. ans (Abs_Question n) ∈ {B}} = {4,11,14,17,19}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {4,11,14,17,19}" thus "n ∈ questionNumber" "ans (Abs_Question n) ∈ {B}" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. ans (Abs_Question n) ∈ {B}}"
hence n: "n ∈ questionNumber" "ans (Abs_Question n) = B" by (auto simp add: questionNumber_def)
thus "n ∈ {4,11,14,17,19}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
have Cs: "{n ∈ questionNumber. ans (Abs_Question n) ∈ {C}} = {}"
proof (intro equalityI subsetI CollectI conjI)
fix n assume "n ∈ {n ∈ questionNumber. ans (Abs_Question n) ∈ {C}}"
hence n: "n ∈ questionNumber" "ans (Abs_Question n) = C" by (auto simp add: questionNumber_def)
thus "n ∈ {}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed simp_all
have Ds: "{n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = {1,3,6,7,9,13,16}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {1,3,6,7,9,13,16}" thus "n ∈ questionNumber" "ans (Abs_Question n) ∈ {D}" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}}"
hence n: "n ∈ questionNumber" "ans (Abs_Question n) = D" by (auto simp add: questionNumber_def)
thus "n ∈ {1,3,6,7,9,13,16}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
have "{n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}} = {1,3,6,7,9,13,16,4,11,14,17,19}" using Bs Cs Ds by auto
hence "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}} = 12" by simp
thus "even (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}})"
"(A = B) = odd (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}})"
"(A = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}} ∈ {1, 4, 9, 16})"
"(A = D) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}} ∈ {2, 3, 5, 7, 11, 13, 17, 19})"
"(A = E) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B, C, D}} ∈ {5, 10, 15, 20})" by simp_all
</code></pre></div></div>
<h3 id="question-13-1">Question 13</h3>
<p>This question follows by enumerating all the questions (<code class="highlighter-rouge">cases n rule:
questionNumber_cases</code>) too.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "{n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A} = {15}"
proof (intro equalityI subsetI CollectI conjI)
fix n :: nat assume "n ∈ {15}" thus "n ∈ questionNumber" "odd n" "ans (Abs_Question n) = A" by (auto simp add: ans_simps questionNumber_def)
next
fix n assume "n ∈ {n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A}"
hence n: "n ∈ questionNumber" "odd n" "ans (Abs_Question n) = A" by (auto simp add: questionNumber_def)
thus "n ∈ {15}" by (cases n rule: questionNumber_cases, auto simp add: ans_simps)
qed
thus "(D = A) = ({n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A} = {9})"
"(D = B) = ({n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A} = {11})"
"(D = C) = ({n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A} = {13})"
"(D = E) = ({n ∈ questionNumber. odd n ∧ ans (Abs_Question n) = A} = {17})" by simp_all
</code></pre></div></div>
<h3 id="question-14-1">Question 14</h3>
<p>We already enumerated all the questions whose answer is <code class="highlighter-rouge">D</code>, and there are 7 of
them:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = 7" using Ds by auto
thus "(B = A) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = 6)"
"(B = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = 8)"
"(B = D) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = 9)"
"(B = E) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}} = 10)" by simp_all
</code></pre></div></div>
<h3 id="questions-15-17">Questions 15-17</h3>
<p>The obligations for these questions are easy to discharge:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "A = A"
"(A = B) = (A = B)"
"(A = C) = (A = C)"
"(A = D) = (A = D)"
"(A = E) = (A = E)"
"(D = A) = (A = D)"
"(D = B) = (A = C)"
"(D = C) = (A = B)"
"A = A"
"(D = E) = (A = E)"
"(B = A) = (D = C)"
"D = D"
"(B = C) = (D = E)"
"(B = D) = (D ∉ {C, D, E})"
"(B = E) = (D = C ∧ D = D ∧ D = E ∧ D ∉ {C, D, E})" by simp_all
</code></pre></div></div>
<h3 id="question-18-1">Question 18</h3>
<p>Since we have already calculated the sets of questions with each answer, these
obligations are easy to discharge as long as we are careful to unfold the right
definitions first.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show "card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = card {n ∈ questionNumber. ans (Abs_Question n) ∈ {B}}" unfolding As Bs by simp
show "(A = B) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = card {n ∈ questionNumber. ans (Abs_Question n) ∈ {C}})" unfolding As Cs by simp
show "(A = C) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = card {n ∈ questionNumber. ans (Abs_Question n) ∈ {D}})" unfolding As Ds by simp
show "(A = D) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} = card {n ∈ questionNumber. ans (Abs_Question n) ∈ {E}})" unfolding As Es by simp
show "(A = E) = (card {n ∈ questionNumber. ans (Abs_Question n) ∈ {A}} ∉ (λS. card {n ∈ questionNumber. ans (Abs_Question n) ∈ S}) ` { {B}, {C}, {D}, {E} })"
unfolding image_insert As Bs by simp
</code></pre></div></div>
<h3 id="question-19-1">Question 19</h3>
<p>The obligations for question 19 are all tautologies:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> show
"(B = A) = (B = A)"
"B = B"
"(B = C) = (B = C)"
"(B = D) = (B = D)"
"(B = E) = (B = E)" "E = E" by simp_all
qed
</code></pre></div></div>
<h2 id="uniqueness">Uniqueness</h2>
<p>It remains to show that the answer we have found is unique. This follows since
we have reasoned our way to a single answer for each question, but this lemma
serves as a useful check that we have actually answered all of the questions.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma (in Test) answer_unique: "f = ans"
proof (intro ext)
fix q
define n where "n ≡ Rep_Question q"
hence q_def: "q = Abs_Question n" by (simp add: n_def Rep_Question_inverse)
have "n ∈ questionNumber" using Rep_Question unfolding n_def.
from this q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 q16 q17 q18 q19 q20
have "answer n = ans (Abs_Question n)"
by (cases rule: questionNumber_cases, simp_all add: ans_simps)
thus "f q = ans q" by (simp add: q_def answer_def)
qed
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>I enjoyed this puzzle. <a href="https://en.wikipedia.org/wiki/Metamagical_Themas">Self reference is kinda
fun</a>. Also, as always,
Isabelle keeps you honest.</p>I stumbled across James Propp’s self-referential aptitude test the other day and thought it’d be fun to formalise it and its solution in Isabelle/HOL to make sure I wasn’t making any invalid logical leaps.Working with timezones2018-08-12T09:09:09+00:002018-08-12T09:09:09+00:00https://davecturner.github.io/2018/08/12/working-with-timezones<p>I do enjoy thinking about some of the strange things that can happen with
<a href="/2018/03/18/stopped-clocks.html">time</a> and <a href="/2017/12/27/timezone-curiosities.html">timezones</a> and it can be amusing, and occasionally
useful, to read some
<a href="http://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time">lists</a>
of
<a href="http://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time">counterexamples</a>
to reasonable-sounding statements about time, but it’s hard to actually get
stuff done with this information alone. All those counterexamples might leave
you thinking that dealing with timezones is basically impossible to get right,
whereas in fact it’s not that difficult once you draw the right pictures. I
thought it’d be useful to share the pictures I find helpful when approaching
timezone-related problems.</p>
<h2 id="local-universal-time-graphs">Local-universal time graphs</h2>
<p>The basic tool for visualising a timezone is something I’m going to call a
<em>local-universal time graph</em> (LUTG) which sounds awfully fancy but is really
just a line graph that relates universal time (on the horizontal axis) to local
time (on the vertical axis). The LUTG for the standard
<a href="https://en.wikipedia.org/wiki/UTC±00:00">UTC</a> timezone looks like this:</p>
<p><img src="/assets/2018-08-timezone-diagrams/01-utc.png" alt="LUTG of UTC" /></p>
<p>If you think this looks fairly boring then you’d be right. UTC is a fairly
boring timezone, and its LUTG reflects this perfectly. A marginally more
interesting timezone is something like
<a href="https://en.wikipedia.org/wiki/UTC%2B04:00"><code class="highlighter-rouge">Etc/GMT-4</code></a> whose LUTG looks like
this:</p>
<p><img src="/assets/2018-08-timezone-diagrams/02-utc-4.png" alt="LUTG of UTC" /></p>
<p>This is still quite boring, admittedly, but it does at least show you
unambiguously whether to add or subtract the 4 hours necessary to convert from
local time to UTC or vice versa. The correct interpretation of the sign of a
timezone offset is just not a thing that seems to stay in my head, and the fact
that what the IANA call <code class="highlighter-rouge">Etc/GMT-4</code> is called <code class="highlighter-rouge">+04:00</code> by the ISO suggests that
there are other people who also struggle with this. So I draw this LUTG and I
find it helps.</p>
<h2 id="changing-offsets">Changing offsets</h2>
<p>The fun and games <em>really</em> begin when you start to look at timezones that
sometimes change their offsets from UTC. This happens in two different ways.
Either they set their clocks forwards …</p>
<p><img src="/assets/2018-08-timezone-diagrams/03-clocks-go-forwards.png" alt="LUTG of clocks going forwards" /></p>
<p>… or they set them back …</p>
<p><img src="/assets/2018-08-timezone-diagrams/04-clocks-go-backwards.png" alt="LUTG of clocks going backwards" /></p>
<h2 id="properties-of-lutgs">Properties of LUTGs</h2>
<p>There’s a few things to notice about these graphs. Firstly, every UTC instant
has a <em>well-defined</em> local time to which it corresponds:</p>
<p><img src="/assets/2018-08-timezone-diagrams/05-utc-to-local-well-defined.png" alt="LUTG of clocks going backwards showing UTC-to-local conversion" /></p>
<p><em>Some</em> local times also have a single well-defined universal time to which they
correspond:</p>
<p><img src="/assets/2018-08-timezone-diagrams/06-local-to-utc-well-defined.png" alt="LUTG of clocks going backwards showing unambiguous local-to-UTC conversion" /></p>
<p>However when the clocks go back, some local times have two corresponding UTC
times:</p>
<p><img src="/assets/2018-08-timezone-diagrams/07-local-to-utc-ambiguous.png" alt="LUTG of clocks going backwards showing ambiguous local-to-UTC conversion" /></p>
<p>Similarly when the clocks go forwards, some local times never occur at all:</p>
<p><img src="/assets/2018-08-timezone-diagrams/08-local-to-utc-missing.png" alt="LUTG of clocks going forwards showing missing local-to-UTC conversion" /></p>
<p>Away from the offset transition, time proceeds at the same rate on both axes:
all the lines are at 45°.</p>
<p><img src="/assets/2018-08-timezone-diagrams/09-lines-45-degrees.png" alt="LUTG of clocks going backwards showing gradients" /></p>
<p>One thing that’s perhaps not obvious from the graph, but which can be
important, is that the transition time belongs only to the <em>later</em> line
segment. Times strictly before the transition time have the earlier offset,
and times that are equal to or later than the transition time have the later
offset. I draw this with an open ○ at the exclusive endpoint and a filled ● at
the inclusive endpoint.</p>
<p><img src="/assets/2018-08-timezone-diagrams/10-inclusive-exclusive.png" alt="LUTG of clocks going backwards showing inclusive/exclusive endpoints" /></p>
<h2 id="offsets-are-vertical">Offsets are <em>vertical</em></h2>
<p>An important point is that offsets from UTC are drawn as the <em>vertical</em>
distance between the line and the LUTG of UTC:</p>
<p><img src="/assets/2018-08-timezone-diagrams/11-offset-is-vertical.png" alt="LUTG of clocks going backwards showing offset is a vertical distance" /></p>
<p>I think this point is absolutely crucial to fully understanding timezone
calculations. It’s tricky because most of the time the vertical distance is the
same as the horizontal distance:</p>
<p><img src="/assets/2018-08-timezone-diagrams/12-vertical-equals-horizontal-offset.png" alt="LUTG of clocks going backwards showing offset is the same horizontally" /></p>
<p>But near to a transition this isn’t true, even where the horizontal distance is
unambiguous and well-defined:</p>
<p><img src="/assets/2018-08-timezone-diagrams/13-vertical-not-equals-horizontal-offset.png" alt="LUTG of clocks going backwards showing offset is the same horizontally" /></p>
<p>It’s very easy to write <a href="https://github.com/JodaOrg/joda-time/blob/5f98d636174cc0c6b76c9f434ee17e2b36577414/src/main/java/org/joda/time/chrono/ZonedChronology.java#L552-L554">plausible-looking code that confuses horizontal and
vertical
offsets</a>
which <a href="https://github.com/elastic/elasticsearch/blob/8114646e12661a860bcc388124eebb74aa8b7ea3/server/src/main/java/org/elasticsearch/common/rounding/Rounding.java#L210-L228">gives undesirable results near to
transitions</a>.</p>
<h2 id="when-do-transitions-occur">When do transitions occur?</h2>
<p>In many timezones there’s two transitions a year, approximately 6 months apart,
that respectively set the clocks forwards and back by an hour. These
transitions are a long way apart, and do not shift the clocks by very much, so
they don’t really interact in most calculations. But what if the transitions
were closer together, and larger, so that a local time might occur three times?</p>
<p><img src="/assets/2018-08-timezone-diagrams/14-interacting-transitions.png" alt="LUTG of clocks going backwards twice in quick succession" /></p>
<p>In theory it’s possible for this to happen, but it hasn’t yet, and would be a
slightly strange thing for a government to decide on. Still, who knows if it’ll
happen in the future? APIs like
<a href="https://docs.oracle.com/javase/8/docs/api/java/time/zone/ZoneRules.html#getValidOffsets-java.time.LocalDateTime-"><code class="highlighter-rouge">java.time.zone.ZoneRules#getValidOffsets</code></a>
do allow for this to occur. The closest two transitions in the current database
occurred three days apart in <code class="highlighter-rouge">Europe/Warsaw</code> on <code class="highlighter-rouge">1944-10-01</code> and <code class="highlighter-rouge">1944-10-04</code>.</p>
<p>There are other things to remember when thinking about when transitions happen:</p>
<ul>
<li>
<p>the southern hemisphere starts each year in summertime, so the
forwards/backwards shifts of their clocks is the other way round from
timezones in the northern hemisphere</p>
</li>
<li>
<p>timezones near to the equator may not shift their clocks at all</p>
</li>
<li>
<p>timezones such as Morocco’s sometimes change their clocks 4 times a year,
returning to standard time for the duration of Ramadan.</p>
</li>
<li>
<p>timezones also occasionally perform an ad-hoc transition to change the
alignment of their clocks with their neighbours. This might even divide a
single timezone in two, if only part of the original timezone changes its
rules. <a href="https://en.wikipedia.org/wiki/Time_in_Indiana">Indiana</a> seems
particularly prone to this sort of thing, and has its own subdirectory in the
IANA timezone database because of it.</p>
</li>
</ul>
<p>In short, it’s fairly hopeless trying to calculate when transitions might occur
in any given timezone, or to expect there to be exactly two every year. You
have to check the database provided by your operating system or libraries. Note
also that the transition rules are set by a political process, so can change
(sometimes at <a href="https://codeofmatt.com/2016/04/23/on-the-timing-of-time-zone-changes/">very short
notice</a>),
which means you have to update your timezone database as new decisions are
made.</p>
<h3 id="future-transitions">Future transitions</h3>
<p>Future entries in the timezone transition database are predictions —
educated guesses based on a pattern of past behaviour — and subject to
revision. This means it’s <a href="http://www.creativedeletion.com/2015/03/19/persisting_future_datetimes.html">wrong to blindly convert all date-time values in
your system into UTC for
storage</a>:
human events like meetings (and legal events like deadlines) are normally given
in local time, and you cannot know for certain the UTC offset that will be in
force at the time of a future event, so <a href="https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/">prematurely converting future local
times to UTC does not
work</a>.</p>
<h2 id="transitions-in-linked-timezones">Transitions in linked timezones</h2>
<p>If you are working with multiple, linked, timezones then be aware of how their
transitions are linked. In the EU, the clocks change at the same universal time
(0100 UTC) which preserves the difference in offsets between neighbouring
timezones:</p>
<p><img src="/assets/2018-08-timezone-diagrams/15-eu-simultaneous-transitions.png" alt="LUTG of multiple clock changes in the EU" /></p>
<p>However, in North America, the clocks change at the same <em>local</em> time (0200)
which temporarily changes the relative offsets between neighbouring zones:</p>
<p><img src="/assets/2018-08-timezone-diagrams/16-us-local-time-transitions.png" alt="LUTG of multiple clock changes in the US" /></p>
<p>Given that timezone offsets are agreed politically and subject to change, this
kind of linkage is not very reliable. It’s much better to ignore it and to
perform the calculations properly using the timezone database instead.</p>
<h2 id="sizes-of-offsets">Sizes of offsets</h2>
<p><img src="/assets/2018-08-timezone-diagrams/17-offset-size.png" alt="How large can offsets be?" /></p>
<p>The majority of offsets from UTC are a whole number of hours, but there are
many that aren’t. Historically, many timezones were set using solar time,
giving quite odd offsets indeed, but since 1980 all offsets in the IANA
database are a multiple of 15 minutes. The last one that wasn’t was
<code class="highlighter-rouge">Pacific/Kiritimati</code> which changed offset from <code class="highlighter-rouge">-10:40</code> to <code class="highlighter-rouge">-10:00</code> at midnight
on <code class="highlighter-rouge">1979-10-01</code>. That’s not to say that a future change might introduce a
non-15-minute offset again, just that at the moment there aren’t any known
ones.</p>
<p>In a perfect world, all offsets would be between <code class="highlighter-rouge">-12:00</code> and <code class="highlighter-rouge">+12:00</code> from
UTC. However, the international date line is not straight, and islands near to
it seem to prefer to be on the Australasian side than the American side, so the
actual range of offsets found in practice is <code class="highlighter-rouge">-12:00</code> to <code class="highlighter-rouge">+14:00</code>. Again,
that’s not to say that a future change might introduce an offset outside that
range, just that there aren’t any at the moment.</p>
<h2 id="sizes-of-transitions">Sizes of transitions</h2>
<p><img src="/assets/2018-08-timezone-diagrams/18-transition-size.png" alt="How large can transitions be?" /></p>
<p>Most regular transitions shift the clocks by a whole hour - in fact, the only
one I know not to is <code class="highlighter-rouge">Australia/Lord_Howe</code> which shifts the clocks by 30
minutes. Ad-hoc transitions can shift the clocks by any amount. The largest
clock changes I know of were in <code class="highlighter-rouge">Antarctica/DumontDUrville</code> which has sometimes
been in UTC and sometimes in <code class="highlighter-rouge">UTC+10:00</code>, with a ten-hour clock change at the
transitions. There have also been numerous transitions by ±24 hours as
timezones shift across the international date line in one direction or the
other, which aren’t strictly a <em>clock</em> change (they don’t affect the time of
day, just the day itself) but which still cause issues for software.</p>
<h2 id="effects-on-midnight-and-other-special-times">Effects on midnight and other special times</h2>
<p>Since a transition can cause local times to be skipped or duplicated, beware of
issues caused by this affecting a “special” time like midnight. Most
transitions try and avoid affecting midnight like this, but there are timezones
like <code class="highlighter-rouge">Atlantic/Azores</code> which set the clocks back an hour at <code class="highlighter-rouge">01:00</code> local time
giving two midnights:</p>
<p><img src="/assets/2018-08-timezone-diagrams/19-duplicated-midnight.png" alt="Duplicating midnight" /></p>
<p>There’s also the mess caused by setting the clocks back <a href="/2017/12/27/timezone-curiosities.html">just after
midnight</a> which makes the two
days overlap in terms of their local times:</p>
<p><img src="/assets/2018-08-timezone-diagrams/20-overlapping-days.png" alt="Overlapping days" /></p>
<p>The opposite can occur if the clocks are set forwards just before midnight too:</p>
<p><img src="/assets/2018-08-timezone-diagrams/21-no-midnight.png" alt="Missing midnights" /></p>
<p>In applications where there are other special local times, e.g. when trying to
round a time to the nearest hour, beware of similar effects. Where offsets
change by an amount that isn’t a whole multiple of an hour, you need to decide
whether to keep rounding to the top of an hour, or to keep the intervals all an
hour long, since you can’t do both.</p>
<h2 id="leap-seconds">Leap seconds</h2>
<p>These diagrams treat universal time (UTC) as if it advances at a constant rate.
In fact this isn’t true, it’s
<a href="https://en.wikipedia.org/wiki/International_Atomic_Time">TAI</a> that advances at
a fixed rate, and UTC differs from TAI by a whole number of seconds that
changes from time to time via the insertion of a leap second. In practice most
data that you come across tends to have already dealt with leap seconds in one
way or another, so a UTC-to-local diagram is appropriate. If the difference was
important then it’d be fine to draw this kind of diagram with TAI on the
horizontal axis instead of UTC.</p>
<h2 id="choosing-a-timezone">Choosing a timezone</h2>
<p>(addendum 2018-08-13) Redditor
<a href="https://www.reddit.com/r/programming/comments/96qhjo/working_with_timezones/e43xt4u">/u/dbxp</a>
points out that I omitted to talk about how to choose the appropriate timezone
for any given calculation, and gives the example of Jerusalem in which the
choice of timezone depends on more than just geography. A similar example to
this is that of the <a href="https://github.com/eggert/tz/blob/5c005615f3f8beaa3eaf4a67ab9c87dc702e1781/northamerica#L296-L300">Mount Washington
Observatory</a>
which apparently keeps to
<a href="https://en.wikipedia.org/wiki/UTC-05:00"><code class="highlighter-rouge">Etc/GMT+5</code></a> even though
geographically it should be in <code class="highlighter-rouge">America/New_York</code>.</p>
<p>The choice of timezone is a human activity, so the only reliable way to choose
a timezone is to expose the choice directly to a human being. Guessing it based
on other factors is likely to fail for some of your users.</p>
<p>Additionally, using a proper timezone name in the UI for this is much better
than trying to abbreviate it cleverly. It’s hard to trust a UI that describes
the UK timezone as <code class="highlighter-rouge">GMT</code>, which suggests that it doesn’t correct for daylight
saving. <a href="https://support.microsoft.com/en-gb/help/973627/microsoft-time-zone-index-values">It’d be nice if Microsoft didn’t do
this</a>:</p>
<blockquote>
<p>(GMT) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London</p>
</blockquote>
<p>😭</p>
<h2 id="conclusion">Conclusion</h2>
<p>A graph showing local time against universal time is a useful thing to draw if
you have to work on some timezone-sensitive system and need help visualising
all the things that might occur. With the right pictures, it’s much easier to
get things right.</p>I do enjoy thinking about some of the strange things that can happen with time and timezones and it can be amusing, and occasionally useful, to read some lists of counterexamples to reasonable-sounding statements about time, but it’s hard to actually get stuff done with this information alone. All those counterexamples might leave you thinking that dealing with timezones is basically impossible to get right, whereas in fact it’s not that difficult once you draw the right pictures. I thought it’d be useful to share the pictures I find helpful when approaching timezone-related problems.Uncrossing lines2018-07-24T09:09:09+00:002018-07-24T09:09:09+00:00https://davecturner.github.io/2018/07/24/uncrossing-lines<p>This is the sixth post in an open-ended series on proving the correctness of
TLA specifications using Isabelle/HOL:</p>
<ul>
<li>
<p><a href="/2018/02/12/tla-in-isabelle.html">TLA+ in Isabelle/HOL</a></p>
</li>
<li>
<p><a href="/2018/02/18/tla-clock-specification.html">Refining specifications in TLA+ and Isabelle/HOL</a></p>
</li>
<li>
<p><a href="/2018/03/23/tla-resource-allocator.html">Using TLA’s induction rule</a></p>
</li>
<li>
<p><a href="/2018/03/24/tla-resource-scheduler.html">Fairness properties in refinement proofs</a></p>
</li>
<li>
<p><a href="/2018/04/11/tortoise-and-hare.html">Floyd’s loop-finding algorithm</a></p>
</li>
<li>
<p><strong>Uncrossing lines</strong></p>
</li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>I recently came across <a href="https://www.youtube.com/watch?v=GX3URhx6i2E">a video lecture by Dijkstra on reasoning about program
correctness</a> in which he discusses
two little algorithms that have interesting correctness proofs. The first has a
slightly surprising safety (i.e. partial correctness) property, but is very
easy to show that it terminates, whereas the second has an obvious safety
property but a trickier termination proof. I thought it’d be interesting to
code the second up in TLA and play around with it.</p>
<p>The setup is that there are <em>n</em> red points and <em>n</em> blue points in the plane,
where each red point is joined to a blue point by a straight line segment:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/01-example-layout-crossed.png" alt="Example layout with crossed line segments" /></p>
<p>The objective is to find a way of pairing up the red points with the blue
points so that none of the line segments cross. The algorithm to do this starts
with any pairing and repeatedly selects a pair of line segments which cross and
“uncrosses” them, swapping the pairings between the two red points and the
corresponding blue points:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/02-example-layout-uncrossed.png" alt="Example layout, with no crossed line segments" /></p>
<p>Since the algorithm repeats until there are no crossing line segments, if it
terminates then it has successfully found an arrangement with no crossing
segments. In other words, it’s easy to see that this algorithm has a good
<a href="https://en.wikipedia.org/wiki/Correctness_(computer_science)"><em>partial
correctness</em></a>
property. However it’s not immediately obvious that this algorithm does ever
terminate. It’s tempting to want to try something like induction on the number
of crossing pairs of segments: the act of uncrossing a pair of segments
certainly removes one crossing, but the problem with this approach is that an
uncrossing can also introduce arbitrarily many other crossings, so this
induction doesn’t work:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/03-introducing-crossings.png" alt="Example of introducing arbitrarily many crossings from a single uncrossing" /></p>
<p>As there are only finitely many points there are only finitely many
arrangements from which to choose, so if the algorithm fails to terminate then
it must repeatedly visit the same state. The trick is to notice that uncrossing
two crossing lines always makes the total length of all the lines strictly
shorter, so it’s not possible to visit any state more than once. This is
because of the triangle inequality:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/04-triangle-inequality.png" alt="The triangle inequality" /></p>
<p>Actually it isn’t always true that the total length always
gets shorter: the triangle inequality is only strict if the triangle is not
degenerate, i.e. if its vertices are not collinear.</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/05-triangle-inequality-strict.png" alt="The degenerate case of the triangle inequality" /></p>
<p>And indeed it’s possible to find cases where the four points involved in an
uncrossing are all collinear, the lines intersect, and yet swapping the
pairings over does not decrease the total length of the lines or remove the
intersection:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/06-badly-collinear.png" alt="Example of four collinear points which cannot be uncrossed" /></p>
<p>Does this count as “crossing”? Arguably no, two line segments are “crossing”
only if there is some of each line segment lies on both sides of the other,
which isn’t the case if the endpoints are collinear. But they do intersect,
which is a little more general, and there are some other cases where the line
segments intersect (but don’t strictly <em>cross</em>) and where swapping the pairing
of the endpoints <em>does</em> remove the intersection:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/07-ok-collinear.png" alt="Example of four collinear points which can be uncrossed" /></p>
<p>Dijkstra avoids this whole issue by insisting that of all the red and blue
points no three of them are collinear, which excludes the problematic case but
also the case above; also if only three of the four endpoints involved in an
intersection are collinear then swapping the pairing always decreases the total
length and removes the intersection:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/08-ok-collinear.png" alt="Example involving three collinear points" /></p>
<p>It turns out that we can be a bit more precise about the conditions under which
this algorithm works.</p>
<p>This post is a tour of
<a href="https://github.com/DaveCTurner/tla-examples/blob/00b9408e746008440a4d0f5af1d04402b4589f86/EWD-pairings.thy"><code class="highlighter-rouge">EWD-pairings.thy</code></a>
in <a href="https://github.com/DaveCTurner/tla-examples">my TLA examples Github
repository</a>.</p>
<h2 id="geometry-in-isabellehol">Geometry in Isabelle/HOL</h2>
<p>There seems to be a surprisingly small amount of geometry included in the
<a href="https://isabelle.in.tum.de">Isabelle/HOL</a> standard library. There’s a theory
of real numbers, and of vector spaces which specialises to ℝ², including the
(non-strict) triangle inequality, but I couldn’t find much about lines and
their intersections and so on, so I started with these definitions:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>type_synonym point = "real × real"
definition lineseg :: "point ⇒ point ⇒ real ⇒ point"
where "lineseg p0 p1 l ≡ (1-l) *R p0 + l *R p1"
(* The *R operator is scalar multiplication *)
definition closed_01 :: "real set"
where "closed_01 ≡ {x. 0 ≤ x ∧ x ≤ 1}"
definition segment_between :: "point ⇒ point ⇒ point set"
where "segment_between p0 p1 ≡ lineseg p0 p1 ` closed_01"
definition segments_cross :: "point ⇒ point ⇒ point ⇒ point ⇒ bool"
where "segments_cross r0 b0 r1 b1
≡ segment_between r0 b0 ∩ segment_between r1 b1 ≠ {}"
</code></pre></div></div>
<p>In words, <code class="highlighter-rouge">lineseg p0 p1</code> is a function <code class="highlighter-rouge">real ⇒ point</code> whose range is the line
through <code class="highlighter-rouge">p0</code> and <code class="highlighter-rouge">p1</code>, and <code class="highlighter-rouge">segment_between p0 p1</code> is the segment of this line
between <code class="highlighter-rouge">p0</code> and <code class="highlighter-rouge">p1</code> (inclusive). The property we care about is whether the
two segments intersect, given by <code class="highlighter-rouge">segments_cross</code>.</p>
<p>Starting from these definitions I did manage to show that, if no three points
are collinear, then uncrossing two crossing segments does indeed decrease the
sums of their lengths, but it was quite a long and unilluminating proof so I
started casting around for examples of other geometry that has been developed
in Isabelle/HOL to see if there were any better ideas.</p>
<h3 id="signed-areas">Signed areas</h3>
<p>I came across <a href="https://www.era.lib.ed.ac.uk/handle/1842/9663">Laura I. Meikle’s PhD
thesis</a> which uses formalisation
of geometric results as a running example to motivate various usability
improvements in automated proof tooling. Included is a substantial
formalisation of a convex hull algorithm, including novel work to show that the
algorithm works if some points are collinear, which seems to be considered as
something of a special case in computational geometry even though I imagine it
occurs quite often in practice. This motivated me to try a little harder and
dig into the cases where some of the points could be collinear and try and
isolate just the problematic cases of collinearity.</p>
<p>The basic idea of Meikle’s formalisation, apparently from <a href="https://www.springer.com/gb/book/9783540556114">Knuth’s Axioms and
Hulls</a>, is to use the idea of
<em>signed area</em> to classify arrangements of three points. The signed area is the
area of the triangle whose vertices are the three points, if ordered
anticlockwise, or the negation of its area if the points are clockwise. This
has a particularly simple formula in terms of the coordinates of the points:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition signedArea :: "point ⇒ point ⇒ point ⇒ real"
where "signedArea p0 p1 p2 ≡ case (p0, p1, p2) of
((x0,y0), (x1,y1), (x2,y2)) ⇒ (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0)"
</code></pre></div></div>
<p>(In fact this gives <em>twice</em> the area of the triangle in question, but really we
mostly only care about the sign, and the extra factor of ½ makes everything
harder, so it’s better just to leave it out). The key point is that if
<code class="highlighter-rouge">signedArea p0 p1 p2</code> is negative then <code class="highlighter-rouge">p2</code> is to the left of the directed line
segment from <code class="highlighter-rouge">p0</code> to <code class="highlighter-rouge">p1</code> and if it is positive then <code class="highlighter-rouge">p2</code> is to the right:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma signedArea_left_example: "signedArea (0,0) (1,0) (1, 1) > 0" by (simp add: signedArea_def)
lemma signedArea_right_example: "signedArea (0,0) (1,0) (1,-1) < 0" by (simp add: signedArea_def)
</code></pre></div></div>
<p>If the signed area is zero then the points are collinear. Since we mostly only
care about the sign of the signed area, it makes sense to throw away its
magnitude like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>datatype Turn = Left | Right | Collinear
definition turn :: "point ⇒ point ⇒ point ⇒ Turn"
where "turn p0 p1 p2 ≡ if 0 < signedArea p0 p1 p2 then Left
else if signedArea p0 p1 p2 < 0 then Right
else Collinear"
</code></pre></div></div>
<p>One cute fact about signed areas is that they interact nicely with the linear
interpolation used in <code class="highlighter-rouge">lineseg</code>. Recalling that <code class="highlighter-rouge">lineseg r1 b1 l ≡ (1-l) *R r1 + l *R b1</code>,
it follows that:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma signedArea_lineseg:
"signedArea r0 b0 (lineseg r1 b1 l)
= (1-l) * signedArea r0 b0 r1
+ l * signedArea r0 b0 b1"
</code></pre></div></div>
<h3 id="classifying-crossing-segments">Classifying crossing segments</h3>
<p>A bunch of preliminary results lead to a quite neat classification of crossing
segments in terms of their turn directions:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma segments_cross_turns:
assumes "segments_cross r0 b0 r1 b1"
shows "collinear {r0, b0, r1, b1}
∨ (turn r0 b0 r1 ≠ turn r0 b0 b1
∧ turn r1 b1 r0 ≠ turn r1 b1 b0)"
</code></pre></div></div>
<p>This means that two line segments intersect either when all four points are
collinear, or else when the two endpoints of each line segment are on different
sides of the other line, and what’s really nice is that this includes cases
where some of the points are collinear: “different sides” means “left and
right” or “left and collinear” or “right and collinear”. In this latter case
the segments do genuinely cross …</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma turns_segments_cross:
assumes "turn r0 b0 r1 ≠ turn r0 b0 b1"
assumes "turn r1 b1 r0 ≠ turn r1 b1 b0"
shows "segments_cross r0 b0 r1 b1"
</code></pre></div></div>
<p>… and swapping the endpoints does decrease the length:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma non_collinear_swap_decreases_length:
assumes distinct: "r0 ≠ r1" "b0 ≠ b1"
assumes distinct_turns: "turn r0 b0 r1 ≠ turn r0 b0 b1"
"turn r1 b1 r0 ≠ turn r1 b1 b0"
shows "dist r0 b1 + dist r1 b0 < dist r0 b0 + dist r1 b1"
</code></pre></div></div>
<p>The side-conditions <code class="highlighter-rouge">r0 ≠ r1</code> and <code class="highlighter-rouge">b0 ≠ b1</code> are necessary: without them,
swapping endpoints doesn’t change the line segments. In the algorithm we do not
allow line segments to share endpoints.</p>
<h3 id="dealing-with-collinear-points">Dealing with collinear points</h3>
<p>The cases where the segments intersect and all four points are collinear are
easy enough to enumerate:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/09-all-collinear-crossings.png" alt="Enumeration of all situations with 4 collinear points in which the segments intersect" /></p>
<p>Of these, the only cases in which swapping the endpoints does not decrease the
total length, or remove the intersection, is the last two in which both
of the blue points are on the same side of the two red points. This suggests
the following definition to try and exclude the problematic case:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition badly_collinear :: "point ⇒ point ⇒ point ⇒ point ⇒ bool"
where "badly_collinear r0 b0 r1 b1
≡ ({b0,b1} ⊆ lineseg r0 r1 ` { l. 1 < l }
∨ {b0,b1} ⊆ lineseg r1 r0 ` { l. 1 < l })"
</code></pre></div></div>
<p>It’s not too hard to show that this is a very problematic case, in the sense
that it means that the line segments intersect …</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma badly_collinear_intersects:
assumes "badly_collinear r0 b0 r1 b1"
shows "segments_cross r0 b0 r1 b1"
</code></pre></div></div>
<p>… you can’t escape by swapping the endpoints …</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma badly_collinear_swap_still_badly_collinear:
assumes "badly_collinear r0 b0 r1 b1"
shows "badly_collinear r0 b1 r1 b0"
</code></pre></div></div>
<p>… and swapping the endpoints doesn’t change the total length of the segments
…</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma badly_collinear_swap_preserves_length:
assumes bad: "badly_collinear r0 b0 r1 b1"
shows "dist r0 b1 + dist r1 b0 = dist r0 b0 + dist r1 b1"
</code></pre></div></div>
<p>… which means that this case definitely prevents the algorithm from
terminating. However, it’s also possible to show that this is the <em>only</em>
situation that can prevent the algorithm from terminating:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma swap_decreases_length:
assumes distinct: "distinct [r0, b0, r1, b1]"
assumes segments_cross: "segments_cross r0 b0 r1 b1"
assumes not_bad: "¬ badly_collinear r0 b0 r1 b1"
shows "dist r0 b1 + dist r1 b0 < dist r0 b0 + dist r1 b1"
</code></pre></div></div>
<p>The proof of this is a slightly fiddly set of nested case splits of the form
that Isabelle excels at compared with doing this manually. It’s really hard to
convince yourself you’ve got all the cases, particularly when working
geometrically, as some of the case splits yield geometrically-impossible
situations which can’t be drawn but which you still have to prove to be
impossible.</p>
<h2 id="setting-up-the-algorithm">Setting up the algorithm</h2>
<p>The analysis above suggests the following setup for modelling the algorithm
itself:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale UncrossingSetup =
fixes redPoints bluePoints :: "point set"
assumes finite_redPoints: "finite redPoints"
assumes finite_bluePoints: "finite bluePoints"
assumes cards_eq: "card redPoints = card bluePoints"
assumes red_blue_disjoint: "redPoints ∩ bluePoints = {}"
assumes not_badly_collinear:
"⋀r1 r2. ⟦ r1 ∈ redPoints; r2 ∈ redPoints ⟧
⟹ card (bluePoints ∩ lineseg r1 r2 ` {l. 1 < l}) ≤ 1"
</code></pre></div></div>
<p>The condition <code class="highlighter-rouge">not_badly_collinear</code> says that if you draw a line segment
between any two red points, and extend it past one of the red points, then it
only ever hits at most one blue point. This allows for a lot more situations
than simply forbidding any collinearity. For instance, all the red points can
be collinear, as can all the blue points:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/10-many-collinear-points.png" alt="Example of a situation excluded by Dijkstra in which this algorithm terminates" /></p>
<p>It’s also permissible to have a bunch of blue points on the line between two
red points and vice versa:</p>
<p><img src="/assets/2018-07-24-uncrossing-lines/11-many-collinear-points.png" alt="Example of a situation excluded by Dijkstra in which this algorithm terminates" /></p>
<p>In this setup, <em>any</em> uncrossing reduces the total length of the line segments
involved:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>context UncrossingSetup
begin
lemma uncross_reduces_length:
fixes r1 r2 b1 b2
assumes colours: "r1 ∈ redPoints" "r2 ∈ redPoints" "b1 ∈ bluePoints" "b2 ∈ bluePoints"
assumes distinct: "r1 ≠ r2" "b1 ≠ b2"
assumes segments_cross: "segments_cross r1 b1 r2 b2"
shows "dist r1 b2 + dist r2 b1 < dist r1 b1 + dist r2 b2"
</code></pre></div></div>
<p>Moreover the total length of any pairing of the red points and the blue points
is in this set…</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition valid_total_lengths :: "real set"
where "valid_total_lengths
≡ (λpairs. ∑ pair ∈ pairs. dist (fst pair) (snd pair))
` Pow (redPoints × bluePoints)"
</code></pre></div></div>
<p>… which is finite …</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma finite_valid_total_lengths: "finite valid_total_lengths"
</code></pre></div></div>
<p>… so this means that transitions which reduce the total length form a
wellfounded relation, suitable for induction:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition valid_length_transitions :: "(real × real) set"
where "valid_length_transitions ≡ Restr {(x,y). x < y} valid_total_lengths"
lemma wf_less_valid_total_length: "wf valid_length_transitions"
</code></pre></div></div>
<h2 id="the-algorithm">The algorithm</h2>
<p>At this point we are in a position to look at the algorithm itself in TLA:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition swapPoints :: "point ⇒ point ⇒ point ⇒ point"
where "swapPoints p0 p1 p ≡ if p = p0 then p1 else if p = p1 then p0 else p"
locale Uncrossing = UncrossingSetup +
fixes blueFromRed :: "(point ⇒ point) stfun"
assumes bv: "basevars blueFromRed"
fixes blueFromRed_range :: stpred
defines "blueFromRed_range
≡ PRED ((op `)<blueFromRed,#redPoints> = #bluePoints)"
fixes step :: action
defines "step ≡ ACT (∃ r0 r1 b0 b1.
#r0 ∈ #redPoints
∧ #r1 ∈ #redPoints
∧ #r0 ≠ #r1
∧ #b0 = id<$blueFromRed,#r0>
∧ #b1 = id<$blueFromRed,#r1>
∧ #(segments_cross r0 b0 r1 b1)
∧ blueFromRed$ = (op ∘)<$blueFromRed,#(swapPoints r0 r1)>)"
fixes Spec :: temporal
defines "Spec ≡ TEMP (Init blueFromRed_range ∧ □[step]_blueFromRed
∧ WF(step)_blueFromRed)"
</code></pre></div></div>
<p>We have a single state variable, <code class="highlighter-rouge">blueFromRed</code>, which assigns the corresponding
blue point to each red point. The predicate <code class="highlighter-rouge">blueFromRed_range</code> holds
initially, and each (non-stuttering) transition finds two intersecting line
segments and uncrosses them. The expression</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>blueFromRed$ = (op ∘)<$blueFromRed,#(swapPoints r0 r1)>)"
</code></pre></div></div>
<p>is the Isabelle translation of what would be written in TLA+ something like</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>blueFromRed' = blueFromRed ∘ (swapPoints r0 r1)
</code></pre></div></div>
<p>i.e. that updating the <code class="highlighter-rouge">blueFromRed</code> function simply swaps an assignment over
and leaves everything else alone, and the predicate <code class="highlighter-rouge">blueFromRed_range</code> is a
translation of</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>blueFromRed ` redPoints = bluePoints
</code></pre></div></div>
<p>i.e. that blueFromRed is a surjection from the red points onto the blue points
(and these sets are finite and have equal cardinalities which means it’s a
bijection). It’s not too hard to show that this predicate is an invariant of
the algorithm:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma blueFromRed_range_Invariant: "⊢ Spec ⟶ □blueFromRed_range"
</code></pre></div></div>
<p>Furthermore it implies that the total length of all the line segments is in
<code class="highlighter-rouge">valid_total_lengths</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition total_length :: "real stfun"
where "total_length s ≡ ∑ r ∈ redPoints. dist r (blueFromRed s r)"
lemma blueFromRed_range_valid_total_length:
"⊢ blueFromRed_range ⟶ total_length ∈ #valid_total_lengths"
</code></pre></div></div>
<p>We can define what it means for all the line segments to be uncrossed:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition all_uncrossed :: stpred
where "all_uncrossed ≡ PRED (∀ r0 r1 b0 b1.
#r0 ∈ #redPoints
∧ #r1 ∈ #redPoints
∧ #r0 ≠ #r1
∧ #b0 = id<blueFromRed,#r0>
∧ #b1 = id<blueFromRed,#r1>
⟶ ¬ #(segments_cross r0 b0 r1 b1))"
</code></pre></div></div>
<p>It follows that the algorithm can only stutter once all line segments are
uncrossed:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma stops_when_all_uncrossed:
"⊢ Spec ⟶ □($all_uncrossed ⟶ blueFromRed$ = $blueFromRed)"
</code></pre></div></div>
<p>All of the preliminary work above allows us to show that a <code class="highlighter-rouge">step</code> transition
causes the change in <code class="highlighter-rouge">total_length</code> to be in <code class="highlighter-rouge">valid_length_transitions</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma step_valid_length_transition:
assumes "(s,t) ⊨ step"
assumes "s ⊨ blueFromRed_range"
shows "(total_length t, total_length s) ∈ valid_length_transitions"
</code></pre></div></div>
<p>From this, and because <code class="highlighter-rouge">valid_length_transitions</code> is a wellfounded relation,
there can only be finitely many <code class="highlighter-rouge">step</code> transitions, which means that eventually
the algorithm does terminate as required.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma "⊢ Spec ⟶ ◇□all_uncrossed"
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>This algorithm is interesting because its correctness proof relies on a
geometric argument, something that I’d not seen before. The actual
computational content (i.e. the interaction with TLA) was quite small and
simple, once the geometry was all sorted out.</p>
<p>I’ve never done any formalisation of geometry before, and the idea of signed
areas helped to show me a way of working that was a lot more elegant than the
bare-hands coordinate geometry that I initially tried. It also helped
massively in the analysis of the problematic situations involving collinear
points. For these situations it was very useful to work in Isabelle as there
were a few places where I had to proceed by enumerating cases and it’s all to
easy to miss one case when working by hand, especially when working with
geometry and doubly so when trying to consider degenerate cases. The proof of
<code class="highlighter-rouge">swap_decreases_length</code> in particular involved nested case splits that would
have been tricky to cover exhaustively by hand.</p>
<p>This algorithm works in terms of real numbers, which made it simple to analyse
but means it cannot be implemented as-is. Naively replacing all the real
numbers with floating-point <code class="highlighter-rouge">double</code> variables seems like it’ll give rise to
some problems, particularly for points that are close to collinear, in part
because the formula for <code class="highlighter-rouge">signedArea</code> has lots of subtractions of quantities
that could be quite similar. This needs more thought as I haven’t yet been able
to construct an obviously-bad situation for <code class="highlighter-rouge">double</code> variables, so bad
behaviour is just a hunch. An obvious cop-out is to compute exactly using
integer or rational coordinates instead.</p>This is the sixth post in an open-ended series on proving the correctness of TLA specifications using Isabelle/HOL:Twinkly lights2018-04-28T09:09:09+00:002018-04-28T09:09:09+00:00https://davecturner.github.io/2018/04/28/twinkly-lights<p>A while back I got hold of a
<a href="https://cdn-shop.adafruit.com/datasheets/WS2811.pdf">WS2811</a>-based LED string
and thought it’d be fun to play around with it with a microcontroller. The
string is a sequence of bulbs (a.k.a. pixels) connected by three wires, where
each bulb contains a red, green and blue LED as well as a WS2811: a little chip
that lets you control the brightnesses of the three LEDs digitally. WS2811 is
one of the controllers behind the <a href="https://learn.adafruit.com/adafruit-neopixel-uberguide/the-magic-of-neopixels">Adafruit
Neopixel</a>
series of individually-controllable LEDs, but you can also get cheaper
WS2811-based LED strings from elsewhere.</p>
<p>These controllers use a serial protocol to set the colour of each pixel in the
string in order. Each pixel’s colour is described by a block of 24 bits: 8 bits
of red level, then 8 bits of green level and then 8 bits of blue level, each
transmitted MSB-first. The first block of 24 bits sets the colour of the pixel
nearest to the controller; the second sets the colour of the second pixel, and
so on. The whole string acts as a big shift register: the first block of 24
bits is consumed by the first pixel, and then it passes all subsequent bits
down the line; the second block of 24 bits sent by the controller is the first
block passed to the second pixel, so it consumes this block and then passes all
subsequent bits along, and so on. Each pixel carries on passing data along the
line for as long as there is data to pass, but at the end of the frame the
pixels all go back into the “consume the first 24 bits” mode, allowing you to
send the next frame. Each pixel keeps displaying its last-received colour until
it receives another one.</p>
<p>The three wires that connect the LEDs are +5V, GND and data, so there’s no
clock line. To avoid the need for a separate clock line the protocol is
entirely timing-based: each bit is transmitted as a 2.5µs-long frame comprising
a period of +5V and then a period of 0V, where the +5V period is 0.5µs for a
clear bit and 1.2µs for a set bit. There is also a “reset” pause of 50µs which
puts each pixel back into the “consume the first 24 bits” mode.</p>
<p>The microcontroller I’m using is an
<a href="https://www.microchip.com/wwwproducts/en/ATmega328P">ATmega328P</a> (the
controller used in Arduinos) with a 16MHz clock. Translating the timings into
cycles, each bit is 40 cycles (2.5µs) which is either 8 cycles (0.5µs) high
then 32 cycles (2.0µs) low for a clear bit, or else 19 cycles (1.2µs) high then
21 cycles (1.3µs) low for a set bit. The reset needs to take at least 800
cycles.</p>
<p>There’s an Arduino library called <a href="http://fastled.io/">FastLED</a> for talking to
WS2811 controllers, which basically works by putting all the colour values for
a frame into an array in RAM and then calling a <code class="highlighter-rouge">show()</code> function that streams
this array out as the necessary carefully-timed stream of pulses. The main
issue with the buffer-and-stream approach is that it uses a lot of RAM for the
buffer. The ATmega328P has 2kB of RAM, which is only enough for 85 24-bit
pixels (minus program overhead), and I’d also maybe like to work with an even
smaller device like the
<a href="https://www.microchip.com/wwwproducts/en/ATtiny85">ATtiny85</a> with just 512B
(up to 21 24-bit pixels).</p>
<p>On the other hand, 40 cycles per bit is absolutely <em>loads</em> when all it needs to
do is switch a data line high, wait for a bit, switch it low, and wait for a
bit more. FastLED’s <code class="highlighter-rouge">show()</code> function must spend most of its time just waiting.
If we could do some calculations in that waiting time then maybe we could
compute the colour that each pixel should be in a just-in-time fashion, i.e.
while transmitting the colour of the previous pixel. This might let us increase
the number of pixels in the string without a corresponding increase in the RAM
that’s needed, meaning we could support arbitrarily many pixels with a very
small controller.</p>
<p>There’s all sorts of effects that are possible to generate algorithmically like
this, and I thought it’d be fun to do something with a bit of
(pseudo-)randomness, so I settled on trying to make the lights twinkle as
pleasingly as possible.</p>
<h2 id="timing-constraints">Timing constraints</h2>
<p>The datasheet indicates that the timing of the pulses must be accurate within
±150ns (±2.4 cycles at 16MHz) but there is some evidence that in practice the
<a href="https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/">constraints are not as strict as
that</a>.
That said, I wanted to see how close I could get to the nominal timings for the
sake of the exercise, so I really wanted to be able to count cycles accurately.
The usual way to write code for this kind of microprocessor is to use something
like C and compile it down to an image that runs on the chip, but this doesn’t
give enough visibility about how many cycles everything takes, so I decided to
drop down to writing raw AVR assembler and using the <a href="http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-0856-AVR-Instruction-Set-Manual.pdf">AVR Instruction Set
Manual</a>
which gives details of the cycle counts of each instruction.</p>
<h2 id="anatomy-of-a-twinkle">Anatomy of a twinkle</h2>
<p>A really good twinkle is more than just switching the light on and then off
again. It looks much better if it starts out dim for a bit, then briefly
flashes brightly, and then goes back to being dim for a bit longer before going
out. It’s also important that the twinkles are not all synchronised. I ended up
settling on a 7-frame animation involving 3 colours that went <code class="highlighter-rouge">DIM</code>, <code class="highlighter-rouge">DIM</code>,
<code class="highlighter-rouge">MEDIUM</code>, <code class="highlighter-rouge">BRIGHT</code>, <code class="highlighter-rouge">MEDIUM</code>, <code class="highlighter-rouge">DIM</code>, <code class="highlighter-rouge">DIM</code>, and decided to read the actual
colours from a table so that I could customise them later on.</p>
<p>To trigger the animations randomly, I used a 7xn table of random bytes (where n
is the number of pixels to drive):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> P I X E L
0 1 2 3 4 5 6 7 8 9 ...
0: dd f1 5a db ba 3d d5 7b 8e e1 ...
F 1: 57 82 4a 8c 60 b6 3f a1 b1 42 ...
R 2: f8 a1 cd 78 4f 85 e1 2b 2c 92 ...
A 3: c0 4b a3 54 52 df f1 f2 4e 09 ...
M 4: 6a a3 5a 5e 7d e4 45 5c 24 06 ...
E 5: f0 a4 61 34 b5 0b 15 14 dc fa ...
6: 5b 94 cf 0e 15 10 cf aa 42 4b ...
</code></pre></div></div>
<p>I chose one particular byte, <code class="highlighter-rouge">0xba</code>, as the trigger value, and used this to
decide on the state of each pixel. In the table here, pixel 4 has the trigger
value <code class="highlighter-rouge">0xba</code> in the top row (corresponding to frame 0) which indicates that
pixel 4 is at frame 0 of the animation and should display the corresponding
colour. None of the other pixels have <code class="highlighter-rouge">0xba</code> in their columns, so they are all
inactive and should display the background colour.</p>
<p>In the next frame the table moves down one row and a new random row is
generated at the top:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> P I X E L
0 1 2 3 4 5 6 7 8 9 ...
0: c7 00 79 e6 f2 4f bd 6e 49 b7 ... <- new row
F 1: dd f1 5a db ba 3d d5 7b 8e e1 ... <- previously frame 0 row
R 2: 57 82 4a 8c 60 b6 3f a1 b1 42 ... <- etc.
A 3: f8 a1 cd 78 4f 85 e1 2b 2c 92 ...
M 4: c0 4b a3 54 52 df f1 f2 4e 09 ...
E 5: 6a a3 5a 5e 7d e4 45 5c 24 06 ...
6: f0 a4 61 34 b5 0b 15 14 dc fa ...
</code></pre></div></div>
<p>Since pixel 4 now has the trigger value <code class="highlighter-rouge">0xba</code> in its frame 1 row, this
indicates that it should display the colour that corresponds with frame 1. This
continues:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> P I X E L
0 1 2 3 4 5 6 7 8 9 ...
0: 93 05 ba d7 83 1f 8a 9f 62 42 ... <- new row
F 1: c7 00 79 e6 f2 4f bd 6e 49 b7 ...
R 2: dd f1 5a db ba 3d d5 7b 8e e1 ...
A 3: 57 82 4a 8c 60 b6 3f a1 b1 42 ...
M 4: f8 a1 cd 78 4f 85 e1 2b 2c 92 ...
E 5: c0 4b a3 54 52 df f1 f2 4e 09 ...
6: 6a a3 5a 5e 7d e4 45 5c 24 06 ...
</code></pre></div></div>
<p>Now pixel 4 has the trigger value <code class="highlighter-rouge">0xba</code> in its frame 2 row, and pixel 2 has
the trigger value in the newly-generated frame 0 row, so pixel 2 also starts
the twinkling animation. This means that the twinkles are not all synchronised,
which looks much nicer than the animations you see with shop-bought Christmas
lights.</p>
<p>In fact, this didn’t quite work exactly as I wanted: having a single trigger
value meant that each pixel only twinkled, on average, once every 256 frames.
At 30 frames per second (fps), 256 frames is around 8 seconds, and this just
looked a little sparse for my taste. I solved this by using <em>two</em> trigger
values, <code class="highlighter-rouge">0xba</code> and <code class="highlighter-rouge">0x6e</code>, which doubled the average twinkling frequency and
looked loads better. In the example above you can see that pixel 7 was
triggered one frame after pixel 4, since it has the second trigger value <code class="highlighter-rouge">0x6e</code>
in its column. A nice bonus feature was that I could do different things with
the different triggers, such as twinkling in two different colours (C1 & C2).</p>
<p>Remember that the colours themselves are going to be stored in a table, so the
job of calculating the colour of a pixel boils down to the need to calculate an
offset into the table. In pseudocode this looked like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>offset = 0;
/* COLOUR C1 COLOUR C2 */
if (randomColumn[0] == 0xba) { offset |= 0x04; } if (randomColumn[0] == 0x6e) { offset |= 0x08; } /* DIM */
if (randomColumn[1] == 0xba) { offset |= 0x04; } if (randomColumn[1] == 0x6e) { offset |= 0x08; } /* DIM */
if (randomColumn[2] == 0xba) { offset |= 0x14; } if (randomColumn[2] == 0x6e) { offset |= 0x18; } /* MEDIUM */
if (randomColumn[3] == 0xba) { offset |= 0x24; } if (randomColumn[3] == 0x6e) { offset |= 0x28; } /* BRIGHT */
if (randomColumn[4] == 0xba) { offset |= 0x14; } if (randomColumn[4] == 0x6e) { offset |= 0x18; } /* MEDIUM */
if (randomColumn[5] == 0xba) { offset |= 0x04; } if (randomColumn[5] == 0x6e) { offset |= 0x08; } /* DIM */
if (randomColumn[6] == 0xba) { offset |= 0x04; } if (randomColumn[6] == 0x6e) { offset |= 0x08; } /* DIM */
</code></pre></div></div>
<p>Here <code class="highlighter-rouge">randomColumn</code> is the column of random numbers corresponding to the
current pixel. The table of colours uses 32 bits (4 bytes) for each colour
rather than 24 to make it possible to do this calculation using only bitwise
operators. Note that the trigger value (and hence the colour of the twinkle)
determines bits 2 and 3 (0x04 vs 0x08) and the position in the animation (and
hence the current brightness) determines bits 4 and 5 (0x00 vs 0x10 vs 0x20).</p>
<p>The use of a bitwise OR neatly handles the rare case where there are two
trigger values in a column at the same time, which can lead to any combination
of bits 2, 3, 4 and 5 being set, essentially giving a third colour (<code class="highlighter-rouge">C3</code>) and a
fourth brightness level. This means that the colour table needs to have entries
for all 16 possible combinations, even if some of them are only rarely used. It
worked best to treat <code class="highlighter-rouge">C3</code> as a copy of the brighter of <code class="highlighter-rouge">C1</code> or <code class="highlighter-rouge">C2</code>, and the
extra brightness level as equivalent to <code class="highlighter-rouge">BRIGHT</code>. Making <code class="highlighter-rouge">C3</code> distinct from
<code class="highlighter-rouge">C1</code> and <code class="highlighter-rouge">C2</code> made a visible difference: it appeared rarely enough to look like
a glitch, but frequently enough to be a distraction.</p>
<p>This pseudocode is obviously very repetitive and it’s tempting to try and
refactor it into something more abstract using a loop and a table or something.
This actually makes things worse in this context: branching, jumps and lookups
all cost cycles (and branching in AVR assembler costs different numbers of
cycles depending on whether the branch is taken or not) and moreover this code
will need to be interleaved with the sending of the previous pixel’s data.
Keeping it unrolled isn’t so bad when compared with the alternatives.</p>
<h1 id="shrinking-the-table">Shrinking the table</h1>
<p>You may have noticed that there’s a problem with using a table of random
numbers with a column for each pixel in this calculation: the goal was to
support arbitrarily many pixels in a fixed amount of memory, beating FastLED’s
need for 3 bytes of RAM per pixel, and here it looks like we’ve made it worse
since we’re consuming 7 bytes of RAM per pixel. The trick is that we don’t need
the whole table in RAM at once. We can calculate each column, decide on the
colour for the corresponding pixel, transmit the colour, and then discard the
column and repeat, without needing to store anything on a per-pixel basis.</p>
<p>This works by generating each row using a pseudo-random number generator
(PRNG). PRNGs are deterministic algorithms that generate random-looking
numbers, but because they are deterministic they will generate the same
sequence of numbers if they are started in the same state. The starting state
is known as a <em>seed</em>. All we need to do is create a PRNG for each row and keep
track of the seeds that were used to start them off. In the next frame if we
shift the seeds down by one (and generate a new random seed for the top row)
then we will generate the same table shifted down by one row, with a new top
row.</p>
<p>The PRNG I chose was a 24-bit <a href="https://en.wikipedia.org/wiki/Linear-feedback_shift_register">linear feedback shift
register</a> (LFSR)
with characteristic polynomial</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X^24 + X^4 + X^3 + X + 1
</code></pre></div></div>
<p>Less abstractly, the PRNG’s state is 3 bytes <code class="highlighter-rouge">B2 B1 B0</code> and its state is
updated by shifting each byte one bit left, carrying overflows from each byte
to the next, and if the shift of the most-significant byte <code class="highlighter-rouge">B2</code> overflows then
XOR byte <code class="highlighter-rouge">B0</code> with <code class="highlighter-rouge">0x1b</code> (<code class="highlighter-rouge">= 2^4 + 2^3 + 2 + 1</code>). The characteristic
polynomial above is chosen because is is
<a href="https://en.wikipedia.org/wiki/Primitive_polynomial_(field_theory)">primitive</a>
so this LFSR gives a <a href="https://en.wikipedia.org/wiki/Maximum_length_sequence">maximal-length
sequence</a>, in the sense
that this PRNG has a cycle length of 2<sup>24</sup>-1. At 30 frames per second
this means that the whole system repeats about once a week, which is massively
overkill for this project; I could have used a 16-bit LFSR with a period of
about half an hour and you wouldn’t have noticed the difference, but I had the
room for a 24-bit one so I used it.</p>
<p>Normally each iteration of a LSFR would be used to generate a single random
<em>bit</em>, so each random byte would take 8 iterations, and each pixel needs 7
bytes so that’s 56 iterations per pixel. There’s quite a lot of spare time in
the system here, but not enough to do that, so I cheated and used the whole of
the least-significant byte at once. This means that there’s quite a strong
correlation between the value generated for one pixel and the next (half of the
time, it’s just multiplied by two) so this’d be a terrible idea for many
applications. Fortunately, it’s not a terrible idea here: we only care about
generating the two trigger values <code class="highlighter-rouge">0xba</code> and <code class="highlighter-rouge">0x6e</code>, and although it’s
impossible to generate these at adjacent pixels in the same row this doesn’t
seem to cause any noticeable artifacts.</p>
<h2 id="sending-the-bits">Sending the bits</h2>
<p>As mentioned above, each bit should take 40 cycles to send. Here’s the basic
structure I used:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ; bit to transmit is r0(7)
; PORTD(0) starts out low
sendbit40:
nop ; 1
nop ; 2
sendbit38:
nop ; 3
sendbit37:
nop ; 4
nop ; 5
nop ; 6
nop ; 7
nop ; 8
nop ; 9
sendbit31:
nop ; 10
nop ; 11
nop ; 12
; start pulse
sbi PORTD, 0 ; 2cy
nop ; 1
nop ; 2
pulseStarted:
nop ; 3
nop ; 4
nop ; 5
; end pulse if sending a 0
sbrs r0, 7 ; 1cy if r0(7) == 0 (no skip), 2cy if r0(7) == 1 (skip)
cbi PORTD, 0 ; 2cy
; == 3 cy if sending a 0, 2cy if sending a 1
nop ; 1
nop ; 2
nop ; 3
nop ; 4
nop ; 5
nop ; 6
nop ; 7
nop ; 8
; end pulse if sending a 1
lsl r0 ; 1cy
brcc sendbit_ret ; 1cy if carry set (no skip, sending a 1), 2cy if carry clear (skip, sending a 0)
cbi PORTD, 0 ; 2cy
; == 3 cy if sending a 1, 2cy if sending a 0
sendbit_ret:
ret ; 4cy
</code></pre></div></div>
<p>Hopefully the long sequences of <code class="highlighter-rouge">nop</code>s illustrate the waiting time that’s
available to play with in this protocol: 12 cycles before the pulse starts,
then 5 within the pulse, then another 8 between the ends of the short and long
pulses. It’s perhaps interesting to note that this is offset by 12 cycles from
the “obvious” structure in which <code class="highlighter-rouge">PORTD(0)</code> is pulled high right at the start.
There’s varying amounts of calculation to do between bits, and it seems to work
better to do whatever’s necessary and <em>then</em> <code class="highlighter-rouge">rcall</code> to an appropriate point in
the initial sequence of <code class="highlighter-rouge">nop</code> instructions, chosen to account for the budget
that’s already been spent. Note that <code class="highlighter-rouge">r0</code> is shifted left within this code, so
most of the time there’s nothing to do between bits and the main code jumps
right back in at <code class="highlighter-rouge">sendbit40</code>. But between bytes (i.e. when switching from red
to green and from green to blue) you have to load the new byte into register
<code class="highlighter-rouge">r0</code> which takes two instructions, so to account for that it jumps back in at
<code class="highlighter-rouge">sendbit38</code>, skipping the two unnecessary <code class="highlighter-rouge">nop</code>s. If you’re being extra-picky
you’ll notice that the code above only consumes 37 cycles; the extra 3 cycles
is consumed by the <code class="highlighter-rouge">rcall</code> instruction to enter it.</p>
<p>It might seem a little strange to conditionally call <code class="highlighter-rouge">cbi PORTD, 0</code> to pull the
output port low at the end: if the port was already low then this would be a
no-op so the branch seems unnecessary. This is to work around the fact that
branching instructions like the earlier <code class="highlighter-rouge">sbrs</code> consume 2 cycles if the branch
is taken and only 1 if execution falls through, so including a branch here
cancels this out and makes sure that the whole routine takes a constant number
of cycles.</p>
<h2 id="interleaving-transmission-and-calculation">Interleaving transmission and calculation</h2>
<p>While sending each pixel’s data we must calculate the data to send for the next
pixel, which involves:</p>
<ul>
<li>updating the PRNGs, computing the next column of the table of random numbers,</li>
<li>calculating the offset into the colour table, and</li>
<li>loading the colour from the colour table.</li>
</ul>
<h3 id="updating-the-prngs">Updating the PRNGs</h3>
<p>Updating each PRNG takes the full 40 cycles when interleaved with the
bit-sending code, so this is written out as a copy of the routine above with
the <code class="highlighter-rouge">nop</code>s replaced by more useful code:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sendbitRNG:
; send one bit using PWM
; and also update a 24-bit LFSR RNG while waiting
; bit to transmit is r0(0) and is sent on $00(0)
.def toTransmit = r0
; RNG uses bytes X, X+1 & X+2 as the current state
; clobbers r16, r17, r18
; adds 3 to X to point it at the next LSFR
; shifts r0 right by 1 to shift in the next bit
; LSB of r16 is random output
; X^24 + X^4 + X^3 + X + 1 is primitive
; 16 8 2 1 = 0x1b
.def RNGB0 = r16
.def RNGB1 = r17
.def RNGB2 = r18
; start of 12 cycles of free time
nop ; 1cy
nop ; 1cy
nop ; 1cy
; load state into r16 (X^7 .. 1), r17 (X^15 .. X^8), r18 (X^23 .. X^16)
ld RNGB0, X+ ; 2cy
ld RNGB1, X+ ; 2cy
ld RNGB2, X ; 2cy
; multiply by X
clc ; 1cy
rol RNGB0 ; 1cy
rol RNGB1 ; 1cy
; TBC ...
; end of 12 cycles of free time
; start pulse
sbi PORTD, 0 ; 2cy
; start of 5 cycles of free time
; ... continue multiplying by X
rol RNGB2 ; 1cy
; carry bit is now set if overflowed
; store the high-order bytes again
st X, RNGB2 ; 2cy
st -X, RNGB1 ; 2cy
; r17 & r18 can be reused
.undef RNGB2
.undef RNGB1
; end of 5 cycles of free time
; end pulse if sending a 0 (8 cycles)
sbrs toTransmit, 7 ; 1cy if r0(7) == 0 (no skip), 2cy if r0(7) == 1 (skip)
cbi PORTD, 0 ; 2cy
; == 3 cy if sending a 0, 2cy if sending a 1
; start of 8 cycles of free time
; calculate remainder mod X^24 + X^4 + X^3 + X + 1
.def modulus = r18
ldi modulus, $1b ; 1cy
brcc nooverflow24 ; 1cy (2 if true, but skips 1cy, so same either way)
eor RNGB0, modulus ; 1cy
nooverflow24:
st -X, RNGB0 ; 2cy
.undef modulus
; update X to point to the next LFSR state
adiw X, 3 ; 2cy
nop ; 1cy
; end of 8 cycles of free time
; end pulse if sending a 1 (19 cycles)
lsl toTransmit ; 1cy
brcc sendbitRNG_ret ; 1cy if carry set (no skip, sending a 1), 2cy if carry clear (skip, sending a 0)
cbi PORTD, 0 ; 2cy
; == 3 cy if sending a 1, 2cy if sending a 0
sendbitRNG_ret:
ret ; 2cy
</code></pre></div></div>
<p>This routine is called 7 times to update the 7 PRNGs in turn.</p>
<h3 id="calculating-the-offset">Calculating the offset</h3>
<p>Calculating the offset is simpler, and can basically be done in the 12 cycles of free time at the start of the bit-sending routine, allowing us to <code class="highlighter-rouge">rjmp</code> straight to the <code class="highlighter-rouge">pulseStarted</code> label:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; ----------------------------------------------------------
; Send the current bit while contributing to the calculation
; of the colour of the next pixel from frame 0
sendbitGetNextPixel_f0_40:
; start of 12 cycles of free time
nop ; 1cy
nop ; 1cy
sendbitGetNextPixel_f0_38:
ld r16, X ; 2cy
adiw X, $03 ; 2cy
; set bits in nextPixelColourIndex to indicate next-pixel-colour
cpi RNGB0, $ba ; 1cy
brne not_colour_1_f0 ; 1cy
ori nextPixelColourIndex, $04 ; 1cy
not_colour_1_f0:
cpi RNGB0, $6e ; 1cy
brne not_colour_2_f0 ; 1cy
ori nextPixelColourIndex, $08 ; 1cy
not_colour_2_f0:
; end of 12 cycles of free time
; start pulse
sbi PORTD, 0 ; 2cy
rjmp pulseStarted ; 2cy
</code></pre></div></div>
<p>Note that we pull <code class="highlighter-rouge">PORTD(0)</code> high before jumping, and jump to a label that’s two cycles later than you might expect. This is a hangover from a time when this calculation took one more cycle, and I think it could probably be removed now along with the two initial <code class="highlighter-rouge">nop</code> instructions. Note also the comparisons against the two trigger values <code class="highlighter-rouge">0xba</code> and <code class="highlighter-rouge">0x6e</code>, and the effect that this has on <code class="highlighter-rouge">nextPixelColourIndex</code>. There’s one of these routines for each of the three brightness levels, and the order in which they are called is important.</p>
<p>The main loop then just sends the bits for one pixel while calling these routines to compute the colour of the next one:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sendPixel:
; arrive here from the per-pixel RJMP loop, i.e.
; having already spent 2 cycles since the last bit.
; *** Look up the colour of the next pixel
; nextPixelColourIndex will be 0/4/8/12 indicating colour of next pixel
ldi YL, low(coltable) ; 1cy
ldi YH, high(coltable) ; 1cy
add YL, nextPixelColourIndex ; 1cy
brcc coltable_no_overflow ; 1cy (in case coltable spans a 0x100 boundary)
inc YH ; 1cy
coltable_no_overflow:
; Y points to the RGB colour values
; send the red byte (in r0) and update the RNGs
ld r0, Y+ ; 2cy
rcall sendbit31 ; send bit R0 (31cy)
wdr ; 1cy
; initialise X to point at the RNG states table
ldi XL, low(rngtable) ; 1cy
ldi XH, high(rngtable) ; 1cy
clr nextPixelColourIndex ; 1cy
rcall sendbit36 ; send bit R1 (37cy)
rcall sendbitRNG ; send bit R2 (40cy)
rcall sendbitRNG ; send bit R3 (40cy)
rcall sendbitRNG ; send bit R4 (40cy)
rcall sendbitRNG ; send bit R5 (40cy)
rcall sendbitRNG ; send bit R6 (40cy)
rcall sendbitRNG ; send bit R7 (40cy)
; green byte
ld r0, Y+ ; 2cy
rcall sendbit38 ; send bit G0 (38cy)
rcall sendbitRNG ; send bit G1 (40cy)
; initialise X to point at the RNG states table
ldi XL, low(rngtable) ; 1cy
ldi XH, high(rngtable) ; 1cy
rcall sendbitGetNextPixel_f0_38 ; send bit G2 (38cy)
rcall sendbitGetNextPixel_f0_40 ; send bit G3 (40cy)
rcall sendbitGetNextPixel_f1_40 ; send bit G4 (40cy)
rcall sendbitGetNextPixel_f2_40 ; send bit G5 (40cy)
rcall sendbitGetNextPixel_f1_40 ; send bit G6 (40cy)
rcall sendbitGetNextPixel_f0_40 ; send bit G7 (40cy)
ld r0, Y+ ; 2cy
rcall sendbitGetNextPixel_f0_38 ; send bit B0 (38cy)
rcall sendbit40 ; send bit B1 (40cy)
rcall sendbit40 ; send bit B2 (40cy)
rcall sendbit40 ; send bit B3 (40cy)
rcall sendbit40 ; send bit B4 (40cy)
rcall sendbit40 ; send bit B5 (40cy)
rcall sendbit40 ; send bit B6 (40cy)
sbiw indexH:indexL, 1 ; 2cy
breq lastBitOfFrame ; 1cy if more pixels to go
rcall sendbit37 ; send bit B7 (37cy)
rjmp sendPixel ; 2cy
</code></pre></div></div>
<p>Notice the use of <code class="highlighter-rouge">rcall sendbit37</code>, <code class="highlighter-rouge">rcall sendbit31</code> etc to send the a bit
with fewer <code class="highlighter-rouge">nop</code> instructions, which ensures that each bit takes exactly 40
instructions to send.</p>
<h2 id="does-it-work">Does it work?</h2>
<p>Sure, I think it works pretty well. Here the colour table is set up with a
white background and red-and-gold twinkles.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/EXDS2wPf6zU" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen=""></iframe>
<p>The framerate of the system is largely determined by the number of pixels
driven. My string only has 50 pixels, but in this video it’s generating data to
drive 800 of them which gives a framerate of around 20 frames per second: 800
pixels multiplied by 24 bits at 2.5µs per bit comes out as around 50ms per
frame. This is a fundamental limit of the WS2811-based devices and not
something that could be beaten with a more powerful controller. I’ve not tried
running this on my 512-byte ATtiny85 but I see no reason why it shouldn’t be
able to drive 800+ pixels too: the memory footprint of this code is well under
512 bytes.</p>
<h2 id="bonus-features">Bonus features</h2>
<p>I also extended the colour table to have multiple modes, along with a
pushbutton to advance to the next mode. I could have used an interrupt to pick
this up but instead I just polled the state of the button between frames: the
timing constraints are very slack here. As a double-bonus I wrote the
last-chosen mode to EEPROM so it persisted across power cycles.</p>
<h2 id="enough-with-the-words">Enough with the words…</h2>
<p><a href="https://gist.github.com/DaveCTurner/3fdf2483244db91a511ede03cd772b7e">Ok ok, here is the whole
program</a>,
and here’s the circuit board (before I added the mode-advancing button). Enjoy!</p>
<p><img src="/assets/2018-04-28-twinklies-controller.jpg" alt="The controller" /></p>A while back I got hold of a WS2811-based LED string and thought it’d be fun to play around with it with a microcontroller. The string is a sequence of bulbs (a.k.a. pixels) connected by three wires, where each bulb contains a red, green and blue LED as well as a WS2811: a little chip that lets you control the brightnesses of the three LEDs digitally. WS2811 is one of the controllers behind the Adafruit Neopixel series of individually-controllable LEDs, but you can also get cheaper WS2811-based LED strings from elsewhere.Arithmetic in the Rijndael field2018-04-11T18:09:09+00:002018-04-11T18:09:09+00:00https://davecturner.github.io/2018/04/11/rijndael-finite-field<p>This is a short note-to-self about doing arithmetic in a finite field of order
2<sup>8</sup> as I can never remember exactly how it works, and have spent time
computing the necessary tables at least twice before and don’t want to do it
again.</p>
<p>Fields of order 2<sup>8</sup> are useful for places where you want to work with
bytes as if they were more like proper numbers, and particularly when you want
to have a sensible notion of what it means to divide one byte by another. This
includes things like</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Advanced_Encryption_Standard">AES (a.k.a. Rijndael)</a></li>
<li><a href="https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing">Shamir’s secret-sharing algorithm</a></li>
<li><a href="https://en.wikipedia.org/wiki/Reed–Solomon_error_correction">Reed-Solomon coding</a></li>
</ul>
<p>The integers mod a prime <em>p</em> form a finite field <em>F<sub>p</sub></em> of order <em>p</em>.
A finite field with order <em>p<sup>n</sup></em> is constructed by taking the quotient
of the ring of polynomials <em>F<sub>p</sub>[x]</em> by a primitive polynomial of
order <em>n</em>. <em>F<sub>p</sub>[x]</em> comprises polynomials in <em>x</em> with coefficients in
<em>F<sub>p</sub></em>, and primitive polynomials are ones that have no proper
factors.</p>
<p>Here <em>p</em> is 2, so the base field <em>F<sub>2</sub></em> has just 2 elements, <em>0</em> and
<em>1</em>, in which addition is XOR and multiplication is AND. The nonprimitive
polynomials of order 8 can be found by taking the products of all nonconstant
polynomials <em>P</em> and <em>Q</em> whose orders sum to 8, and the primitive polynomials
are whatever is left, which are the following:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>x<sup>8</sup></th>
<th>+ x<sup>7</sup></th>
<th>+ x<sup>6</sup></th>
<th>+ x<sup>5</sup></th>
<th>+ x<sup>4</sup></th>
<th>+ x<sup>3</sup></th>
<th>+ x<sup>2</sup></th>
<th>+ x</th>
<th>+ 1</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x011b</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x011d</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x012b</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x012d</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0139</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x013f</td>
<td>x<sup>8</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x014d</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x015f</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0163</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0165</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0169</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0171</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0177</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x017b</td>
<td>x<sup>8</sup></td>
<td> </td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x0187</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x018b</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x018d</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x019f</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01a3</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01a9</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01b1</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01bd</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td> </td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01c3</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01cf</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01d7</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01dd</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td> </td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01e7</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td> </td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01f3</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td> </td>
<td>+ x</td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01f5</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td> </td>
<td>+ x<sup>2</sup></td>
<td> </td>
<td>+ 1</td>
</tr>
<tr>
<td>0x01f9</td>
<td>x<sup>8</sup></td>
<td>+ x<sup>7</sup></td>
<td>+ x<sup>6</sup></td>
<td>+ x<sup>5</sup></td>
<td>+ x<sup>4</sup></td>
<td>+ x<sup>3</sup></td>
<td> </td>
<td> </td>
<td>+ 1</td>
</tr>
</tbody>
</table>
<p><a href="https://en.wikipedia.org/wiki/Finite_field_arithmetic#Rijndael%27s_finite_field">Rijndael’s finite
field</a>
uses the quotient of <em>F<sub>2</sub>[x]</em> by the first of these, x<sup>8</sup> +
x<sup>4</sup> + x<sup>3</sup> + x + 1.</p>
<p>Each element of the Rijndael field is an equivalence class of polynomials, one
of which, <em>P</em>, has degree less than 8, and the bit pattern <em>B(P)</em> of the
coefficients of that representative maps the Rijndael field onto the values
that a byte can take. Addition of elements corresponds simply to XOR of the
corresponding bytes. Multiplication is more involved, but a good way to work is
by calculating a log table and its inverse, and then adding logs instead of
trying to multiply the bytes directly.</p>
<p>Not all bases are suitable for taking logs here, because some elements’ powers
do not hit every other element. For instance, <em>x<sup>51</sup></em> = 1, so <em>x</em> is
not a suitable base. The simplest suitable base is <em>x+1</em>, because no power of
<em>x+1</em> below 255 equals 1. Fortunately, it’s quite simple to compute the product
of an arbitrary element by <em>x+1</em> in terms of their bit patterns:</p>
<blockquote>
<p><em>B((x+1)P) = B(P) XOR (B(P) ≪ 1) XOR (CARRY ? 0x1b : 0x00)</em></p>
</blockquote>
<p>Here <em>CARRY</em> corresponds to the most-significant bit of <em>B(P)</em>, which is lost
when calculating <em>B(P) ≪ 1</em> but which is equivalent to <em>0x1b</em> because
x<sup>8</sup> = x<sup>4</sup> + x<sup>3</sup> + x + 1 according to the
quotient, and <em>B(x<sup>4</sup> + x<sup>3</sup> + x + 1) = 0x1b</em>.</p>
<p>Working entirely with the bit patterns, the powers of <em>B(x+1)</em> are as follows:</p>
<table class="java-type-table">
<thead>
<tr>
<th><code class="highlighter-rouge">__</code></th>
<th><code class="highlighter-rouge">_0</code></th>
<th><code class="highlighter-rouge">_1</code></th>
<th><code class="highlighter-rouge">_2</code></th>
<th><code class="highlighter-rouge">_3</code></th>
<th><code class="highlighter-rouge">_4</code></th>
<th><code class="highlighter-rouge">_5</code></th>
<th><code class="highlighter-rouge">_6</code></th>
<th><code class="highlighter-rouge">_7</code></th>
<th><code class="highlighter-rouge">_8</code></th>
<th><code class="highlighter-rouge">_9</code></th>
<th><code class="highlighter-rouge">_a</code></th>
<th><code class="highlighter-rouge">_b</code></th>
<th><code class="highlighter-rouge">_c</code></th>
<th><code class="highlighter-rouge">_d</code></th>
<th><code class="highlighter-rouge">_e</code></th>
<th><code class="highlighter-rouge">_f</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="highlighter-rouge">0_</code></td>
<td><code class="highlighter-rouge">01</code></td>
<td><code class="highlighter-rouge">03</code></td>
<td><code class="highlighter-rouge">05</code></td>
<td><code class="highlighter-rouge">0f</code></td>
<td><code class="highlighter-rouge">11</code></td>
<td><code class="highlighter-rouge">33</code></td>
<td><code class="highlighter-rouge">55</code></td>
<td><code class="highlighter-rouge">ff</code></td>
<td><code class="highlighter-rouge">1a</code></td>
<td><code class="highlighter-rouge">2e</code></td>
<td><code class="highlighter-rouge">72</code></td>
<td><code class="highlighter-rouge">96</code></td>
<td><code class="highlighter-rouge">a1</code></td>
<td><code class="highlighter-rouge">f8</code></td>
<td><code class="highlighter-rouge">13</code></td>
<td><code class="highlighter-rouge">35</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">1_</code></td>
<td><code class="highlighter-rouge">5f</code></td>
<td><code class="highlighter-rouge">e1</code></td>
<td><code class="highlighter-rouge">38</code></td>
<td><code class="highlighter-rouge">48</code></td>
<td><code class="highlighter-rouge">d8</code></td>
<td><code class="highlighter-rouge">73</code></td>
<td><code class="highlighter-rouge">95</code></td>
<td><code class="highlighter-rouge">a4</code></td>
<td><code class="highlighter-rouge">f7</code></td>
<td><code class="highlighter-rouge">02</code></td>
<td><code class="highlighter-rouge">06</code></td>
<td><code class="highlighter-rouge">0a</code></td>
<td><code class="highlighter-rouge">1e</code></td>
<td><code class="highlighter-rouge">22</code></td>
<td><code class="highlighter-rouge">66</code></td>
<td><code class="highlighter-rouge">aa</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">2_</code></td>
<td><code class="highlighter-rouge">e5</code></td>
<td><code class="highlighter-rouge">34</code></td>
<td><code class="highlighter-rouge">5c</code></td>
<td><code class="highlighter-rouge">e4</code></td>
<td><code class="highlighter-rouge">37</code></td>
<td><code class="highlighter-rouge">59</code></td>
<td><code class="highlighter-rouge">eb</code></td>
<td><code class="highlighter-rouge">26</code></td>
<td><code class="highlighter-rouge">6a</code></td>
<td><code class="highlighter-rouge">be</code></td>
<td><code class="highlighter-rouge">d9</code></td>
<td><code class="highlighter-rouge">70</code></td>
<td><code class="highlighter-rouge">90</code></td>
<td><code class="highlighter-rouge">ab</code></td>
<td><code class="highlighter-rouge">e6</code></td>
<td><code class="highlighter-rouge">31</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">3_</code></td>
<td><code class="highlighter-rouge">53</code></td>
<td><code class="highlighter-rouge">f5</code></td>
<td><code class="highlighter-rouge">04</code></td>
<td><code class="highlighter-rouge">0c</code></td>
<td><code class="highlighter-rouge">14</code></td>
<td><code class="highlighter-rouge">3c</code></td>
<td><code class="highlighter-rouge">44</code></td>
<td><code class="highlighter-rouge">cc</code></td>
<td><code class="highlighter-rouge">4f</code></td>
<td><code class="highlighter-rouge">d1</code></td>
<td><code class="highlighter-rouge">68</code></td>
<td><code class="highlighter-rouge">b8</code></td>
<td><code class="highlighter-rouge">d3</code></td>
<td><code class="highlighter-rouge">6e</code></td>
<td><code class="highlighter-rouge">b2</code></td>
<td><code class="highlighter-rouge">cd</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">4_</code></td>
<td><code class="highlighter-rouge">4c</code></td>
<td><code class="highlighter-rouge">d4</code></td>
<td><code class="highlighter-rouge">67</code></td>
<td><code class="highlighter-rouge">a9</code></td>
<td><code class="highlighter-rouge">e0</code></td>
<td><code class="highlighter-rouge">3b</code></td>
<td><code class="highlighter-rouge">4d</code></td>
<td><code class="highlighter-rouge">d7</code></td>
<td><code class="highlighter-rouge">62</code></td>
<td><code class="highlighter-rouge">a6</code></td>
<td><code class="highlighter-rouge">f1</code></td>
<td><code class="highlighter-rouge">08</code></td>
<td><code class="highlighter-rouge">18</code></td>
<td><code class="highlighter-rouge">28</code></td>
<td><code class="highlighter-rouge">78</code></td>
<td><code class="highlighter-rouge">88</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">5_</code></td>
<td><code class="highlighter-rouge">83</code></td>
<td><code class="highlighter-rouge">9e</code></td>
<td><code class="highlighter-rouge">b9</code></td>
<td><code class="highlighter-rouge">d0</code></td>
<td><code class="highlighter-rouge">6b</code></td>
<td><code class="highlighter-rouge">bd</code></td>
<td><code class="highlighter-rouge">dc</code></td>
<td><code class="highlighter-rouge">7f</code></td>
<td><code class="highlighter-rouge">81</code></td>
<td><code class="highlighter-rouge">98</code></td>
<td><code class="highlighter-rouge">b3</code></td>
<td><code class="highlighter-rouge">ce</code></td>
<td><code class="highlighter-rouge">49</code></td>
<td><code class="highlighter-rouge">db</code></td>
<td><code class="highlighter-rouge">76</code></td>
<td><code class="highlighter-rouge">9a</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">6_</code></td>
<td><code class="highlighter-rouge">b5</code></td>
<td><code class="highlighter-rouge">c4</code></td>
<td><code class="highlighter-rouge">57</code></td>
<td><code class="highlighter-rouge">f9</code></td>
<td><code class="highlighter-rouge">10</code></td>
<td><code class="highlighter-rouge">30</code></td>
<td><code class="highlighter-rouge">50</code></td>
<td><code class="highlighter-rouge">f0</code></td>
<td><code class="highlighter-rouge">0b</code></td>
<td><code class="highlighter-rouge">1d</code></td>
<td><code class="highlighter-rouge">27</code></td>
<td><code class="highlighter-rouge">69</code></td>
<td><code class="highlighter-rouge">bb</code></td>
<td><code class="highlighter-rouge">d6</code></td>
<td><code class="highlighter-rouge">61</code></td>
<td><code class="highlighter-rouge">a3</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">7_</code></td>
<td><code class="highlighter-rouge">fe</code></td>
<td><code class="highlighter-rouge">19</code></td>
<td><code class="highlighter-rouge">2b</code></td>
<td><code class="highlighter-rouge">7d</code></td>
<td><code class="highlighter-rouge">87</code></td>
<td><code class="highlighter-rouge">92</code></td>
<td><code class="highlighter-rouge">ad</code></td>
<td><code class="highlighter-rouge">ec</code></td>
<td><code class="highlighter-rouge">2f</code></td>
<td><code class="highlighter-rouge">71</code></td>
<td><code class="highlighter-rouge">93</code></td>
<td><code class="highlighter-rouge">ae</code></td>
<td><code class="highlighter-rouge">e9</code></td>
<td><code class="highlighter-rouge">20</code></td>
<td><code class="highlighter-rouge">60</code></td>
<td><code class="highlighter-rouge">a0</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">8_</code></td>
<td><code class="highlighter-rouge">fb</code></td>
<td><code class="highlighter-rouge">16</code></td>
<td><code class="highlighter-rouge">3a</code></td>
<td><code class="highlighter-rouge">4e</code></td>
<td><code class="highlighter-rouge">d2</code></td>
<td><code class="highlighter-rouge">6d</code></td>
<td><code class="highlighter-rouge">b7</code></td>
<td><code class="highlighter-rouge">c2</code></td>
<td><code class="highlighter-rouge">5d</code></td>
<td><code class="highlighter-rouge">e7</code></td>
<td><code class="highlighter-rouge">32</code></td>
<td><code class="highlighter-rouge">56</code></td>
<td><code class="highlighter-rouge">fa</code></td>
<td><code class="highlighter-rouge">15</code></td>
<td><code class="highlighter-rouge">3f</code></td>
<td><code class="highlighter-rouge">41</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">9_</code></td>
<td><code class="highlighter-rouge">c3</code></td>
<td><code class="highlighter-rouge">5e</code></td>
<td><code class="highlighter-rouge">e2</code></td>
<td><code class="highlighter-rouge">3d</code></td>
<td><code class="highlighter-rouge">47</code></td>
<td><code class="highlighter-rouge">c9</code></td>
<td><code class="highlighter-rouge">40</code></td>
<td><code class="highlighter-rouge">c0</code></td>
<td><code class="highlighter-rouge">5b</code></td>
<td><code class="highlighter-rouge">ed</code></td>
<td><code class="highlighter-rouge">2c</code></td>
<td><code class="highlighter-rouge">74</code></td>
<td><code class="highlighter-rouge">9c</code></td>
<td><code class="highlighter-rouge">bf</code></td>
<td><code class="highlighter-rouge">da</code></td>
<td><code class="highlighter-rouge">75</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">a_</code></td>
<td><code class="highlighter-rouge">9f</code></td>
<td><code class="highlighter-rouge">ba</code></td>
<td><code class="highlighter-rouge">d5</code></td>
<td><code class="highlighter-rouge">64</code></td>
<td><code class="highlighter-rouge">ac</code></td>
<td><code class="highlighter-rouge">ef</code></td>
<td><code class="highlighter-rouge">2a</code></td>
<td><code class="highlighter-rouge">7e</code></td>
<td><code class="highlighter-rouge">82</code></td>
<td><code class="highlighter-rouge">9d</code></td>
<td><code class="highlighter-rouge">bc</code></td>
<td><code class="highlighter-rouge">df</code></td>
<td><code class="highlighter-rouge">7a</code></td>
<td><code class="highlighter-rouge">8e</code></td>
<td><code class="highlighter-rouge">89</code></td>
<td><code class="highlighter-rouge">80</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">b_</code></td>
<td><code class="highlighter-rouge">9b</code></td>
<td><code class="highlighter-rouge">b6</code></td>
<td><code class="highlighter-rouge">c1</code></td>
<td><code class="highlighter-rouge">58</code></td>
<td><code class="highlighter-rouge">e8</code></td>
<td><code class="highlighter-rouge">23</code></td>
<td><code class="highlighter-rouge">65</code></td>
<td><code class="highlighter-rouge">af</code></td>
<td><code class="highlighter-rouge">ea</code></td>
<td><code class="highlighter-rouge">25</code></td>
<td><code class="highlighter-rouge">6f</code></td>
<td><code class="highlighter-rouge">b1</code></td>
<td><code class="highlighter-rouge">c8</code></td>
<td><code class="highlighter-rouge">43</code></td>
<td><code class="highlighter-rouge">c5</code></td>
<td><code class="highlighter-rouge">54</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">c_</code></td>
<td><code class="highlighter-rouge">fc</code></td>
<td><code class="highlighter-rouge">1f</code></td>
<td><code class="highlighter-rouge">21</code></td>
<td><code class="highlighter-rouge">63</code></td>
<td><code class="highlighter-rouge">a5</code></td>
<td><code class="highlighter-rouge">f4</code></td>
<td><code class="highlighter-rouge">07</code></td>
<td><code class="highlighter-rouge">09</code></td>
<td><code class="highlighter-rouge">1b</code></td>
<td><code class="highlighter-rouge">2d</code></td>
<td><code class="highlighter-rouge">77</code></td>
<td><code class="highlighter-rouge">99</code></td>
<td><code class="highlighter-rouge">b0</code></td>
<td><code class="highlighter-rouge">cb</code></td>
<td><code class="highlighter-rouge">46</code></td>
<td><code class="highlighter-rouge">ca</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">d_</code></td>
<td><code class="highlighter-rouge">45</code></td>
<td><code class="highlighter-rouge">cf</code></td>
<td><code class="highlighter-rouge">4a</code></td>
<td><code class="highlighter-rouge">de</code></td>
<td><code class="highlighter-rouge">79</code></td>
<td><code class="highlighter-rouge">8b</code></td>
<td><code class="highlighter-rouge">86</code></td>
<td><code class="highlighter-rouge">91</code></td>
<td><code class="highlighter-rouge">a8</code></td>
<td><code class="highlighter-rouge">e3</code></td>
<td><code class="highlighter-rouge">3e</code></td>
<td><code class="highlighter-rouge">42</code></td>
<td><code class="highlighter-rouge">c6</code></td>
<td><code class="highlighter-rouge">51</code></td>
<td><code class="highlighter-rouge">f3</code></td>
<td><code class="highlighter-rouge">0e</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">e_</code></td>
<td><code class="highlighter-rouge">12</code></td>
<td><code class="highlighter-rouge">36</code></td>
<td><code class="highlighter-rouge">5a</code></td>
<td><code class="highlighter-rouge">ee</code></td>
<td><code class="highlighter-rouge">29</code></td>
<td><code class="highlighter-rouge">7b</code></td>
<td><code class="highlighter-rouge">8d</code></td>
<td><code class="highlighter-rouge">8c</code></td>
<td><code class="highlighter-rouge">8f</code></td>
<td><code class="highlighter-rouge">8a</code></td>
<td><code class="highlighter-rouge">85</code></td>
<td><code class="highlighter-rouge">94</code></td>
<td><code class="highlighter-rouge">a7</code></td>
<td><code class="highlighter-rouge">f2</code></td>
<td><code class="highlighter-rouge">0d</code></td>
<td><code class="highlighter-rouge">17</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">f_</code></td>
<td><code class="highlighter-rouge">39</code></td>
<td><code class="highlighter-rouge">4b</code></td>
<td><code class="highlighter-rouge">dd</code></td>
<td><code class="highlighter-rouge">7c</code></td>
<td><code class="highlighter-rouge">84</code></td>
<td><code class="highlighter-rouge">97</code></td>
<td><code class="highlighter-rouge">a2</code></td>
<td><code class="highlighter-rouge">fd</code></td>
<td><code class="highlighter-rouge">1c</code></td>
<td><code class="highlighter-rouge">24</code></td>
<td><code class="highlighter-rouge">6c</code></td>
<td><code class="highlighter-rouge">b4</code></td>
<td><code class="highlighter-rouge">c7</code></td>
<td><code class="highlighter-rouge">52</code></td>
<td><code class="highlighter-rouge">f6</code></td>
<td><code class="highlighter-rouge">--</code></td>
</tr>
</tbody>
</table>
<p>The inverse of this table is the log table to use when performing
multiplications:</p>
<table class="java-type-table">
<thead>
<tr>
<th><code class="highlighter-rouge">__</code></th>
<th><code class="highlighter-rouge">_0</code></th>
<th><code class="highlighter-rouge">_1</code></th>
<th><code class="highlighter-rouge">_2</code></th>
<th><code class="highlighter-rouge">_3</code></th>
<th><code class="highlighter-rouge">_4</code></th>
<th><code class="highlighter-rouge">_5</code></th>
<th><code class="highlighter-rouge">_6</code></th>
<th><code class="highlighter-rouge">_7</code></th>
<th><code class="highlighter-rouge">_8</code></th>
<th><code class="highlighter-rouge">_9</code></th>
<th><code class="highlighter-rouge">_a</code></th>
<th><code class="highlighter-rouge">_b</code></th>
<th><code class="highlighter-rouge">_c</code></th>
<th><code class="highlighter-rouge">_d</code></th>
<th><code class="highlighter-rouge">_e</code></th>
<th><code class="highlighter-rouge">_f</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="highlighter-rouge">0_</code></td>
<td><code class="highlighter-rouge">--</code></td>
<td><code class="highlighter-rouge">00</code></td>
<td><code class="highlighter-rouge">19</code></td>
<td><code class="highlighter-rouge">01</code></td>
<td><code class="highlighter-rouge">32</code></td>
<td><code class="highlighter-rouge">02</code></td>
<td><code class="highlighter-rouge">1a</code></td>
<td><code class="highlighter-rouge">c6</code></td>
<td><code class="highlighter-rouge">4b</code></td>
<td><code class="highlighter-rouge">c7</code></td>
<td><code class="highlighter-rouge">1b</code></td>
<td><code class="highlighter-rouge">68</code></td>
<td><code class="highlighter-rouge">33</code></td>
<td><code class="highlighter-rouge">ee</code></td>
<td><code class="highlighter-rouge">df</code></td>
<td><code class="highlighter-rouge">03</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">1_</code></td>
<td><code class="highlighter-rouge">64</code></td>
<td><code class="highlighter-rouge">04</code></td>
<td><code class="highlighter-rouge">e0</code></td>
<td><code class="highlighter-rouge">0e</code></td>
<td><code class="highlighter-rouge">34</code></td>
<td><code class="highlighter-rouge">8d</code></td>
<td><code class="highlighter-rouge">81</code></td>
<td><code class="highlighter-rouge">ef</code></td>
<td><code class="highlighter-rouge">4c</code></td>
<td><code class="highlighter-rouge">71</code></td>
<td><code class="highlighter-rouge">08</code></td>
<td><code class="highlighter-rouge">c8</code></td>
<td><code class="highlighter-rouge">f8</code></td>
<td><code class="highlighter-rouge">69</code></td>
<td><code class="highlighter-rouge">1c</code></td>
<td><code class="highlighter-rouge">c1</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">2_</code></td>
<td><code class="highlighter-rouge">7d</code></td>
<td><code class="highlighter-rouge">c2</code></td>
<td><code class="highlighter-rouge">1d</code></td>
<td><code class="highlighter-rouge">b5</code></td>
<td><code class="highlighter-rouge">f9</code></td>
<td><code class="highlighter-rouge">b9</code></td>
<td><code class="highlighter-rouge">27</code></td>
<td><code class="highlighter-rouge">6a</code></td>
<td><code class="highlighter-rouge">4d</code></td>
<td><code class="highlighter-rouge">e4</code></td>
<td><code class="highlighter-rouge">a6</code></td>
<td><code class="highlighter-rouge">72</code></td>
<td><code class="highlighter-rouge">9a</code></td>
<td><code class="highlighter-rouge">c9</code></td>
<td><code class="highlighter-rouge">09</code></td>
<td><code class="highlighter-rouge">78</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">3_</code></td>
<td><code class="highlighter-rouge">65</code></td>
<td><code class="highlighter-rouge">2f</code></td>
<td><code class="highlighter-rouge">8a</code></td>
<td><code class="highlighter-rouge">05</code></td>
<td><code class="highlighter-rouge">21</code></td>
<td><code class="highlighter-rouge">0f</code></td>
<td><code class="highlighter-rouge">e1</code></td>
<td><code class="highlighter-rouge">24</code></td>
<td><code class="highlighter-rouge">12</code></td>
<td><code class="highlighter-rouge">f0</code></td>
<td><code class="highlighter-rouge">82</code></td>
<td><code class="highlighter-rouge">45</code></td>
<td><code class="highlighter-rouge">35</code></td>
<td><code class="highlighter-rouge">93</code></td>
<td><code class="highlighter-rouge">da</code></td>
<td><code class="highlighter-rouge">8e</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">4_</code></td>
<td><code class="highlighter-rouge">96</code></td>
<td><code class="highlighter-rouge">8f</code></td>
<td><code class="highlighter-rouge">db</code></td>
<td><code class="highlighter-rouge">bd</code></td>
<td><code class="highlighter-rouge">36</code></td>
<td><code class="highlighter-rouge">d0</code></td>
<td><code class="highlighter-rouge">ce</code></td>
<td><code class="highlighter-rouge">94</code></td>
<td><code class="highlighter-rouge">13</code></td>
<td><code class="highlighter-rouge">5c</code></td>
<td><code class="highlighter-rouge">d2</code></td>
<td><code class="highlighter-rouge">f1</code></td>
<td><code class="highlighter-rouge">40</code></td>
<td><code class="highlighter-rouge">46</code></td>
<td><code class="highlighter-rouge">83</code></td>
<td><code class="highlighter-rouge">38</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">5_</code></td>
<td><code class="highlighter-rouge">66</code></td>
<td><code class="highlighter-rouge">dd</code></td>
<td><code class="highlighter-rouge">fd</code></td>
<td><code class="highlighter-rouge">30</code></td>
<td><code class="highlighter-rouge">bf</code></td>
<td><code class="highlighter-rouge">06</code></td>
<td><code class="highlighter-rouge">8b</code></td>
<td><code class="highlighter-rouge">62</code></td>
<td><code class="highlighter-rouge">b3</code></td>
<td><code class="highlighter-rouge">25</code></td>
<td><code class="highlighter-rouge">e2</code></td>
<td><code class="highlighter-rouge">98</code></td>
<td><code class="highlighter-rouge">22</code></td>
<td><code class="highlighter-rouge">88</code></td>
<td><code class="highlighter-rouge">91</code></td>
<td><code class="highlighter-rouge">10</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">6_</code></td>
<td><code class="highlighter-rouge">7e</code></td>
<td><code class="highlighter-rouge">6e</code></td>
<td><code class="highlighter-rouge">48</code></td>
<td><code class="highlighter-rouge">c3</code></td>
<td><code class="highlighter-rouge">a3</code></td>
<td><code class="highlighter-rouge">b6</code></td>
<td><code class="highlighter-rouge">1e</code></td>
<td><code class="highlighter-rouge">42</code></td>
<td><code class="highlighter-rouge">3a</code></td>
<td><code class="highlighter-rouge">6b</code></td>
<td><code class="highlighter-rouge">28</code></td>
<td><code class="highlighter-rouge">54</code></td>
<td><code class="highlighter-rouge">fa</code></td>
<td><code class="highlighter-rouge">85</code></td>
<td><code class="highlighter-rouge">3d</code></td>
<td><code class="highlighter-rouge">ba</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">7_</code></td>
<td><code class="highlighter-rouge">2b</code></td>
<td><code class="highlighter-rouge">79</code></td>
<td><code class="highlighter-rouge">0a</code></td>
<td><code class="highlighter-rouge">15</code></td>
<td><code class="highlighter-rouge">9b</code></td>
<td><code class="highlighter-rouge">9f</code></td>
<td><code class="highlighter-rouge">5e</code></td>
<td><code class="highlighter-rouge">ca</code></td>
<td><code class="highlighter-rouge">4e</code></td>
<td><code class="highlighter-rouge">d4</code></td>
<td><code class="highlighter-rouge">ac</code></td>
<td><code class="highlighter-rouge">e5</code></td>
<td><code class="highlighter-rouge">f3</code></td>
<td><code class="highlighter-rouge">73</code></td>
<td><code class="highlighter-rouge">a7</code></td>
<td><code class="highlighter-rouge">57</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">8_</code></td>
<td><code class="highlighter-rouge">af</code></td>
<td><code class="highlighter-rouge">58</code></td>
<td><code class="highlighter-rouge">a8</code></td>
<td><code class="highlighter-rouge">50</code></td>
<td><code class="highlighter-rouge">f4</code></td>
<td><code class="highlighter-rouge">ea</code></td>
<td><code class="highlighter-rouge">d6</code></td>
<td><code class="highlighter-rouge">74</code></td>
<td><code class="highlighter-rouge">4f</code></td>
<td><code class="highlighter-rouge">ae</code></td>
<td><code class="highlighter-rouge">e9</code></td>
<td><code class="highlighter-rouge">d5</code></td>
<td><code class="highlighter-rouge">e7</code></td>
<td><code class="highlighter-rouge">e6</code></td>
<td><code class="highlighter-rouge">ad</code></td>
<td><code class="highlighter-rouge">e8</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">9_</code></td>
<td><code class="highlighter-rouge">2c</code></td>
<td><code class="highlighter-rouge">d7</code></td>
<td><code class="highlighter-rouge">75</code></td>
<td><code class="highlighter-rouge">7a</code></td>
<td><code class="highlighter-rouge">eb</code></td>
<td><code class="highlighter-rouge">16</code></td>
<td><code class="highlighter-rouge">0b</code></td>
<td><code class="highlighter-rouge">f5</code></td>
<td><code class="highlighter-rouge">59</code></td>
<td><code class="highlighter-rouge">cb</code></td>
<td><code class="highlighter-rouge">5f</code></td>
<td><code class="highlighter-rouge">b0</code></td>
<td><code class="highlighter-rouge">9c</code></td>
<td><code class="highlighter-rouge">a9</code></td>
<td><code class="highlighter-rouge">51</code></td>
<td><code class="highlighter-rouge">a0</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">a_</code></td>
<td><code class="highlighter-rouge">7f</code></td>
<td><code class="highlighter-rouge">0c</code></td>
<td><code class="highlighter-rouge">f6</code></td>
<td><code class="highlighter-rouge">6f</code></td>
<td><code class="highlighter-rouge">17</code></td>
<td><code class="highlighter-rouge">c4</code></td>
<td><code class="highlighter-rouge">49</code></td>
<td><code class="highlighter-rouge">ec</code></td>
<td><code class="highlighter-rouge">d8</code></td>
<td><code class="highlighter-rouge">43</code></td>
<td><code class="highlighter-rouge">1f</code></td>
<td><code class="highlighter-rouge">2d</code></td>
<td><code class="highlighter-rouge">a4</code></td>
<td><code class="highlighter-rouge">76</code></td>
<td><code class="highlighter-rouge">7b</code></td>
<td><code class="highlighter-rouge">b7</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">b_</code></td>
<td><code class="highlighter-rouge">cc</code></td>
<td><code class="highlighter-rouge">bb</code></td>
<td><code class="highlighter-rouge">3e</code></td>
<td><code class="highlighter-rouge">5a</code></td>
<td><code class="highlighter-rouge">fb</code></td>
<td><code class="highlighter-rouge">60</code></td>
<td><code class="highlighter-rouge">b1</code></td>
<td><code class="highlighter-rouge">86</code></td>
<td><code class="highlighter-rouge">3b</code></td>
<td><code class="highlighter-rouge">52</code></td>
<td><code class="highlighter-rouge">a1</code></td>
<td><code class="highlighter-rouge">6c</code></td>
<td><code class="highlighter-rouge">aa</code></td>
<td><code class="highlighter-rouge">55</code></td>
<td><code class="highlighter-rouge">29</code></td>
<td><code class="highlighter-rouge">9d</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">c_</code></td>
<td><code class="highlighter-rouge">97</code></td>
<td><code class="highlighter-rouge">b2</code></td>
<td><code class="highlighter-rouge">87</code></td>
<td><code class="highlighter-rouge">90</code></td>
<td><code class="highlighter-rouge">61</code></td>
<td><code class="highlighter-rouge">be</code></td>
<td><code class="highlighter-rouge">dc</code></td>
<td><code class="highlighter-rouge">fc</code></td>
<td><code class="highlighter-rouge">bc</code></td>
<td><code class="highlighter-rouge">95</code></td>
<td><code class="highlighter-rouge">cf</code></td>
<td><code class="highlighter-rouge">cd</code></td>
<td><code class="highlighter-rouge">37</code></td>
<td><code class="highlighter-rouge">3f</code></td>
<td><code class="highlighter-rouge">5b</code></td>
<td><code class="highlighter-rouge">d1</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">d_</code></td>
<td><code class="highlighter-rouge">53</code></td>
<td><code class="highlighter-rouge">39</code></td>
<td><code class="highlighter-rouge">84</code></td>
<td><code class="highlighter-rouge">3c</code></td>
<td><code class="highlighter-rouge">41</code></td>
<td><code class="highlighter-rouge">a2</code></td>
<td><code class="highlighter-rouge">6d</code></td>
<td><code class="highlighter-rouge">47</code></td>
<td><code class="highlighter-rouge">14</code></td>
<td><code class="highlighter-rouge">2a</code></td>
<td><code class="highlighter-rouge">9e</code></td>
<td><code class="highlighter-rouge">5d</code></td>
<td><code class="highlighter-rouge">56</code></td>
<td><code class="highlighter-rouge">f2</code></td>
<td><code class="highlighter-rouge">d3</code></td>
<td><code class="highlighter-rouge">ab</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">e_</code></td>
<td><code class="highlighter-rouge">44</code></td>
<td><code class="highlighter-rouge">11</code></td>
<td><code class="highlighter-rouge">92</code></td>
<td><code class="highlighter-rouge">d9</code></td>
<td><code class="highlighter-rouge">23</code></td>
<td><code class="highlighter-rouge">20</code></td>
<td><code class="highlighter-rouge">2e</code></td>
<td><code class="highlighter-rouge">89</code></td>
<td><code class="highlighter-rouge">b4</code></td>
<td><code class="highlighter-rouge">7c</code></td>
<td><code class="highlighter-rouge">b8</code></td>
<td><code class="highlighter-rouge">26</code></td>
<td><code class="highlighter-rouge">77</code></td>
<td><code class="highlighter-rouge">99</code></td>
<td><code class="highlighter-rouge">e3</code></td>
<td><code class="highlighter-rouge">a5</code></td>
</tr>
<tr>
<td><code class="highlighter-rouge">f_</code></td>
<td><code class="highlighter-rouge">67</code></td>
<td><code class="highlighter-rouge">4a</code></td>
<td><code class="highlighter-rouge">ed</code></td>
<td><code class="highlighter-rouge">de</code></td>
<td><code class="highlighter-rouge">c5</code></td>
<td><code class="highlighter-rouge">31</code></td>
<td><code class="highlighter-rouge">fe</code></td>
<td><code class="highlighter-rouge">18</code></td>
<td><code class="highlighter-rouge">0d</code></td>
<td><code class="highlighter-rouge">63</code></td>
<td><code class="highlighter-rouge">8c</code></td>
<td><code class="highlighter-rouge">80</code></td>
<td><code class="highlighter-rouge">c0</code></td>
<td><code class="highlighter-rouge">f7</code></td>
<td><code class="highlighter-rouge">70</code></td>
<td><code class="highlighter-rouge">07</code></td>
</tr>
</tbody>
</table>
<p>Here are the same tables in an easier-to-copy-and-paste format.</p>
<p>Powers of <em>x+1</em>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x01, 0x03, 0x05, 0x0f, 0x11, 0x33, 0x55, 0xff, 0x1a, 0x2e, 0x72, 0x96, 0xa1,
0xf8, 0x13, 0x35, 0x5f, 0xe1, 0x38, 0x48, 0xd8, 0x73, 0x95, 0xa4, 0xf7, 0x02,
0x06, 0x0a, 0x1e, 0x22, 0x66, 0xaa, 0xe5, 0x34, 0x5c, 0xe4, 0x37, 0x59, 0xeb,
0x26, 0x6a, 0xbe, 0xd9, 0x70, 0x90, 0xab, 0xe6, 0x31, 0x53, 0xf5, 0x04, 0x0c,
0x14, 0x3c, 0x44, 0xcc, 0x4f, 0xd1, 0x68, 0xb8, 0xd3, 0x6e, 0xb2, 0xcd, 0x4c,
0xd4, 0x67, 0xa9, 0xe0, 0x3b, 0x4d, 0xd7, 0x62, 0xa6, 0xf1, 0x08, 0x18, 0x28,
0x78, 0x88, 0x83, 0x9e, 0xb9, 0xd0, 0x6b, 0xbd, 0xdc, 0x7f, 0x81, 0x98, 0xb3,
0xce, 0x49, 0xdb, 0x76, 0x9a, 0xb5, 0xc4, 0x57, 0xf9, 0x10, 0x30, 0x50, 0xf0,
0x0b, 0x1d, 0x27, 0x69, 0xbb, 0xd6, 0x61, 0xa3, 0xfe, 0x19, 0x2b, 0x7d, 0x87,
0x92, 0xad, 0xec, 0x2f, 0x71, 0x93, 0xae, 0xe9, 0x20, 0x60, 0xa0, 0xfb, 0x16,
0x3a, 0x4e, 0xd2, 0x6d, 0xb7, 0xc2, 0x5d, 0xe7, 0x32, 0x56, 0xfa, 0x15, 0x3f,
0x41, 0xc3, 0x5e, 0xe2, 0x3d, 0x47, 0xc9, 0x40, 0xc0, 0x5b, 0xed, 0x2c, 0x74,
0x9c, 0xbf, 0xda, 0x75, 0x9f, 0xba, 0xd5, 0x64, 0xac, 0xef, 0x2a, 0x7e, 0x82,
0x9d, 0xbc, 0xdf, 0x7a, 0x8e, 0x89, 0x80, 0x9b, 0xb6, 0xc1, 0x58, 0xe8, 0x23,
0x65, 0xaf, 0xea, 0x25, 0x6f, 0xb1, 0xc8, 0x43, 0xc5, 0x54, 0xfc, 0x1f, 0x21,
0x63, 0xa5, 0xf4, 0x07, 0x09, 0x1b, 0x2d, 0x77, 0x99, 0xb0, 0xcb, 0x46, 0xca,
0x45, 0xcf, 0x4a, 0xde, 0x79, 0x8b, 0x86, 0x91, 0xa8, 0xe3, 0x3e, 0x42, 0xc6,
0x51, 0xf3, 0x0e, 0x12, 0x36, 0x5a, 0xee, 0x29, 0x7b, 0x8d, 0x8c, 0x8f, 0x8a,
0x85, 0x94, 0xa7, 0xf2, 0x0d, 0x17, 0x39, 0x4b, 0xdd, 0x7c, 0x84, 0x97, 0xa2,
0xfd, 0x1c, 0x24, 0x6c, 0xb4, 0xc7, 0x52, 0xf6
</code></pre></div></div>
<p>Logs base <em>x+1</em>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0x00, 0x19, 0x01, 0x32, 0x02, 0x1a, 0xc6, 0x4b, 0xc7, 0x1b, 0x68, 0x33, 0xee,
0xdf, 0x03, 0x64, 0x04, 0xe0, 0x0e, 0x34, 0x8d, 0x81, 0xef, 0x4c, 0x71, 0x08,
0xc8, 0xf8, 0x69, 0x1c, 0xc1, 0x7d, 0xc2, 0x1d, 0xb5, 0xf9, 0xb9, 0x27, 0x6a,
0x4d, 0xe4, 0xa6, 0x72, 0x9a, 0xc9, 0x09, 0x78, 0x65, 0x2f, 0x8a, 0x05, 0x21,
0x0f, 0xe1, 0x24, 0x12, 0xf0, 0x82, 0x45, 0x35, 0x93, 0xda, 0x8e, 0x96, 0x8f,
0xdb, 0xbd, 0x36, 0xd0, 0xce, 0x94, 0x13, 0x5c, 0xd2, 0xf1, 0x40, 0x46, 0x83,
0x38, 0x66, 0xdd, 0xfd, 0x30, 0xbf, 0x06, 0x8b, 0x62, 0xb3, 0x25, 0xe2, 0x98,
0x22, 0x88, 0x91, 0x10, 0x7e, 0x6e, 0x48, 0xc3, 0xa3, 0xb6, 0x1e, 0x42, 0x3a,
0x6b, 0x28, 0x54, 0xfa, 0x85, 0x3d, 0xba, 0x2b, 0x79, 0x0a, 0x15, 0x9b, 0x9f,
0x5e, 0xca, 0x4e, 0xd4, 0xac, 0xe5, 0xf3, 0x73, 0xa7, 0x57, 0xaf, 0x58, 0xa8,
0x50, 0xf4, 0xea, 0xd6, 0x74, 0x4f, 0xae, 0xe9, 0xd5, 0xe7, 0xe6, 0xad, 0xe8,
0x2c, 0xd7, 0x75, 0x7a, 0xeb, 0x16, 0x0b, 0xf5, 0x59, 0xcb, 0x5f, 0xb0, 0x9c,
0xa9, 0x51, 0xa0, 0x7f, 0x0c, 0xf6, 0x6f, 0x17, 0xc4, 0x49, 0xec, 0xd8, 0x43,
0x1f, 0x2d, 0xa4, 0x76, 0x7b, 0xb7, 0xcc, 0xbb, 0x3e, 0x5a, 0xfb, 0x60, 0xb1,
0x86, 0x3b, 0x52, 0xa1, 0x6c, 0xaa, 0x55, 0x29, 0x9d, 0x97, 0xb2, 0x87, 0x90,
0x61, 0xbe, 0xdc, 0xfc, 0xbc, 0x95, 0xcf, 0xcd, 0x37, 0x3f, 0x5b, 0xd1, 0x53,
0x39, 0x84, 0x3c, 0x41, 0xa2, 0x6d, 0x47, 0x14, 0x2a, 0x9e, 0x5d, 0x56, 0xf2,
0xd3, 0xab, 0x44, 0x11, 0x92, 0xd9, 0x23, 0x20, 0x2e, 0x89, 0xb4, 0x7c, 0xb8,
0x26, 0x77, 0x99, 0xe3, 0xa5, 0x67, 0x4a, 0xed, 0xde, 0xc5, 0x31, 0xfe, 0x18,
0x0d, 0x63, 0x8c, 0x80, 0xc0, 0xf7, 0x70, 0x07
</code></pre></div></div>This is a short note-to-self about doing arithmetic in a finite field of order 28 as I can never remember exactly how it works, and have spent time computing the necessary tables at least twice before and don’t want to do it again.Floyd’s loop-finding algorithm2018-04-11T09:09:09+00:002018-04-11T09:09:09+00:00https://davecturner.github.io/2018/04/11/tortoise-and-hare<p>This is the fifth post in an open-ended series on proving the correctness of
TLA specifications using Isabelle/HOL:</p>
<ul>
<li>
<p><a href="/2018/02/12/tla-in-isabelle.html">TLA+ in Isabelle/HOL</a></p>
</li>
<li>
<p><a href="/2018/02/18/tla-clock-specification.html">Refining specifications in TLA+ and Isabelle/HOL</a></p>
</li>
<li>
<p><a href="/2018/03/23/tla-resource-allocator.html">Using TLA’s induction rule</a></p>
</li>
<li>
<p><a href="/2018/03/24/tla-resource-scheduler.html">Fairness properties in refinement proofs</a></p>
</li>
<li>
<p><strong>Floyd’s loop-finding algorithm</strong></p>
</li>
<li>
<p><a href="/2018/07/24/uncrossing-lines.html">Uncrossing lines</a></p>
</li>
</ul>
<h2 id="floyds-loop-finding-algorithm">Floyd’s loop-finding algorithm</h2>
<p>This post is about <a href="https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_Tortoise_and_Hare">Floyd’s tortoise-and-hare
algorithm</a>
for detecting a loop in a linked list using constant space. The basic idea is
you have two pointers (called <code class="highlighter-rouge">tortoise</code> and <code class="highlighter-rouge">hare</code>) and you walk them along
the list with the hare going two steps for every one step of the tortoise. If
the hare finds the end of the list then you know there’s no loop, and if the
hare meets up with the tortoise again then you know there is a loop.</p>
<p>I came across this example in <a href="https://hillelwayne.com/post/list-of-tla-examples/">Hillel Wayne’s list of TLA+
examples</a> which linked to
<a href="https://lorinhochstein.wordpress.com/2017/10/16/the-tortoise-and-the-hare-in-tla/">a post by Lorin
Hochstein</a>
in which the TLA+ toolbox was used to show this algorithm’s <a href="https://en.wikipedia.org/wiki/Correctness_(computer_science)">partial
correctness</a>: it
never gives the wrong answer, although it may never give any answer at all,
which is a safety property. I thought it’d be a nice exercise to strengthen
this to a total correctness argument by showing that the algorithm does
eventually terminate, i.e. a liveness property, using Isabelle/HOL.</p>
<p>This post is a tour of
<a href="https://github.com/DaveCTurner/tla-examples/blob/8e93381e46ed279a9424a25e2b29e6d8863c1629/TortoiseHare.thy"><code class="highlighter-rouge">TortoiseHare.thy</code></a>
in <a href="https://github.com/DaveCTurner/tla-examples">my TLA examples Github
repository</a>.</p>
<h2 id="preliminaries-on-linked-lists">Preliminaries on linked lists</h2>
<p>We fix a linked list up-front as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale LinkedList =
fixes headCell :: 'Cell
fixes nextCell :: "'Cell ⇒ 'Cell option"
assumes finiteNext: "finite { c. nextCell c ≠ None }"
</code></pre></div></div>
<p><code class="highlighter-rouge">headCell</code> is the start of the list, and the <code class="highlighter-rouge">nextCell</code> function either has
<code class="highlighter-rouge">nextCell c = Some c'</code> if <code class="highlighter-rouge">c'</code> follows <code class="highlighter-rouge">c</code> in the list, or else <code class="highlighter-rouge">nextCell c =
None</code> if <code class="highlighter-rouge">c</code> has no successor. We need to make some kind of assumption that
rules out infinite lists, and I elected to assume that only finitely many
<code class="highlighter-rouge">nextCell</code>s have been assigned. It’d also be fine to make the stronger
assumption that there are only finitely many cells, and the weaker assumption
that there are only finitely many cells reachable from <code class="highlighter-rouge">headCell</code>, but I liked
this one the best.</p>
<p>In order to state the algorithm it’s useful to have a helper function for
moving the <code class="highlighter-rouge">hare</code> two steps forward, called <code class="highlighter-rouge">nextNextCell</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition nextNextCell :: "'Cell ⇒ 'Cell option"
where "nextNextCell c ≡ case nextCell c of None ⇒ None | Some c' ⇒ nextCell c'"
</code></pre></div></div>
<h2 id="definitions">Definitions</h2>
<p>The specification extends the <code class="highlighter-rouge">LinkedList</code> locale:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>locale TortoiseHare = LinkedList
where headCell = headCell
for headCell :: 'Cell
+
</code></pre></div></div>
<p>There are three variables. We already met <code class="highlighter-rouge">tortoise</code> and <code class="highlighter-rouge">hare</code> which point to
cells, and <code class="highlighter-rouge">hasLoop</code> is a <code class="highlighter-rouge">bool option</code> that indicates whether the algorithm is
still running (<code class="highlighter-rouge">hasLoop = None</code>) or if it has detected a loop (<code class="highlighter-rouge">hasLoop = Some
True</code>) or found that there is no loop (<code class="highlighter-rouge">hasLoop = Some False</code>):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes tortoise :: "'Cell stfun"
fixes hare :: "'Cell stfun"
fixes hasLoop :: "bool option stfun"
assumes bv: "basevars (tortoise, hare, hasLoop)"
</code></pre></div></div>
<p>Initially the tortoise and the hare are at the start of the list and the
algorithm has not terminated:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes Initial :: stpred
defines "Initial ≡ PRED
tortoise = #headCell ∧ hare = #headCell ∧ hasLoop = #None"
</code></pre></div></div>
<p>A step of the algorithm involves moving <code class="highlighter-rouge">tortoise</code> to its <code class="highlighter-rouge">nextCell</code> and <code class="highlighter-rouge">hare</code>
to its <code class="highlighter-rouge">nextNextCell</code>, and then checking if the hare has met the tortoise again
or not:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes Step :: action
defines "Step ≡ ACT $hasLoop = #None
∧ Some<hare$> = nextNextCell<$hare>
∧ Some<tortoise$> = nextCell<$tortoise>
∧ hasLoop$ = (if hare$ = tortoise$ then #(Some True)
else #None)"
</code></pre></div></div>
<p>It’s only possible to take this step if the <code class="highlighter-rouge">hare</code> has a <code class="highlighter-rouge">nextNextCell</code> - if
not then it has found the end of the loop and the algorithm terminates with
<code class="highlighter-rouge">hasLoop = Some False</code>.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes Finish :: action
defines "Finish ≡ ACT $hasLoop = #None
∧ nextNextCell<$hare> = #None
∧ hasLoop$ = #(Some False)"
</code></pre></div></div>
<p>Notice that in this case we do not need to define the values for <code class="highlighter-rouge">tortoise$</code>
and <code class="highlighter-rouge">hare$</code>: the algorithm has terminated so it does not matter what values
they hold. The <code class="highlighter-rouge">Step</code> and <code class="highlighter-rouge">Finish</code> actions are the only possible:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes Next :: action
defines "Next ≡ ACT (Step ∨ Finish)"
</code></pre></div></div>
<p>The full spec combines the definitions above with fairness conditions that
indicate that the algorithm always eventually takes a step or else terminates:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> fixes Spec :: temporal
defines "Spec ≡ TEMP (Init Initial ∧ □[Next]_(tortoise, hare, hasLoop)
∧ WF(Step)_(tortoise, hare, hasLoop)
∧ WF(Finish)_(tortoise, hare, hasLoop))"
</code></pre></div></div>
<h2 id="linked-lists-and-transitive-closures">Linked lists and transitive closures</h2>
<p>Isabelle has some useful machinery for working with transitive closures, which
are important here, so here are some definitions that help to bridge the gap
from the presentation of a linked list above.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition r :: "('Cell × 'Cell) set"
where "r ≡ {(c1, c2). nextCell c1 = Some c2}"
</code></pre></div></div>
<p>The relation <code class="highlighter-rouge">r</code> is the successor relation: the set of pairs of adjacent cells.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition loopExists :: bool
where "loopExists ≡ ∃ c. (headCell, c) ∈ rtrancl r ∧ (c, c) ∈ trancl r"
</code></pre></div></div>
<p>The functions <code class="highlighter-rouge">trancl</code> and <code class="highlighter-rouge">rtrancl</code> return the transitive closure and the
reflexive-transitive closure of their input. Isabelle likes to write these as
superscript <code class="highlighter-rouge">+</code> and <code class="highlighter-rouge">*</code> respectively but they’re too hard to type and the
difference is too subtle for me at the font size I prefer, so I like to spell
the names in full. In more detail, <code class="highlighter-rouge">trancl r</code> is all pairs of cells that are
reachable in one or more steps, and <code class="highlighter-rouge">rtrancl r</code> is all pairs reachable in zero
or more steps. The <code class="highlighter-rouge">+</code> and <code class="highlighter-rouge">*</code> notation is known as the <a href="https://en.wikipedia.org/wiki/Kleene_star">Kleene plus and Kleene
star</a>. This means that <code class="highlighter-rouge">loopExists</code>
records the existence of a loop that is reachable from <code class="highlighter-rouge">headCell.</code></p>
<p>It is clear that each cell has at most one successor:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma unique_successor: "(c, c1) ∈ r ⟹ (c, c2) ∈ r ⟹ c1 = c2" by (auto simp add: r_def)
</code></pre></div></div>
<p>It is also important that the set of reachable cells from anywhere is finite,
which follows from assumption <code class="highlighter-rouge">finiteNext</code> above:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma finiteList: "finite {c'. (c, c') ∈ rtrancl r}"
</code></pre></div></div>
<p>In the case where <code class="highlighter-rouge">c2</code> directly follows <code class="highlighter-rouge">c1</code> and <code class="highlighter-rouge">c3</code> transitively follows
<code class="highlighter-rouge">c1</code>, <code class="highlighter-rouge">c3</code> must be reachable from <code class="highlighter-rouge">c2</code>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma next_step:
assumes c12: "(c1, c2) ∈ r" and c13: "(c1, c3) ∈ trancl r"
shows "(c2, c3) ∈ rtrancl r"
</code></pre></div></div>
<p>It seems that the case where the loop is “tight”, i.e. comprises one cell, is a
bit of a special case, and the following lemma is useful for handling this. It
says the (obvious) fact that the only cell that is reacahable from a tight loop
is the cell in the loop.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma tight_loop_eq:
assumes cc': "(c, c') ∈ rtrancl r" and loop: "(c, c) ∈ r"
shows "c' = c"
</code></pre></div></div>
<p>It’s useful to have a slightly stronger property when a loop exists: it means
that there is a cell which is ahead of all cells in the list:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma loopExists_always_ahead:
assumes "loopExists"
shows "∃ c. (headCell, c) ∈ rtrancl r
∧ (∀ c'. (headCell, c') ∈ rtrancl r ⟶ (c', c) ∈ trancl r)"
</code></pre></div></div>
<p>This allows us to show that <code class="highlighter-rouge">loopExists</code> is equivalent to knowing that every
cell that is reachable from <code class="highlighter-rouge">headCell</code> has a successor:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma loopExists_no_end:
"loopExists = (∀ c. (headCell, c) ∈ rtrancl r ⟶ nextCell c ≠ None)"
proof (cases loopExists)
</code></pre></div></div>
<h2 id="safety">Safety</h2>
<p>The basic safety property is</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⊢ Spec ⟶ □(hasLoop ≠ #(Some (¬ loopExists)))
</code></pre></div></div>
<p>In more detail, recall that <code class="highlighter-rouge">hasLoop = Some b</code> when the algorithm has
terminated, where the boolean <code class="highlighter-rouge">b</code> indicates whether a loop was found or not.
This safety property says that <code class="highlighter-rouge">hasLoop</code> can never be <code class="highlighter-rouge">Some (¬ loopExists)</code> -
it is always either <code class="highlighter-rouge">None</code> (if the algorithm has not terminated) or else <code class="highlighter-rouge">Some
loopExists</code> if it correctly determined the existence of a loop, which is the
partial correctness property we wanted.</p>
<p>In fact we show a slightly stronger property:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition Invariant :: stpred where "Invariant ≡ PRED
hasLoop = #None ⟶ ((#headCell, tortoise) ∈ #(rtrancl r)
∧ (tortoise, hare) ∈ #(rtrancl r))"
definition Safety :: stpred where
"Safety ≡ PRED Invariant ∧ hasLoop ≠ #(Some (¬ loopExists))"
</code></pre></div></div>
<p>The extra invariant says that (while the algorithm hasn’t terminated) the
<code class="highlighter-rouge">tortoise</code> is always reachable from <code class="highlighter-rouge">headCell</code> and the <code class="highlighter-rouge">hare</code> is always
reachable from the <code class="highlighter-rouge">tortoise</code>.</p>
<p>It turns out that many of the proofs that follow break down into four cases:</p>
<ul>
<li>a step is taken as normal (and no loop is found)</li>
<li>a step is taken as normal and the hare catches the tortoise, so a loop is
found</li>
<li>the hare is at a cell with no successor, so no loop is possible, or</li>
<li>the hare is at a cell with a successor but <em>this</em> cell has no successor, so
again no loop is possible.</li>
</ul>
<p>It’s useful to capture this observation in a cases lemma (as was done <a href="/2018/03/24/tla-resource-scheduler.html#safety">last
time</a>). The statement
of this lemma here is ludicrously long, mainly because there are so many cells
involved in the “step” cases:</p>
<ul>
<li>the <code class="highlighter-rouge">headCell</code></li>
<li>the tortoise (old and new positions)</li>
<li>the hare (old and new positions, and the intervening cell)</li>
</ul>
<p>There’s quite a big set of reachability relationships between these cells, most
of which are important at some point in one of the proofs, so the cases lemma
states and proves them all at once to avoid duplication later. Here goes…</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma square_Next_cases [consumes 2, case_names unchanged Step FoundLoop LastHare PenultimateHare]:
assumes s_Safety: "s ⊨ Safety" and sq_Next: "(s,t) ⊨ [Next]_(tortoise, hare, hasLoop)"
assumes unchanged: "
⟦ tortoise t = tortoise s
; hare t = hare s
; hasLoop t = hasLoop s
; (s,t) ⊨ ¬Step
; (s,t) ⊨ ¬Finish
⟧ ⟹ P"
assumes Step: "⋀h'.
⟦ hare t ≠ tortoise t
; hasLoop s = None
; hasLoop t = None
; (headCell, tortoise s) ∈ rtrancl r
; (headCell, hare s) ∈ rtrancl r
; (headCell, tortoise t) ∈ trancl r
; (headCell, h') ∈ trancl r
; (headCell, hare t) ∈ trancl r
; (tortoise s, hare s) ∈ rtrancl r
; (tortoise s, tortoise t) ∈ r
; (tortoise s, h') ∈ trancl r
; (tortoise s, hare t) ∈ trancl r
; (hare s, h') ∈ r
; (hare s, hare t) ∈ trancl r
; (tortoise t, h') ∈ rtrancl r
; (tortoise t, hare t) ∈ trancl r
; (h', hare t) ∈ r
; (s,t) ⊨ Step
; (s,t) ⊨ ¬Finish
; (s,t) ⊨ ¬unchanged(tortoise, hare, hasLoop)
⟧ ⟹ P"
assumes FoundLoop: "⋀h'.
⟦ hare t = tortoise t
; hasLoop s = None
; hasLoop t = Some True
; loopExists
; (headCell, tortoise s) ∈ rtrancl r
; (headCell, hare s) ∈ rtrancl r
; (headCell, tortoise t) ∈ trancl r
; (headCell, h') ∈ trancl r
; (headCell, hare t) ∈ trancl r
; (tortoise s, hare s) ∈ rtrancl r
; (tortoise s, tortoise t) ∈ r
; (tortoise s, h') ∈ trancl r
; (tortoise s, hare t) ∈ trancl r
; (hare s, h') ∈ r
; (hare s, hare t) ∈ trancl r
; (tortoise t, h') ∈ rtrancl r
; (tortoise t, hare t) ∈ trancl r
; (h', hare t) ∈ r
; (s,t) ⊨ Step
; (s,t) ⊨ ¬Finish
; (s,t) ⊨ ¬unchanged(tortoise, hare, hasLoop)
⟧ ⟹ P"
assumes LastHare: "
⟦ hasLoop s = None
; hasLoop t = Some False
; ¬ loopExists
; nextCell (hare s) = None
; (headCell, tortoise s) ∈ rtrancl r
; (headCell, hare s) ∈ rtrancl r
; (tortoise s, hare s) ∈ rtrancl r
; (s,t) ⊨ ¬Step
; (s,t) ⊨ Finish
; (s,t) ⊨ ¬unchanged(tortoise, hare, hasLoop)
⟧ ⟹ P"
assumes PenultimateHare: "⋀h'.
⟦ hasLoop s = None
; hasLoop t = Some False
; ¬ loopExists
; (hare s, h') ∈ r
; nextCell h' = None
; (headCell, tortoise s) ∈ rtrancl r
; (headCell, hare s) ∈ rtrancl r
; (headCell, h') ∈ trancl r
; (tortoise s, hare s) ∈ rtrancl r
; (tortoise s, h') ∈ trancl r
; (hare s, h') ∈ r
; (s,t) ⊨ ¬Step
; (s,t) ⊨ Finish
; (s,t) ⊨ ¬unchanged(tortoise, hare, hasLoop)
⟧ ⟹ P"
shows P
</code></pre></div></div>
<p>The proof of this is a little laborious, but it pays off: the safety lemma
follows almost automatically. Here is the proof in full:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma safety: "⊢ Spec ⟶ □Safety"
proof invariant
fix sigma
assume sigma: "sigma ⊨ Spec"
thus "sigma ⊨ Init Safety"
unfolding Invariant_def Safety_def Spec_def Initial_def r_def
by (auto simp add: Init_def)
show "sigma ⊨ stable Safety"
proof (intro Stable)
from sigma show "sigma ⊨ □[Next]_(tortoise, hare, hasLoop)"
by (auto simp add: Spec_def)
show "⊢ $Safety ∧ [Next]_(tortoise, hare, hasLoop) ⟶ Safety$"
proof (intro actionI temp_impI, elim temp_conjE, unfold unl_before)
fix s t
assume "s ⊨ Safety" "(s,t) ⊨ [Next]_(tortoise, hare, hasLoop)"
thus "(s,t) ⊨ Safety$"
by (cases rule: square_Next_cases, auto simp add: Safety_def Invariant_def loopExists_no_end)
qed
qed
qed
</code></pre></div></div>
<h2 id="liveness--termination">Liveness & termination</h2>
<p>The liveness proof for the case where there is a loop is really quite different
from the case where there isn’t. If there isn’t a loop then the argument is
roughly that there is a cell with no successor (by lemma <code class="highlighter-rouge">loopExists_no_end</code>
above) and each transition takes the hare one step closer to finding it. If
there is a loop then there are two phases: firstly, each transition takes the
tortoise closer to being in the loop, and once the tortoise is in the loop then
each transition takes the hare one step closer to catching up with the
tortoise.</p>
<p>It is useful to formalise this idea of “closer” as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fun iterateNextCell :: "nat ⇒ 'Cell ⇒ 'Cell option"
where "iterateNextCell 0 c = Some c"
| "iterateNextCell (Suc n) c
= (case iterateNextCell n c of None ⇒ None
| Some c' ⇒ nextCell c')"
</code></pre></div></div>
<p>The function <code class="highlighter-rouge">iterateNextCell n</code> returns the <code class="highlighter-rouge">n</code><sup>th</sup> successor of its
input, satisfying properties like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma iterateNextCell_sum: "iterateNextCell (a + b) c
= (case iterateNextCell a c of None ⇒ None | Some c' ⇒ iterateNextCell b c')"
</code></pre></div></div>
<p>This allows us to define a function which measures the distance between two
cells:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition distanceBetween :: "'Cell ⇒ 'Cell ⇒ nat"
where "distanceBetween c1 c2 ≡ LEAST n. iterateNextCell n c1 = Some c2"
</code></pre></div></div>
<p>An interesting feature of Isabelle is that functions do not need to be
well-defined on all their inputs: <code class="highlighter-rouge">distanceBetween c1 c2</code> only makes sense when
<code class="highlighter-rouge">c2</code> is reachable from <code class="highlighter-rouge">c1</code>, but there is no mention of this condition in its
definition. If given bad input, <code class="highlighter-rouge">distanceBetween</code> doesn’t throw an exception or
fail to terminate or anything like that, it simply returns an arbitrary result.
This works because if we know nothing about <code class="highlighter-rouge">distanceBetween c1 c2</code> when <code class="highlighter-rouge">c2</code>
is unreachable from <code class="highlighter-rouge">c1</code> then we cannot use its value in any meaningful way,
which means that any statements about it will have to have ensure that the
right precondition holds. It might seem more obvious to put in a guard
condition as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition distanceBetweenOrZero :: "'Cell ⇒ 'Cell ⇒ nat"
where "distanceBetweenOrZero c1 c2
≡ if (c1, c2) ∈ rtrancl r
then (LEAST n. iterateNextCell n c1 = Some c2)
else 0"
</code></pre></div></div>
<p>This function is actually harder to use because it clearly defines its value
for all arguments, and yet its value is meaningless for unreachable inputs (and
would remain meaningless if <code class="highlighter-rouge">0</code> were replaced by any other number) so the
reachability precondition will still be necessary when using it. Moreover there
is a risk that we actually use its value on unreachable inputs, which can be
avoided if the return value is arbitrary. We can avoid that risk using an
<code class="highlighter-rouge">option</code> type to model the partiality of the function with something like this:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>definition maybeDistanceBetween :: "'Cell ⇒ 'Cell ⇒ nat option"
where "maybeDistanceBetween c1 c2
≡ if (c1, c2) ∈ rtrancl r
then Some (LEAST n. iterateNextCell n c1 = Some c2)
else None"
</code></pre></div></div>
<p>Again, this is harder to use, and is no safer than the unguarded definition.</p>
<p>The preferred <code class="highlighter-rouge">distanceBetween</code> function satisfies all sorts of useful
properties, all of which have preconditions that guarantee reachability of its
arguments in one way or another:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma iterateNextCell_distanceBetween:
assumes "(c1, c2) ∈ rtrancl r"
shows "iterateNextCell (distanceBetween c1 c2) c1 = Some c2"
lemma distanceBetween_atMost:
assumes "iterateNextCell n c1 = Some c2" shows "distanceBetween c1 c2 ≤ n"
lemma distanceBetween_0 [simp]: "distanceBetween c c = 0"
lemma distanceBetween_0_eq:
assumes "(c1, c2) ∈ rtrancl r"
shows "(distanceBetween c1 c2 = 0) = (c1 = c2)"
lemma distanceBetween_le_1:
assumes "(c1, c2) ∈ r" shows "distanceBetween c1 c2 ≤ 1"
lemma distanceBetween_triangle:
assumes c12: "(c1, c2) ∈ rtrancl r" and c23: "(c2, c3) ∈ rtrancl r"
shows "distanceBetween c1 c3 ≤ distanceBetween c1 c2 + distanceBetween c2 c3"
lemma distanceBetween_eq_Suc:
assumes c13: "(c1, c3) ∈ rtrancl r" and c13_ne: "c1 ≠ c3"
and c12: "(c1, c2) ∈ r"
shows "distanceBetween c1 c3 = Suc (distanceBetween c2 c3)"
</code></pre></div></div>
<p>There is one other property that turns out to be useful: any cell has at most
one predecessor <em>in a loop</em>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma loop_unique_previous:
assumes c1c: "(c1, c) ∈ r" and c1_loop: "(c1, c1) ∈ trancl r"
assumes c2c: "(c2, c) ∈ r" and c2_loop: "(c2, c2) ∈ trancl r"
shows "c1 = c2"
</code></pre></div></div>
<p>The proof of this works by computing the length of the loop that <code class="highlighter-rouge">c1</code> is in,
i.e. <code class="highlighter-rouge">1 + distanceBetween c c1</code>, and then showing that this is the same as the
length of the loop that <code class="highlighter-rouge">c2</code> is in.</p>
<p>It’s also useful to specialise rule <code class="highlighter-rouge">WF1</code> for this specification, to cut out
some duplication:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma WF1_Spec:
fixes A :: action
fixes P Q :: stpred
assumes 0: "⊢ Spec ⟶ WF(A)_(tortoise, hare, hasLoop)"
assumes 1: "⋀s t.
⟦ s ⊨ P; s ⊨ Safety; (s,t) ⊨ [Next]_(tortoise, hare, hasLoop) ⟧
⟹ t ⊨ P ∨ Q"
assumes 2: "⋀s t.
⟦ s ⊨ P; s ⊨ Safety; (s,t) ⊨ [Next]_(tortoise, hare, hasLoop) ⟧
⟹ s ⊨ Enabled(<A>_(tortoise, hare, hasLoop))"
assumes 3: "⋀s t.
⟦ s ⊨ P; s ⊨ Safety; (s,t) ⊨ [Next]_(tortoise, hare, hasLoop);
(s,t) ⊨ A; (s,t) ⊨ ¬unchanged (tortoise, hare, hasLoop) ⟧
⟹ t ⊨ Q"
shows "⊢ Spec ⟶ (P ↝ Q)"
</code></pre></div></div>
<p>The final “utility” lemma is to characterise the conditions under which the
<code class="highlighter-rouge">Step</code> action is enabled, which is when the <code class="highlighter-rouge">hare</code> has a second successor (and
the algorithm has not already terminated):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma
assumes s_Safety: "s ⊨ Safety"
assumes nextNext: "nextNextCell (hare s) ≠ None"
assumes notFinished: "hasLoop s = None"
shows Enabled_StepI: "s ⊨ Enabled (<Step>_(tortoise, hare, hasLoop))"
</code></pre></div></div>
<p>Now the liveness proof can properly begin. The first step is to capture the
last transition in the no-loop case:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma hare_endgame: "⊢ Spec ⟶ (nextNextCell<hare> = #None ↝ hasLoop ≠ #None)"
</code></pre></div></div>
<p>This follows from the fairness of the <code class="highlighter-rouge">Finish</code> action by a relatively
straightforward application of the <code class="highlighter-rouge">WF1_Spec</code> rule above. One minor wrinkle is
that <code class="highlighter-rouge">nextNextCell<hare> = #None</code> might be true after the algorithm has already
terminated, in which case the <code class="highlighter-rouge">Finish</code> action is not enabled, so we need to use
the diamond rule to consider the two cases <code class="highlighter-rouge">hasLoop = #None</code> and <code class="highlighter-rouge">hasLoop ≠
#None</code> separately.</p>
<p>The next step is to show that if the algorithm does not terminate then the
tortoise visits every reachable cell:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma tortoise_visits_everywhere:
assumes hd_c: "(headCell, c) ∈ rtrancl r"
shows "⊢ Spec ⟶ ◇(hasLoop ≠ #None ∨ tortoise = #c)"
</code></pre></div></div>
<p>The key to proving this is to introduce a variable <code class="highlighter-rouge">n</code>, that tracks the
distance between the <code class="highlighter-rouge">tortoise</code> and the target cell, <code class="highlighter-rouge">c</code>, and then to observe
that <code class="highlighter-rouge">n</code> always decreases (or the algorithm terminates), so that eventually the
<code class="highlighter-rouge">tortoise</code> visits the target cell:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⊢ Spec
⟶ ((∃n. (tortoise, #c) ∈ #(rtrancl r)
∧ hasLoop = #None ∧ tortoise ≠ #c
∧ distanceBetween<tortoise, #c> = #n)
↝ hasLoop ≠ #None ∨ tortoise = #c)
</code></pre></div></div>
<p>Since here the structure that underpins the induction does not change, it’s
possible that this could have been done with an external induction, but I
elected to use the <a href="/2018/03/23/tla-resource-allocator.html#conclusion">internal wellfounded induction rule</a> instead. It’s a bit of a pain
to have to account for all of the things that <em>might</em> happen in a step: the
full statement about a single <code class="highlighter-rouge">Step</code> transition is as follows:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⊢ Spec
⟶ ((tortoise, #c) ∈ #(rtrancl r)
∧ hasLoop = #None
∧ tortoise ≠ #c
∧ distanceBetween<tortoise, #c> = #n
∧ nextNextCell<hare> ≠ #None
↝ hasLoop ≠ #None
∨ tortoise = #c
∨ (∃n'. #((n', n) ∈ {(n, n'). n < n'})
∧ (tortoise, #c) ∈ #(rtrancl r)
∧ hasLoop = #None
∧ tortoise ≠ #c
∧ distanceBetween<tortoise, #c> = #n'))"
</code></pre></div></div>
<p>The preconditions are necessary:</p>
<ul>
<li><code class="highlighter-rouge">(tortoise, #c) ∈ #(rtrancl r)</code> means we haven’t overshot and passed the
target cell already.</li>
<li><code class="highlighter-rouge">hasLoop = #None</code> means the algorithm hasn’t already terminated.</li>
<li><code class="highlighter-rouge">tortoise ≠ #c</code> means we haven’t found the target cell yet, or else <code class="highlighter-rouge">n</code> would
be <code class="highlighter-rouge">0</code> and wouldn’t be able to decrease, and</li>
<li><code class="highlighter-rouge">nextNextCell<hare> ≠ #None</code> means that the <code class="highlighter-rouge">Step</code> transition is actually
enabled.</li>
</ul>
<p>The diamond rule is used repeatedly to introduce these preconditions, by
handling the cases where they aren’t true separately.</p>
<p>Onto the main theorem:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>theorem liveness: "⊢ Spec ⟶ ◇(hasLoop ≠ #None)"
proof (cases loopExists)
</code></pre></div></div>
<p>The two cases (<code class="highlighter-rouge">loopExists</code> vs <code class="highlighter-rouge">¬ loopExists</code>) are very different. The latter
is much simpler: using <code class="highlighter-rouge">loopExists_no_end</code> yields a reachable cell <code class="highlighter-rouge">cLast</code> with
no successor, and then <code class="highlighter-rouge">tortoise_visits_everywhere</code> shows that either the
algorithm terminates or else the tortoise visits <code class="highlighter-rouge">cLast</code>. This is enough: the
<code class="highlighter-rouge">hare</code> is always reachable from the <code class="highlighter-rouge">tortoise</code> because of the safety invariant,
and at <code class="highlighter-rouge">cLast</code> the <code class="highlighter-rouge">tortoise</code> has no successor so the <code class="highlighter-rouge">hare</code> must also have no
successor, so <code class="highlighter-rouge">hare_endgame</code> applies and the algorithm terminates.</p>
<p>The case where <code class="highlighter-rouge">loopExists</code> is true is a little trickier. The first step is to
note that, because there is a loop, there is a reachable cell <code class="highlighter-rouge">cLoop</code> in the
loop, which the <code class="highlighter-rouge">tortoise</code> eventually reaches. Since the <code class="highlighter-rouge">hare</code> is always
reachable from the <code class="highlighter-rouge">tortoise</code>, this means the <code class="highlighter-rouge">hare</code> is also in the loop, which
means that the <code class="highlighter-rouge">tortoise</code> is now reachable from the <code class="highlighter-rouge">hare</code>.</p>
<p>From here we want to be able to say that every step now decrements
<code class="highlighter-rouge">distanceBetween<tortoise,hare></code> so that eventually they meet and the algorithm
terminates, but it is sadly not quite so simple, because there is one corner
case in which this is not true: if <code class="highlighter-rouge">cLoop = headCell</code> then the <code class="highlighter-rouge">tortoise</code> is in
the loop at the system’s initial state, but in the initial state
<code class="highlighter-rouge">distanceBetween<tortoise,hare> = 0</code> and yet the algorithm hasn’t terminated:
the hare must go 1½ times around the loop until it meets the tortoise again,
and the first step therefore <em>increases</em> <code class="highlighter-rouge">distanceBetween<tortoise,hare></code>.</p>
<p>To get around this case, rather than doing induction on
<code class="highlighter-rouge">distanceBetween<tortoise,hare></code> directly we can introduce an <em>inductor</em>, a
tuple of values, ordered lexicographically, which does decrease on each step.
Here the inductor is a pair:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#inductor = (tortoise = hare, distanceBetween<hare, tortoise>)
</code></pre></div></div>
<p>The first component decreases from <code class="highlighter-rouge">True</code> to <code class="highlighter-rouge">False</code> in the corner case
mentioned above, and in all other cases the first component remains <code class="highlighter-rouge">False</code> and
the second component decreases. Using this inductor leaves us with the
following statement to prove:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⊢ Spec ⟶
(hasLoop = #None
∧ (#cLoop, tortoise) ∈ #(trancl r)
∧ #inductor = (tortoise = hare, distanceBetween<hare, tortoise>)
↝ hasLoop ≠ #None
∨ (∃inductor'. #((inductor', inductor)
∈ {(False, True)} <*lex*> {(i, j). i < j})
∧ hasLoop = #None
∧ (#cLoop, tortoise) ∈ #(trancl r)
∧ #inductor' = (tortoise = hare, distanceBetween<hare, tortoise>)))
</code></pre></div></div>
<p>This is not so complicated:</p>
<ul>
<li><code class="highlighter-rouge">hasLoop = #None</code> means the algorithm hasn’t already terminated.</li>
<li><code class="highlighter-rouge">(#cLoop, tortoise) ∈ #(trancl r)</code> is a useful invariant that indicates that
the tortoise is still in the loop.</li>
<li><code class="highlighter-rouge">#inductor = (tortoise = hare, distanceBetween<hare, tortoise>)</code> defines the
inductor.</li>
</ul>
<p>It’s also true for a single step, so rule <code class="highlighter-rouge">WF1_Spec</code> applies. There are two
cases to show, depending on whether <code class="highlighter-rouge">tortoise = hare</code> or not. If <code class="highlighter-rouge">tortoise =
hare</code> then after one step either <code class="highlighter-rouge">tortoise ≠ hare</code> or else the algorithm has
terminated, so this is simple. If <code class="highlighter-rouge">tortoise ≠ hare</code> then either the algorithm
terminates or else <code class="highlighter-rouge">distanceBetween<hare, tortoise></code> decreases. This last part
follows from these three statements:</p>
<ul>
<li><code class="highlighter-rouge">distanceBetween (hare s) (tortoise s) = Suc (distanceBetween h' (tortoise s))</code></li>
<li><code class="highlighter-rouge">distanceBetween h' (tortoise t) ≤ Suc (distanceBetween h' (tortoise s))</code></li>
<li><code class="highlighter-rouge">distanceBetween h' (tortoise t) = Suc (distanceBetween (hare t) (tortoise t))</code></li>
</ul>
<p>For context, the states <code class="highlighter-rouge">s</code> and <code class="highlighter-rouge">t</code> are the states before and after the
transition, and <code class="highlighter-rouge">h'</code> is the cell in between <code class="highlighter-rouge">hare s</code> and <code class="highlighter-rouge">hare t</code>. The first
and third statements say that moving the <code class="highlighter-rouge">hare</code> one cell forwards reduces the
distance to the tortoise by 1, which requres that the <code class="highlighter-rouge">hare</code> does not overtake
the <code class="highlighter-rouge">tortoise</code> in either of these steps, but fortunately we’ve already
accounted for this. The third statement says that moving the <code class="highlighter-rouge">tortoise</code> one
step forward does not increase the distance from the hare by more than one, so
that overall the distance <em>does</em> decrease. The sequence of operations is
important here:</p>
<ol>
<li>
<p>Move the <code class="highlighter-rouge">hare</code> one step forward, decreasing the distance by 1.</p>
</li>
<li>
<p>Move the <code class="highlighter-rouge">tortoise</code> one step forward, increasing the distance by at most 1.</p>
</li>
<li>
<p>Move the <code class="highlighter-rouge">hare</code> one step forward, decreasing the distance by 1.</p>
</li>
</ol>
<p>Any other sequence does not behave so nicely. For example, if the <code class="highlighter-rouge">tortoise</code>
moves forwards first then it may meet the <code class="highlighter-rouge">hare</code>, reducing the distance to <code class="highlighter-rouge">0</code>,
and then moving the hare forward increases the distance again by some arbitrary
amount that turns out to be equal to the previous decrease, but it’s a bit of a
pain to show this.</p>
<p>From here, the proof goes through without any further difficulty.</p>
<h2 id="conclusion">Conclusion</h2>
<p>It was hopefully interesting to see how it works to model the partial function
<code class="highlighter-rouge">distanceBetween</code> as a total function which returns an arbitrary value when its
arguments are not reachable, rather than using a specific value or explicit
partiality via the <code class="highlighter-rouge">option</code> type.</p>
<p>I was a little surprised by how branchy this proof was. I thought it was going
to be quite straightforward, but it needed slightly special handling for the
cases where the loop was tight (i.e. <code class="highlighter-rouge">nextCell cLoop = Some cLoop</code>) and for
when the loop included the head of the list. These do seem to be special cases
that aren’t quite covered by the informal proof I was following, and although
they’re not hard to cover I find it satisfying when Isabelle finds these kinds
of corner cases that informal proofs skip.</p>
<p>As always, I tried a number of approaches before settling on the final one
discussed here. In retrospect, because I was expecting a less branchy proof I
was mostly trying to find a way to avoid the branches I kept encountering,
which needed to deal with the cases where the tortoise and hare were at the
same cell without the algorithm having terminated (e.g. at the start, or if the
tortoise moved “first” in each transition). Once I’d realised that these case
splits really were unavoidable then the proof went through and it didn’t take
much tidying up to get to where it is here.</p>
<p>This was a nice exercise, and I’d like to thank
<a href="https://hillelwayne.com/post/list-of-tla-examples/">Hillel</a> and
<a href="https://lorinhochstein.wordpress.com/2017/10/16/the-tortoise-and-the-hare-in-tla/">Lorin</a>
for giving me the idea.</p>This is the fifth post in an open-ended series on proving the correctness of TLA specifications using Isabelle/HOL:Partial functions in Isabelle/HOL2018-04-09T09:09:09+00:002018-04-09T09:09:09+00:00https://davecturner.github.io/2018/04/09/partial-functions-isabelle<p>A short note about <a href="http://www.joachim-breitner.de/blog/732-Isabelle_functions__Always_total%2C_sometimes_undefined">Joachim Breitner’s post on the totality of Isabelle functions</a> which says:</p>
<blockquote>
<p>For example, no Isabelle command allows you define a function <code class="highlighter-rouge">bogus :: () ⇒ nat</code> with the equation <code class="highlighter-rouge">bogus () = Suc (bogus ())</code>, because this equation does not have a solution.</p>
</blockquote>
<p>In fact, the following is completely legitimate:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function bogus :: "unit ⇒ nat" where "bogus () = Suc (bogus ())"
by pat_completeness auto
</code></pre></div></div>
<p><code class="highlighter-rouge">function</code> lets you define a function and defer the termination proof until later. Until termination is proved, all the generated facts
about the function have an extra precondition involving a predicate <code class="highlighter-rouge">bogus_dom</code> that restricts the fact to elements of the domain of the function:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Scratch.bogus.psimps: bogus_dom () ⟹ bogus () = Suc (bogus ())
Scratch.bogus.pinduct: bogus_dom ?a0.0 ⟹ (bogus_dom () ⟹ ?P () ⟹ ?P ()) ⟹ ?P ?a0.0
Scratch.bogus.pelims: bogus ?x = ?y ⟹ bogus_dom ?x ⟹ (?x = () ⟹ ?y = Suc (bogus ()) ⟹ bogus_dom () ⟹ ?P) ⟹ ?P
</code></pre></div></div>
<p>This allows you to work with the partial definition of the function in a somewhat natural way. It’s a bit of a pain, but it is less painful
than completely forbidding the partial definition. At some point you can try and turn these facts into the following total facts using a termination proof:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Scratch.bogus.simps: bogus () = Suc (bogus ())
Scratch.bogus.elims: bogus ?x = ?y ⟹ (?x = () ⟹ ?y = Suc (bogus ()) ⟹ ?P) ⟹ ?P
</code></pre></div></div>
<p>The termination proof of course is impossible here:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>termination bogus proof (intro allI)
> proof (prove)
> goal (1 subgoal):
> 1. ⋀x. bogus_dom x
</code></pre></div></div>
<p>In other words we must show that every element is in the domain of the function, which is false. Perhaps interestingly it’s false more because <code class="highlighter-rouge">bogus ()</code> is defined in terms of itself, rather than because there is no value <code class="highlighter-rouge">n :: nat</code> satisfying <code class="highlighter-rouge">n = Suc n</code>: the termination proof provides a local introduction lemma:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>local.termination: wf ?R ⟹ ((), ()) ∈ ?R ⟹ All bogus_dom
</code></pre></div></div>
<p>This tells us that in order to show termination we need to find a wellfounded relation that relates each equation in the function’s definition to the values on which it depends, and any relation that includes <code class="highlighter-rouge">((), ())</code> is clearly not wellfounded.</p>
<p>Technically, <code class="highlighter-rouge">bogus</code> is indeed a total function of type <code class="highlighter-rouge">unit ⇒ nat</code>, but until we know anything about its domain its values are completely arbitrary. This means that theorems like</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lemma "bogus () ≥ 0" by auto
</code></pre></div></div>
<p>are true, simply because that’s true of any natural number, and <code class="highlighter-rouge">bogus ()</code> is <em>some</em> natural number, even if we don’t know which one. In some contexts, the difference is important, but as a working user of Isabelle the difference doesn’t matter too much.</p>A short note about Joachim Breitner’s post on the totality of Isabelle functions which says: