<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://bertptrs.nl/feed.xml" rel="self" type="application/atom+xml" /><link href="https://bertptrs.nl/" rel="alternate" type="text/html" hreflang="en-GB" /><updated>2026-04-08T12:25:02+02:00</updated><id>https://bertptrs.nl/feed.xml</id><title type="html">bertptrs.nl</title><subtitle>My name is Bert Peters and I&apos;m a software developer/computer enthusiast/nerd. I write articles and create demos about technology and miscellaneous items that strike my fancy, and I fail to hide my love for Rust.
</subtitle><author><name>Bert Peters</name></author><entry><title type="html">How to review an AUR package</title><link href="https://bertptrs.nl/2026/01/30/how-to-review-an-aur-package.html" rel="alternate" type="text/html" title="How to review an AUR package" /><published>2026-01-30T18:57:00+01:00</published><updated>2026-01-30T18:57:00+01:00</updated><id>https://bertptrs.nl/2026/01/30/how-to-review-an-aur-package</id><content type="html" xml:base="https://bertptrs.nl/2026/01/30/how-to-review-an-aur-package.html"><![CDATA[<p>On Friday, July 18th, 2025, the Arch Linux team was notified that three <abbr title="Arch User Repository">AUR</abbr> packages had been
uploaded that contained malware. A few maintainers including myself took care of deleting these
packages, removing all traces of the malicious code, and protecting against future malicious
uploads.</p>

<p>My fellow maintainer <a href="https://archlinux.org/people/package-maintainers/#mh4ckt3mh4ckt1c4s">Quentin Michaud</a> already did a nice <a href="https://www.mh4ckt3mh4ckt1c4s.xyz/blog/aur-chaos-malware-analysis/">write-up about how the malware
worked</a>, so I won’t go into detail too much about that. If you want to know more about
that, read his blog. Instead, I’d like to do a crash course on how these packaging scripts work, and
how you would review them yourself.</p>

<h2 id="what-is-the-aur">What is the <abbr title="Arch User Repository">AUR</abbr>?</h2>

<p>The Arch User Repository is a collection of packaging scripts, <a href="https://man.archlinux.org/man/PKGBUILD.5.en"><code class="language-plaintext highlighter-rouge">PKGBUILD</code></a> files,
created by users. Anyone who creates an account on <a href="https://aur.archlinux.org/">aur.archlinux.org</a>, can upload an Arch
Linux packaging script, granted one doesn’t already exist for the same name. There are of course
<a href="https://wiki.archlinux.org/title/AUR_submission_guidelines#Rules_of_submission">some rules</a> around what you can submit (e.g. don’t duplicate other official or
<abbr title="Arch User Repository">AUR</abbr> packages) but generally anything goes.</p>

<p>Each package has one primary maintainer, by default whoever uploaded the packaging script first, but
that can change over time, either by that maintainer transferring responsibility themselves, or by
moderators removing the maintainer for various reasons. This is not a democracy, or even a
meritocracy, but it works, and there is a lot of useful software on there.</p>

<p>Installing from <abbr title="Arch User Repository">AUR</abbr> packaging scripts is not quite as simple as installing from the main packaging
repos. You can run <a href="https://man.archlinux.org/man/makepkg.8"><code class="language-plaintext highlighter-rouge">makepkg</code></a> on a <code class="language-plaintext highlighter-rouge">PKGBUILD</code> and install the resulting package, but this
gets more difficult as those <code class="language-plaintext highlighter-rouge">PKGBUILD</code>s often depend on other packages only found in the <abbr title="Arch User Repository">AUR</abbr>. To
make this easier, people often use <abbr title="Arch User Repository">AUR</abbr>-helpers, which provide a <code class="language-plaintext highlighter-rouge">pacman</code>-like experience to manage
them. That does come with some drawbacks, however.</p>

<p>Anyone who wants to do so can upload their <code class="language-plaintext highlighter-rouge">PKGBUILD</code> to the <abbr title="Arch User Repository">AUR</abbr>. That is great to lower the barrier
to entry, and it is how I got my start contributing to Arch Linux. Unfortunately, not everyone has
the best intentions, and it has happened that people upload malware. Because of this, it is crucial
that you vet the <code class="language-plaintext highlighter-rouge">PKGBUILD</code>s that you install.</p>

<h2 id="what-do-pkgbuilds-look-like">What do <code class="language-plaintext highlighter-rouge">PKGBUILD</code>s look like?</h2>

<p>As mentioned before, the <abbr title="Arch User Repository">AUR</abbr> does not contain packages, but rather contains build scripts to create
packages. Arch Linux <code class="language-plaintext highlighter-rouge">PKGBUILD</code>s are simply bash scripts that follow a certain pattern. Take for
example the following example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="rouge-code"><pre><span class="c"># Maintainer: John Doe &lt;john@example.org&gt;</span>
<span class="nv">pkgname</span><span class="o">=</span>example
<span class="nv">pkgver</span><span class="o">=</span>1.0
<span class="nv">pkgrel</span><span class="o">=</span>1
<span class="nv">pkgdesc</span><span class="o">=</span><span class="s2">"An example package"</span>
<span class="nb">arch</span><span class="o">=(</span>x86_64<span class="o">)</span>
<span class="nv">url</span><span class="o">=</span><span class="s2">"https://example.org/"</span>
<span class="nv">license</span><span class="o">=(</span><span class="s1">'WTFPL'</span><span class="o">)</span>
<span class="nb">install</span><span class="o">=</span><span class="s2">"example.install"</span>
<span class="nb">source</span><span class="o">=(</span><span class="s2">"https://example.org/</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">.tar.gz"</span>
        <span class="s2">"</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">.patch::https://git.example.org/example/pull/42.patch"</span><span class="o">)</span>
<span class="nv">sha256sums</span><span class="o">=(</span><span class="s2">"de4bba005e86b4fbf0399e2cb492b1e941099b951a45137212507c2cbc8fae63"</span>
            <span class="s2">"b6dc933311bc2357cc5fc636a4dbe41a01b7a33b583d043a7f870f3440697e27"</span><span class="o">)</span>

prepare<span class="o">()</span> <span class="o">{</span>
    <span class="nb">cd</span> <span class="s2">"</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">"</span>
    patch <span class="nt">-p1</span> <span class="nt">-i</span> <span class="s2">"</span><span class="nv">$srcdir</span><span class="s2">/</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">.patch"</span>
<span class="o">}</span>

build<span class="o">()</span> <span class="o">{</span>
    <span class="nb">cd</span> <span class="s2">"</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">"</span>
    ./configure <span class="nt">--prefix</span><span class="o">=</span>/usr
    make
<span class="o">}</span>

check<span class="o">()</span> <span class="o">{</span>
    <span class="nb">cd</span> <span class="s2">"</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">"</span>
    make <span class="nt">-k</span> check
<span class="o">}</span>

package<span class="o">()</span> <span class="o">{</span>
    <span class="nb">cd</span> <span class="s2">"</span><span class="nv">$pkgname</span><span class="s2">-</span><span class="nv">$pkgver</span><span class="s2">"</span>
    make <span class="nv">DESTDIR</span><span class="o">=</span><span class="s2">"</span><span class="nv">$pkgdir</span><span class="s2">/"</span> <span class="nb">install</span>
<span class="o">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>It starts with a maintainer-line, which is not used programmatically but helps users of the software
contact the maintainer if something doesn’t work. It is good practice to also have the emails of
previous maintainers there, to recognise their contributions. Then we come to a few metadata
variables.</p>

<h3 id="metadata">Metadata</h3>

<dl>
  <dt><code class="language-plaintext highlighter-rouge">pkgname</code></dt>
  <dd>defines the package we are building at the moment.</dd>
  <dt><code class="language-plaintext highlighter-rouge">pkgver</code></dt>
  <dd>signifies which upstream release of the software is being built. This number should only ever
increase, as <code class="language-plaintext highlighter-rouge">pacman</code> will use this number to determine if an update is necessary.<sup id="fnref:versions"><a href="#fn:versions" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></dd>
  <dt><code class="language-plaintext highlighter-rouge">pkgrel</code></dt>
  <dd>is the package release number. This release number is incremented every time an update is done to
the <code class="language-plaintext highlighter-rouge">PKGBUILD</code> without updating the <code class="language-plaintext highlighter-rouge">pkgver</code>, such as when the packaging options change. It resets
to <code class="language-plaintext highlighter-rouge">1</code> every time a new upstream release is done.</dd>
  <dt><code class="language-plaintext highlighter-rouge">pkgdesc</code></dt>
  <dd>is a short, human-readable description of the package. This is shown in package lists and is used
for searching, but doesn’t affect the built software otherwise.</dd>
  <dt><code class="language-plaintext highlighter-rouge">arch</code></dt>
  <dd>is a list of valid CPU architectures this package can be built for. It can also be the special
array <code class="language-plaintext highlighter-rouge">('any')</code>, which does not (only) denote that this package can be built for any architecture,
but rather that the resulting package does not contain any architecture-specific code. This is
common for packages written in languages such as Python.</dd>
  <dt><code class="language-plaintext highlighter-rouge">url</code></dt>
  <dd>is just link a package user might go for more information and does not otherwise affect the
package.</dd>
  <dt><code class="language-plaintext highlighter-rouge">license</code></dt>
  <dd>is the licence under which this package is distributed. This used to be an array of all applicable
licences for the project, but since the meaning of that is a bit ambiguous, these days the licence
array should hold a single item which is an <a href="https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/">SPDX license expression</a>.</dd>
  <dt><code class="language-plaintext highlighter-rouge">install</code></dt>
  <dd>is usually not present in a <code class="language-plaintext highlighter-rouge">PKGBUILD</code>. If present, it refers to the name of a script (included
alongside the package file) with specific bash functions that will get executed when the package
is installed, upgraded, or removed. It is run from <code class="language-plaintext highlighter-rouge">pacman</code>, and therefore as root.</dd>
  <dt><code class="language-plaintext highlighter-rouge">source</code></dt>
  <dd>is an array of sources. These can be either downloadable URLs, version control URLs such as git,
or simple file names, meaning the file in question in part of the package files.</dd>
  <dt><code class="language-plaintext highlighter-rouge">sha256sums</code></dt>
  <dd>is another array, of the same length as sources, specifying the expected <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256</a> checksums for
the package sources. There are other hashing algorithms available, from <code class="language-plaintext highlighter-rouge">crc32sums</code> to <code class="language-plaintext highlighter-rouge">b2sums</code>.
Multiple sets of digests may be specified but at least one should be. The magic value <code class="language-plaintext highlighter-rouge">SKIP</code> may be
used to skip verifying checksums for certain files, for example for external checksums, or PGP
signatures.</dd>
</dl>

<p>After all that metadata, we get to the actual code that does something: the packaging functions.</p>

<h3 id="build-functions">Build functions</h3>

<p>Building packages is done in 4 stages, each with their own bash function. It starts with
<code class="language-plaintext highlighter-rouge">prepare()</code>. This function is expected to make all the necessary changes to the source code, such as
applying patch files or editing out certain lines with <code class="language-plaintext highlighter-rouge">sed</code>. In certain ecosystems, such as Node.js
and Rust, it also downloads the dependencies. Ideally, all steps following <code class="language-plaintext highlighter-rouge">prepare()</code> no longer
need network access.</p>

<p>Then we get to <code class="language-plaintext highlighter-rouge">build()</code>, which, as the name suggests, builds the package. This is the place to call
configure scripts, compilers, and whatever else produces the binaries involved. This is, generally,
the part that takes time.</p>

<p>After packaging, there is usually a <code class="language-plaintext highlighter-rouge">check()</code> step. Here we want to check whether the package
functions as intended, within the Arch Linux packaging. What to do here varies, but most <code class="language-plaintext highlighter-rouge">PKGBUILD</code>s
try to run the package’s unit tests. Arch Linux generally has never versions of libraries than the
ones upstream projects test with, and such tests form a reasonable smoke test that things did not
break in unexpected ways.</p>

<p>Finally, the <code class="language-plaintext highlighter-rouge">package()</code> function is called. This is the only function that’s technically required,
all the others can be omitted. This function should move the files, generated in the <code class="language-plaintext highlighter-rouge">build()</code> step,
into their final positions relative to some <code class="language-plaintext highlighter-rouge">$pkgdir</code>. Many ecosystems provide (an equivalent of)
<code class="language-plaintext highlighter-rouge">make install</code> for this, which makes life easier, but often you will simply have to spell this out.</p>

<h2 id="what-to-look-out-for">What to look out for</h2>

<p>Now that we know what the different parts of a <code class="language-plaintext highlighter-rouge">PKGBUILD</code> mean, we can talk about reviewing its
contents. I want to go over a few of the items I find most important, but this is by no means an
exhaustive list. The fact I’m writing this will probably affect the strategies people use in some
way. Nevertheless, let’s fist…</p>

<h3 id="check-the-sources-array">Check the <code class="language-plaintext highlighter-rouge">sources</code> array</h3>

<p>Regardless of what the <code class="language-plaintext highlighter-rouge">PKGBUILD</code> itself says, if the sources it’s going to compile are malicious,
nothing else really matters. Make sure that you trust the upstream project, and that files are
downloaded from a reasonable place, such as tags in official git repositories, or source releases on
the official website. PGP signatures from the authors are generally even better, so you know who
created, or at least signed off on the sources.</p>

<p>The Arch Linux project has <a href="https://rfc.archlinux.page/0046-upstream-package-sources/">accepted an RFC on what sources should be used</a>, in order
to have the most trustworthy sources for packaging. While such rigour isn’t necessary or, in some
cases, even desirable for the <abbr title="Arch User Repository">AUR</abbr>, you can use it as inspiration.</p>

<p>Patches are also sources, and malware can hide in them. You should have a look at any patches being
applied to the source code, and, if possible where they come from. It’s not unusual to apply
merged-but-not-released changes from upstream to deal with newer versions of libraries, but make
sure it makes sense.</p>

<h3 id="check-if-the-build-steps-make-sense">Check if the build steps make sense</h3>

<p>The <code class="language-plaintext highlighter-rouge">prepare()</code>, <code class="language-plaintext highlighter-rouge">build()</code>, <code class="language-plaintext highlighter-rouge">check()</code>, and <code class="language-plaintext highlighter-rouge">package()</code> functions are inherently code that will be
executed on your machine when you build a package so if any</p>

<ul>
  <li>
    <p>No downloads should happen in <code class="language-plaintext highlighter-rouge">build()</code>, <code class="language-plaintext highlighter-rouge">check()</code>, or <code class="language-plaintext highlighter-rouge">package()</code>, and ideally not in
<code class="language-plaintext highlighter-rouge">prepare()</code>, though many ecosystems (Go, Node.js, Rust to name a few) require it.</p>
  </li>
  <li>
    <p>The commands run by the script should be obvious packaging commands, and any custom scripts should
come from the upstream repos. Often the packaging script matches the upstream “how to install” but
often it doesn’t, as package maintainers generally call the build tools directly rather than call
wrapper scripts.</p>
  </li>
  <li>
    <p>Build scripts should not run <code class="language-plaintext highlighter-rouge">sudo</code> or anything similar. If it does that anyway, it’s wrong. At
best, it’s a packaging error, as <code class="language-plaintext highlighter-rouge">sudo</code> shouldn’t be expected to work in a non-interactive
environment like a build chroot. Sometimes a packager mistakenly tries to move package files into
place instead of adding them to the package.</p>
  </li>
</ul>

<h3 id="scrutinise-install-scripts">Scrutinise install scripts</h3>

<p>Install scripts are, as mentioned above, rarely seen, but if when they are, they need to be
scrutinised as their contents will run, <em>as root</em>, when a package is installed, upgraded, or
removed. Most software doesn’t need this, as common use cases have safer alternatives.</p>

<p>Similarly, newly installed <code class="language-plaintext highlighter-rouge">pacman</code> hooks should also raise an eyebrow. Pacman hooks install to
<code class="language-plaintext highlighter-rouge">/usr/share/libalpm/hooks</code> (and sometimes to <code class="language-plaintext highlighter-rouge">/etc/pacman.d/hooks</code> though that’s incorrect) and can
run a command whenever its triggers are hit. The ones included in the main Arch repos are benign and
in fact reduce the need for <code class="language-plaintext highlighter-rouge">install</code> scripts, by automatically regenerating font- and icon caches
as needed, regenerating the <code class="language-plaintext highlighter-rouge">initramfs</code>, and many more. An arbitrary <code class="language-plaintext highlighter-rouge">PKGBUILD</code> adding one of their
own is unusual, and you should pay attention to what that hook is trying to do.</p>

<h3 id="if-you-dont-understand-it-dont-use-it">If you don’t understand it, don’t use it</h3>

<p>While we try to keep it clean, the <abbr title="Arch User Repository">AUR</abbr> is freely accessible to anyone who figures out how to get
past the CAPTCHA, and anyone can upload code to it. Even popular, well-intentioned code can have
bugs sometimes and <a href="https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/issues/123">do harm to your system</a>. The <abbr title="Arch User Repository">AUR</abbr> is, at the best of times,
maintained by volunteers, many of which are currently getting their feet wet for the first time
while packaging. Mistakes can and will happen, and that’s okay.</p>

<h2 id="when-something-might-be-malicious">When something might be malicious</h2>

<p>Now that we have a hunch as to what might be malicious, we can decide what to do with it. The
easiest way is to simply ask. The best place for that, in my opinion, is the <a href="irc://irc.libera.chat:+6697/archlinux-aur"><code class="language-plaintext highlighter-rouge">#archlinux-aur</code> IRC
channel</a> on <a href="https://libera.chat/">Libera</a>, which is the official venue for <abbr title="Arch User Repository">AUR</abbr> discussion.
<a href="https://bbs.archlinux.org/viewforum.php?id=38">The forums</a> are also a good place to ask. If all fails, there is also the <a href="https://lists.archlinux.org/mailman3/lists/aur-general.lists.archlinux.org/">mailing list</a>.</p>

<p>If the package is indeed malicious, one of the Package Maintainers can take it down and block the
offending user(s). Should they try to avoid their ban, the measures will also escalate. As a user,
that’s where your input ends.</p>

<h2 id="that-feels-too-simple">That feels too simple</h2>

<p>And it is. The <abbr title="Arch User Repository">AUR</abbr> was made for a different kind of internet. More cooperative, less hostile. It’s
based on trust that users will generally do sensible things.<sup id="fnref:sensible"><a href="#fn:sensible" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> The <abbr title="Arch User Repository">AUR</abbr> is old software. It
started as “just some FTP server” where people could upload files. Such a system would not be
designed in 2026.</p>

<p>There are improvements to be had for sure. The moderation systems are somewhat archaic, the
contribution workflow is not what people expect. The licensing of <code class="language-plaintext highlighter-rouge">PKGBUILD</code> files, especially when
the original author is long gone, is tricky.<sup id="fnref:tricky"><a href="#fn:tricky" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> A pull-request like system is often suggested.
I think that would be a real improvement. In general, the system needs more love. That’s what you
get with open source projects; if no one wants to work on it, it gets no work. And that’s okay.</p>

<p>Right now I believe there is no end in sight for the <abbr title="Arch User Repository">AUR</abbr>. We can keep going as-is for a while
longer. But if people were to step up to make things better, we can do a lot more.</p>

<hr />

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <p>Cover image <a href="https://pixabay.com/illustrations/cybersecurity-palm-print-7119402/">Cybersecurity palm
print</a> by <a href="https://pixabay.com/users/thedigitalartist-202249/">Pete
Linforth</a></p>

  <p>More detail about the structure of <code class="language-plaintext highlighter-rouge">PKGBUILD</code> files can be found in its <a href="https://man.archlinux.org/man/core/pacman/PKGBUILD.5.en">man
page</a>.</p>

  <p>While I am a member of the Arch Linux package maintainer team, this post and all of the advice in it
is my opinion and does not (necessarily) reflect that of Arch Linux as a whole.</p>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:versions">
      <p>It’s slightly more complicated than comparing just the <code class="language-plaintext highlighter-rouge">pkgver</code>. The <code class="language-plaintext highlighter-rouge">pkgrel</code> is used
as a tiebreaker, and <code class="language-plaintext highlighter-rouge">epoch</code> exists for when the version needs to jump backwards for whatever
reason. However <a href="#fnref:versions" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:sensible">
      <p>It is also built for a world where web scrapers do not try to gobble up as much data as
they can. The git history viewer wasn’t made for LLM-crawler abuse. <a href="https://anubis.techaro.lol/">Anubis</a> has helped a little
though. <a href="#fnref:sensible" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:tricky">
      <p>In many or even most cases, <code class="language-plaintext highlighter-rouge">PKGBUILD</code> files should not be copyrightable, as they are
largely mandatory text without creative input. No particular author owns <code class="language-plaintext highlighter-rouge">./configure &amp;&amp; make &amp;&amp;
make install</code>. But it’s not unthinkable that something required sufficiently complex code to
build within the Arch Linux ecosystem, and that might qualify. What are the terms by which these
files were shared? I don’t know, I’m not a lawyer. <a href="#fnref:tricky" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="Arch" /><category term="Linux" /><summary type="html"><![CDATA[On Friday, July 18th, 2025, the Arch Linux team was notified that three AUR packages had been uploaded that contained malware. A few maintainers including myself took care of deleting these packages, removing all traces of the malicious code, and protecting against future malicious uploads.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2026/security.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2026/security.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Misunderstanding that “Dependency” comic</title><link href="https://bertptrs.nl/2025/11/24/misunderstanding-that-dependency-comic.html" rel="alternate" type="text/html" title="Misunderstanding that “Dependency” comic" /><published>2025-11-24T08:52:00+01:00</published><updated>2025-11-26T22:00:49+01:00</updated><id>https://bertptrs.nl/2025/11/24/misunderstanding-that-dependency-comic</id><content type="html" xml:base="https://bertptrs.nl/2025/11/24/misunderstanding-that-dependency-comic.html"><![CDATA[<p>Over the course of 2025, every single major cloud provider has failed. In June, Google Cloud had
issues taking down Cloud Storage for many users. In late October, Amazon Web Services had <a href="https://builtin.com/articles/aws-outage-what-happened">a massive
outage</a> in their main hub, <code class="language-plaintext highlighter-rouge">us-east-1</code>, affecting many services <a href="https://arstechnica.com/gadgets/2025/10/smart-beds-leave-sleepers-hot-and-bothered-during-aws-outage/">as well as some
people’s beds</a>. A little over a week later Microsoft Azure had a <a href="https://tweakers.net/nieuws/240148/microsoft-365-en-azure-hebben-storing-toegang-tot-zakelijke-omgeving-is-beperkt.html">widespread outage</a> that managed to <a href="https://tweakers.net/nieuws/240888/microsoft-lost-oorzaak-op-van-azure-storing-die-onder-meer-ns-trof.html">significantly disrupt train service in the Netherlands</a>, and
probably also things that matter. Now last week, <a href="https://blog.cloudflare.com/18-november-2025-outage/">Cloudflare takes down large swaths of the
internet</a> in a way that causes non-tech people to learn Cloudflare exists. And
every single time, people share that one XKCD comic.</p>

<figure>

  <p><img src="/assets/2025/xkcd/dependency.png" alt="XKCD 2347: Dependency" /></p>

  <figcaption>

    <p><a href="https://xkcd.com/2347/">XKCD 2347: Dependency</a>. Original caption: “Someday ImageMagick will finally break for good
and we’ll have a long period of scrambling as we try to reassemble civilization from the rubble.”</p>

  </figcaption>

</figure>

<p>Except the random person in Nebraska has been replaced by AWS, Google Cloud, or whoever caused the
outage this week. And that kind of bothers me, because it misses the point entirely.</p>

<h2 id="cloud-providers-arent-small-indie-companies">Cloud providers aren’t small indie companies</h2>

<p>The original comic is a joke and expression of concern at the fact that lots of our modern
technology depends on small projects that largely are maintained by a single driven developer
writing code in their spare time. They are important, yet fragile. This is not comparable to the
outages we’ve seen this year.</p>

<p>To contrast, these four cloud providers are, for better or worse, important to the web as we know
it. But they’re not small. We should recognize that these are huge players, with revenues larger
than the <abbr title="Gross Domestic Product">GDP</abbr> of many countries.<sup id="fnref:gdp"><a href="#fn:gdp" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> Cloudflare isn’t anywhere near as big as the other three, but
it still has a proportionally gigantic impact on the web due to how much data flows through them.</p>

<p>In addition to how important they are, they are all also among the largest and most valuable
companies in the world. It’s concerning how reliant we are on just this handful of players, and
<a href="https://nos.nl/artikel/2584877-belastingdienst-verplaatst-mail-naar-microsoft-ondanks-zorgen-over-meekijken-vs">when governments become more reliant on them</a>, that is a huge risk. It is however
the same, boring risk of influence and dependence it always is with large companies, rather than a
risk of single individuals disappearing and taking our technology with them.</p>

<h2 id="support-your-guy-in-nebraska">Support your guy in Nebraska</h2>

<p>There are many people who are effectively a one-man show supporting most of modern technology in
their spare time without compensation, and those deserve our support as a society, or at the very
least recognition. I’ve tried to highlight a few here.</p>

<ul>
  <li>
    <p>The comic waves at ImageMagick, which was created by <strong>John Cristy</strong> while working at DuPont. This
is really the only fact I can find about it. Every article delving into the history of ImageMagick
mentions this and nothing else. I don’t know much about John Cristy, so I can’t comment on him.
However, ImageMagick is currently maintained by <strong><a href="https://github.com/dlemstra">Dirk Lemstra</a></strong>, who lives in the Netherlands.
He does appear to do this all in his spare time.</p>
  </li>
  <li>
    <p>Finnish developer <strong><a href="https://github.com/Larhzu">Lasse Collin</a></strong> is the main force behind the <a href="https://tukaani.org/xz/">XZ compression library</a>.
Unfortunately, he is more famous for the social engineering attack on him than he is for his work.
Regardless, XZ is an essential piece of software for many ecosystems that require high compression
rate regardless of CPU cost. It has been the Arch Linux package compression algorithm of choice
for years until it was replaced with <code class="language-plaintext highlighter-rouge">zstd</code>.</p>
  </li>
  <li>
    <p><a href="https://curl.se/">cURL</a> developer and AI slop victim <strong><a href="https://daniel.haxx.se/">Daniel Stenberg</a></strong> has surprisingly beaten the odds and
made a career out of maintaining his open source project, but that doesn’t make him less
admirable. Did I recommend his blog enough yet? He lives in Stockholm, Sweden.</p>
  </li>
  <li>
    <p>The late <strong><a href="https://en.wikipedia.org/wiki/Bram_Moolenaar">Bram Moolenaar</a></strong> is known for two things: the <a href="https://github.com/vim/vim">VIM</a> text editor, and his support for
orphaned children in Uganda. The latter of the two has long been the first message you see when
opening the former. He lived in Lisse, the Netherlands, and the editor he created continues to be
one of the most important tool on the belt of system administrators everywhere.</p>
  </li>
</ul>

<p>This is not meant to be an exhaustive list, though if I’ve overlooked someone significant (perhaps
actually in Nebraska?) then don’t take that as me disregarding their contributions. There are many
more, but the very nature of who they are prevents listing them all. I might update this list for a
while, so do get in touch.<sup id="fnref:candidates"><a href="#fn:candidates" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<h3 id="update-november-26th">Update November 26th</h3>

<p>I asked and people delivered. Thank you to all of you who reached out with helpful suggestions. The
following maintainers also deserve our recognition:</p>

<ul>
  <li>
    <p><a href="https://aevum.de/">Nick Wellnhofer</a> has been maintaining <code class="language-plaintext highlighter-rouge">libxml2</code> for years, while it is part of basically every
functional Linux, MacOS, or even Windows installation out there. <a href="https://gitlab.gnome.org/GNOME/libxml2/-/issues/913">Recently he wrote about the
demands put upon the project by these bigger organisations, and how it affects his
work.</a> He lives in München, Germany.</p>
  </li>
  <li>
    <p>The privilege escalation program <a href="https://www.sudo.ws/"><code class="language-plaintext highlighter-rouge">sudo</code></a>, present on almost every Linux system, has been
maintained by <a href="https://www.millert.dev/">Todd C. Miller</a> for more than 30 years, both as part of his job and as a volunteer.
His Github profile picture is the XKCD about <code class="language-plaintext highlighter-rouge">sudo</code>, which made me giggle. He lives in Boulder,
Colorado, United States.</p>
  </li>
</ul>

<p>And finally, I’d like to give an honourable mention for the <a href="https://sqlite.org/index.html">SQLite</a> team. They don’t <em>technically</em>
qualify under my rules, but they’ve been suggested more than once and it’s definitely an
underappreciated piece of software. Curiously, they do not even take contributions, made their own
version control system, and release the code as public domain rather than one of the more common
licenses.</p>

<h2 id="what-next">What next?</h2>

<p>Relevant XKCD comics are nothing new to the field. When I was still in university I was
educated<sup id="fnref:educated"><a href="#fn:educated" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> by my friends on the ones that apply most often. I have since become part of the
problem and have been making references to it with others. In a way it’s a rite of passage for a
certain kind of geek. So here I am trying to educate the next generation on what things mean.</p>

<p>On the bright side: some people on Reddit <a href="https://old.reddit.com/r/ProgrammerHumor/comments/1p0zayx/letsbehonestthattheyareprettybig/">do appear to “get it”</a> and point out the sheer
ridiculousness of implying the cloud providers are tiny unassuming parts. Others have instead taken
the joke <a href="https://old.reddit.com/r/ProgrammerHumor/comments/1p204nx/actuallycompleteversion/">and ran with it, off of a cliff</a>, to the point where it no longer makes sense. By
the time I finish writing this, it’s probably already old and not funny any more. Nature is healing,
I suppose.</p>

<hr />

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <p><a href="https://xkcd.com/2347/">XKCD 2347: Dependency</a> by Randall Munroe, licensed under a Creative Commons Attribution-NonCommercial 2.5 License.</p>

  <p>Cover image <a href="https://pixabay.com/photos/ruin-ancient-house-colonnade-2572599/">Ruin</a> by
<a href="https://pixabay.com/users/folgore186-6070344/">Massimiliano Sconda</a> from Pixabay.</p>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:gdp">
      <p>If Microsoft were a country, and we’d take <a href="https://www.microsoft.com/investor/reports/ar24/">their annual
revenue</a> (2024) as their <abbr title="Gross Domestic Product">GDP</abbr>, it would make
them the 57th largest country by <abbr title="Gross Domestic Product">GDP</abbr>, ahead of Qatar and just behind Hungary. <a href="https://www.sec.gov/Archives/edgar/data/1652044/000130817925000513/goog012714-ars.pdf">Google would be
45th</a>,
just behind Iran. <a href="#fnref:gdp" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:candidates">
      <p>For people to qualify as “person in Nebraska,” they should be the sole, largely
unpaid maintainer for a widely-used open source project. Please, do open my eyes to the many
talented people out there. <a href="#fnref:candidates" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:educated">
      <p>Perhaps it’s more fair to call it indoctrinated, but that makes it sound like a bad
thing. <a href="#fnref:educated" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="opinion" /><category term="xkcd" /><summary type="html"><![CDATA[Over the course of 2025, every single major cloud provider has failed. In June, Google Cloud had issues taking down Cloud Storage for many users. In late October, Amazon Web Services had a massive outage in their main hub, us-east-1, affecting many services as well as some people’s beds. A little over a week later Microsoft Azure had a [widespread outage][Azure outage] that managed to significantly disrupt train service in the Netherlands, and probably also things that matter. Now last week, Cloudflare takes down large swaths of the internet in a way that causes non-tech people to learn Cloudflare exists. And every single time, people share that one XKCD comic.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2025/xkcd/ruin.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2025/xkcd/ruin.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Rust edition 2024 annotated</title><link href="https://bertptrs.nl/2025/02/23/rust-edition-2024-annotated.html" rel="alternate" type="text/html" title="Rust edition 2024 annotated" /><published>2025-02-23T21:38:00+01:00</published><updated>2025-02-23T21:38:00+01:00</updated><id>https://bertptrs.nl/2025/02/23/rust-edition-2024-annotated</id><content type="html" xml:base="https://bertptrs.nl/2025/02/23/rust-edition-2024-annotated.html"><![CDATA[<p>Last Thursday Rust 1.85 was released, and with it, edition 2024 has dropped. The new edition is
significantly larger than the two editions that preceded it, and contains many small but significant
quality of life improvements to the language. In this post, I’d like to explain what an edition is,
and summarize all the changes that were made to the language I love. If you need the details, I
recommend reading the <a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html">edition guide</a>, but for a general overview, read on.</p>

<h2 id="what-is-an-edition">What is an edition?</h2>

<p>Rust has, at the time when 1.0 was being finalized, made a promise: <a href="https://blog.rust-lang.org/2014/10/30/Stability.html">code that compiles with any 1.x
version of the compiler, should compile without hassle with a later 1.y version of the
compiler.</a> Unless behaviour was obviously a bug, or code should keep
compiling. This is a beautiful ideal, but it also limits what changes you can make. This is where
editions come in.</p>

<p>An edition in Rust is effectively a compatibility mode for the compiler. It is a way to make certain
changes “not exist” for older code, while allowing improvements for newer code. Code compiled in
older editions can be freely mixed with code with newer editions and vice versa. This makes it
easier to upgrade to newer compiler versions, as you can be generally sure that code will continue
to compile and work as expected.</p>

<p>As a simple example, the first ever edition, in 2018, made <code class="language-plaintext highlighter-rouge">async</code> a keyword. That meant that
variables could no longer be called <code class="language-plaintext highlighter-rouge">async</code>, but that was deemed worth it in order to enable
asynchronous programming. Crucially, code that existed before that time, could now use what is
called “edition 2015”, could continue to exist unchanged and new code could depend on existing
libraries, while using newer features itself.</p>

<p>Of course, that means that code compiled with edition 2015 could not use <code class="language-plaintext highlighter-rouge">async fn</code>, but that
functionality wouldn’t be released until late 2019 anyway. Edition 2024 offers a similar selection
of small changes, fixing small pain points in the language that would otherwise break the backwards
compatibility promise.</p>

<h2 id="core-language">Core language</h2>

<p>Most of the changes in Edition 2024 affect the core language constructs and as such will be the most
visible, as they apply regardless of what standard library features you use. Luckily, only one is
likely to require manual changes to your code base, if at all. Let’s walk through them.</p>

<h3 id="return-position-impl-trait-lifetime-capture-rules">Return-position <code class="language-plaintext highlighter-rouge">impl Trait</code> lifetime capture rules</h3>

<p>Putting <code class="language-plaintext highlighter-rouge">impl Trait</code> in the position of the return type can make your code easier to read, but it
can cause issues when lifetimes get involved.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">numbers</span><span class="p">(</span><span class="n">nums</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">i32</span><span class="p">])</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="nb">Iterator</span><span class="o">&lt;</span><span class="n">Item</span><span class="o">=</span><span class="nb">i32</span><span class="o">&gt;</span> <span class="p">{</span>
  <span class="n">nums</span><span class="nf">.iter</span><span class="p">()</span><span class="nf">.copied</span><span class="p">()</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The above will fail to compile in edition 2021, as the type <code class="language-plaintext highlighter-rouge">impl Iterator</code> is assumed to be
<code class="language-plaintext highlighter-rouge">'static</code>, that is, it doesn’t borrow anything, while it instead borrows from the <code class="language-plaintext highlighter-rouge">nums</code> slice.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre>   Compiling playground v0.0.1 (/playground)
error[E0700]: hidden type for `impl Iterator&lt;Item = i32&gt;` captures lifetime that does not appear in bounds
 --&gt; src/lib.rs:2:3
  |
1 | fn numbers(nums: &amp;[i32]) -&gt; impl Iterator&lt;Item=i32&gt; {
  |                  ------     ----------------------- opaque type defined here
  |                  |
  |                  hidden type `Copied&lt;std::slice::Iter&lt;'_, i32&gt;&gt;` captures the anonymous lifetime defined here
2 |   nums.iter().copied()
  |   ^^^^^^^^^^^^^^^^^^^^
  |
help: add a `use&lt;...&gt;` bound to explicitly capture `'_`
  |
1 | fn numbers(nums: &amp;[i32]) -&gt; impl Iterator&lt;Item=i32&gt; + use&lt;'_&gt; {
  |                                                     +++++++++
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The error message even has the fix: add <code class="language-plaintext highlighter-rouge">+ use&lt;'_&gt;</code> to state that the lifetime of the slice is
captured into the returned iterator, so the compiler may correctly reason about its use.</p>

<p>The change coming in edition 2024 is a change in default captures. Instead of capturing none of the
lifetimes by default, <code class="language-plaintext highlighter-rouge">impl Trait</code> will now by default capture all lifetimes. That means that with
edition 2024, the example above will compile as-is. To revert to the previous behaviour, you can
explicitly capture no lifetimes:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">display</span><span class="p">(</span><span class="n">label</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">ret</span><span class="p">:</span> <span class="k">impl</span> <span class="nb">Sized</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="nb">Sized</span> <span class="o">+</span> <span class="k">use</span><span class="o">&lt;&gt;</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"{label}"</span><span class="p">);</span>
    <span class="n">ret</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I’m not sure whether this change is an improvement; in my code it empirically introduces more
unnecessary lifetimes than it removes workarounds. <code class="language-plaintext highlighter-rouge">'static</code> lifetime by default seems like the more
sensible choice. Nevertheless, the change has been made, so</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html">Edition guide - RPIT capture rules</a></li>
</ul>

<h3 id="changes-to-temporary-scope">Changes to temporary scope</h3>

<p>Edition 2024 comes with two small quality of life changes with respect to the lifetime of specific
temporaries. These are situations you might have hit already, that have easy workarounds, but now
they just work out of the box.</p>

<p>In edition 2021, when using <code class="language-plaintext highlighter-rouge">if let</code>, any temporaries created in the matching expression will live
for the entirety of the <code class="language-plaintext highlighter-rouge">if</code>/<code class="language-plaintext highlighter-rouge">else if</code>/<code class="language-plaintext highlighter-rouge">else</code> branch. This can have some unexpected results:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">get_cached_or_init</span><span class="p">(</span><span class="n">cache</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">RefCell</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span>
    <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">value</span><span class="p">)</span> <span class="o">=</span> <span class="n">cache</span><span class="nf">.borrow</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">value</span><span class="nf">.clone</span><span class="p">()</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">value</span> <span class="o">=</span> <span class="nf">complicated_string_gen</span><span class="p">();</span>
        <span class="o">*</span><span class="n">cache</span><span class="nf">.borrow_mut</span><span class="p">()</span> <span class="o">=</span> <span class="n">value</span><span class="nf">.clone</span><span class="p">();</span> <span class="c1">// PANIC! Already borrowed</span>
        <span class="n">value</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Starting edition 2024, the temporary scope for the guard expression will end at the end of the <code class="language-plaintext highlighter-rouge">if</code>
branch, and the above code will be valid as-is. If you need the guard to remain valid for the entire
duration, you can use a <code class="language-plaintext highlighter-rouge">match</code> block instead.</p>

<p>Another small change has to do with the lifetime of temporaries in tail expressions. According to
the 2021 temporary scope rules, a temporary created inside such a tail expression will live until
the end of the next temporary scope, which is <em>after</em> local variables have been dropped. This can
cause unexpected errors:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="c1">// Before 2024</span>
<span class="k">fn</span> <span class="nf">f</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">usize</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="nn">RefCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="s">".."</span><span class="p">);</span>
    <span class="n">c</span><span class="nf">.borrow</span><span class="p">()</span><span class="nf">.len</span><span class="p">()</span> <span class="c1">// error[E0597]: `c` does not live long enough</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Example taken from the edition guide as I have hit this only once, ever, and couldn’t think of a
simple use case. Nevertheless, the <code class="language-plaintext highlighter-rouge">Borrow</code> temporary generated by <code class="language-plaintext highlighter-rouge">borrow()</code> is here expected to
live until the end of the function, but <code class="language-plaintext highlighter-rouge">c</code> is cleaned up before that, so it doesn’t work. In
edition 2024, the temporary’s lifetime is shortened and it will compile as-is.</p>

<p>This may in some cases have side effects when used with temporary lifetime extension, but I expect
this will be unlikely to occur in real code.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html">Edition guide - <code class="language-plaintext highlighter-rouge">if let</code> temporary scope</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html">Edition guide - Tail expression temporary scope</a></li>
  <li><a href="https://github.com/rust-lang/lang-team/blob/master/design-meeting-minutes/2023-03-15-temporary-lifetimes.md#lifetime-extension">Temporary lifetime extension</a></li>
</ul>

<h3 id="match-ergonomics-reservations">Match ergonomics reservations</h3>

<p>There is a small change to how <code class="language-plaintext highlighter-rouge">ref</code>, <code class="language-plaintext highlighter-rouge">mut</code>, and <code class="language-plaintext highlighter-rouge">ref mut</code> and friends may be used. In edition 2024,
it is an error to use these capture modifiers in a non-explicit pattern, that is, a pattern using
match ergonomics. In practice, this looks as follows:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">fn</span> <span class="nf">f</span><span class="p">(</span><span class="n">opt</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="nb">i32</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="k">let</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="nf">Some</span><span class="p">(</span><span class="k">ref</span> <span class="k">mut</span> <span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="n">opt</span> <span class="p">{</span>
        <span class="c1">// valid, val is &amp;mut i32</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="n">opt</span> <span class="p">{</span>
        <span class="c1">// valid, val is &amp;mut i32, match ergonomics used</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="k">mut</span> <span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="n">opt</span> <span class="p">{</span>
        <span class="c1">// Valid in edition 2021, invalid in edition 2024</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The stated reason for this change is that the syntax is intended to be used for match ergonomics
expansion in the future. I don’t know what this expansion would look like, so I can’t comment on the
usefulness, but I will say that the current compiler interpretation is not that useful or
predictable, so not a lot is lost.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html]">Edition guide - Match ergonomics reservations</a></li>
</ul>

<h3 id="changes-to-unsafe">Changes to <code class="language-plaintext highlighter-rouge">unsafe</code></h3>

<p><code class="language-plaintext highlighter-rouge">unsafe</code> in Rust is used to denote sections of code where the compiler cannot prove all the
properties of the code that are required for the code to be well-behaved. Edition 2024 requires you
to mark more sections as <code class="language-plaintext highlighter-rouge">unsafe</code> that previously did not require it.</p>

<p>The first change, is that <code class="language-plaintext highlighter-rouge">extern</code> blocks are now required to be marked as <code class="language-plaintext highlighter-rouge">unsafe</code>. Function and
constant definitions in an <code class="language-plaintext highlighter-rouge">extern</code> block must match their counterparts across the FFI bridge, but
the compiler cannot ascertain that they do. Marking the block <code class="language-plaintext highlighter-rouge">unsafe</code> should signal the developer
that they must pay attention.</p>

<p>Rust added the ability for attributes to be <code class="language-plaintext highlighter-rouge">unsafe</code> as well. A few existing attributes now require
being marked <code class="language-plaintext highlighter-rouge">unsafe</code>. The names used for <code class="language-plaintext highlighter-rouge">#[no_mangle]</code> and <code class="language-plaintext highlighter-rouge">#[export_name]</code> need to be globally
unique, otherwise unexpected linking errors may occur. As such, they are now <code class="language-plaintext highlighter-rouge">#[unsafe(no_mangle)]</code>
and <code class="language-plaintext highlighter-rouge">#[unsafe(export_name)]</code>. In addition, <code class="language-plaintext highlighter-rouge">#[link_section]</code> must now be unsafe, as it can be used
to add symbols to a section that might not work, such as adding mutable data to a read-only section.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html">Edition guide - Unsafe <code class="language-plaintext highlighter-rouge">extern</code> blocks</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-attributes.html">Edition guide - Unsafe attributes</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html">Edition guide - <code class="language-plaintext highlighter-rouge">unsafe_op_in_unsafe_fn</code> warning</a></li>
</ul>

<h3 id="no-more-references-to-static-mut-data">No more references to <code class="language-plaintext highlighter-rouge">static mut</code> data</h3>

<p>In Rust, the existence of a reference <code class="language-plaintext highlighter-rouge">&amp;T</code> presumes (barring interior mutability) no writes will
happen to the data being referenced. This has always been in conflict with <code class="language-plaintext highlighter-rouge">static mut</code> variables,
as any thread anywhere may be writing to them at any point in time. Edition 2024 therefore makes it
an error to have a reference to <code class="language-plaintext highlighter-rouge">static mut</code> data. This was previously a warning.</p>

<p>The primary solution to get around this, is by not having <code class="language-plaintext highlighter-rouge">static mut</code> data. Make sure your global
data is covered by a mutex or similar. If you must have <code class="language-plaintext highlighter-rouge">static mut</code>, any access to it will have to
go through the pointer APIs, and you will have to take responsibility for upholding Rust’s safety
guarantees.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html">Edition guide - Disallow references to static mut</a></li>
</ul>

<h3 id="never-type-coercion">Never type coercion</h3>

<p>The <a href="https://doc.rust-lang.org/std/primitive.never.html">Never</a> type, <code class="language-plaintext highlighter-rouge">!</code>, is a type of which no value can ever exist. This is used in the language as
the return value for functions that do not return, such as <code class="language-plaintext highlighter-rouge">panic!()</code>. It signals to the compiler
and to a developer that a code path will never (heh) be taken. To ease type checking in these
scenarios, the Never type will coerce to any other type.</p>

<p>Sometimes, the code doesn’t offer an unambiguous type to coerce to, in which case the fallback is
used. Edition 2024 changes the fallback for this coercion from the unit type <code class="language-plaintext highlighter-rouge">()</code> to the Never type
itself. This might cause minor issues for cases where a trait is implemented for <code class="language-plaintext highlighter-rouge">()</code> but not for
<code class="language-plaintext highlighter-rouge">!</code>. The workaround is to explicitly coerce to the unit type in those situations by adding type
annotations.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/std/primitive.never.html">The never type</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html">Edition guide - Never type fallback change</a></li>
</ul>

<h3 id="macro-fragment-specifiers">Macro fragment specifiers</h3>

<p>Macro fragment specifiers have, in my opinion, an unfortunate name. What it refers to is effectively
the <strong>type</strong> of an argument of a <code class="language-plaintext highlighter-rouge">macro_rules!</code>-style macro.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nd">macro_rules!</span> <span class="n">my_print</span> <span class="p">{</span>
    <span class="p">(</span><span class="nv">$val:expr</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nd">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="nv">$val</span><span class="p">);</span> <span class="p">};</span>
<span class="p">}</span>

<span class="c1">// later</span>
<span class="nd">my_print!</span><span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">3</span><span class="p">);</span> <span class="c1">// prints 6</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here, <code class="language-plaintext highlighter-rouge">$val</code> is captured, but only if it is an expression. <code class="language-plaintext highlighter-rouge">my_print!(match)</code> would not be accepted.
Edition 2024 brings the <code class="language-plaintext highlighter-rouge">:tt</code> fragment specifier up-to-date with the new additions to the language,
allowing it to match <code class="language-plaintext highlighter-rouge">_</code>- and <code class="language-plaintext highlighter-rouge">const</code> expressions as part of an <code class="language-plaintext highlighter-rouge">:expr</code> fragment. If you must
maintain the previous behaviour, you can replace <code class="language-plaintext highlighter-rouge">:expr</code> with <code class="language-plaintext highlighter-rouge">:expr_2021</code>.</p>

<p>In addition, it is now an error to have macro fragments without type specifiers. Previously, this
would be alright, as long as the variant with the missing fragment specifier wasn’t matched. Simply
remove the variant without the specifier, or add an appropriate specifier.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c1">// Compiles in edition 2021, compile error in edition 2024</span>
<span class="nd">macro_rules!</span> <span class="n">broken_macro</span> <span class="p">{</span>
    <span class="p">()</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"Valid variant"</span><span class="p">);</span> <span class="p">};</span>
    <span class="p">(</span><span class="nv">$without_specifier</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span> <span class="nd">unreachable!</span><span class="p">(</span><span class="s">"Cannot match this"</span><span class="p">);</span> <span class="p">};</span>
<span class="p">}</span>

<span class="nd">broken_macro!</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>References:</p>

<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html">Edition guide - Macro fragment specifiers</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/missing-macro-fragment-specifiers.html">Edition guide - Missing macro fragment specifiers</a></li>
</ul>

<h3 id="new-reserved-constructs">New reserved constructs</h3>

<p>Edition 2024 adds a new reserved keyword, <code class="language-plaintext highlighter-rouge">gen</code>, in anticipation of the upcoming <a href="https://blog.rust-lang.org/inside-rust/2023/10/23/coroutines.html">generators</a> which
may or may not be called coroutines. If you really want to continue naming your variables <code class="language-plaintext highlighter-rouge">gen</code>, you
can use raw identifiers.</p>

<p>In addition, Rust now reserves one or more <code class="language-plaintext highlighter-rouge">#</code> in a row followed by a string literal, and two or
more <code class="language-plaintext highlighter-rouge">#</code> in a row not separated by whitespace. There are no workarounds to this.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html">Edition guide - <code class="language-plaintext highlighter-rouge">gen</code> keyword</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/reserved-syntax.html">Edition guide - Reserved syntax</a></li>
</ul>

<h2 id="standard-library">Standard library</h2>

<p>Aside from core language features, the standard library also has some backwards-incompatible
changes.</p>

<h3 id="changes-to-the-prelude">Changes to the prelude</h3>

<p>The prelude defines all the types you never have to import, like <code class="language-plaintext highlighter-rouge">String</code>. In edition 2024, <code class="language-plaintext highlighter-rouge">Future</code>
and <code class="language-plaintext highlighter-rouge">IntoFuture</code> are added to the prelude. At this point, six years after the <code class="language-plaintext highlighter-rouge">async</code> keyword was
introduced, this is unlikely to cause issues in regular code bases.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/prelude.html">Edition guide - Changes to the prelude</a></li>
  <li><a href="https://doc.rust-lang.org/std/prelude/rust_2024/index.html">Prelude 2024</a></li>
</ul>

<h3 id="add-intoiterator-to-boxed-slices">Add <code class="language-plaintext highlighter-rouge">IntoIterator</code> to boxed slices</h3>

<p>In all editions, <code class="language-plaintext highlighter-rouge">Box&lt;[T]&gt;</code> will now implement <code class="language-plaintext highlighter-rouge">IntoIterator&lt;Item=T&gt;</code>. This could be a breaking
change, as previously, <code class="language-plaintext highlighter-rouge">my_box.into_iter()</code> would evaluate to <code class="language-plaintext highlighter-rouge">&amp;[T]::into_iter</code>, which iterates by
reference rather than by value.</p>

<p>To mitigate this, direct calls to <code class="language-plaintext highlighter-rouge">Box::&lt;[T]&gt;::into_iterator</code> will be hidden in previous editions,
which maintains the previous behaviour. You might remember this is the same workaround that was
chosen back in edition 2021, when <code class="language-plaintext highlighter-rouge">IntoIterator</code> was added to <code class="language-plaintext highlighter-rouge">[T; N]</code>.</p>

<p>Trait implementations cannot be edition specific, but this way, old code gets to keep working.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html">Edition guide - Add <code class="language-plaintext highlighter-rouge">IntoIterator</code> for <code class="language-plaintext highlighter-rouge">Box&lt;[T]&gt;</code></a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html">Edition 2021 guide - Add <code class="language-plaintext highlighter-rouge">IntoIterator</code> for arrays</a></li>
</ul>

<h3 id="newly-unsafe-functions">Newly unsafe functions</h3>

<p>Over the years, some functions have been added to the standard library that turned out to be
problematic in certain cases. Despite attempts to make them safe and well-behaved in all
circumstances, they fundamentally cannot be. Edition 2024 marks these functions as <code class="language-plaintext highlighter-rouge">unsafe</code> to
signal that these functions require special care.</p>

<p>Two functions for modifying environment variables, <code class="language-plaintext highlighter-rouge">std::env::{set_var, remove_var}</code> have been the
subject of a complicated range of CVEs I don’t want to get in to, but crucially, they cannot be used
safely in a multithreaded environment. Rust tried to fix this by adding a <code class="language-plaintext highlighter-rouge">RwLock</code> internally that
protects the internal state, but this turns out to still be unsafe when combined with the many
<code class="language-plaintext highlighter-rouge">libc</code> functions that might be calling <code class="language-plaintext highlighter-rouge">getenv</code> in unrelated threads. As such, these functions are
now <code class="language-plaintext highlighter-rouge">unsafe</code> and should only be used in single-threaded environments.</p>

<p><code class="language-plaintext highlighter-rouge">std::os::unix::process::CommandExt::before_exec</code> is also unsafe now, in addition to being
deprecated. <code class="language-plaintext highlighter-rouge">pre_exec</code> was introduced in Rust 1.37 as an <code class="language-plaintext highlighter-rouge">unsafe</code> replacement for it, but now
<code class="language-plaintext highlighter-rouge">before_exec</code> will reflect its <code class="language-plaintext highlighter-rouge">unsafe</code> status.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/newly-unsafe-functions.html">Edition guide - Newly unsafe functions</a></li>
</ul>

<h2 id="cargo">Cargo</h2>

<p>The package manager/build system/holy grail received some minor breaking changes that will generally
make life easier.</p>

<p>First, <code class="language-plaintext highlighter-rouge">cargo</code> got a new dependency resolver that will take the current Rust version into account.
In previous editions, this resolver can be opted into by setting <code class="language-plaintext highlighter-rouge">resolver = "3"</code>. From my initial
testing, it doesn’t work as well as it could, mostly because crates claim supported rust versions
they very much do not. I expect this to get better in the future, and to at the very least make it
easier to test for rust version support and maintain backwards compatibility.</p>

<p>A minor change is that the naming of options has been made more consistent. <code class="language-plaintext highlighter-rouge">Cargo.toml</code> (and
related) now consistently use <code class="language-plaintext highlighter-rouge">kebab-case</code> for all keys. Previously, <code class="language-plaintext highlighter-rouge">snake_case</code> was allowed for
some (but not all) keys.</p>

<p>Finally, there is some clean-up with workspace dependencies: it is now an error to disable
<code class="language-plaintext highlighter-rouge">default-features</code> for an inherited dependency when the workspace does not disable
<code class="language-plaintext highlighter-rouge">default-features</code>. This never worked; the dependency would always have its default features enabled
regardless as features in Rust are additive. Now the compiler will simply let you know.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/cargo-resolver.html">Edition guide - Cargo: Rust-version aware resolver</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/cargo-table-key-names.html">Edition guide - Cargo: Table and key name consistency</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/cargo-inherited-default-features.html">Edition guide - Cargo: reject unused inherited default-features</a></li>
</ul>

<h2 id="rustdoc">Rustdoc</h2>

<p>While technically breaking changes, Rustdoc mainly received some nice quality-of-life improvements.
In edition 2024, Rustdoc tests will be combined into a single binary as much as possible. This
should generally improve compile times for the tests. It is not always possible to combine tests.
<code class="language-plaintext highlighter-rouge">compile_fail</code> examples will always be compiled separately, and some other obvious breakage will be
detected and avoided. If you need to enforce that an example is compiled separately, you can add the
<code class="language-plaintext highlighter-rouge">standalone_crate</code> language tag to ensure that specific example will be compiled on its own.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="cd">/// Compiles example code</span>
<span class="cd">///</span>
<span class="cd">/// ```standalone_crate</span>
<span class="cd">/// println!("Hello, world!");</span>
<span class="cd">/// ```</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>A smaller, but more breaking change is that nested <code class="language-plaintext highlighter-rouge">include!</code>s in your documentation are now handled
relative to the included document rather than to the original source location. That is, if your code
uses something like <code class="language-plaintext highlighter-rouge">#[doc=include_str!(../README.md)]</code>, any <code class="language-plaintext highlighter-rouge">include_bytes!()</code> that might be in
your examples will now be handled relative to <code class="language-plaintext highlighter-rouge">README.md</code>.</p>

<h2 id="rustfmt">Rustfmt</h2>

<p>The biggest change to Rustfmt is that as of edition 2024, Rustfmt can have its own editions with
backwards-incompatible changes now. By default, it will use the same edition for formatting as it
will for compiling, but you can override this by setting <code class="language-plaintext highlighter-rouge">style_edition</code> in <code class="language-plaintext highlighter-rouge">rustfmt.toml</code>. This
allows you to opt in to improved formatting, while not raising the minimum supported rust version.
That said, let’s look at the breaking changes that are included.</p>

<h3 id="formatting-fixes">Formatting fixes</h3>

<p>Most of the changes to Rustfmt don’t involve sweeping changes to the resulting format, but rather
fix circumstances where the formatter did not work correctly. The edition guide has examples for all
changes, but I’ve tried to summarize them here.</p>

<ul>
  <li>Trailing comments after a line-end comment will no longer be aligned as if they were related.</li>
  <li>Strings in comments will no longer be indented.</li>
  <li>Long strings will no longer cause the formatter to stop formatting the expression that contains
them.</li>
  <li><code class="language-plaintext highlighter-rouge">impl</code> block generics will no longer have a double-indent.</li>
  <li>Some complicated <code class="language-plaintext highlighter-rouge">fn</code> blocks would previously get a strange indentation and no longer do. The
complex <code class="language-plaintext highlighter-rouge">fn</code> blocks required to trigger this are still not very readable.</li>
  <li>Nested tuple indexings will no longer get an extraneous space.</li>
  <li><code class="language-plaintext highlighter-rouge">return</code>, <code class="language-plaintext highlighter-rouge">break</code>, and <code class="language-plaintext highlighter-rouge">continue</code> statements inside a <code class="language-plaintext highlighter-rouge">match</code> block now consistently end with a
semicolon.</li>
  <li>Long array and slice patterns now wrap to avoid overflow.</li>
  <li>The last expression statement in a block will now be formatted as a one-liner most of the time.</li>
  <li>The formatting between a macro call and a function call is now more consistent.</li>
  <li>Closures containing a single loop will now get a block to wrap the closure body. This is the only
change that feels like a regression to me.</li>
  <li>Empty lines in <code class="language-plaintext highlighter-rouge">where</code> blocks are now removed.</li>
  <li><code class="language-plaintext highlighter-rouge">else</code> clauses from a <code class="language-plaintext highlighter-rouge">let else</code> with an attribute are no longer formatted incorrectly.</li>
  <li>Comment- and macro argument wrapping no longer has an off-by-one bug.</li>
  <li>Comments with <code class="language-plaintext highlighter-rouge">=&gt;</code> inside a <code class="language-plaintext highlighter-rouge">match</code> block are no longer formatted incorrectly.</li>
  <li>Having more than one inner attributes in a <code class="language-plaintext highlighter-rouge">match</code> block no longer causes extraneous indents.</li>
</ul>

<p>All but one of these should only ever improve formatting in my opinion, thus working around this
shouldn’t be necessary, but you can still ignore these improvements and avoid a diff in your code by
opting for style edition 2021 now.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rustfmt-formatting-fixes.html">Edition guide - Rustfmt: Formatting fixes</a></li>
</ul>

<h3 id="sort-order-changes">Sort order changes</h3>

<p>Rustfmt doesn’t sort things often in source code, but when it does, the resulting order makes sense.
Edition 2024 makes two changes to this. First, raw identifiers, used when an identifier overlaps
with a keyword, will now correctly sort in the position the identifier would, rather than sorting
based on the raw identifier prefix <code class="language-plaintext highlighter-rouge">r#</code>. This should generally put the resulting list in a more
“sensible” order.</p>

<p>In addition to that, the sort order was changed from lexicographical to an algorithm the guide
refers to as “version-sort.” The main difference to this is that it will handle strings containing
numbers better, so that <code class="language-plaintext highlighter-rouge">NonZeroU8</code> will now sort before <code class="language-plaintext highlighter-rouge">NonZeroU16</code> rather than after it, and
lowercase characters will now sort after uppercase characters.</p>

<p>References:</p>
<ul>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rustfmt-raw-identifier-sorting.html">Edition guide - Rustfmt: Raw identifier sorting</a></li>
  <li><a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rustfmt-version-sorting.html">Edition guide - Rustfmt: Version sorting</a></li>
</ul>

<h2 id="to-summarize">To summarize</h2>

<p>Rust 2024 is characterized not by big changes, but rather by a plethora of small ones. I expect for
most code bases, very few changes will have to be made, if any at all, and most of the changes that
do have to be made can be addressed by <code class="language-plaintext highlighter-rouge">cargo fix</code>.</p>

<p>To me, this seems like a healthy state for the language. Change keeps happening, but most of it can
be relied on to remain recognizable to people in the future. The language isn’t stagnating, but it’s
also not adding a torrent of new features and syntax any more. This kind of slow evolution should
allow for greater adoption in the real world, as the world sees that you don’t have to spend half
your time keeping up with all the new changes. The future of the language seems alright, and I’m
here for it.</p>

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <p>Content in this article summarizes and uses small sections of the <a href="https://doc.rust-lang.org/nightly/edition-guide/rust-2024/index.html">2024 Rust edition guide</a>. I have attempted to provide clear attributes from sections that were directly taken. The
contents of this book are a product of many contributors and is available under the MIT and Apache
2.0 licences.</p>

  <p>Cover image “String rope rust chains” by
<a href="https://pixabay.com/users/photojacques-15061670/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=4826751&quot;">Jacques</a>
from Pixabay.</p>

</footer>]]></content><author><name>Bert Peters</name></author><category term="rust" /><summary type="html"><![CDATA[Last Thursday Rust 1.85 was released, and with it, edition 2024 has dropped. The new edition is significantly larger than the two editions that preceded it, and contains many small but significant quality of life improvements to the language. In this post, I’d like to explain what an edition is, and summarize all the changes that were made to the language I love. If you need the details, I recommend reading the edition guide, but for a general overview, read on.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2025/rust-edition-2024.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2025/rust-edition-2024.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Infrastructure as Advent of Code</title><link href="https://bertptrs.nl/2025/02/04/infrastructure-as-advent-of-code.html" rel="alternate" type="text/html" title="Infrastructure as Advent of Code" /><published>2025-02-04T20:31:00+01:00</published><updated>2025-02-04T20:31:00+01:00</updated><id>https://bertptrs.nl/2025/02/04/infrastructure-as-advent-of-code</id><content type="html" xml:base="https://bertptrs.nl/2025/02/04/infrastructure-as-advent-of-code.html"><![CDATA[<p>In the cold of December we have but one thing to keep us warm: our laptops, trying to solve Advent
of Code puzzles with inefficient algorithms. This year, 2024, is the tenth edition, and the puzzles
are filled with more Easter eggs than ever before. Unfortunately, I’m not interested in Easter eggs,
or solving the puzzles. I am a DevOps engineer, and I’m going to apply Infrastructure as Code
principles to Advent of Code.</p>

<p>After burning myself out halfway through the puzzles last year, I decided I was going to make it
easy on myself this year, and use Python, as <a href="https://xkcd.com/353/">most problems are simply solved in the standard
library</a> and most other things are easily solved with <a href="https://numpy.org/">Numpy</a> or <a href="https://networkx.org/">NetworkX</a>. Then a
friend told me I was being suspiciously normal, so I opted to honour my day job in DevOps and, as a
challenge, implement as many puzzle solutions as possible in <a href="https://developer.hashicorp.com/terraform">Terraform</a>.</p>

<p>Terraform, as we will go into more detail about soon, is not exactly a programming language. Its
intended use is to define a desired set of cloud infrastructure, which you can then feed to the
<code class="language-plaintext highlighter-rouge">terraform</code> binary and tell it to “make it so.” As such, you might be surprised that it can do
programming at all. After all, it’s a configuration tool, and you would like your configuration to
be simple, static, and predictable<sup id="fnref:predictable"><a href="#fn:predictable" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. While that’s partially true, years of adding
convenience to the language have definitely created some structures that could be used to solve
puzzles.</p>

<p>If you’re not interested in learning Terraform, you can skip ahead to <a href="#the-puzzles">the discussion of the
puzzles</a>.</p>

<h2 id="what-is-terraform">What is Terraform?</h2>

<p>HashiCorp Terraform, to call it by its full, <a href="https://www.hashicorp.com/trademark-policy">nominatively fair use</a> name, is a tool used
to codify infrastructure. In its most basic form, you use <code class="language-plaintext highlighter-rouge">provider</code> plugins to write <code class="language-plaintext highlighter-rouge">resource</code>
definitions, which can then be <code class="language-plaintext highlighter-rouge">apply</code>‘d to make reality match your configuration. For example, the
following configuration would set up a server in the Netherlands in Google Cloud Platform.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="k">provider</span> <span class="s2">"google"</span> <span class="p">{</span>
  <span class="nx">project</span> <span class="o">=</span> <span class="s2">"my-gcp-project"</span>
<span class="p">}</span>

<span class="k">resource</span> <span class="s2">"google_compute_instance"</span> <span class="s2">"my_server"</span> <span class="p">{</span>
  <span class="nx">name</span>         <span class="o">=</span> <span class="s2">"my-server"</span>
  <span class="nx">machine_type</span> <span class="o">=</span> <span class="s2">"n2-standard-2"</span>
  <span class="nx">zone</span>         <span class="o">=</span> <span class="s2">"europe-west4-a"</span>

  <span class="nx">boot_disk</span> <span class="p">{</span>
    <span class="nx">initialize_params</span> <span class="p">{</span>
      <span class="nx">image</span> <span class="o">=</span> <span class="s2">"debian-cloud/debian-12"</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>And this works pretty well. When your operation grows, you will however generally accumulate more
and more servers, and you want to avoid repeating yourself for each one of them. Because of this,
over time, several features have been added that allow you to not repeat yourself, do little
calculations, and overall express yourself more concisely.</p>

<h2 id="our-tools">Our tools</h2>

<p>As mentioned, Terraform is not intended for heavy computational use, but it has a few things that we
might put to work as such any way. Let’s go over what we have to work with.</p>

<h3 id="data-types">Data types</h3>

<p>Terraform has a reasonably complete set of <a href="https://developer.hashicorp.com/terraform/language/expressions/types">data types</a>, though they rarely come up due to
automatic coercion. We have basic primitives <code class="language-plaintext highlighter-rouge">string</code>, <code class="language-plaintext highlighter-rouge">number</code><sup id="fnref:number"><a href="#fn:number" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>, and boolean, as well as
collection types <code class="language-plaintext highlighter-rouge">list</code>, <code class="language-plaintext highlighter-rouge">set</code>, and <code class="language-plaintext highlighter-rouge">map</code>. The <code class="language-plaintext highlighter-rouge">map</code> type only has string keys, but its value type
can vary. The collections can also be composed, so <code class="language-plaintext highlighter-rouge">list(map(number))</code> is a valid type. <code class="language-plaintext highlighter-rouge">null</code> is
also a type, but for our purposes it doesn’t really come up.</p>

<p>Even though the documentation insists that casts are rarely necessary because everything generally
coerces to the right type, this stops being true as soon as you start to do any programming. The
string <code class="language-plaintext highlighter-rouge">"0"</code> will indeed coerce to <code class="language-plaintext highlighter-rouge">0</code> when passed to a context that expects a number, but these
values are not equal.</p>

<div class="language-hcl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nx">run</span> <span class="s2">"type_equality"</span> <span class="p">{</span>
  <span class="nx">assert</span> <span class="p">{</span>
    <span class="nx">condition</span>     <span class="o">=</span> <span class="s2">"0"</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="nx">error_message</span> <span class="o">=</span> <span class="s2">"Integers aren't equal to strings!"</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>For completeness, it should be noted that there are two more data types, <code class="language-plaintext highlighter-rouge">tuple</code> and <code class="language-plaintext highlighter-rouge">object</code>, that
are simply <code class="language-plaintext highlighter-rouge">list</code> and <code class="language-plaintext highlighter-rouge">map</code> but with different types of values rather than all the same. I will not
be using those for our solution programs.</p>

<h3 id="structure">Structure</h3>

<p>Our Terraform project is structured in modules. A module is effectively some directory with <code class="language-plaintext highlighter-rouge">.tf</code>
files<sup id="fnref:files"><a href="#fn:files" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>. Terraform doesn’t really have an ordering between files, they should all be viewed
together at once as if they were one big file. In its simplest form, a module defines some input
<code class="language-plaintext highlighter-rouge">variable</code>s and some <code class="language-plaintext highlighter-rouge">output</code> variables that are derived from that. For example, the module below
takes a number and doubles it.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">variable</span> <span class="s2">"my_num"</span> <span class="p">{</span>
    <span class="nx">type</span> <span class="o">=</span> <span class="nx">number</span>
<span class="p">}</span>

<span class="k">output</span> <span class="s2">"my_result"</span> <span class="p">{</span>
    <span class="nx">value</span> <span class="o">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">my_num</span> <span class="o">*</span> <span class="mi">2</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>It’s often useful to compute a repeated bit once, and then use it later. You can do so with <code class="language-plaintext highlighter-rouge">local</code>
variables:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nx">locals</span> <span class="p">{</span>
    <span class="nx">double</span> <span class="o">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">my_num</span> <span class="o">*</span> <span class="mi">2</span>
    <span class="nx">quadruple</span> <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">double</span> <span class="o">*</span> <span class="mi">2</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In a way, modules are like functions: they take some input, do their transformations on it, and
produce an observable output. Any local variables are implementation details and are hidden from the
caller. Modules can also call each other to create more complex structures. Here, they either point
to a module in some well-known registry, but you can also simply point to different directory of
Terraform files. You can then refer to the outputs of that module to continue your computation.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">module</span> <span class="s2">"doubler_as_a_service"</span> <span class="p">{</span>
    <span class="nx">source</span> <span class="o">=</span> <span class="s2">"./doubler"</span>

    <span class="nx">my_num</span> <span class="o">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">outer_input</span>
<span class="p">}</span>

<span class="k">output</span> <span class="s2">"is_42"</span> <span class="p">{</span>
    <span class="nx">value</span> <span class="o">=</span> <span class="k">module</span><span class="p">.</span><span class="nx">doubler_as_a_service</span><span class="p">.</span><span class="nx">my_result</span> <span class="o">==</span> <span class="mi">42</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>It should be noted that the order that expressions and definitions appear in the files do not
matter; instead, Terraform attempts to build a <a href="/2022/06/23/deadlock-free-mutexes-and-directed-acyclic-graphs.html">directed acyclic graph</a> between all definitions, and
then computes everything starting from the leaves. It does so in parallel, which has the neat little
side effect that it’s trivial to write highly multithreaded code in Terraform.</p>

<h3 id="basic-arithmetic">Basic arithmetic</h3>

<p>It might seem minor given the above areas, but Terraform also has a reasonably complete set of
arithmetic operators, which combine in all the ways you would expect. In addition, it also has
boolean logic, though it doesn’t do short-circuiting for con- and disjunctions. <strong>This will come to
bite us later</strong>, but we can make it work.</p>

<h3 id="loop-structures">Loop structures</h3>

<p>Terraform doesn’t have loops directly, but it does have two sets of primitives that we can use as
loops in our programs. Both have roughly the same limitations:</p>

<ul>
  <li>Iterations cannot depend on the result of a previous iteration</li>
  <li>The number of iterations is always bounded</li>
  <li>We cannot exit early</li>
  <li>We <em>can</em> skip individual iterations conditionally</li>
</ul>

<p>This, it turns out, is good enough for many purposes.</p>

<h4 id="comprehensions">Comprehensions</h4>

<p>If you are familiar with Python, you will have seen list- and dictionary comprehensions, as a way of
creating these collections from an iterator, without a for loop.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">first_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
    <span class="n">first_list</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">n</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span>

<span class="n">second_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span> <span class="o">*</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In Terraform, we don’t have the luxury of for loops, but we do have <a href="https://developer.hashicorp.com/terraform/language/expressions/for">for expressions</a> which are
essentially list- and map comprehensions. We will be using those as our primary looping mechanism.
Terraform has roughly the same functionality as Python here. We can loop over one thing, construct
another, and optionally skip elements that don’t meet certain conditions.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="nx">locals</span> <span class="p">{</span>
    <span class="nx">list_example</span> <span class="o">=</span> <span class="p">[</span><span class="nx">for</span> <span class="nx">n</span> <span class="nx">in</span> <span class="nx">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="o">:</span> <span class="mi">2</span> <span class="p">*</span> <span class="nx">n</span><span class="p">]</span>
    <span class="nx">map_example</span> <span class="o">=</span> <span class="p">{</span><span class="nx">for</span> <span class="nx">n</span> <span class="nx">in</span> <span class="nx">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span><span class="o">:</span> <span class="nx">n</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">*</span> <span class="mi">2</span><span class="p">}</span>

    <span class="nx">list_from_map</span> <span class="o">=</span> <span class="p">[</span>
        <span class="nx">for</span> <span class="nx">key</span><span class="p">,</span> <span class="nx">value</span> <span class="nx">in</span> <span class="kd">local</span><span class="p">.</span><span class="nx">map_example</span> <span class="o">:</span>
        <span class="nx">value</span>
        <span class="c1"># Skip keys that are divisible by three</span>
        <span class="nx">if</span> <span class="nx">key</span> <span class="o">%</span> <span class="mi">3</span> <span class="o">!=</span> <span class="mi">0</span>
    <span class="p">]</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>As previously, mentioned, keys in a map will always be strings. This is luckily mostly managed by
automatic type conversion, so you can use integers as map keys just fine.</p>

<p>There is one more special comprehension. For maps, it is an error to have more than one value for
the same key. For the cases where you actually do have more than one value per key, you can use the
grouping map comprehension. This combines all values with the same key into a list, in order of
appearance.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nx">locals</span> <span class="p">{</span>
    <span class="nx">grouped_map</span> <span class="o">=</span> <span class="p">{</span>
      <span class="nx">for</span> <span class="nx">n</span> <span class="nx">in</span> <span class="nx">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">)</span><span class="o">:</span>
      <span class="nx">n</span> <span class="o">%</span> <span class="mi">7</span> <span class="o">=&gt;</span> <span class="nx">n</span><span class="p">...</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="repetition">Repetition</h4>

<p>Aside the earlier comprehensions, we can also repeat resources and modules using the meta arguments
<code class="language-plaintext highlighter-rouge">count</code> and <code class="language-plaintext highlighter-rouge">for_each</code>. <code class="language-plaintext highlighter-rouge">count</code> takes a number and repeats a given resource or module that many
times. <code class="language-plaintext highlighter-rouge">for_each</code> takes a map and instantiates a given resource or module once for each key in the
map.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">module</span> <span class="s2">"count_module"</span> <span class="p">{</span>
    <span class="nx">source</span> <span class="o">=</span> <span class="s2">"./source"</span>
    <span class="nx">count</span> <span class="o">=</span> <span class="mi">10</span>
    <span class="c1"># The 0-based index of the resource is available to customize the different modules</span>
    <span class="nx">input_var</span> <span class="o">=</span> <span class="nx">count</span><span class="p">.</span><span class="nx">index</span>
<span class="p">}</span>

<span class="k">module</span> <span class="s2">"foreach_module"</span> <span class="p">{</span>
    <span class="nx">source</span> <span class="o">=</span> <span class="s2">"./source"</span>
    <span class="nx">for_each</span> <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">some_map</span>
    <span class="c1"># the key and value of the current map entry are available to use</span>
    <span class="nx">input_var</span> <span class="o">=</span> <span class="nx">each</span><span class="p">.</span><span class="nx">key</span> <span class="o">+</span> <span class="nx">each</span><span class="p">.</span><span class="nx">value</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">count</code> can also be used in a conditional this way: by setting <code class="language-plaintext highlighter-rouge">count</code> to 0, you can conditionally
turn of that particular module. This is, as of now, also the only way to have conditionally-created
resources in Terraform, even when using it as intended. It feels like a hack every time you do.</p>

<h3 id="standard-functions">Standard functions</h3>

<p>Aside from code structures we might use, Terraform also offers a collection of <a href="https://developer.hashicorp.com/terraform/language/functions">functions</a> that you
can use to express complicated structures that you otherwise wouldn’t be able to. The main
workhorses for the puzzles were <code class="language-plaintext highlighter-rouge">split</code> and the various regex operations, as they allowed me to
parse the input in about the same way as I would in Python.</p>

<h3 id="unit-testing">Unit testing</h3>

<p>Any development experience will be infinitely more awful if you can’t rapidly iterate, and for that,
you need a way to quickly test your code. Terraform offers both unit and integration tests for this,
where it will run your code either in <code class="language-plaintext highlighter-rouge">plan</code> or in <code class="language-plaintext highlighter-rouge">apply</code> mode respectively, and allows you.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="nx">locals</span> <span class="p">{</span>
  <span class="nx">two_times_two</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="mi">2</span>
<span class="p">}</span>

<span class="nx">run</span> <span class="s2">"test_math"</span> <span class="p">{</span>
  <span class="nx">assert</span> <span class="p">{</span>
    <span class="nx">condition</span>     <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">two_times_two</span> <span class="o">==</span> <span class="mi">4</span>
    <span class="nx">error_message</span> <span class="o">=</span> <span class="s2">"Maths machine broke"</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I’ve used this to encode the sample inputs and their from the stories into test cases, so I could
quickly check whether my code gave the right answer yet.</p>

<h2 id="what-we-dont-have">What we don’t have</h2>

<p>With all the features outlined above, one might think Terraform is a pretty normal, albeit rather
quirky programming language. This is wrong. It is an expressive declarative configuration language.
As such, we don’t have any method to mutability. Variables are declared exactly once, with their
initial value. Due to the lack of loops, there is no way to declare a variable again with a new
value. Because of this, algorithms that walk through a grid following some arbitrary rules are
impossible to implement. You can’t write Dijkstra’s algorithm without maintaining some to-do list.
You might be able to with sufficient recursion, except you can’t.</p>

<p>It might seem tempting to have modules call themselves conditionally to achieve some sense of
looping or smart recursion, but this doesn’t work. At the early stages of evaluation, Terraform
builds a tree of your module structure to figure out in what order it should do its work. At this
point, no other values have been evaluated, so it assumes that any use of a <code class="language-plaintext highlighter-rouge">module</code> actually
happens. You can kind of achieve recursion by hard-coding your maximum depth into your folder
structure, and you even can kind of avoid repeating yourself by using symlinks</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>outer_module/
└── recursive_module/
    └── main.tf
        └── recursive_module/
            └── main.tf (symlink to ../main.tf)
                └── recursive_module/
                    └── recursion_end.tf
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This is of course not very fun to create, and since all recursion happens unconditionally, you will
always suffer the overhead of having this entire structure, even if you don’t need it. In other
words, it’s slow. This is even worse if you have some conditional branching, as the modules for both
branches will be instantiated.</p>

<h2 id="the-puzzles">The puzzles</h2>

<p>Now that we’ve established that programming in Terraform is a bad idea, let’s try to do it anyway. I
will go over the various puzzles that worked out, and the workarounds that were needed to get there.
With these odds, I’m happy with 18, perhaps 20 stars.</p>

<h3 id="day-1-linked-lists">Day 1: Linked lists</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day01/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/1">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>We start the puzzles easy, but we also encounter the limitations of automatic type coercion for the
first time. It is simple enough to parse the lines of numbers into two lists of numbers. After that,
we do have to explicitly cast the values to numbers before using the <code class="language-plaintext highlighter-rouge">sort</code> function.</p>

<p>The <code class="language-plaintext highlighter-rouge">sort</code> function will only sort numbers by value; any other value will be sorted
lexicographically instead. That is reasonable behaviour, but can be unexpected when sorting numeric
strings. This behaviour is also not documented; the documentation suggests that any form of sorting
happens lexicographically regardless of type. Two stars.</p>

<h3 id="day-2-report-redaction">Day 2: Report redaction</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day02">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/2">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>On the second day, we get the first use of modules as reusable functions. We are asked to check
whether a list is either ascending or descending, and whether the difference for each step is
between one and three, inclusive. We can write a module that computes that for a given list.</p>

<p>For the second part, we are asked whether we can make the lists that do not yet match our
requirements, pass the check after removing a single element from them. There exists a nice Dynamic
Programming solution that can check whether a list of numbers can be fixed like that in a single
pass, but since this is only the second puzzle, the lists are nice and short (up to eight elements)
and simply checking whether the deletion of any one of those elements results in a valid list is
good enough. Two stars.</p>

<h3 id="day-3-to-multiply-or-not-to-multiply">Day 3: To multiply or not to multiply</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day03/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/3">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>The third puzzle gives us garbled string like <code class="language-plaintext highlighter-rouge">sub(2b,3)r7do()mul(3,4)don't()mul(3,10)</code> and asked
what the total for all the included multiplications <code class="language-plaintext highlighter-rouge">mul(a,b)</code> is. There are many ways to do this,
but the easiest to me is to use a simple regular expression to find all the multiplications.
Terraform’s regular expression engine proved capable of this just fine.</p>

<p>After solving that, the twist for the second puzzle is that there are additional <code class="language-plaintext highlighter-rouge">do()</code> and
<code class="language-plaintext highlighter-rouge">don't()</code> instructions included in the string. Any <code class="language-plaintext highlighter-rouge">mul</code> after a <code class="language-plaintext highlighter-rouge">don't()</code> should be ignored until
the next <code class="language-plaintext highlighter-rouge">do()</code>.</p>

<p>At first, I thought this would be very hard to do in Terraform as my Python solution keeps a boolean
indicating whether the last thing I’ve seen is a <code class="language-plaintext highlighter-rouge">do()</code> or a <code class="language-plaintext highlighter-rouge">don't()</code>, which is mutation. I did
manage to implement this by tokenizing the string into a sequence of <code class="language-plaintext highlighter-rouge">mul()</code>, <code class="language-plaintext highlighter-rouge">do()</code>, and <code class="language-plaintext highlighter-rouge">don't()</code>,
then for every <code class="language-plaintext highlighter-rouge">mul()</code>, check whether the most recent <code class="language-plaintext highlighter-rouge">do()</code> has a higher index than the most recent
<code class="language-plaintext highlighter-rouge">don't()</code>. This implementation was made slightly harder by lack of a good “last matching element
before position” function, though you can construct one using <code class="language-plaintext highlighter-rouge">slice()</code>, <code class="language-plaintext highlighter-rouge">reverse()</code>, and <code class="language-plaintext highlighter-rouge">index()</code>
functions.</p>

<p>I was then informed by a colleague, who was attempting to complete the puzzles in an even less
suitable programming tool<sup id="fnref:tool"><a href="#fn:tool" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>, that this is clearly the hard way to do this. You can simply use
regular expression text replacements to filter out the parts you should ignore. You can first
replace all <code class="language-plaintext highlighter-rouge">don't()</code>s until the next <code class="language-plaintext highlighter-rouge">do()</code> with the empty string, and finally delete any remaining
<code class="language-plaintext highlighter-rouge">don't()</code> until the end of the string. Just be sure that you have enabled multiline matching in your
regex engine, as the input spans multiple lines. After that, <a href="https://github.com/bertptrs/adventofcode/commit/edb07672010ad6911ad783d0a8ee0678011b520a">I updated my
implementation</a> which performed significantly better.</p>

<p>Originally I had thought that this approach would be impossible, as I had interpreted the problem as
a matching-brackets kind of problem, where every <code class="language-plaintext highlighter-rouge">don't()</code> should be matched up to a <code class="language-plaintext highlighter-rouge">do()</code>. This is
famously impossible with regular expressions due to the <a href="https://en.wikipedia.org/wiki/Pumping_lemma_for_regular_languages">pumping lemma</a>. The regular expression
solution however does work, because you don’t have to match these tokens; the most recent gets to
decide, so this fits perfectly into a regular language. Regardless, another two stars.</p>

<h3 id="day-4-crazy-crossword">Day 4: Crazy crossword</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day04">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/4">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>The fourth puzzle is a giant crossword puzzle. The puzzle is very simple, as it only contains the
word “XMAS”. We have to find how often it contains this word. The way to solve this in Terraform is
not that different from how it would be in any other language: look at every individual point in the
crossword, look in all directions, and see if you hit anything you want to see.</p>

<figure>

  <p><img src="/assets/2025/aoc-2024/crossword.webp" alt="An X-MAS crossword" /></p>

  <figcaption>

    <p>Can you find all the X-MAS I hid in here? Crossword generated with <a href="https://crosswyrd.app">Crosswyrd</a>, which isn’t really
relevant within the scope of this article, but I found it a nice website.</p>

  </figcaption>

</figure>

<p>The way I ended up implementing this, is with a module that is called once for every cell of the
grid, then from there, call a second module that walks in a specified direction, and see what the
grid spells in each of those directions. Here we hit a tiny wall.</p>

<p>Terraform modules turn out to have significant overhead, and evaluating these roughly <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>140</mn><mo>×</mo><mn>140</mn><mo>×</mo><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mn>8</mn><mo stretchy="false">)</mo><mo>=</mo><mn>176400</mn></mrow><annotation encoding="application/x-tex">140 \times
140 \times (1 + 8) = 176400</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">140</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">140</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord">8</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">176400</span></span></span></span> modules requires about 10GiB of RAM. I noticed this problem in the
previous days’ puzzles, but here it became too much for my poor laptop. If we want this to run at
all, we have to reduce the number of modules we expand. Luckily we can simplify the problem a
little.</p>

<p>Horizontal instances of words are easy: we can simply do a string search for <code class="language-plaintext highlighter-rouge">XMAS</code> and <code class="language-plaintext highlighter-rouge">SAMX</code> in
our input text, which we can do once for the entire input. This has immediately reduces the work we
have to do by 22%, because we no longer have to search in those two directions.</p>

<p>The other six directions don’t have simplifications that are this nice, but we can at least reduce
the amount of work by half by only searching of three of them, and searching for both <code class="language-plaintext highlighter-rouge">XMAS</code> and
<code class="language-plaintext highlighter-rouge">SAMX</code> at the same time.</p>

<p>Together, these optimizations reduce the amount of RAM required enough so that I can run this on my
laptop again.</p>

<p>For the second part, it turns out we weren’t looking for the word <code class="language-plaintext highlighter-rouge">XMAS</code>, but rather for pairs of
the word <code class="language-plaintext highlighter-rouge">MAS</code> intersecting each other to make an <code class="language-plaintext highlighter-rouge">X</code>. A+ joke. This turns out to be much simpler
than searching for <code class="language-plaintext highlighter-rouge">XMAS</code>, as the orientation is largely fixed. We simply look at all <code class="language-plaintext highlighter-rouge">A</code>s, and see
whether both diagonals running through the <code class="language-plaintext highlighter-rouge">A</code> spell <code class="language-plaintext highlighter-rouge">MAS</code>. I wrote a separate module for this,
though thinking back, it could’ve been inlined as well.</p>

<p>This adds two more stars, though I have the code commented out in my Terraform project as the time
to solve it hampers my iteration speed considerably. If you really want to, you can uncomment it to
see it run.</p>

<h3 id="day-5-directing-cyclic-graphs">Day 5: Directing cyclic graphs</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day05/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/5">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>Today we are print lists of numbers, and we have a set of rules where one page must be printed in
front of the other. For the first part, we must find how many of our lists are valid to be printed.
To check this, you could try to find a topological order for the rules, and see whether the print
lists follow this order. As <a href="https://www.reddit.com/r/adventofcode/comments/1h74k1o/2024_day_5_the_ring_in_the_rules/">various people on Reddit figured out</a>, the rules are
cyclic, so this topological order doesn’t exist. There is however a different way.</p>

<p>For every rule that says <code class="language-plaintext highlighter-rouge">A</code> must come before <code class="language-plaintext highlighter-rouge">B</code>, we can use this knowledge to see that if we ever
see a <code class="language-plaintext highlighter-rouge">B</code> in a list, the rest of that list must not contain <code class="language-plaintext highlighter-rouge">A</code>. We can use <a href="#comprehensions">the grouping map
comprehension</a> to build a blacklist for every number, then walk iteratively through
our lists and for every position, check if tail starting after that position contains any numbers on
the blacklist for the number at that position. This is kind of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>3</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^3)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> <em>per list</em>, but the lists
are still relatively short and it works out.</p>

<p>For the second part, we are required to figure out the correct order for the lists that aren’t in
the correct order. I have not managed to design a sorting algorithm that works in Terraform, so I’m
going to consider this one impossible. First incomplete puzzle, one star.</p>

<h3 id="day-8-radio-stars">Day 8: Radio stars</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day08">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/8">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>Our arch nemesis, the Easter Bunny, is using an array of radio antennae to convince the masses to
buy cheap imitation chocolate. <a href="https://en.wikipedia.org/wiki/Nesquik">This is of course a completely unrealistic scenario and no real life
bunny would ever do this.</a> Nevertheless, we have to stop him.</p>

<p>Each of the antennae emits a certain frequency, and when two antennae with the same frequency
interact, it causes antinodes, where the amplitude of the waves is maximized. That is unfortunately
where the real-life accuracy ends, as the puzzle definition for antinodes works differently.</p>

<p>In part one, antinodes for each pair of antennae are located on all places that are twice as far
from one antenna as they are from the other. The puzzle is nice, having no pairs at a distance of an
integer multiple of three from each other, so every pair <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo separator="true">,</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">a, b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">b</span></span></span></span> has two antinodes:</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><msub><mi>v</mi><mn>1</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>a</mi><mo>−</mo><mo stretchy="false">(</mo><mi>b</mi><mo>−</mo><mi>a</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><msub><mi>v</mi><mn>2</mn></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>b</mi><mo>+</mo><mo stretchy="false">(</mo><mi>b</mi><mo>−</mo><mi>a</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align*}
  v_1 &amp; = a - (b - a) \\
  v_2 &amp; = b + (b - a)
\end{align*}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:3em;vertical-align:-1.25em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.75em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.25em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.75em;"><span style="top:-3.91em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.25em;"><span></span></span></span></span></span></span></span></span></span></span></span>

<p>The rest of the algorithm is then fairly straightforward in Terraform: loop over all grid
coordinates to locate the antennae, group by frequency, compute all pairs, compute the resulting
antinodes from those pairs, check whether they land inside the grid, and finally build a set out of
all of these resulting nodes.</p>

<p>The second part adds slightly more nodes to the puzzle by having any grid point that is collinear
with the pair of nodes be an antinode. That simplifies the list of grid points to</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>v</mi><mi>k</mi></msub><mo>=</mo><mi>a</mi><mo>+</mo><mi>k</mi><mo>⋅</mo><mo stretchy="false">(</mo><mi>b</mi><mo>−</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">v_k = a + k \cdot (b - a)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mclose">)</span></span></span></span></span>

<p>for all integers <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span>. For simplicity, I had <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.03148em;">k</span></span></span></span> go from <code class="language-plaintext highlighter-rouge">-max(width, height)</code> to <code class="language-plaintext highlighter-rouge">max(width,
height)</code> which should cover any grid point that may be an antinode. In theory, you should compute
divide the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>−</mo><mi>a</mi></mrow><annotation encoding="application/x-tex">b - a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">b</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> vector by the greatest common denominator of its components, but at least in my
puzzle it appears not to matter. It is however possible so let me show you. Euclid’s algorithm for
greatest common denominator is usually written recursively as:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">def</span> <span class="nf">gcd</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
    <span class="k">if</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="n">a</span><span class="p">:</span>
        <span class="k">return</span> <span class="nf">gcd</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
    <span class="k">elif</span> <span class="n">a</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">b</span>
    <span class="k">return</span> <span class="nf">gcd</span><span class="p">(</span><span class="n">b</span> <span class="o">%</span> <span class="n">a</span><span class="p">,</span> <span class="n">a</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Euclid’s algorithm is very efficient, and runs (worst case) in <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(\log_{\phi}(n))</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1302em;vertical-align:-0.3802em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mop"><span class="mop">lo<span style="margin-right:0.01389em;">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.242em;"><span style="top:-2.4559em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">ϕ</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.3802em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">))</span></span></span></span> recursive
calls, where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϕ</mi></mrow><annotation encoding="application/x-tex">\phi</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">ϕ</span></span></span></span> is the golden ratio, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> is the smaller of our two numbers. Since our
width is bounded to our input, we can linearly write out the algorithm for enough steps to cover
that. So I did.</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="rouge-code"><pre><span class="nx">locals</span> <span class="p">{</span>
  <span class="nx">dx</span> <span class="o">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">second</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="kd">var</span><span class="p">.</span><span class="nx">first</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
  <span class="nx">dy</span> <span class="o">=</span> <span class="kd">var</span><span class="p">.</span><span class="nx">second</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="kd">var</span><span class="p">.</span><span class="nx">first</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>

  <span class="c1"># sort() doesn't work as it turns the numbers into strings</span>
  <span class="nx">gcd0</span> <span class="o">=</span> <span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dx</span><span class="p">)</span> <span class="o">&lt;</span> <span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dy</span><span class="p">)</span> <span class="o">?</span> <span class="p">[</span><span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dx</span><span class="p">),</span> <span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dy</span><span class="p">)]</span> <span class="o">:</span> <span class="p">[</span><span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dy</span><span class="p">),</span> <span class="nx">abs</span><span class="p">(</span><span class="kd">local</span><span class="p">.</span><span class="nx">dx</span><span class="p">)]</span>

  <span class="c1"># Do as many iterations as necessary.</span>
  <span class="nx">gcd1</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd0</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd0</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd0</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd0</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd0</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd2</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd1</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd1</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd1</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd1</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd3</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd2</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd2</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd2</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd4</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd3</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd3</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd3</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd3</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd3</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd5</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd4</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd4</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd4</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd4</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd4</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd6</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd5</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd5</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd5</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd5</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd5</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd7</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd6</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd6</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd6</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd6</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd6</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd8</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd7</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd7</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd7</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd7</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd7</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd9</span>  <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd8</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd8</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd8</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd8</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd8</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>
  <span class="nx">gcd10</span> <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd9</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="p">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd9</span> <span class="o">:</span> <span class="p">[</span><span class="kd">local</span><span class="p">.</span><span class="nx">gcd9</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">%</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd9</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd9</span><span class="p">[</span><span class="mi">0</span><span class="p">]]</span>

  <span class="c1"># 10 iterations should cover numbers up to 55, which is more than the width/height</span>
  <span class="nx">gcd</span> <span class="o">=</span> <span class="kd">local</span><span class="p">.</span><span class="nx">gcd10</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>One final thing to mention is that I while writing the section explaining how the eighth puzzle was
impossible, I realized it was indeed very much possible to do so. Another two stars.</p>

<h3 id="day-11-collatz-angels">Day 11: Collatz’ Angels</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day11">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/11">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>It was going so well, but suddenly the puzzles start requiring real programming and I couldn’t get
Terraform to do any of it. Luckily, this day works surprisingly okay. We are asked to simulate a set
of strange stones.</p>

<p>The stones have numbers written on them, and every time we blink, the stones change and split up
according to the following rules:</p>

<ul>
  <li>if the stone is labelled <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>, it is replaced by a stone labelled <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>,</li>
  <li>else, if the number has an odd number of digits, it is multiplied by <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2024</mn></mrow><annotation encoding="application/x-tex">2024</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2024</span></span></span></span>,</li>
  <li>else, the stone is split into two separate stones: one labelled with the first half of the digits,
and one labelled with the second half. A stone labelled 2005 would split into on two labelled 20
and 5 respectively.</li>
</ul>

<p>We are to simulate this process for 25 blinks, and then for 75 blinks. The step that’s splitting the
stones in two is suspicious, as exponential growth would make our pool of stones really big really
fast. There has to be a trick to this.</p>

<aside>

  <p>Let’s take a quick detour around the <a href="https://en.wikipedia.org/wiki/Collatz_conjecture">Collatz conjecture</a>, also known as the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>n</mi><mo>+</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">3n + 1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">3</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>
conjecture. It describes a similar iterative process, where we start with a number <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span>, and modify
it as follows:</p>

  <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.36em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>n</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>if </mtext><mi>n</mi><mo>≡</mo><mn>0</mn><mspace></mspace><mspace width="0.4444em"/><mo stretchy="false">(</mo><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow><mspace width="0.3333em"/><mn>2</mn><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mn>3</mn><mi>n</mi><mo>+</mo><mn>1</mn></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mtext>if </mtext><mi>n</mi><mo>≡</mo><mn>1</mn><mspace></mspace><mspace width="0.4444em"/><mo stretchy="false">(</mo><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow><mspace width="0.3333em"/><mn>2</mn><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></mstyle></mtd></mtr></mtable></mrow></mrow><annotation encoding="application/x-tex">f(n) = \begin{cases} n/2 &amp;\text{if } n \equiv 0 \pmod{2},\\[4px] 3n+1 &amp; \text{if } n\equiv 1 \pmod{2} .\end{cases}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:3.2815em;vertical-align:-1.3908em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size4">{</span></span><span class="mord"><span class="mtable"><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8907em;"><span style="top:-3.8907em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord mathnormal">n</span><span class="mord">/2</span></span></span><span style="top:-2.0492em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord">3</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3908em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:1em;"></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.8907em;"><span style="top:-3.8907em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord text"><span class="mord">if </span></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">0</span><span class="mspace allowbreak"></span><span class="mspace" style="margin-right:0.4444em;"></span><span class="mopen">(</span><span class="mord"><span class="mord"><span class="mord mathrm">mod</span></span></span><span class="mspace" style="margin-right:0.3333em;"></span><span class="mord">2</span><span class="mclose">)</span><span class="mpunct">,</span></span></span><span style="top:-2.0492em;"><span class="pstrut" style="height:3.008em;"></span><span class="mord"><span class="mord text"><span class="mord">if </span></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≡</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">1</span><span class="mspace allowbreak"></span><span class="mspace" style="margin-right:0.4444em;"></span><span class="mopen">(</span><span class="mord"><span class="mord"><span class="mord mathrm">mod</span></span></span><span class="mspace" style="margin-right:0.3333em;"></span><span class="mord">2</span><span class="mclose">)</span><span class="mord">.</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.3908em;"><span></span></span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span></span>

  <p>That is, if it is even, we divide it by two, if it is odd, we multiply it by three and add one.
Let’s start with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>12</mn></mrow><annotation encoding="application/x-tex">12</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">12</span></span></span></span>. This is even, so we divide by two and get to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>6</mn></mrow><annotation encoding="application/x-tex">6</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">6</span></span></span></span>. <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>6</mn></mrow><annotation encoding="application/x-tex">6</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">6</span></span></span></span> is still even, so
we divide again and get to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn></mrow><annotation encoding="application/x-tex">3</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">3</span></span></span></span>. This is odd, so we multiply and add to reach <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>10</mn></mrow><annotation encoding="application/x-tex">10</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">10</span></span></span></span>. Then, to simplify</p>

  <span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mn>10</mn><mo>→</mo><mn>5</mn><mo>→</mo><mn>16</mn><mo>→</mo><mn>8</mn><mo>→</mo><mn>4</mn><mo>→</mo><mn>2</mn><mo>→</mo><mn>1</mn><mo>→</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">10 \to 5 \to 16 \to 8 \to 4 \to 2 \to 1 \to 4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">10</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">5</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">16</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">8</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">→</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span></span></span></span></span>

  <p>When we reach <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span>, we will loop back to <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn></mrow><annotation encoding="application/x-tex">4</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">4</span></span></span></span>, and this pattern repeats forever. It turns out,
every integer that anyone has ever tried will reach this loop eventually, though it has not been
proven. <a href="https://www.youtube.com/watch?v=094y1Z2wpJg">Veritasium has recently done an episode</a> on the history of the problem.</p>

</aside>

<p>The rules for the problem are rather similar in their reduction to the rules from the Collatz
conjecture, so it makes sense that there is some loop involved. Of course, we have to deal with an
exponential number of stones, but this loop will provide the solution to that.</p>

<p>It turns out, that despite the number of stones expanding rather quickly, we will only every have
three thousand something unique labels on those stones. We can use this fact to group stones with
the same number on them, as they will evolve in the same way. We simply have to multiply the result
with the number of like-labelled stones we have. In other words, our algorithm works as follows:</p>

<ol>
  <li>Convert the input into a map of <code class="language-plaintext highlighter-rouge">{stone_label: number_of_stones_with_that_label}</code></li>
  <li>Then, for every step of the simulation:
    <ol>
      <li>For each label and count, convert them to a list of new labels and counts</li>
      <li>Group these new counts by label</li>
      <li>Sum the counts grouped by label to create a new map</li>
    </ol>
  </li>
  <li>After all steps are done, sum all the map values to arrive at the final number</li>
</ol>

<p>This is essentially the same algorithm I used in Python too, though with some issues due to
Terraform. First, how do we write out 75 steps? My original idea was to do something like the
following:</p>

<div class="language-terraform highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">module</span> <span class="s2">"step"</span> <span class="p">{</span>
  <span class="nx">source</span> <span class="o">=</span> <span class="s2">"./step"</span>
  <span class="nx">count</span> <span class="o">=</span> <span class="mi">75</span>

  <span class="nx">input_map</span> <span class="o">=</span> <span class="nx">count</span><span class="p">.</span><span class="nx">index</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">?</span> <span class="kd">local</span><span class="p">.</span><span class="nx">input_map</span> <span class="o">:</span> <span class="nx">step</span><span class="p">[</span><span class="nx">count</span><span class="p">.</span><span class="nx">index</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="nx">output_map</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Unfortunately, this doesn’t work: Terraform considers all instances of repeated resources and
modules to be the same thing. Despite this hypothetical <code class="language-plaintext highlighter-rouge">step</code> module never actually referring to
itself, Terraform does consider it a cycle, and will error when you try to evaluate this. The
solution of course is to copy-and-paste this <code class="language-plaintext highlighter-rouge">step</code> module 75 times.</p>

<p>The second issue encountered here happened only when I moved to optimize my code a little.
Originally, the <code class="language-plaintext highlighter-rouge">step</code> module called a separate <code class="language-plaintext highlighter-rouge">transform</code> module on every label individually and
transformed it into a list of different labels. This involves a fairly large number of modules, so I
wanted to inline the computation, because that way the overhead would be significantly reduced.
After copying the code out of the module and into the parent, the code stopped working.</p>

<p>I managed to track this down to the fact that the input arguments for the module had a fixed type
coerced the arguments to integers. The stone labels were stored as the key in a dictionary, but this
coercion ensured that by the time they encountered my logic, they were in fact numbers. After
inlining, this was no longer the case, and I got to discover that <code class="language-plaintext highlighter-rouge">0 != "0"</code>, even though everything
else still works.</p>

<p>Despite all that, another two stars for the collection.</p>

<h3 id="day-13-unfair-fun-fair">Day 13: Unfair fun fair</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day13">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/13">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>After another day that was clearly impossible due to the 2D maze solving required, we end up with
one that is just as easy in Terraform as it is in any other language. We are in control of a series
of claw machines, each with two buttons. For each machine, those buttons move the claw a different
number along the X and Y axes. The question is then how many times we have to press each button to
get the claw to reach some specific position, and whether it is even possible.</p>

<p>The problem text explicitly asks for the minimal number of presses, but this is a red herring: there
is exactly one unique combination of button press numbers that will bring us to the goal. This is
because the underlying problem we are solving is the solution to the following linear equation:</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>a</mi><mi>x</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mi>x</mi></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>a</mi><mi>y</mi></msub></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mi>y</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>a</mi><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>b</mi><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi></mrow></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mo>=</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>t</mi><mi>x</mi></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>t</mi><mi>y</mi></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\begin{bmatrix}
a_x &amp; b_x \\
a_y &amp; b_y
\end{bmatrix}

\begin{bmatrix}
a_{press} \\
b_{press}
\end{bmatrix}

=

\begin{bmatrix}
t_x\\
t_y
\end{bmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.45em;"><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.95em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.45em;"><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.95em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.45em;"><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight">ress</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight">ress</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.95em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:2.4em;vertical-align:-0.95em;"></span><span class="minner"><span class="mopen delimcenter" style="top:0em;"><span class="delimsizing size3">[</span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.45em;"><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.95em;"><span></span></span></span></span></span></span></span><span class="mclose delimcenter" style="top:0em;"><span class="delimsizing size3">]</span></span></span></span></span></span></span>

<p>This results in a system of two linear equations with two unknowns, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi></mrow></msub></mrow><annotation encoding="application/x-tex">a_{press}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight">ress</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mrow><mi>p</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>s</mi></mrow></msub></mrow><annotation encoding="application/x-tex">b_{press}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">p</span><span class="mord mathnormal mtight">ress</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span>,
which you can then solve for as you’ve learned in high school. To filter out the instances where the
claw cannot reach the destination, simply verify that the solution you found still works after
rounding to integers. Part two simply ensures that you weren’t brute-forcing button combinations by
adding a huge offset to the number. Another two stars.</p>

<h3 id="day-14-bathroom-break">Day 14: Bathroom break</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day14/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/14">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>Suddenly, there are a bunch of robots racing around the restroom, all with a known initial position
and speed, teleporting when they hit a wall. We are asked to simulate where they end up after 100
seconds. This is simple modular multiplication, which Terraform can do easily.</p>

<p>The second half of the puzzle requires us to find the moment in time where all robots line up to
form a picture of a Christmas tree. Unfortunately, I have not been able to come up with a heuristic
for this that works in Terraform. Alas, another lone star.</p>

<h3 id="day-19-magical-towels">Day 19: Magical towels</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day19/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/19">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>My favourite joke from last year returns as we arrive at the <a href="/2024/01/02/advent-of-code-2023-let-it-snow.html">Hot Springs</a> of Advent of Code lore.
Here, we are to see whether we can make a given pattern of <a href="https://mtg.fandom.com/wiki/Color">five colours</a> by sequencing
together some towels with specific patterns that we have at our disposal. Both our towels’ pattern
and the target are sequences of <code class="language-plaintext highlighter-rouge">w</code>, <code class="language-plaintext highlighter-rouge">u</code>, <code class="language-plaintext highlighter-rouge">b</code>, <code class="language-plaintext highlighter-rouge">r</code>, and <code class="language-plaintext highlighter-rouge">g</code>. We can actually build a regular
expression of our towel patterns, of <code class="language-plaintext highlighter-rouge">^(every|single|towel|pattern)*$</code> and see whether the target
patterns match this. This makes part 1 solvable in Terraform.</p>

<p>Unfortunately, for part two, the question changes to how many different ways we can construct to
create each pattern. To the best of my knowledge this requires actual recursion, which we can’t do
with our limited tools. One more star.</p>

<h3 id="day-25-keys-to-success">Day 25: Keys to success</h3>

<p><a href="https://github.com/bertptrs/adventofcode/tree/master/2024/bonus/day25/main.tf">Code <i class="fa-solid fa-code"></i></a> - <a href="https://adventofcode.com/2024/day/25">Puzzle <i class="fa-solid fa-puzzle-piece"></i></a></p>

<p>Our last day is coincidentally also the last puzzle I managed to solve. We are given a list of
schematics for keys and locks, and we must find out how many combinations of keys and locks could
theoretically fit each other. A key does not fit if the height of one of its teeth pin stacks
overlaps with one of the matching pin stacks<sup id="fnref:pin_stacks"><a href="#fn:pin_stacks" class="footnote" rel="footnote" role="doc-noteref">5</a></sup> in the lock. This is simple enough; the
challenge is in parsing these heights from the input. Our diagrams look as follows:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre>#####
##.##
##.##
.#.##
.#...
.....
.....

.....
.....
#....
#....
#.##.
#.###
#####
</pre></td></tr></tbody></table></code></pre></div></div>

<p>We can tell whether an entry is a lock or a key by seeing whether the first row is full. The first
item here is a lock with heights <code class="language-plaintext highlighter-rouge">[3, 5, 1, 4, 4]</code> and the second is a key with heights <code class="language-plaintext highlighter-rouge">[5, 1, 3,
3, 2]</code>. These almost fit each other, except the first pin overlaps.</p>

<p>The diagrams would be significantly easier to parse if they were horizontal, but with a slightly
inefficient for loop, we can parse the heights, check every lock against every key, and determine
the potential matches.</p>

<p>This is our final star. I didn’t manage to get anywhere close to getting all other stars, so the
part 2 bonus star for today remains locked. That brings us to a nice and round 18 (out of 50) stars
achievable in pure Terraform.</p>

<h2 id="what-didnt-work">What didn’t work</h2>

<p>So despite getting about one third of the stars available, there are a lot of things that I could
not get Terraform to do. For completeness, I’d like to go through a few reasons why I deemed puzzles
impossible and what might yet be possible.</p>

<ul>
  <li>
    <p>Any algorithm that requires mutating state is immediately out, as we don’t have mutable variables.
Sometimes I could work around this by adding another factor <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> to the algorithmic complexity,
but this only works in specific cases.</p>
  </li>
  <li>
    <p>Any walking through a maze generally requires mutation to do efficiently, which means that any
puzzle that involved walking through a maze was immediately impossible.</p>
  </li>
  <li>
    <p>Some puzzles can theoretically be phrased in terms that Terraform may compute, but anything that
requires a bunch of modules is likely to be prohibitively slow. The overhead of expanding a module
appears on the order of several kilobytes, even for otherwise empty modules.</p>
  </li>
</ul>

<p>This all said, I have a hunch that both parts of <a href="https://adventofcode.com/2024/day/7">day 7</a> might be possible, by using
the fake-bounded recursion that I discussed previously. It would require <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo>⋅</mo><msup><mn>2</mn><mi>l</mi></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \cdot 2^l)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> modules
for the first part and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo>⋅</mo><msup><mn>3</mn><mi>l</mi></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n \cdot 3^l)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0991em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord">3</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8491em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.01968em;">l</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> for part two, with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">n</span></span></span></span> the number of samples and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>l</mi></mrow><annotation encoding="application/x-tex">l</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span></span></span></span>
the length of the lists. I don’t believe my computer can do that.</p>

<h2 id="final-thoughts">Final thoughts</h2>

<p>There exists a literary technique called <a href="https://en.wikipedia.org/wiki/Constrained_writing">constrained writing</a>, where the writer binds themselves to
an unnecessary restriction and tries to make their story work anyway. The book “Initial
Instructions” by Joseph Prouser for example is a translation of the Book of Genesis that does not
use the letter “E”. Instead of creating the heavens and the earth, it starts:</p>

<blockquote>
  <p>God’s initial act in His constantly unfolding work of cosmogony was formation of our world and its
sky.</p>
</blockquote>

<p>I am not a religious person, but playing with language in this way tickles me pink. The point of the
exercise is to make you see your work differently. The restrictions force you to be creative to work
around them, which in turn leads you to new ideas.</p>

<p>Attempting to problem-solve with Terraform made me think differently. Last year I wrote about being
burned out on Advent of Code, but this year I had the most fun I’ve had since doing the <a href="/2018/01/25/advent-of-code-polyglot-edition.html">polyglot
challenge</a>. Terraform is very much not made for programming, but finding new ways to use the
primitives it does offer together has been a great way to keep me thinking.</p>

<p>I encourage anyone to try a similarly restrictive challenge for Advent of Code, or any other
programming puzzle. If you instead send me a pull request in this style, I do reserve the right to
request changes. Happy New Year.</p>

<hr />

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <p>Cover image <a href="https://pixabay.com/photos/terraforming-mars-colonization-5268447/">“Terraforming
Mars”</a> by Tumisu, licensed under
the Pixabay licence.</p>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:predictable">
      <p>As long as you aren’t part of the Javascript ecosystem, in which case it is
perfectly fine for a config file to be arbitrary code. I think. <a href="#fnref:predictable" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:number">
      <p>What exactly that number is, is unspecified, but it appears to be a 64 bit floating point
number. <a href="#fnref:number" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:files">
      <p>This is not entirely true, Terraform files may also be plain JSON, and you can have some
variable definition files, and a bunch of other things, but let’s ignore that for the time
being. <a href="#fnref:files" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:tool">
      <p>They were attempting to complete the puzzles using the
<a href="https://www.channable.com/">Channable</a> tool. This is not even close to a programming language;
it’s an e-commerce automation tool, but it has a rules engine, and if you try really hard you
can indeed solve some of the puzzles with it. <a href="#fnref:tool" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:pin_stacks">
      <p>If any lock picking aficionados read this, and I turn out to completely wrong in my
terminology, please don’t correct me. <a href="#fnref:pin_stacks" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="advent-of-code" /><category term="devops" /><category term="terraform" /><summary type="html"><![CDATA[In the cold of December we have but one thing to keep us warm: our laptops, trying to solve Advent of Code puzzles with inefficient algorithms. This year, 2024, is the tenth edition, and the puzzles are filled with more Easter eggs than ever before. Unfortunately, I’m not interested in Easter eggs, or solving the puzzles. I am a DevOps engineer, and I’m going to apply Infrastructure as Code principles to Advent of Code.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2025/aoc-2024/terraforming.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2025/aoc-2024/terraforming.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Deleting emails will not save the planet</title><link href="https://bertptrs.nl/2024/08/24/deleting-emails-will-not-save-the-planet.html" rel="alternate" type="text/html" title="Deleting emails will not save the planet" /><published>2024-08-24T20:40:00+02:00</published><updated>2024-08-24T20:40:00+02:00</updated><id>https://bertptrs.nl/2024/08/24/deleting-emails-will-not-save-the-planet</id><content type="html" xml:base="https://bertptrs.nl/2024/08/24/deleting-emails-will-not-save-the-planet.html"><![CDATA[<p>A while ago I saw a post on LinkedIn that piqued my interest, not because it was any good, but
because it was impressively wrong. It claimed that, to quote, <strong>“if every email user deleted just 10
emails, it would save enough electricity to power millions of households each year”</strong>. This is not
only wrong, it is obviously wrong. In this post, I’d like to dive into why it’s wrong, how one might
come to think it’s right, and perhaps what better message you could put out there to save the
planet.</p>

<p>The claim is not even unique to LinkedIn, it has been widely circulated in the news, with variations
on the severity of the claims, such as on <a href="https://eenvandaag.avrotros.nl/item/je-oude-mails-fotos-en-videos-verwijderen-heeft-meer-effect-voor-het-klimaat-dan-een-dagje-minder-vlees-eten/" rel="nofollow">national television</a> complete with
terrible data security advice, <a href="https://www.bbc.co.uk/news/technology-55002423" rel="nofollow">the BBC</a>, as well as
<a href="https://www.euronews.com/green/2023/04/08/sending-one-less-email-a-day-could-help-reduce-the-carbon-footprint-of-your-inbox" rel="nofollow">various</a> <a href="https://ourworld.unu.edu/en/uncovering-the-carbon-footprint-of-everything" rel="nofollow">other</a> <a href="https://leavemealone.com/save-the-planet/" rel="nofollow">outlets</a>.
That kind of attention doesn’t appear out of nothing, so let’s examine the claim a bit further.</p>

<h2 id="why-its-wrong">Why it’s wrong</h2>

<p>Houses are big, and emails are not. This is an intuition that I think we can start from. For the
savings of 80 billion deleted emails to power millions of houses, they have to be expensive to save.
This is not, in fact, the case. Let’s do an oversimplified comparison.</p>

<p>At the time of writing, there are 2587 emails in my inbox, which weigh a total of
167MB<sup id="fnref:email-size"><a href="#fn:email-size" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. This works out to an average of 61kB of data per email. More than I expected,
but as we will see, not that much. Assuming all eight billion people alive<sup id="fnref:alive"><a href="#fn:alive" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> today delete ten
emails, that works out to roughly five petabytes (<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≈</mo><mn>5</mn><mo>⋅</mo><mn>1</mn><msup><mn>0</mn><mn>15</mn></msup></mrow><annotation encoding="application/x-tex">\approx 5 \cdot 10^{15}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4831em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">5</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">15</span></span></span></span></span></span></span></span></span></span></span></span> bytes) of data.</p>

<p>A petabyte is a lot of data, but not that much by today’s standards. You can buy <a href="https://tweakers.net/pricewatch/1738710/toshiba-mg09-sata-standard-512e-18tb.html">an 18TB hard drive
for €280</a> so for the low, low price of €77 thousand you too can store this much data.
Alternatively, you could pay for <a href="https://cloud.google.com/products/calculator/estimate-preview/476f0816-5741-47ff-b960-743076af3633">Google Cloud Storage</a>, and store all that data for about €92
thousand a month, going as low as €5.5 thousand a month if you opt for Archive Storage. Since we’re
talking about emails we would otherwise delete and therefore can’t be that valuable, I’d argue that
archive storage will do nicely.</p>

<p>With our calculations above, we now have a good enough picture of what the potential savings are of
us deleting all of those emails. We cannot directly compare the monetary cost of storing this data
to the carbon footprint of the power consumption of millions of homes, but we can make a good guess
that it doesn’t compare. Considering that the average household over a thousand euros a year on
energy (both power and heating) a year, multiplied by millions of households, it should be obvious
that there are several orders of magnitude in between them in terms of cost.</p>

<h2 id="how-do-these-claims-happen">How do these claims happen</h2>

<p>“Why do people believe things that are false” is a big question that I am not qualified to answer.
For the claim central to this article, however, we can make some educated guesses. Searching for the
origin of this claim, led me to <a href="https://carbonliteracy.com/the-carbon-cost-of-an-email-2/">this article about the footprint of an email</a>. In
the article, it is claimed that the carbon footprint of a longer email sent from a laptop and read
on a laptop causes the equivalent of 17 grams of CO2 to be emitted. There are a few caveats to this
number but let’s assume that it’s true. This gives us a CO2 “compensation” of 138 thousand tonnes.</p>

<p>Now for our millions of households. I’m going to take the numbers for the Netherlands because I live
there, but other European countries should be similar. According to the Central Bureau for
Statistics, <a href="https://www.cbs.nl/en-gb/figures/detail/81528ENG">the average household uses 2640 kWh of electricity annually</a>. Our
World in Data mentions that on average, the Netherlands emits 325 grams of CO2 per kWh of energy.
Putting that all together and solving for the number of households gives us:</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mn>8.1</mn><mo>⋅</mo><mn>1</mn><msup><mn>0</mn><mn>9</mn></msup><mtext>people</mtext><mo>⋅</mo><mn>10</mn><mfrac><mtext>emails</mtext><mtext>person</mtext></mfrac><mo>⋅</mo><mn>17</mn><mfrac><mtext>g</mtext><mtext>email</mtext></mfrac></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mi>n</mi><mtext> households</mtext><mo>⋅</mo><mn>2640</mn><mfrac><mtext>kWh</mtext><mtext>household</mtext></mfrac><mo>⋅</mo><mn>325</mn><mfrac><mtext>g</mtext><mtext>kWh</mtext></mfrac></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mi>n</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mn>1.6</mn><mo>⋅</mo><mn>1</mn><msup><mn>0</mn><mn>6</mn></msup></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align*}
  8.1\cdot 10^9 \text{people} \cdot 10 \frac{\text{emails}}{\text{person}} \cdot 17 \frac{\text{g}}{\text{email}} &amp; = n \text{ households} \cdot 2640 \frac{\text{kWh}}{\text{household}} \cdot 325 \frac{\text{g}}{\text{kWh}} \\
  n &amp;= 1.6 \cdot 10^6
\end{align*}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:4.076em;vertical-align:-1.788em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.288em;"><span style="top:-4.288em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord">8.1</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">9</span></span></span></span></span></span></span></span><span class="mord text"><span class="mord">people</span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">10</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">person</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">emails</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.8804em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">17</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">email</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">g</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-2.2434em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord mathnormal">n</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.788em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.288em;"><span style="top:-4.288em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord mathnormal">n</span><span class="mord text"><span class="mord"> households</span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">2640</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">household</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">kWh</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">325</span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.1076em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">kWh</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord text"><span class="mord">g</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.686em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span><span style="top:-2.2434em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">1.6</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">6</span></span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:1.788em;"><span></span></span></span></span></span></span></span></span></span></span></span>

<p>Not quite millions of households, but at least a single million. This number is directly
proportional to the carbon footprint of an email, and that’s gone down over the years. This is
unfortunately where things stop lining up. The carbon footprint of an email, as written by
Berners-Lee, is not in its transport, nor the electricity. It’s in the embodied carbon of your
laptop. While the book doesn’t provide figures for a laptop, other than noting that it has more
embodied carbon than a phone, for a phone this looks something as follows:</p>

<figure>

  <p><img src="/assets/2024/emails/phoneprint.svg" alt="2% electricity, 14 networks and data centres, 84% phone" /></p>

  <figcaption>

    <p>Overview of the carbon footprint of sending and reading an email via the smartphone. Figure
reproduced from “How Bad Are Bananas” page 17. Very little of the CO2 impact of the process is
related to its electricity use.</p>

  </figcaption>

</figure>

<p>“Embodied in the phone” in this case means that the CO2 was emitted in the process of creating the
phone, averaged out over its lifetime, for the duration of dealing with the email. In a fun twist of
events, it causes <em>more</em> CO2 to delete the emails, as you will be spending time and energy on doing
those actions. There are of course caveats to that too, and eventually, the amount of data does start
to matter.</p>

<p>One thing that is interesting to note, is how little CO2 is involved with producing electricity
these days. My recent bike trip in June back and forth to Den Haag, about 27 kilometers, had a
carbon footprint comparable to 2 days of my domestic electricity use.<sup id="fnref:bikeprint"><a href="#fn:bikeprint" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<h2 id="why-make-a-point-out-of-this">Why make a point out of this</h2>

<p>You might be asking yourself, why anyone would go through the trouble of fact-checking a claim that
as posted on a social media platform largely without consequences or persistence. And the answer to
that is twofold. The first part is very simple.</p>

<figure>

  <p><img src="/assets/2024/emails/duty_calls.png" alt="Someone is wrong on the internet" /></p>

  <figcaption>

    <p>We can’t just let people onto the internet and have them tell lies. <a href="https://xkcd.com/386/">XKCD
386</a>.</p>

  </figcaption>

</figure>

<p>The second part however is more personal, so bear with me as I step on my soap box. The climate
change discussion has long been poisoned by less than truthful information. Big institutions pay a
lot to pretend there’s any debate at all that the climate is changing. Sweeping changes will have to
be made by every single country around the world. For some, <a href="https://www.vice.com/en/article/ypww4m/the-nations-destined-to-be-swallowed-by-the-sea">it may already be too late</a>.</p>

<p>Claims like the one that sparked this article do not deny the need for change, but they are in my
opinion harmful in different ways. In a small part, they give climate deniers ammunition (“Look at
these ridiculous claims, who knows what else they might be lying about”) but as part of the bigger
picture they maintain the illusion that climate change is a personal issue that will be addressed if
we all do our part. That is not the world we live in.</p>

<p>Berners-Lee, in his book, champions the 5-tonne lifestyle, where everything you do in life should
amount to no more than 5,000 kilograms of CO2 equivalent emissions per year. This is a noble goal
and one that I respect immensely. The world would be a better place if more people lived like that.
This is, for reference, about 40% of the average per capita emissions of the United
Kingdom<sup id="fnref:ukprint"><a href="#fn:ukprint" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>. Unfortunately, it is not a structurally feasible solution in my opinion.</p>

<p>There is a lot of talk in politics about a carbon tax as if it’s something new, but we’re already
there. Countries around the world are effectively already paying for it. My famously under-sea-level
home country of the Netherlands has to invest in raising, widening, and doubling the dikes to deal
with the rising sea levels. I live in a rich country and it will be fine for the foreseeable future.
Many Pacific island nations will have to do similarly expensive work without those resources. A
small number of countries benefited greatly from emitting greenhouse gases, boosting economies, and
bringing wealth, but the costs of the warming planet are borne by the world as a whole as a
worldwide flat tax. This won’t go away by itself either, due to the <a href="https://en.wikipedia.org/wiki/Tragedy_of_the_commons">tragedy of the commons</a>:
everyone individually benefits by making more CO2 emissions, which inevitably makes life worse for
everyone else.</p>

<p>The classic solution to this tragedy is to achieve consensus on how to manage the shared resources,
which translates to worldwide treaties about emissions. The <a href="https://unfccc.int/resource/docs/2015/cop21/eng/l09r01.pdf">2015 Paris Agreement</a>
was a step in this direction, though it does not appear to be doing nearly enough. We should as a
planet agree where our priorities lie, and ensure that the environmental costs of our activities are
properly priced in for those undertaking them, rather than borne by the world as a whole. Flying is
the easiest example, due to plane fuel being barely taxed at all, but there are other examples.</p>

<p>While the tech sector is not nearly as big as that, carbon footprint wise, bitcoin still famously
uses more energy than several developed countries, <a href="https://www.nytimes.com/interactive/2021/09/03/climate/bitcoin-carbon-footprint-electricity.html">of which Finland is the most recent example I
could find</a>, though this number has likely changed significantly over the last
three years. The “new” kid on the block, AI, is also growing significantly, and <a href="https://www.bbc.com/news/technology-67053139?ecmid=12">is expected to
consume more energy than the entirety of the Netherlands</a>. Going back to the classics, a
lasting rumour stated that a single Google Search is roughly proportional to boiling a kettle of
water, though recent estimates put that number far lower<sup id="fnref:search"><a href="#fn:search" class="footnote" rel="footnote" role="doc-noteref">5</a></sup>. No matter what it is, its utility
should be weighed against its cost.</p>

<p>The internet as a whole consumes a lot of power but it also brings a lot of utility. AI and recently
large language models have been very powerful and can improve human productivity. Bitcoin is
certainly a thing that exists. I do not mean to argue that we should abandon all that, though we
should further incentivize computational efficiency, and reduce the carbon footprint of the power
requirements that remain. Not just for tech, but for everything, and not just as an ideal, but as
global policy.</p>

<h2 id="take-away">Take away</h2>

<p>To get back to the original topic and maybe contradict this article’s title a little: individual
actions can’t solve the problem, but they can help. You could eat less meat or dairy. You could
avoid flying. The concept of a carbon footprint isn’t without merit. It is a good teaching tool to
make people aware of the impacts of their actions.</p>

<p>Even though the cost of an email is small, that doesn’t mean that you can send as many of them as
you want. People send more emails today than they would’ve ever sent traditional mail, and so the
carbon saved from winding down the post service is more than compensated for by the additional
emissions from email.</p>

<p>You won’t save the planet by deleting emails, but sending fewer emails is still a good goal to have,
just as any small improvement can be, as long as we don’t forget the big picture. Try to unsubscribe
from all of those marketing emails. Design your web apps to not triple confirm every single thing
that happens. And if you’re discussing details that need some figuring out, maybe don’t send a
follow-up email every day. Phil, if you’re reading this, stop it. You’re stressing me out.</p>

<hr />

<p><em>During the writing of this article I reached out to mr. Berners-Lee for comment, whose assistant
thanked me for getting in touch, but declined to comment due to time constraints.</em></p>

<hr />

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <p>Cover image “Wind mills” by
<a href="https://pixabay.com/photos/wind-mills-energy-clouds-power-6928590/">Enrique</a>, licensed under the
Pixabay license.</p>

  <p>Much of the information in this article came from <strong>How Bad Are Bananas? The Carbon Footprint of
Everything</strong>, by Mike Berners-Lee, revised 2020 edition. Originally published in 2020 by Profile
Books Ltd. ISBN 978-1788163811. Sections of the book have been quoted with permission.</p>

  <p>XKCD #386 <a href="https://xkcd.com/386/">Duty calls</a> by Randall Munroe, licensed under the <a href="https://creativecommons.org/licenses/by-nc/2.5/">Creative
Commons Attribution-NonCommercial 2.5 License</a>.</p>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:email-size">
      <p>This is the on-disk size, so every email is rounded up to the nearest full page
(4096B) of data. <a href="#fnref:email-size" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:alive">
      <p>At the time of writing, there were 8,122,973,678 people alive according to
<a href="https://www.worldometers.info/world-population/">Worldmeters.com</a>. Parents will have to
compensate for their infants, who might not have ten emails to delete yet. <a href="#fnref:alive" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:bikeprint">
      <p>Generously assuming 120g/km based on “Cycling a mile”, How Bad Are Bananas p. 33, and
my flexitarian diet. <a href="#fnref:bikeprint" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:ukprint">
      <p>“How can I cut my footprint?”, p. 196, How Bad Are Bananas. <a href="#fnref:ukprint" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:search">
      <p>0.5g to 8.2g depending on specifics, “A Google Search”, p. 18, How Bad Are Bananas <a href="#fnref:search" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="fact-check" /><summary type="html"><![CDATA[A while ago I saw a post on LinkedIn that piqued my interest, not because it was any good, but because it was impressively wrong. It claimed that, to quote, “if every email user deleted just 10 emails, it would save enough electricity to power millions of households each year”. This is not only wrong, it is obviously wrong. In this post, I’d like to dive into why it’s wrong, how one might come to think it’s right, and perhaps what better message you could put out there to save the planet.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2024/emails/cover.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2024/emails/cover.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Advent of Code 2023: Let it snow</title><link href="https://bertptrs.nl/2024/01/02/advent-of-code-2023-let-it-snow.html" rel="alternate" type="text/html" title="Advent of Code 2023: Let it snow" /><published>2024-01-02T23:25:00+01:00</published><updated>2024-01-02T23:25:00+01:00</updated><id>https://bertptrs.nl/2024/01/02/advent-of-code-2023-let-it-snow</id><content type="html" xml:base="https://bertptrs.nl/2024/01/02/advent-of-code-2023-let-it-snow.html"><![CDATA[<p>For the ninth December in a row, I’m playing with Advent of Code. <a href="https://adventofcode.com/">Advent of Code</a> is a series of 50
puzzles published by Eric Wastl, where you try to solve Christmas from some far-fetched horror.
Every day from December 1st to December 25th, two puzzles become available, but the second is
revealed only after you provide the answer to the first. In this post I will go over how you can
solve them, and hopefully some interesting concepts along the way.</p>

<p>In this year’s story, there’s a problem with the amount of snow around the world<sup id="fnref:climate"><a href="#fn:climate" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> which
(while <a href="/2016/02/02/advent-of-code-in-review.html">a problem you’ve solved before</a>) requires drastic measures such as strapping
you into a trebuchet and sending you into the sky, and into a set of puzzles.</p>

<p>As a bonus challenge, I tried to make the program that solves all of these problems run in less than
the time a Python interpreter to start up and do nothing. On my questionable laptop, this takes 37
milliseconds. I did not succeed in this; after day 16 the budget was more than spent, and I gave up
on hyper-optimizing. I’ll include a few tips that I used below for reference.</p>

<h2 id="the-puzzles">The puzzles</h2>

<h3 id="day-6-joe-speedboat"><a href="https://adventofcode.com/2023/day/6">Day 6</a>: Joe Speedboat</h3>

<p>You’re given a few toy boats, where you can press a button to let them gain speed, and when you
release the button the boat travels at that speed <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow><annotation encoding="application/x-tex">v</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span></span></span> proportional to how long you held the
button, which needs to cover some known distance <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span> within a given time <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span></span></span></span>. In other words,
we’re looking for all the values <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo>:</mo><mi>v</mi><mo stretchy="false">(</mo><mi>t</mi><mo>−</mo><mi>v</mi><mo stretchy="false">)</mo><mo>&gt;</mo><mi>d</mi></mrow><annotation encoding="application/x-tex">v: v (t - v) &gt; d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span>.</p>

<p>The values given are disappointingly small so you <em>can</em> just brute force all possible <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mo>&lt;</mo><mi>v</mi><mo>&lt;</mo><mi>t</mi></mrow><annotation encoding="application/x-tex">0 &lt; v &lt; t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6835em;vertical-align:-0.0391em;"></span><span class="mord">0</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&lt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span></span></span></span>,
there’s room to be smart here. The solution that I should’ve thought of first is to realize that
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><msup><mi>v</mi><mn>2</mn></msup><mo>+</mo><mi>v</mi><mi>t</mi><mo>&gt;</mo><mi>d</mi></mrow><annotation encoding="application/x-tex">-v^2 + vt &gt; d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8974em;vertical-align:-0.0833em;"></span><span class="mord">−</span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6542em;vertical-align:-0.0391em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span> is a simple quadratic equation that you can just solve using the quadratic
formula. This is also the fastest way I know to do this, and I’ve revised my initial solution to
reflect this. However, I’ve been doing too much Leetcode lately and I’m not thinking of maths, I’m
thinking of algorithmic tricks.</p>

<p>This is a problem that can also be solved using <a href="https://en.wikipedia.org/wiki/Binary_search_algorithm">binary search</a> to find the minimum value that goes
further than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span>. You can trivially prove that the distance covered is maximized when you spend
half the allotted time holding the button, and use that as your upper bound. You can set your lower
bound at 0 or 1, whichever you prefer.</p>

<p>Using these bounds, you can check in the middle between the two bounds. If that speed makes it
further than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span></span></span></span>, it is your new upper bound. On the other hand, if it doesn’t, your lower bound
is the middle you tried, plus one, since you know the middle doesn’t work. You can repeat this until
the lower bound and the upper bound are equal.</p>

<p>What surprised me is that for sufficiently small numbers, as present in part 1 of the puzzle, this
algorithm is very nearly as fast as solving the quadratic equation directly. My only guess is that
the square root needs to do a couple of rounds of the Newton-Raphson-method, or whatever estimate my
CPU is using to compute roots. For larger numbers, the quadratic formula still decisively wins.</p>

<h3 id="day-8-spooky-scary-camel-paths"><a href="https://adventofcode.com/2023/day/8">Day 8</a>: Spooky scary camel paths</h3>

<p>In today’s puzzle, we are stranded in a desert, and you have to follow a set of instructions telling
you to go left or right, along a map that tells you where you end up as you go left or right at a
given node. For the first part, there is no actual pathfinding involved; you only need to follow the
instructions and accept that the map doesn’t make sense in Euclidean space. The only “smart” thing I
managed to do here was to interpret all node names as base 26 numbers, alleviating the need for
expensive hash maps. More on that later.</p>

<p>For the second part, our problem gets more interesting. We don’t start at node <code class="language-plaintext highlighter-rouge">AAA</code>, we start
simultaneously at all nodes that end in <code class="language-plaintext highlighter-rouge">A</code>, and we’re only done once all of our parallel realities
arrive at a node that ends in <code class="language-plaintext highlighter-rouge">Z</code> at the same time. Of course, you can try to simulate it all, but
for my input, it requires <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>≈</mo><mn>1</mn><msup><mn>0</mn><mn>13</mn></msup></mrow><annotation encoding="application/x-tex">\approx 10^{13}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4831em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">13</span></span></span></span></span></span></span></span></span></span></span></span> steps, which with my implementation takes
approximately forever.</p>

<p>Our only way forward then must be to find a pattern of when the camels arrive at “end” nodes, then
use the Chinese Remainder Theorem<sup id="fnref:crt"><a href="#fn:crt" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> to figure out the period of the whole cycle and determine
when things align. There are various ways in which this can be made harder; there could be several
end nodes on every cycle, which would blow the problem up.</p>

<figure>

  <p class="figure-img"><img src="/assets/2024/aoc-2023/camels-small.webp" alt="Visualisation of the paths the individual camels travel" /></p>

  <figcaption class="figure-caption">

    <p>Paths that the ghosts travel. There are multiple edges between the same nodes, as it matters for the
cycle to exist where in the left/right instructions list you are. The vector version of this image
can be found as <a href="https://gist.github.com/bertptrs/4b9fce293e3c84c54df92ba778e7baf4">a 19MB SVG on Github Gist</a> as it’s too large to be on this blog.</p>

  </figcaption>

</figure>

<p>It turns out the input is not only not evil, it’s kind. There is only one end node in every loop,
and the period of each loop is equal to the offset (the time it takes to reach the first end state).
Because of that, our final answer is simply the lowest common multiple of all individual offsets.
This is hard to see in the visualizations, but you can already see that for the most part, the path
is a line around a circle without random jumps.</p>

<p>We can <a href="/assets/2024/aoc-2023/camels-simplified.svg">simplify the graph above</a> a bit by removing duplicate edges. This
reveals the paths for what they truly are: two circles, where you “randomly” switch between the
inner and outer circles. At the end of one of the circles is your exit node, and then you start
right back in, roughly at the same place your starting node put you. You can see that in the insert
below.</p>

<figure>

  <p class="figure-img"><img src="/assets/2024/aoc-2023/camels-inset.svg" alt="Inset of simplified paths that highlights the start and end of a loop" /></p>

  <figcaption class="figure-caption">

    <p>Highlight of the cycle starting in <code class="language-plaintext highlighter-rouge">JVA</code> and ending in <code class="language-plaintext highlighter-rouge">CJZ</code>. Note how <code class="language-plaintext highlighter-rouge">JVA</code> joins the loop
immediately after <code class="language-plaintext highlighter-rouge">CJZ</code>, and also how the branching differs before and after the end node. Before
<code class="language-plaintext highlighter-rouge">CJZ</code>, the branching only goes away from the path with an end node, whereas afterwards, it is once
again strongly interconnected</p>

  </figcaption>

</figure>

<p>In the making of these visualizations, I managed to crash both Graphviz and Imagemagick, and I
figured out that Graphviz does in fact have a line length limit. Due to that, making this
explanation turned out to be harder than solving the actual problem. Oh well.</p>

<h3 id="day-14-rolling-stones"><a href="https://adventofcode.com/2023/day/14">Day 14</a>: Rolling stones</h3>

<p>Today we are simulating a bunch of rolling rocks lying on top of a mirror. We tilt the mirror in one
of the cardinal directions and then observe the rolling rocks <code class="language-plaintext highlighter-rouge">O</code> slide into either the edges or
into the stationary rocks <code class="language-plaintext highlighter-rouge">#</code>.</p>

<p>The first part of the puzzle verifies that you’ve understood the mechanics of the rolling boulders,
and then the second part asks you to do this a ridiculous number of times. Luckily, at some point,
the system will start to repeat a pattern and you can take a shortcut. You can use any cycle-finding
algorithm to do so (I prefer a modified version of <a href="https://en.wikipedia.org/wiki/Cycle_detection#Brent's_algorithm">Brent’s Algorithm</a>) to find this shortcut, and
then simulate a much smaller number of iterations.</p>

<p>For me, this was the obvious solution, but I’ve been spoiled by 9 years of Advent of Code and other
competitive programming interests. I was not going to write about this puzzle in detail, but a
colleague who is not as familiar with all that asked me how he was supposed to know that it would
repeat, and I didn’t have a clear answer for that. It <em>feels</em> like it should repeat, and even rather
quickly, and while I cannot pinpoint why, I can show my clues:</p>

<ul>
  <li>The number of rolling rocks doesn’t change</li>
  <li>The rocks will (after one cycle) always end up somewhere close to the bottom-left corner</li>
  <li>There is a limited amount of space to put the boulders there, and therefore a limited number of
configurations.</li>
</ul>

<p>That same colleague asked me where that intuition came from. For me, it came from a childhood toy.
The very same toy that caused me to fear the second part today after I was reading part one. Instead
of “what happens when you shake this thing a bunch of times” which is what we got, I feared the
question: how do you have to shake the plate to end up with all the stones in one particular corner?
Luckily it wasn’t that, and the toy below was not necessary to solve today’s puzzle.</p>

<figure>

  <p class="figure-img"><img src="/assets/2024/aoc-2023/labyrinth.webp" alt="Wooden ball labyrinth" /></p>

  <figcaption class="figure-caption">

    <p>Childhood toys turned into programmers’ nightmares. Special thanks to Pixabay user
<a href="https://pixabay.com/photos/labyrinth-wood-to-play-bullet-red-1738044/"><strong>Alexas_photos</strong></a> for uploading the only royalty-free image of this thing I could find.
I hope your animal protection work is going well.</p>

  </figcaption>

</figure>

<h3 id="day-24-linear-algebra-101"><a href="https://adventofcode.com/2023/day/24">Day 24</a>: Linear Algebra 101</h3>

<p>Welcome to the lecture, please take a seat. We are inside a hail storm, and we want to know if and
where the hailstones collide. Every hailstone <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>h</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">h_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> is defined by its three-dimensional current
position <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">d_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> and velocity <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>v</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">v_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.5806em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> vectors.</p>

<h4 id="part-one">Part one</h4>

<p>For the first half of the problem, we want to know which hailstones <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>h</mi><mi>i</mi></msub><mo separator="true">,</mo><msub><mi>h</mi><mi>j</mi></msub></mrow><annotation encoding="application/x-tex">h_i, h_j</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord"><span class="mord mathnormal">h</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span> collide with each
other in just the first two dimensions, and whether that happens in some limited bounding box. The
first idea I came up with was to start with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><msub><mi>d</mi><mi>i</mi></msub><mo>⃗</mo></mover><mo>+</mo><mi>t</mi><mo>⋅</mo><mover accent="true"><msub><mi>v</mi><mi>i</mi></msub><mo>⃗</mo></mover><mo>=</mo><msub><mi>d</mi><mi>j</mi></msub><mo>+</mo><mi>u</mi><mo>⋅</mo><mover accent="true"><msub><mi>v</mi><mi>j</mi></msub><mo>⃗</mo></mover></mrow><annotation encoding="application/x-tex">\vec{d_i} + t \cdot \vec{v_i} = d_j + u \cdot
\vec{v_j}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1274em;vertical-align:-0.15em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2355em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.864em;vertical-align:-0.15em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2355em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4445em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.0001em;vertical-align:-0.2861em;"></span><span class="mord accent"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2355em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span>. Since we’re talking about two dimensions, this gives us the following <a href="https://en.wikipedia.org/wiki/System_of_linear_equations">system of linear
equations</a>:</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>d</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>+</mo><mi>t</mi><mo>⋅</mo><msub><mi>v</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>=</mo><msub><mi>d</mi><mrow><mi>j</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>+</mo><mi>u</mi><mo>⋅</mo><msub><mi>v</mi><mrow><mi>j</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub><mspace linebreak="newline"></mspace><msub><mi>d</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>+</mo><mi>t</mi><mo>⋅</mo><msub><mi>v</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>=</mo><msub><mi>d</mi><mrow><mi>j</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>+</mo><mi>u</mi><mo>⋅</mo><msub><mi>v</mi><mrow><mi>j</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub><mspace linebreak="newline"></mspace></mrow><annotation encoding="application/x-tex">d_{i, x} + t \cdot v_{i, x} = d_{j, x} + u \cdot v_{j, x} \\
d_{i, y} + t \cdot v_{i, y} = d_{j, y} + u \cdot v_{j, y} \\</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4445em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span><span class="mspace newline"></span><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.9805em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4445em;"></span><span class="mord mathnormal">u</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7167em;vertical-align:-0.2861em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span><span class="mspace newline"></span></span></span></span>

<p>This is a linear system of two equations with two unknowns, so this has either zero solutions or one
solution<sup id="fnref:linalg"><a href="#fn:linalg" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> in most meaningful cases, or infinitely many if the initial position and
velocities happen to be equal. You can solve this with various techniques and I tried implementing
the substitution method as it allows you to work in integers without losing precision.
Unfortunately, this is hard and I got demotivated.</p>

<p>Luckily the puzzle is not as I’d feared, and our bounding is small enough to fit within the
contiguous integer region of a double-precision float<sup id="fnref:floats"><a href="#fn:floats" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> so you can solve the problem with
them just fine. This allows you to rewrite the equations into the form of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi><mo>=</mo><msub><mi>a</mi><mi>i</mi></msub><mo>⋅</mo><mi>x</mi><mo>+</mo><msub><mi>b</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">y = a_i \cdot x + b_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.5945em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>
by computing the slope<sup id="fnref:slope"><a href="#fn:slope" class="footnote" rel="footnote" role="doc-noteref">5</a></sup> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi></mrow><annotation encoding="application/x-tex">a</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">a</span></span></span></span> from the velocity vector and using this instead. When we set
these equal as in the equation below, we only have one variable, which is trivial to solve. You can
then work backwards to discover whether <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6151em;"></span><span class="mord mathnormal">t</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow><annotation encoding="application/x-tex">u</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">u</span></span></span></span> are indeed in the future.</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msub><mi>a</mi><mi>i</mi></msub><mo>⋅</mo><mi>x</mi><mo>+</mo><msub><mi>b</mi><mi>i</mi></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msub><mi>a</mi><mi>j</mi></msub><mo>⋅</mo><mi>x</mi><mo>+</mo><msub><mi>b</mi><mi>j</mi></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mo stretchy="false">(</mo><msub><mi>a</mi><mi>i</mi></msub><mo>−</mo><msub><mi>a</mi><mi>j</mi></msub><mo stretchy="false">)</mo><mi>x</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msub><mi>b</mi><mi>j</mi></msub><mo>−</mo><msub><mi>b</mi><mi>i</mi></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mi>x</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mfrac><mrow><msub><mi>b</mi><mi>j</mi></msub><mo>−</mo><msub><mi>b</mi><mi>i</mi></msub></mrow><mrow><msub><mi>a</mi><mi>i</mi></msub><mo>−</mo><msub><mi>a</mi><mi>j</mi></msub></mrow></mfrac></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align*}
a_i \cdot x + b_i &amp;= a_j \cdot x + b_j \\
(a_i - a_j) x &amp;= b_j - b_i \\
x &amp;= \frac{b_j - b_i}{a_i - a_j} \\
\end{align*}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:5.6435em;vertical-align:-2.5718em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.0718em;"><span style="top:-5.6032em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.1032em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mord mathnormal">x</span></span></span><span style="top:-2.0718em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord mathnormal">x</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.5718em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.0718em;"><span style="top:-5.6032em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.1032em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.0718em;"><span class="pstrut" style="height:3.3714em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.3714em;"><span style="top:-2.314em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">a</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.677em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight" style="margin-right:0.05724em;">j</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">b</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.9721em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.5718em;"><span></span></span></span></span></span></span></span></span></span></span></span>

<h4 id="part-two">Part two</h4>

<p>Unfortunately, the second half is not nearly as easy. None of the hailstones seem to collide when
looking at all three dimensions. Instead, we’ll throw a rock into the storm at some known speed and
make it hit every hailstone. This already gives us six unknowns: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span></span></span></span>, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow><annotation encoding="application/x-tex">z</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal" style="margin-right:0.04398em;">z</span></span></span></span> for both
our velocity and starting position.</p>

<p>At our disposal is a rather infinite supply of equations: each individual hailstone. But we only
need three. This gives us three additional unknowns (the time of impact for each of the stones) but
also nine equations, one for every dimension of every hailstone.</p>

<p>This number makes intuitive sense as well for me. When you consider only 1 hailstone, you can hit it
from basically any direction at any speed. With two hailstones, there are still a lot of options
that work out, and they form a kind of plane. This still makes sense, since you have two unbound
variables in this case, which do indeed form a plane. Only with three hailstones, there is one path
that uniquely intersects all of them. You can of course add more hailstones, but they will only
complicate your system of equations.</p>

<p>It turns out you don’t need nine equations, you can make it work with six. Remember, we’re solving
the following equations:</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mtable rowspacing="0.25em" columnalign="right left" columnspacing="0em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mi mathvariant="normal">∀</mi><mi>i</mi><mo>:</mo><msub><mi>t</mi><mi>i</mi></msub><mo>⋅</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mi>i</mi></msub><mo>+</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mi>i</mi></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msub><mi>t</mi><mi>i</mi></msub><mo>⋅</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo>+</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><msub><mi>t</mi><mi>i</mi></msub><mo stretchy="false">(</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mi>i</mi></msub><mo>−</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mi>i</mi></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mo stretchy="false">(</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mi>i</mi></msub><mo>−</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo stretchy="false">)</mo><mo>×</mo><mo stretchy="false">(</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mi>i</mi></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="true"><mrow><mrow></mrow><mo>=</mo><mn>0</mn></mrow></mstyle></mtd></mtr></mtable><annotation encoding="application/x-tex">\begin{align*}
\forall i : t_{i} \cdot \vec{v}_i + \vec{d}_i &amp; = t_{i} \cdot \vec{v}_{rock} + \vec{d}_{rock} \\
t_{i} (\vec{v}_i - \vec{v}_{rock}) &amp; = \vec{d}_{rock} - \vec{d}_i \\
(\vec{v}_i - \vec{v}_{rock}) × (\vec{d}_{rock} - \vec{d}_i) &amp; = 0 \\
\end{align*}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:4.9123em;vertical-align:-2.2062em;"></span><span class="mord"><span class="mtable"><span class="col-align-r"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.7062em;"><span style="top:-4.7287em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">∀</span><span class="mord mathnormal">i</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">:</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.0913em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-1.4538em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">×</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.2062em;"><span></span></span></span></span></span><span class="col-align-l"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:2.7062em;"><span style="top:-4.7287em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.0913em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.4538em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mord">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:2.2062em;"><span></span></span></span></span></span></span></span></span></span></span></span>

<p>That last step may seem weird (it gets rid of the time factor) but it’s actually a property of the
<a href="https://en.wikipedia.org/wiki/Cross_product">cross product</a> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>×</mo></mrow><annotation encoding="application/x-tex">×</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6667em;vertical-align:-0.0833em;"></span><span class="mord">×</span></span></span></span>. Two parallel vectors have a cross product of zero and since the vector
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>t</mi><mi>i</mi></msub><mo stretchy="false">(</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mi>i</mi></msub><mo>−</mo><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">t_{i} (\vec{v}_i - \vec{v}_{rock})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">i</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> is equal to the vector <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub><mo>−</mo><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\vec{d}_{rock} - \vec{d}_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1274em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:1.1274em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>, it is
also by definition parallel to it, and a scalar multiplier such as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>t</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">t_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7651em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> can be divided out
without changing the direction. This is a bilinear equation in both <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>v</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\vec{v}_{rock}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.864em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.714em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span></span><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.2077em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> and
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mover accent="true"><mi>d</mi><mo>⃗</mo></mover><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi></mrow></msub></mrow><annotation encoding="application/x-tex">\vec{d}_{rock}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1274em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord accent"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.9774em;"><span style="top:-3em;"><span class="pstrut" style="height:3em;"></span><span class="mord mathnormal">d</span></span><span style="top:-3.2634em;"><span class="pstrut" style="height:3em;"></span><span class="accent-body" style="left:-0.0688em;"><span class="overlay" style="height:0.714em;width:0.471em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.471em' height='0.714em' style='width:0.471em' viewBox='0 0 471 714' preserveAspectRatio='xMinYMin'><path d='M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z'/></svg></span></span></span></span></span></span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span>, which you can then set equal between different pairs of hailstones and solve it.</p>

<p>Anyway, since solving large systems of equations math is not my forte I enlisted the help of a
general linear algebra crate, <a href="https://docs.rs/ndarray/latest/ndarray/"><code class="language-plaintext highlighter-rouge">ndarray</code></a>. By itself, it cannot solve <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo>⋅</mo><mi>x</mi><mo>=</mo><mi>b</mi></mrow><annotation encoding="application/x-tex">A \cdot x = b</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6833em;"></span><span class="mord mathnormal">A</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.6944em;"></span><span class="mord mathnormal">b</span></span></span></span>
unfortunately, so I had to add a separate crate that can do it. The supported way appears to be
<code class="language-plaintext highlighter-rouge">ndarray-linalg</code> which wraps the classical Fortran lapack library. I briefly tried to get that to
work, but I couldn’t get it to pick up on the <code class="language-plaintext highlighter-rouge">openblas</code> libraries in my system, and then it wanted
to compile them itself. I instead found the <a href="https://github.com/rust-ml/linfa-linalg"><code class="language-plaintext highlighter-rouge">linfa-linalg</code></a> crate that offers the
same functionality in pure rust. It doesn’t appear very maintained, but I can attest that it works
perfectly fine. That just leaves the equation to solve. I put it below.</p>

<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mn>0</mn></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub></mrow></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>v</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>v</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>v</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>z</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>d</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>x</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>d</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>y</mi></mrow></msub></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><msub><mi>d</mi><mrow><mi>r</mi><mi>o</mi><mi>c</mi><mi>k</mi><mo separator="true">,</mo><mi>z</mi></mrow></msub></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mo>=</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>x</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>1</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><mo stretchy="false">)</mo><mo>−</mo><mo stretchy="false">(</mo><msub><mi>d</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>0</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo>−</mo><msub><mi>d</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>y</mi></mrow></msub><msub><mi>v</mi><mrow><mn>2</mn><mo separator="true">,</mo><mi>z</mi></mrow></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\begin{bmatrix}
    v_{1,y} - v_{0,y} &amp; v_{0,x} - v_{1,x} &amp; 0 &amp; d_{0,y} - d_{1,y} &amp; d_{1,x} - d_{0,x} &amp; 0 \\
    v_{2,y} - v_{0,y} &amp; v_{0,x} - v_{2,x} &amp; 0 &amp; d_{0,y} - d_{2,y} &amp; d_{2,x} - d_{0,x} &amp; 0 \\
    v_{1,z} - v_{0,z} &amp; 0 &amp; v_{0,x} - v_{1,x} &amp; d_{0,z} - d_{1,z} &amp; 0 &amp; d_{1,x} - d_{0,x} \\
    v_{2,z} - v_{0,z} &amp; 0 &amp; v_{0,x} - v_{2,x} &amp; d_{0,z} - d_{2,z} &amp; 0 &amp; d_{2,x} - d_{0,x} \\
    0 &amp; v_{1,z} - v_{0,z} &amp; v_{0,y} - v_{1,y} &amp; 0 &amp; d_{0,z} - d_{1,z} &amp; d_{1,y} - d_{0,y} \\
    0 &amp; v_{2,z} - v_{0,z} &amp; v_{0,y} - v_{2,y} &amp; 0 &amp; d_{0,z} - d_{2,z} &amp; d_{2,y} - d_{0,y} \\
\end{bmatrix}

\begin{bmatrix}
    v_{rock, x} \\
    v_{rock, y} \\
    v_{rock, z} \\
    d_{rock, x} \\
    d_{rock, y} \\
    d_{rock, z} \\
\end{bmatrix}
=
\begin{bmatrix}
    (d_{0,y} v_{0,x} - d_{1,y} v_{1,x}) - (d_{0,x} v_{0,y} - d_{1,x} v_{1,y}) \\
    (d_{0,y} v_{0,x} - d_{2,y} v_{2,x}) - (d_{0,x} v_{0,y} - d_{2,x} v_{2,y}) \\
    (d_{0,z} v_{0,x} - d_{1,z} v_{1,x}) - (d_{0,x} v_{0,z} - d_{1,x} v_{1,z}) \\
    (d_{0,z} v_{0,x} - d_{2,z} v_{2,x}) - (d_{0,x} v_{0,z} - d_{2,x} v_{2,z}) \\
    (d_{0,z} v_{0,y} - d_{1,z} v_{1,y}) - (d_{0,y} v_{0,z} - d_{1,y} v_{1,z}) \\
    (d_{0,z} v_{0,y} - d_{2,z} v_{2,y}) - (d_{0,y} v_{0,z} - d_{2,y} v_{2,z}) \\
\end{bmatrix}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:7.2001em;vertical-align:-3.35em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M403 1759 V84 H666 V0 H319 V1759 v3600 v1759 h347 v-84
H403z M403 1759 V0 H319 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span><span class="arraycolsep" style="width:0.5em;"></span><span class="arraycolsep" style="width:0.5em;"></span><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord">0</span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M347 1759 V0 H0 V84 H263 V1759 v3600 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M403 1759 V84 H666 V0 H319 V1759 v3600 v1759 h347 v-84
H403z M403 1759 V0 H319 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mathnormal mtight">roc</span><span class="mord mathnormal mtight" style="margin-right:0.03148em;">k</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M347 1759 V0 H0 V84 H263 V1759 v3600 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:7.2001em;vertical-align:-3.35em;"></span><span class="minner"><span class="mopen"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M403 1759 V84 H666 V0 H319 V1759 v3600 v1759 h347 v-84
H403z M403 1759 V0 H319 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span><span class="mord"><span class="mtable"><span class="col-align-c"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-6.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-4.81em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-3.61em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-2.41em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight">x</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-1.21em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">1</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span><span style="top:-0.01em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">0</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">−</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mord"><span class="mord mathnormal">d</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.03588em;">y</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0359em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span><span class="mpunct mtight">,</span><span class="mord mathnormal mtight" style="margin-right:0.04398em;">z</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2861em;"><span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span><span class="mclose"><span class="delimsizing mult"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:3.85em;"><span style="top:-5.8499em;"><span class="pstrut" style="height:9.2em;"></span><span style="width:0.667em;height:7.200em;"><svg xmlns="http://www.w3.org/2000/svg" width='0.667em' height='7.200em' viewBox='0 0 667 7200'><path d='M347 1759 V0 H0 V84 H263 V1759 v3600 v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v3600 v1759 h84z'/></svg></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:3.35em;"><span></span></span></span></span></span></span></span></span></span></span></span>

<h3 id="other-days">Other days</h3>

<p>Not every puzzle has a lot of detail to it, but I still want to share my notes for them as they
might be of use to someone else. In order:</p>

<ul>
  <li>
    <p><a href="https://adventofcode.com/2023/day/1">Day 1</a> I used the <a href="https://docs.rs/aho-corasick/latest/aho_corasick/"><code class="language-plaintext highlighter-rouge">aho-corasick</code></a> crate to detect overlapping matches, as
    regular regex tends not to find overlapping matches. You need overlapping matches to correctly
    identify the value of <code class="language-plaintext highlighter-rouge">fiveight</code> as 58 rather than 55. This is an optimal solution on paper,
    but a colleague of mine showed that since the words we’re searching for are relatively slow,
    just hardcoding the matching code from the beginning or end is even faster.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/2">Day 2</a>: You can ignore the individual grabs of marbles and just look at the largest number
    of marbles present for any particular colour. To my surprise, this still worked for part two.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/3">Day 3</a>: Part 1 was relatively straightforward, for part 2 the key insight is that you want
    to store the values that are connected to cogs per cog. Also small assumption that the numbers
    are only connected to a single cog, but this happens to be true for my input.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/4">Day 4</a>: One observation: lottery tickets are integers below 100 (since they’re at most two
    digits) so you can represent the tickets in a set as bits in a 128-bit integer. This makes
    determining winners a simple bitwise AND operation.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/5">Day 5</a>: All mappings are the same, so you can just make them the same. I initially thought
    of using a BTree for look-ups per section, but a sorted array has the same time complexity and
    better cache locality.</p>

    <p>For part two, Rust happens to be fast enough that my part 1 solution could solve it by brute
    force in under a minute, but by transforming entire ranges of seeds at once you can speed it
    up considerably.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/7">Day 7</a>: Fun puzzle, only one snag. <strong>You should read the text well.</strong> I completely missed
    that the J moves down in the priority order of the second part, which cost me an unreasonable
    amount of time.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/9">Day 9</a>: This was not too difficult, but it was fun to think about how to implement this
    efficiently. For me the, crucial observation was that you are computing the backwards
    diagonals as shown in the examples. Then after finishing the puzzle, I realized that you don’t
    have to special-case the initial values and that if you treat those as derivatives as well the
    whole system still works.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/10">Day 10</a>: Part one is not interesting, but for part two I learned something new about
    geometry. You can use a kind of ray-casting to count the number of times you cross an edge of
    a polygon, and if that’s odd, you’re on the inside. I’ve seen this called the crossing lines
    theorem but I can no longer find the sources where I got it from and now I wonder whether it
    was all a dream.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/11">Day 11</a>: Classic Advent of Code puzzle. The first half shows you a simple problem, and
    the second verifies you didn’t pick the hardest way of solving it.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/12">Day 12</a>: “Hot Springs” referring to actual metal springs is quite possibly the best pun
    in the event’s history. Eric, if you’re reading this, it’s okay that you’re not sorry. I’m
    proud. Also, the problem is a good introduction to <a href="https://en.wikipedia.org/wiki/Dynamic_programming">Dynamic Programming</a>, should you need one.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/15">Day 15</a>: The puzzle was designed to teach you about hash maps, and then make you use
    them. The only tricky part is that for part 2 you need to remember the insertion order of the
    items. In Python, the <code class="language-plaintext highlighter-rouge">dict</code> already does that, in other languages, you need to opt into it.
    The data structure you’re looking for is called the linked hash map. It combines the
    traditional hash map with a linked list going through the items, which maintains the insertion
    order.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/16">Day 16</a>: Just do it, still looking for a way to be smarter than “just try every
    individual position.”</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/17">Day 17</a>: Dijkstra and A* are still interesting algorithms. No other work is required.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/18">Day 18</a>: Carrying on from day 10, we’re looking for the area of a polygon. The crossing
    lines theorem I used back then might still work, but there’s also the shoelace formula, which
    elegantly computes the area as long as you know the coordinates of the corners.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/20">Day 20</a>: The start of the final stretch, and for me the first truly hard challenge. I
    didn’t like that part twp requires a deeper investigation of the network. I always feel coding
    specifically for my input instead of for the problem is doing it “wrong.” I made a
    visualisation to see whether the structure would make more sense. It does not.</p>
  </li>
</ul>

<figure>

  <p class="figure-img"><img src="/assets/2024/aoc-2023/network.svg" alt="Day 20 signal network" /></p>

  <figcaption class="figure-caption">

    <p>A diagram of the signal nodes and connections in day 20. Conjunctions are shown as red triangles,
and flip-flips are shown as light-blue boxes.</p>

  </figcaption>

</figure>

<ul>
  <li>
    <p><a href="https://adventofcode.com/2023/day/21">Day 21</a>: Second unreasonably hard puzzle. In the first half, you learn the magic of
    parity. Because you cannot move diagonally, and therefore cannot waste moves, you can only
    ever reach squares that are 1. reachable in fewer than 64 steps, and 2. have an even distance.</p>

    <p>Part two, I don’t quite know. For reasons that are caused by the shape of the real input but not
the sample input, there’s a repeating pattern. The number of garden plots you can reach whenever
you reach the edge of a map tile (after 65 steps, and then every 131 steps) fits a quadratic
expression. You can simulate until you reach the first three, and then extrapolate from there.
Conveniently, the number of steps you have to simulate is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>65</mn><mo>+</mo><mn>2023</mn><mo>⋅</mo><mn>131</mn></mrow><annotation encoding="application/x-tex">65 + 2023 \cdot 131</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">65</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">2023</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">131</span></span></span></span>.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/22">Day 22</a>: Still a lot of work, but in the end, it was a fun, messy simulation. Nothing is
    fundamentally hard about it. You can do a topological sort to avoid a repeated simulation in
    part two, but not doing it is still reasonably fast.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/23">Day 23</a>: It took me embarrassingly long to figure out my graph simplification code. The
    main trick I went with in the end is to distinguish between “junction I need to do something
    with” and “path I need to do something with.”</p>

    <p>The <a href="https://en.wikipedia.org/wiki/Longest_path_problem">longest path problem</a> is fundamentally NP-hard, though the slopes remove any potential
 cycles in part 1. For part 2, this is no longer true, and you get to brute force all of the
 potential routes. Takes a while, even in Rust, but it’s not hard assuming you programmed part 1
 right.</p>
  </li>
  <li>
    <p><a href="https://adventofcode.com/2023/day/25">Day 25</a>: Finally, I have relevant expertise. The puzzle tries to teach you about the
    <a href="https://en.wikipedia.org/wiki/Minimum_cut">Minimum cut</a> problem, and once upon a time, I wrote a thesis around the related maximum flow
    problem. The minimum cut is the minimum amount of (weighted) connections that need to be cut
    to be disjoint. In this case, we’re not looking for that, since we’re given the answer: three.
    Instead, we’re interested in where to cut.</p>

    <p>I tried to implement <a href="https://en.wikipedia.org/wiki/Karger%27s_algorithm">Karger’s algorithm</a> but I failed to correctly detect the cases where it
 doesn’t find the cut correctly. Instead, I opted to repeatedly find the shortest path between two
 random nodes, and count how often each edge is used. The most used edges are maybe probably the
 right ones.</p>
  </li>
</ul>

<p>And that’s all the notes I had on the puzzles.</p>

<h2 id="performance-tricks">Performance tricks</h2>

<p>During the first half of the event, I worked on over-optimizing my puzzle solutions in hopes of
solving all puzzles in less than the time it takes for a Python interpreter to start up. I got the
idea from a blog post I read a few years ago but I’m not able to find any more. Nevertheless, here
are what few tips I have based on my experience trying to optimize my runtime.</p>

<ul>
  <li>
    <p><strong>Don’t use hashmaps if you can avoid it.</strong> While your access is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.02778em;">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span>, your constant factor is
important, and Rust’s hash function is somewhat inefficient by design, for security reasons. Many
times, if you can use a large array and map your keys to integers yourself, you should.</p>
  </li>
  <li>
    <p><strong>Avoid allocations.</strong> Try to reuse heap-allocated collections by passing buffers to inner
functions. You also want to avoid nested collections, as you’ll easily start getting bottlenecked
on the allocator. On <a href="https://adventofcode.com/2023/day/9">Day 9</a> for example, I managed to reduce the number of lists in my input to
two, by storing all of the numbers in one list and then in a second list store ranges of indices
that belong to each line. Admittedly, you could implement this puzzle with streaming which might
even be faster.</p>
  </li>
  <li>
    <p><strong>Avoid large types.</strong> Rust written naturally has a bunch of “moves”, where the compiler has to
effectively <code class="language-plaintext highlighter-rouge">memcopy</code> a variable from one place to another. In most cases, the optimizer can make
this efficient by constructing things in their final location, but this does not always happen. To
help, you can instead use indirect storage. For example, instead of an <code class="language-plaintext highlighter-rouge">[u8; 1024]</code>, you can
instead use a <code class="language-plaintext highlighter-rouge">Box&lt;[u8]&gt;</code>, or even a <code class="language-plaintext highlighter-rouge">Box&lt;[u8; 1024]&gt;</code>, but do note that constructing the latter
will sometimes run into stack overflow issues.</p>
  </li>
  <li>
    <p><strong>Be mindful of your iteration order.</strong> DFS is generally better than BFS by a surprising margin,
even with an explicit stack. The improved locality of your data access is surprisingly important
for your cache hit rate. The same goes for iterating over grids; if you can mostly read the grid
in the order that it is stored in memory, it will likely be faster.</p>
  </li>
</ul>

<p>All of these tricks combined helped me achieve a total runtime of slightly under 0.4 seconds. Much
worse than I’d originally set out to, but I’m pretty content with the result.</p>

<div class="row">

  <div class="col-md">

    <figure>

      <p class="figure-img"><img src="/assets/2024/aoc-2023/cumulative-time.svg" alt="Cumulative time spent by the solution program over each day." /></p>

      <figcaption class="figure-caption">

        <p>Cumulative time spent by the solution program every day. Notice how the last few days take up most
of the run time. Target time is from a previous year’s attempts to finish in under 0.5 seconds.</p>

      </figcaption>

    </figure>

  </div>

  <div class="col-md">

    <figure>

      <p class="figure-img"><img src="/assets/2024/aoc-2023/individual-time.svg" alt="Individual time spent by the solution program over each day." /></p>

      <figcaption class="figure-caption">

        <p>Individual time spent on each part. Notice the scale is logarithmic.</p>

      </figcaption>

    </figure>

  </div>

</div>

<h2 id="in-conclusion">In conclusion</h2>

<p>This year’s edition of Advent of Code was pretty hard. According to <a href="https://www.reddit.com/r/adventofcode/comments/18qgcuh/all_years_my_totally_subjective_and_a_little_bit/">the subjective opinion of some
guy on Reddit</a>, it is the second hardest edition so far. To me, it was the first year
that I truly got burned out near the end.</p>

<p>The last five days required a lot of work to solve, either because programming them was somewhat
tedious, complicated math was involved, or because you shouldn’t try to solve the generic problem
and should look at your input. They were not fundamentally wrong, but the lack of rest days in
between was a bit much. The Reddit threads also show a pattern of “I just threw Z3 at it and it
solved it” which is the opposite of fun to me.</p>

<p>I know I’m doing the puzzles wrong by trying to over-optimize on time, but even with that in mind, I
don’t think this was particularly fun. You might also notice that I didn’t have any fun and elegant
algorithms to talk about in this review.</p>

<p>Should there be a next edition<sup id="fnref:edition"><a href="#fn:edition" class="footnote" rel="footnote" role="doc-noteref">6</a></sup> I’ll probably still participate, but in a language that is
more suitable for bodging together a quick and dirty solution. Python is always nice, though if I
find something new along the way that’s also cool.</p>

<p>Let me not discourage you; Advent of Code was still fun. It’s kept me working hard throughout the
month, and finishing my 450th star was still satisfying.</p>

<p>Happy New Year, and let’s meet back up for the puzzles in December.</p>

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <ul>
    <li><a href="https://pixabay.com/photos/snowfall-winter-snow-snowflakes-201496/">Cover image</a> by
<strong>Kristamonique</strong>, licensed under the Pixabay license</li>
    <li><a href="https://pixabay.com/photos/labyrinth-wood-to-play-bullet-red-1738044/">Wooden ball labyrinth</a> by <strong>Alexas_photos</strong>, licensed under the Pixabay license.</li>
  </ul>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:climate">
      <p>A rather realistic story, at least in the Netherlands. <a href="#fnref:climate" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:crt">
      <p>As long as the puzzles keep using it, I’m going to keep mentioning it, mostly because it’s
one of the few mathsy things I know. <a href="#fnref:crt" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:linalg">
      <p>This is a theorem that I think has a name, but I don’t know any more. Please send me an
email and I’ll correct this. <a href="#fnref:linalg" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:floats">
      <p>Floating points are roughly equivalent to scientific notation in base two, written as <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mo>⋅</mo><msup><mn>2</mn><mi>e</mi></msup></mrow><annotation encoding="application/x-tex">m
\cdot 2^e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4445em;"></span><span class="mord mathnormal">m</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6644em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6644em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">e</span></span></span></span></span></span></span></span></span></span></span> where the mantissa <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> and the exponent <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">e</span></span></span></span>. While the finer details are
irrelevant, the important bit is that, with both <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow><annotation encoding="application/x-tex">m</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">m</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">e</span></span></span></span> fixed-width integers, there’s a
limited range of values you can represent exactly. For double-precision floats, the largest
integer up until where every single integer is representable exactly happens to be
9,007,199,254,740,992, or roughly 22 times the upper limit of our bounding box. <a href="#fnref:floats" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:slope">
      <p>It is of course possible for the slope to be undefined, whenever a hailstone has an <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>
velocity of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>. This does not happen in the input, but if it did, it even simplifies the
equations, as suddenly you know the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> coordinate of the collision already. It’s where you
started. <a href="#fnref:slope" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:edition">
      <p>Why ever stop at nine when you can stop at ten? <a href="#fnref:edition" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="rust" /><category term="advent-of-code" /><summary type="html"><![CDATA[For the ninth December in a row, I’m playing with Advent of Code. Advent of Code is a series of 50 puzzles published by Eric Wastl, where you try to solve Christmas from some far-fetched horror. Every day from December 1st to December 25th, two puzzles become available, but the second is revealed only after you provide the answer to the first. In this post I will go over how you can solve them, and hopefully some interesting concepts along the way.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2024/aoc-2023/cover.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2024/aoc-2023/cover.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to host a static Next.JS website with Nginx</title><link href="https://bertptrs.nl/2023/08/17/how-to-host-a-static-next-js-website-with-nginx.html" rel="alternate" type="text/html" title="How to host a static Next.JS website with Nginx" /><published>2023-08-17T11:47:00+02:00</published><updated>2023-08-17T11:47:00+02:00</updated><id>https://bertptrs.nl/2023/08/17/how-to-host-a-static-next-js-website-with-nginx</id><content type="html" xml:base="https://bertptrs.nl/2023/08/17/how-to-host-a-static-next-js-website-with-nginx.html"><![CDATA[<p><a href="https://nextjs.org/">Next.JS</a> is a fairly nice way of building a multi-page, mostly statically rendered website
with React and making it make sense. It actually solves the problem of “what if a React app was not
a Single Page Application” pretty well, but it’s somewhat particular about how it wants to be
deployed.</p>

<p>To be precise, it wants to run as a <a href="https://nodejs.org/en">Node.js</a> process on your server. Unfortunately, I have a bit of
an aversion to running node processes on my server<sup id="fnref:node"><a href="#fn:node" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> so I wanted to avoid that. Luckily, this
is actually a somewhat supported process. Today I’d like to elaborate on the “somewhat” bit, and
hopefully help someone else looking to solve the same problem.</p>

<h2 id="creating-a-static-export">Creating a static export</h2>

<p>Let’s start with the good part: Next.JS actually has a pretty nice <a href="https://nextjs.org/docs/app/building-your-application/deploying/static-exports">static export feature</a>
that allows you to generate a static version of a website, and ship it as just a bunch of files.
This is great because it avoids running an additional web server process just to serve an ostensibly
static website.</p>

<p>In previous versions of Next.JS, the export was made through the <code class="language-plaintext highlighter-rouge">next export</code> command. You’d still
need to <code class="language-plaintext highlighter-rouge">next build</code> first, making it somewhat inconvenient. In version 13.3 however, the static
export became a configuration setting. <strong>To be clear, this article explains how to set this up for
Next.JS 13.3 and up.</strong></p>

<p>To start, we need to update our <code class="language-plaintext highlighter-rouge">next.config.js</code> file to create a static export as its build result.
This s simple enough:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="cm">/**
 * @type {import('next').NextConfig}
 */</span>
<span class="kd">const</span> <span class="nx">nextConfig</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">output</span><span class="p">:</span> <span class="dl">'</span><span class="s1">export</span><span class="dl">'</span><span class="p">,</span>
<span class="p">};</span>
 
<span class="kr">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nx">nextConfig</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>That is actually all. An <code class="language-plaintext highlighter-rouge">out</code> folder should not contain the static export of your website.</p>

<h3 id="image-optimization">Image optimization</h3>

<p>Except it might not be. If you use any images on your website at all. You will be greeted with the
following message instead:</p>

<blockquote>
  <p>Image Optimization using the default loader is not compatible with export.</p>
</blockquote>

<p>By default, Next.JS tries to serve images that are optimized for the specific resolution and quality
preferences of the user. This, in theory, saves data, but it also has the potential to slow things
down due to reducing cache hit rates and generally introduces complexity.</p>

<p>Next.JS provides <a href="https://nextjs.org/docs/pages/building-your-application/deploying/static-exports#image-optimization">support for external optimization services</a> which you can use,
but in most cases, it offers a very minor performance improvement. My recommendation is therefore to
make an appropriately sized WebP] export of your images, either lossy or lossless, and forego
optimization altogether.</p>

<p>The error message already explains how to disable image optimization, but for completeness, the final configuration file looks like this:</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="cm">/**
 * @type {import('next').NextConfig}
 */</span>
<span class="kd">const</span> <span class="nx">nextConfig</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">output</span><span class="p">:</span> <span class="dl">'</span><span class="s1">export</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">images</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">unoptimized</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">};</span>
 
<span class="kr">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="nx">nextConfig</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="other-unsupported-features">Other unsupported features</h3>

<p>Even with the issues regarding images fixed, there are still some things that don’t work. In
general, features that require configuration of the response beyond its contents won’t work, be it
headers or status code. You also cannot run server side code, because there is no server.</p>

<h2 id="configuring-nginx">Configuring Nginx</h2>

<p>With our archive of files in hand, we can now try to configure Nginx to host it. This is almost
straightforward, except that, while Next.JS likes to have URLs without file extensions, but the
export does generate files with file extensions. The file for a URL like <code class="language-plaintext highlighter-rouge">/page</code> will be
<code class="language-plaintext highlighter-rouge">/page.html</code>, and occasionally for reasons I don’t quite understand <code class="language-plaintext highlighter-rouge">/page/index.html</code>.</p>

<p>It’s possible to fix this with Nginx configuration, by using the <a href="https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files"><code class="language-plaintext highlighter-rouge">try_files</code></a> feature.
The only other thing that needs configuring is the 404 page, which is conveniently generated as
<code class="language-plaintext highlighter-rouge">404.html</code>. With that in mind, our (simplified) configuration looks as follows:<sup id="fnref:nginx"><a href="#fn:nginx" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>

    <span class="kn">root</span> <span class="n">/your/web/root/</span><span class="p">;</span>
    <span class="kn">index</span> <span class="s">index.html</span><span class="p">;</span>

    <span class="kn">error_page</span> <span class="mi">404</span> <span class="n">/404.html</span><span class="p">;</span>

    <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
        <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="nv">$uri</span><span class="n">/</span> <span class="nv">$uri</span><span class="s">.html</span> <span class="p">=</span><span class="mi">404</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we can upload the files generated before to our web root, and the website should be served the
way we expect it to.</p>

<h2 id="in-conclusion">In conclusion</h2>

<p>While it’s a shame that the default way to host Next.JS is a dedicated Node process, it’s pleasantly
simple to statically host it statically on a shared web server. Not just that, but ever since I
started using the feature in January of this year, the list of unsupported features has steadily
shrunk.</p>

<p>As of now, it seems that most features that could reasonably be supported by some static files is
actually supported. Things that require setting custom headers, status codes, or even doing server
side work obviously isn’t there, but if your website is actually static, most things will actually
work.</p>

<p>So with that in mind, I encourage anyone to try to ditch their Node.js servers and run everything
through a nice and boring Nginx server.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:node">
      <p>You might even say I have an aversion to running javascript anywhere, but this very website
and the article you’re reading would prove otherwise. We could still use less of it. <a href="#fnref:node" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:nginx">
      <p>The export documentation for Next.JS currently has a sample Nginx configuration very much
like the one listed. I am still adding mine as <a href="https://web.archive.org/web/20230103014908/https://nextjs.org/docs/advanced-features/static-html-export">the original documentation I worked from back in
January</a> didn’t have one and I think my simplified version is better. <a href="#fnref:nginx" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="[&quot;tutorial&quot;]" /><summary type="html"><![CDATA[Next.JS is a fairly nice way of building a multi-page, mostly statically rendered website with React and making it make sense. It actually solves the problem of “what if a React app was not a Single Page Application” pretty well, but it’s somewhat particular about how it wants to be deployed.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2023/static.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2023/static.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How does async Rust work</title><link href="https://bertptrs.nl/2023/04/27/how-does-async-rust-work.html" rel="alternate" type="text/html" title="How does async Rust work" /><published>2023-04-27T11:10:00+02:00</published><updated>2023-04-27T11:10:00+02:00</updated><id>https://bertptrs.nl/2023/04/27/how-does-async-rust-work</id><content type="html" xml:base="https://bertptrs.nl/2023/04/27/how-does-async-rust-work.html"><![CDATA[<p>Rust has a burgeoning async system. If your application is heavy on IO, you should simply “use
async” and everything will work efficiently. You can have <code class="language-plaintext highlighter-rouge">async fn</code>, <code class="language-plaintext highlighter-rouge">.await</code> whenever that could
be worked on in the background while the CPU does something useful. Then you learn to add
<a href="https://tokio.rs/">Tokio</a> for it to do anything and things may seem like magic. Fortunately, computers do not
work by magic yet, so we can try to simplify things and get a better understanding. Today I want to
do just that.</p>

<p>The very quick version is that asynchronous functions in Rust are just syntactical sugar for regular
functions, but instead of directly returning the value it gives you a complex <a href="https://en.wikipedia.org/wiki/Finite-state_machine">state machine</a> that
implements the <code class="language-plaintext highlighter-rouge">Future</code> trait.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="c1">// The following function</span>
<span class="k">async</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="c1">// …</span>
<span class="p">}</span>

<span class="c1">// Actually desugars to</span>
<span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">impl</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="nb">i32</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// …</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The compiler then generates an appropriate type and implementation. That is helpful, but it’s still
overly complicated. I will focus on what I think are the two most important components, the
<a href="#the-future-trait"><code class="language-plaintext highlighter-rouge">Future</code> trait</a> and the <a href="#the-executor">executor</a>, and finally show my minimal
executor implementation that can be used to run your async code. Let’s start with the trait first.</p>

<h2 id="the-future-trait">The <code class="language-plaintext highlighter-rouge">Future</code> trait</h2>

<p>The <a href="https://doc.rust-lang.org/std/future/trait.Future.html"><code class="language-plaintext highlighter-rouge">Future</code></a> trait, while fundamental to how async Rust works, is a rather simple one.
It’s so simple, here it is in its entirety:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">pub</span> <span class="k">trait</span> <span class="n">Future</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">poll</span><span class="p">(</span><span class="k">self</span><span class="p">:</span> <span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Poll</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Output</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Output</code> associated type denotes what type the future will eventually return, and the <code class="language-plaintext highlighter-rouge">poll</code>
method will check whether the asynchronous process is complete yet and will return either
<code class="language-plaintext highlighter-rouge">Poll::Ready(Self::Output)</code> if it was complete or <code class="language-plaintext highlighter-rouge">Poll::Pending</code> if it’s not. That <code class="language-plaintext highlighter-rouge">Pin&lt;&amp;mut Self&gt;</code>
has complicated reasons for its existence that we will largely ignore in this article.</p>

<p>Crucially, a Future by itself does nothing. It should return quickly with either a <code class="language-plaintext highlighter-rouge">Poll::Pending</code>
or <code class="language-plaintext highlighter-rouge">Poll::Ready</code> so that the program can either continue to check other futures or go back to sleep.
The actual <strong>work</strong> of the future should happen elsewhere.</p>

<p>The only other interesting thing about the <code class="language-plaintext highlighter-rouge">poll</code> method is that it takes a <code class="language-plaintext highlighter-rouge">Context</code> method. The
context one purpose as of the time of writing: to provide you with (a reference to) a <code class="language-plaintext highlighter-rouge">Waker</code> that
can later wake the Executor. We will talk about that next.</p>

<h3 id="an-example-future">An example <code class="language-plaintext highlighter-rouge">Future</code></h3>

<p>So async functions get turned into <code class="language-plaintext highlighter-rouge">impl Future</code> types, but something has to be async in the end. To
illustrate this, let’s look at a simple future that returns nothing, but spawns a thread and
completes when the thread it spawned is done.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="rouge-code"><pre><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nb">Arc</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">sync</span><span class="p">::</span><span class="nn">atomic</span><span class="p">::{</span><span class="n">AtomicBool</span><span class="p">,</span> <span class="n">Ordering</span><span class="p">};</span>

<span class="k">struct</span> <span class="n">ThreadedFuture</span> <span class="p">{</span>
    <span class="n">started</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span>
    <span class="n">completed</span><span class="p">:</span> <span class="nb">Arc</span><span class="o">&lt;</span><span class="n">AtomicBool</span><span class="o">&gt;</span>
<span class="p">};</span>

<span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">future</span><span class="p">::</span><span class="n">Future</span> <span class="k">for</span> <span class="n">ThreadedFuture</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="p">();</span>

    <span class="k">fn</span> <span class="nf">poll</span><span class="p">(</span><span class="k">mut</span> <span class="k">self</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">pin</span><span class="p">::</span><span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="k">Self</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">cx</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Context</span><span class="o">&lt;</span><span class="nv">'_</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Poll</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">::</span><span class="n">Output</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// 1. Check if the completion was already done</span>
        <span class="k">if</span> <span class="o">!</span><span class="k">self</span><span class="py">.completed</span><span class="nf">.load</span><span class="p">(</span><span class="nn">Ordering</span><span class="p">::</span><span class="n">Acquire</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="o">!</span><span class="k">self</span><span class="py">.started</span> <span class="p">{</span>
                <span class="c1">// 2. If we have not completed and haven't started the thread, start a background thread.</span>
                <span class="k">self</span><span class="py">.started</span> <span class="o">=</span> <span class="k">true</span><span class="p">;</span>

                <span class="k">let</span> <span class="n">completer</span> <span class="o">=</span> <span class="nn">Arc</span><span class="p">::</span><span class="nf">clone</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="py">.completed</span><span class="p">);</span>
                <span class="k">let</span> <span class="n">waker</span> <span class="o">=</span> <span class="n">cx</span><span class="nf">.waker</span><span class="p">()</span><span class="nf">.clone</span><span class="p">();</span>

                <span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
                    <span class="c1">// 3. First mark the future as completed</span>
                    <span class="n">completer</span><span class="nf">.store</span><span class="p">(</span><span class="k">true</span><span class="p">,</span> <span class="nn">Ordering</span><span class="p">::</span><span class="n">Release</span><span class="p">);</span>
                    <span class="c1">// 4. Then wake up the executor</span>
                    <span class="n">waker</span><span class="nf">.wake</span><span class="p">();</span>
                <span class="p">});</span>
            <span class="p">}</span>

            <span class="c1">// 5. Now we've set up the async completion but haven't finished yet, so return Pending.</span>
            <span class="nn">Poll</span><span class="p">::</span><span class="n">Pending</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="c1">// 6. Already completed, so return.</span>
            <span class="nn">Poll</span><span class="p">::</span><span class="nf">Ready</span><span class="p">(())</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>There is a bunch of boilerplate here, but the basics are relatively simple:</p>

<ol>
  <li>Check if we have completed the future. If not, check if we’ve started the async computation</li>
  <li>If not, spawn a thread in the background that will eventually complete the computation</li>
  <li>In that thread, mark ourselves as completed</li>
  <li>And wake up</li>
  <li>Then return a <code class="language-plaintext highlighter-rouge">Pending</code> state</li>
  <li>Finally, return the <code class="language-plaintext highlighter-rouge">Ready</code> state if we turned out to have completed.</li>
</ol>

<h2 id="the-executor">The Executor</h2>

<p>The other piece of the async puzzle is the executor. Executors primarily need to poll futures, go to
sleep when there’s nothing to do, and provide a suitable <code class="language-plaintext highlighter-rouge">Waker</code> to be woken up with. Many
implementations, such as <code class="language-plaintext highlighter-rouge">tokio</code>, provide a ton of additional functionality, but deep down all it
needs to do is complete the following steps:</p>

<figure class="w-100">

  <p><img src="/assets/2023/async-rust/future-sequence.svg" alt="Sequence diagram for an executor and some future" /></p>

  <figcaption>

    <p>Sequence diagram for an executor executing a future.</p>

  </figcaption>

</figure>

<p>When the user provides a <code class="language-plaintext highlighter-rouge">Future</code> to the executor, the executor will start polling it until it
finally returns a value. This polling is not a busy loop; instead, the executor will wait until it
receives a wake-up.<sup id="fnref:busy"><a href="#fn:busy" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> This signal can come from any thread, or even a simple C-style signal
handler, and will generally be specific to the asynchronous process underpinning the operation.
Alternatively, in flow chart form:</p>

<figure class="w-100">

  <p><img src="/assets/2023/async-rust/future-flowchart.svg" alt="Flow chart for executing futures" /></p>

  <figcaption>

    <p>How an executor completes a future</p>

  </figcaption>

</figure>

<p>For the implementation of the executor, it doesn’t matter where the signal comes from. As long as it
causes a wake-up, the future should be polled again. With this knowledge, we can construct a basic
executor. Except we don’t have to; in the documentation of the standard library you can find <a href="https://doc.rust-lang.org/std/task/trait.Wake.html#examples">an
intentionally somewhat buggy executor implementation</a><sup id="fnref:buggy"><a href="#fn:buggy" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> that implements just this.</p>

<p>I could write a <em>correct</em> sample executor like I did an example future, but it so happens I already
did and published it as a crate. Let’s have a look at that now.</p>

<h2 id="introducing-beul">Introducing Beul</h2>

<p>The main reason for this article is the 1.0.0 release of <a href="https://docs.rs/beul/latest/beul/">Beul</a>. Beul is a safe, minimalistic
futures executor based on the ideas above. It is so minimalistic, it is only 84 lines of (commented)
code and its entire public API looks like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">pub</span> <span class="k">fn</span> <span class="n">execute</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="k">impl</span> <span class="n">Future</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">T</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The code is relatively straightforward from there and if you’re still trying to grasp the idea of
async execution I recommend you check it out. The obvious question is then, what would one use this
for, instead of a fully featured async framework? The main reasons are writing tests of
executor-agnostic async code, and working with async libraries in largely synchronous code.</p>

<p>It is possible to nest calls to <code class="language-plaintext highlighter-rouge">beul::execute</code>, where you call some sync code from your async code
which in turn uses Beul to call async code. In many cases, it is still better for performance to not
do this, and make sure to <code class="language-plaintext highlighter-rouge">.await</code> any asynchronous function calls whenever possible.</p>

<h3 id="prior-art">Prior art</h3>

<p>Beul is hardly the first minimal futures executor. Many crates implement the same thing with similar
ideas. It seems appropriate to acknowledge the ones I am aware of, and why I think Beul might be a
better choice.</p>

<ul>
  <li>
    <p><a href="https://crates.io/crates/pollster">Pollster</a> provides largely the same implementation as this crate, with one minor use of unsafe
code that can be removed as of Rust 1.68. It uses an extension trait to provide its blocking
functionality. It has recently also gained a set of (optional) procedural macros that allow you to
annotate async <code class="language-plaintext highlighter-rouge">main()</code> or tests in a way where they can be executed normally. Beul is slightly
more minimal and uses dynamic dispatch to reduce code size.</p>
  </li>
  <li>
    <p><a href="https://crates.io/crates/futures-executor">futures-executor</a> provides several executors as well as utilities to make working with futures
simpler. Its <a href="https://docs.rs/futures-executor/latest/futures_executor/fn.block_on.html"><code class="language-plaintext highlighter-rouge">block_on</code></a> executor is similar to Beul’s API, though calls to it can
intentionally not be nested. It’s also quite a large crate.</p>
  </li>
  <li>
    <p><a href="https://crates.io/crates/extreme">extreme</a> has the same API as Beul but predates the <code class="language-plaintext highlighter-rouge">Wake</code> trait and as such has to use
unsafe Rust for its executor implementation. <code class="language-plaintext highlighter-rouge">extreme</code> is also licensed under the GNU Public
License which may make it unsuitable for many applications. The versioning scheme is somewhat
peculiar though fine.</p>
  </li>
  <li>
    <p><a href="https://crates.io/crates/yaar">Yet Another Async Runtime</a> (or <code class="language-plaintext highlighter-rouge">yaar</code>) and <a href="https://crates.io/crates/safina-executor">safina-executor</a> both provide more of a general
executor framework rather than simply something that executes futures. They can make sense if you
need more, but for simple future execution, Beul should suffice.</p>
  </li>
</ul>

<p>And if that doesn’t convince you, Beul extreme are the only ones among these who have moved to or
past 1.0.0, which should make the fine and sarcastic people of <a href="https://0ver.org/">ZeroVer</a> happy.</p>

<h3 id="the-future">The future</h3>

<p>With the current state of standard-library asynchronous Rust, I consider Beul “finished.” It
executes futures, it does so efficiently, and there are (as of now) no major improvements possible
to how it works.</p>

<p>Of course, that can all still change. Future Rust will surely have a more extensive async standard
library and I’d like Beul to support it. For now, this is all.</p>

<h2 id="in-conclusion">In conclusion</h2>

<p>Asynchronous Rust consists of executors that repeatedly poll futures until they are complete. When a
future is still pending, the executor will go do something else, usually sleep, before it polls the
future again. Repeat until completion.</p>

<p>With that, I hope I’ve provided an intuition for what is happening under the hood. Ideally, you will
never need to look into the details, but when the abstraction gets too leaky, now you know. Also, I
would appreciate you checking out Beul. And that’s it.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:busy">
      <p>It is valid for any implementation to <em>be</em> a busy loop, as a future should behave normally
under spurious polling. It would not be the best use of resources of course. <a href="#fnref:busy" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:buggy">
      <p>This example is slightly oversimplified and subject to both spurious wake-ups (as
<code class="language-plaintext highlighter-rouge">thread::park</code> can do that) and race conditions (since a wake-up can come in after a poll but
before the executor parks) so it is not immediately useful. <a href="#fnref:buggy" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="rust" /><summary type="html"><![CDATA[Rust has a burgeoning async system. If your application is heavy on IO, you should simply “use async” and everything will work efficiently. You can have async fn, .await whenever that could be worked on in the background while the CPU does something useful. Then you learn to add Tokio for it to do anything and things may seem like magic. Fortunately, computers do not work by magic yet, so we can try to simplify things and get a better understanding. Today I want to do just that.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2023/async-rust/cover.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2023/async-rust/cover.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Disassembling the KingFast F6 PRO</title><link href="https://bertptrs.nl/2023/02/21/disassembling-the-kingfast-f6-pro.html" rel="alternate" type="text/html" title="Disassembling the KingFast F6 PRO" /><published>2023-02-21T21:30:00+01:00</published><updated>2023-02-21T21:30:00+01:00</updated><id>https://bertptrs.nl/2023/02/21/disassembling-the-kingfast-f6-pro</id><content type="html" xml:base="https://bertptrs.nl/2023/02/21/disassembling-the-kingfast-f6-pro.html"><![CDATA[<p>This is the KingFast F6 Pro 240 GB <abbr title="Solid state drive">SSD</abbr>. Despite its name, it is neither King nor Fast. I suspect the
name might have been chosen for its similarity to Kingston’s brand. It is said the disk is so slow
using it qualifies as inhuman punishment under the UN’s <a href="https://www.un.org/en/about-us/universal-declaration-of-human-rights">Universal Declaration of Human Rights</a>. Yet
somehow a copy of them has found their way into my hands. Today, I’d like to figure out what makes it
so slow, and take you along on my journey.</p>

<h2 id="background">Background</h2>

<p>Like many techie people, I get asked for my opinion on (and preferably a solution to) my family’s
computer woes. In this case, my mother had bought a refurbished desktop PC, which after about one
year of use had become excruciatingly slow. IO would frequently pause for multiple seconds while the
disk reset. All credit to my mother here: this is a valid problem and she had correctly diagnosed it
was caused by the disk being terrible. Now that we know that, we can move on to the thing itself.</p>

<p>Looking at the case, you might notice the giant QR code on the label. The link was dead when I
started writing this article, but now points to a website displaying ads for shady apps and
“connection opportunities” so I will not link it. I can’t find for certain when the disk was a
released to the market, but <a href="https://tweakers.net/pricewatch/1122131/kingfast-f6-pro-240gb.html">Tweakers pricewatch suggests December 2017</a>. That is
suspiciously recent for a website to be this dead.</p>

<p>While erasing the disk for disposal, I found that this pattern of bad performance was pretty
reproducible: I could average around 6 MB/s write<sup id="fnref:throughput"><a href="#fn:throughput" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> speed, but this was because it
alternated between 30 MB/s bursts and multiple seconds of nothing. This behaviour seemed familiar to
me: it is roughly the performance I get from the SD cards my Raspberry Pi eats. That made me wonder
whether it was an <abbr title="Solid state drive">SSD</abbr> at all, and not just a collection of SD cards. Time to find out.</p>

<h2 id="software-robustness">Software robustness</h2>

<p>Most disks will support <a href="https://en.wikipedia.org/wiki/Self-Monitoring,_Analysis_and_Reporting_Technology">S.M.A.R.T.</a>, a technology where disks can self-assess their health and
track certain potentially harmful or ominous events happening over their lifetime. Think of the
number of bad sectors, but also simply the number of hours the device has been powered on. To see
whether this disk was actually just very dead somehow, I looked up the S.M.A.R.T. data. This started
out okay:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre>=== START OF INFORMATION SECTION ===
Device Model:     KingFast
Serial Number:    REDACTED
Firmware Version: R0831B0
User Capacity:    240,057,409,536 bytes [240 GB]
Sector Size:      512 bytes logical/physical
Rotation Rate:    Solid State Device
Form Factor:      2.5 inches
TRIM Command:     Available, deterministic, zeroed
Device is:        Not in smartctl database 7.3/5319
ATA Version is:   ACS-2 T13/2015-D revision 3
SATA Version is:  SATA 3.2, 6.0 Gb/s (current: 6.0 Gb/s)
Local Time is:    Thu Nov 10 22:05:38 2022 CET
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
</pre></td></tr></tbody></table></code></pre></div></div>

<p>But somehow accessing the health metrics does not quite work</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>Error -4 occurred at disk power-on lifetime: 0 hours (0 days + 0 hours)
  When the command that caused the error occurred, the device was active or idle.   
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The “<code class="language-plaintext highlighter-rouge">error -4</code>” in this message is not an unusual error code, it should be an index into the error
log. However, the error log is a beautiful sea of nothingness. Alright, so the error reporting
doesn’t work, and the metrics are probably rubbish. I had one more trick: S.M.A.R.T. self-tests.
Sufficiently broken drives will generally report something of use there.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>SMART Self-test log structure revision number 1
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed without error       00%      1639         -
# 2  Short offline       Completed without error       00%      1639         -
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Yet again, all clean. So let’s cast aside the software analysis and try to understand what makes it
tick on the inside.</p>

<h2 id="taking-it-apart">Taking it apart</h2>

<p>After asking the disk nicely why it was so slow failed, we might as well get it open. The case had a
kind of warranty seal on it, but it was already broken. It did prevent me from opening the case
regardless. This is a great deal for the manufacturer, as the warranty is void before I even
begin.<sup id="fnref:warranty"><a href="#fn:warranty" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<figure>

  <p><img src="/assets/2023/kingfast/seal.webp" alt="A broken yet unbroken seal" /></p>

  <figcaption>
    <p>The seal was clearly broken, but also still prevented the case from being opened.</p>
  </figcaption>

</figure>

<p>After breaking the seal with a little more conviction, we can now open the case. There are no screws
or anything; you simply slide a screwdriver in between one of the gaps on the corners and pry the
case open.</p>

<figure>

  <p><img src="/assets/2023/kingfast/to-open.webp" alt="Close up of the corner to open" /></p>

  <figcaption>
    <p>The notch on the side happens to almost exactly fit my voltage finder. Coincidence? ▲</p>
  </figcaption>

</figure>

<!-- TODO: add section opening the case -->

<p>Finally, with the case open we can start to identify the components and see what we can learn.</p>

<figure>

  <p><img src="/assets/2023/kingfast/layout.webp" alt="Layout of the chip." /></p>

  <figcaption>

    <p>Layout of the chip in the drive. Flash chips highlighted in yellow, <abbr title="Solid state drive">SSD</abbr> controller in red, and
<abbr title="Serial Advanced Technology Attachment">SATA</abbr> connectors unmissable on the left</p>

  </figcaption>

</figure>

<p>I have highlighted the different components in the picture. The first bit to talk about is the <abbr title="Solid state drive">SSD</abbr>
controller.</p>

<h3 id="ssd-controller"><abbr title="Solid state drive">SSD</abbr> controller</h3>

<p>An <a href="https://en.wikipedia.org/wiki/Flash_memory_controller"><abbr title="Solid state drive">SSD</abbr> controller</a> or flash memory controller exists because flash is actually a fickle technology
that needs some help to actually be functional. Naively implemented SSDs would have a very short
life cycle since any individual bit can only have a limited number of writes. The controller fixes
this with various techniques that are out of scope for this article. The thing to note is that they
are important and that they need to be good.</p>

<p>The controller included in the KingFast F6 Pro is a SiliconMotion SM2258XT, which as far as I can
tell is a pretty normal chip. I even found its <a href="https://www.siliconmotion.com/download/3H6/a/SM2258XT_PB_EN_201910.pdf">fact sheet</a> which is actually interesting, though it
also contains a stock photo featuring a woman more commonly known as the jealous girlfriend in the
<a href="https://en.wikipedia.org/wiki/Distracted_boyfriend">distracted boyfriend meme</a>.<sup id="fnref:memewiki"><a href="#fn:memewiki" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> This is not technically relevant, I just thought it was
funny.</p>

<p>One other interesting thing to note is that if you look up this chip, most of the image results are
from other people who’ve taken apart their KingFast F6.</p>

<h3 id="flash-memory">Flash memory</h3>

<p>So if the <abbr title="Solid state drive">SSD</abbr> controller is, generally speaking, an okay component, the actual storage might be
iffy. For that we are looking at the <a href="https://en.wikipedia.org/wiki/Flash_memory">flash memory</a>. These hold the reprogrammable NAND gates that
store the data.</p>

<p>Here I am less sure on exactly what components we have. The best lead I’ve got is the product number
<code class="language-plaintext highlighter-rouge">29F32B2ALCMG2</code>, which is <em>probably</em> made by Intel, though I cannot find a definitive source on
that. There are a lot of complaints about it (sometimes in relation with KingFast) on Chinese
internet forums and some tweets from Chinese Twitter users are not that positive either.<sup id="fnref:twitter"><a href="#fn:twitter" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> I
will refrain from linking them, but searching for the model should bring it all up.</p>

<p>That was of course before a friend recommended I ask the Electrical Engineering Stackexchange, where
someone provided <a href="https://electronics.stackexchange.com/a/654392/333100">a plausible description of the chip</a> within minutes. This suggests
that the chips hold 32 GiB of data. With the five visible chips, that would space for 160 GiB of
data. That begs the question of how this drive stores 240 GiB.</p>

<figure>

  <p><img src="/assets/2023/kingfast/back-layout.webp" alt="Layout of the back of the board." /></p>

  <figcaption>

    <p>One more flash memory hiding on the back of the board</p>

  </figcaption>

</figure>

<p>Normally, SSDs have a small bit of extra capacity, called over-provisioning, which should deal with
particular data blocks being worn out and needing to be replaced. For example, the Western Digital
NVMe <abbr title="Solid state drive">SSD</abbr> that powers my laptop has a true capacity of 512 GiB but only provides 476.9 GiB for actual
use, meaning it has an over-provisioning of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mn>512</mn><mo>−</mo><mn>476.9</mn></mrow><mn>476.9</mn></mfrac><mo>≈</mo><mn>7.4</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">\frac{512 - 476.9}{476.9} \approx 7.4\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">476.9</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">512</span><span class="mbin mtight">−</span><span class="mord mtight">476.9</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">7.4%</span></span></span></span>. This is
somewhat consistent with <a href="https://semiconductor.samsung.com/resources/white-paper/S190311-SAMSUNG-Memory-Over-Provisioning-White-paper.pdf">this paper from Samsung</a> which states they use <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>6.7</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">6.7\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">6.7%</span></span></span></span>
over-provisioning, and highlight the effects of leaving even more space.</p>

<p>Compare this to our patient. It advertises 240057409536 bytes. Curiously, this is neither a round
number in binary gigabytes (1 GiB is <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>30</mn></msup></mrow><annotation encoding="application/x-tex">2^{30}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">30</span></span></span></span></span></span></span></span></span></span></span></span> bytes) nor SI gigabytes (1 GB = <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><msup><mn>0</mn><mn>9</mn></msup></mrow><annotation encoding="application/x-tex">10^9</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">9</span></span></span></span></span></span></span></span></span></span></span> bytes).
SSDs are generally measured in the former, while spinning platters are measured in the latter
because they sound bigger that way. Instead, the drive claims to be <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>29303883</mn><mo>⋅</mo><msup><mn>2</mn><mn>13</mn></msup></mrow><annotation encoding="application/x-tex">29303883 \cdot 2^{13}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">29303883</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">13</span></span></span></span></span></span></span></span></span></span></span></span> bytes,
which you can factor into <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>13</mn></msup><mo>⋅</mo><msup><mn>3</mn><mn>3</mn></msup><mo>⋅</mo><mn>7</mn><mo>⋅</mo><mn>155047</mn></mrow><annotation encoding="application/x-tex">2^{13} \cdot 3^3 \cdot 7 \cdot 155047</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">13</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.8141em;"></span><span class="mord"><span class="mord">3</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">3</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">7</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">⋅</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.6444em;"></span><span class="mord">155047</span></span></span></span> if you really want to.</p>

<p>Weird sizing aside, the KingFast F6 Pro also advertises itself as having that exact same number of
bytes. That gives us a whopping <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">0\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8056em;vertical-align:-0.0556em;"></span><span class="mord">0%</span></span></span></span> of over-provisioning. However, assuming what we know from
the components on the board is correct, the drive doesn’t even <em>have</em> that number of bytes. Instead,
we are left with <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mn>5</mn><mo>⋅</mo><msup><mn>2</mn><mn>35</mn></msup><mo>−</mo><mn>240057409536</mn></mrow><mn>240057409536</mn></mfrac><mo>≈</mo><mo>−</mo><mn>28</mn><mi mathvariant="normal">%</mi></mrow><annotation encoding="application/x-tex">\frac{5 \cdot 2^{35} - 240057409536}{240057409536} \approx -28\%</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.3629em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:1.0179em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">240057409536</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">5</span><span class="mbin mtight">⋅</span><span class="mord mtight"><span class="mord mtight">2</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8913em;"><span style="top:-2.931em;margin-right:0.0714em;"><span class="pstrut" style="height:2.5em;"></span><span class="sizing reset-size3 size1 mtight"><span class="mord mtight"><span class="mord mtight">35</span></span></span></span></span></span></span></span></span><span class="mbin mtight">−</span><span class="mord mtight">240057409536</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:0.8333em;vertical-align:-0.0833em;"></span><span class="mord">−</span><span class="mord">28%</span></span></span></span>
over-provisioning. In other words, it wouldn’t be over-provisioned, it would be under-provisioned by
roughly <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>2</mn><mn>7</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{2}{7}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.1901em;vertical-align:-0.345em;"></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.8451em;"><span style="top:-2.655em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">7</span></span></span></span><span style="top:-3.23em;"><span class="pstrut" style="height:3em;"></span><span class="frac-line" style="border-bottom-width:0.04em;"></span></span><span style="top:-3.394em;"><span class="pstrut" style="height:3em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">2</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.345em;"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span></span></span></span> of its capacity!</p>

<h2 id="in-conclusion">In conclusion</h2>

<p>There is no real conclusion, at least not yet. Without the official data sheet for the flash memory,
we cannot be sure that the drive indeed is missing ±63 GiB of storage. The drives might be 64 GiB a
piece, which would be plenty. It would be odd to have so much extra space, but it’s possible.</p>

<p>Even if my suspicions about the missing capacity turn out to be correct, it would still be
interesting to know what kind of black magic the <abbr title="Solid state drive">SSD</abbr> controller is doing to pretend there’s more
storage than there actually is. It could be compression, it could silently drop data<sup id="fnref:dropping"><a href="#fn:dropping" class="footnote" rel="footnote" role="doc-noteref">5</a></sup> or
it could do something more complicated. My mother somehow hasn’t lost data that we noticed, but that
might just be luck combined with a helping of mostly cloud-based document storage.</p>

<p>With my (lack of) tools and expertise, this is as far as I can dig into the problem for now. Please
do get in touch if you can help me fill in some of the remaining blanks. I can say one thing for
certain, though: this is a terrible <abbr title="Solid state drive">SSD</abbr> and you should buy a different one. That’s what I did, and
my mother has been happy ever since.</p>

<footer>

  <h2 id="disclaimer">Disclaimer</h2>

  <p>I’m not really a hardware expert, so consider all of the above with a sufficiently large scoop of
salt. All measurements on the disk are done as described, with all potential problems in methodology
present. Provisioning calculations were made with potentially incorrect data, though the result
wouldn’t make sense if the chips were twice as big either, so the data is funky regardless. Also the
thing about human rights is obviously a joke.</p>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:throughput">
      <p>This is not a typo; while expected throughput for a <abbr title="Serial Advanced Technology Attachment">SATA</abbr> <abbr title="Solid state drive">SSD</abbr> should be around 50MB/s,
I was only doing about ⅛th of that. <a href="#fnref:throughput" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:warranty">
      <p>In the hypothetical scenario where I thought claiming warranty would be both an option
and a good idea. <a href="#fnref:warranty" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:memewiki">
      <p>Of course this image has a wiki article. <a href="#fnref:memewiki" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:twitter">
      <p>This all despite the fact that Twitter is apparently blocked in China. Did you know
there are apparently 10 million Chinese twitter users? <a href="#fnref:twitter" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:dropping">
      <p>This is actually <a href="https://www.vice.com/en/article/akek8e/walmart-30tb-ssd-hard-drive-scam-sd-cards">somewhat
common</a>
unfortunately. The drive will simply pretend to write data except when you use more than the
real capacity your data will simply vanish. <a href="#fnref:dropping" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><summary type="html"><![CDATA[This is the KingFast F6 Pro 240 GB SSD. Despite its name, it is neither King nor Fast. I suspect the name might have been chosen for its similarity to Kingston’s brand. It is said the disk is so slow using it qualifies as inhuman punishment under the UN’s Universal Declaration of Human Rights. Yet somehow a copy of them has found their way into my hands. Today, I’d like to figure out what makes it so slow, and take you along on my journey.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2023/kingfast/cover.webp" /><media:content medium="image" url="https://bertptrs.nl/assets/2023/kingfast/cover.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Updating a 6 year old Jekyll &amp;amp; Bootstrap website</title><link href="https://bertptrs.nl/2022/11/08/updating-a-6-year-old-jekyll-bootstrap-website.html" rel="alternate" type="text/html" title="Updating a 6 year old Jekyll &amp;amp; Bootstrap website" /><published>2022-11-08T18:53:00+01:00</published><updated>2022-11-08T18:53:00+01:00</updated><id>https://bertptrs.nl/2022/11/08/updating-a-6-year-old-jekyll-bootstrap-website</id><content type="html" xml:base="https://bertptrs.nl/2022/11/08/updating-a-6-year-old-jekyll-bootstrap-website.html"><![CDATA[<p>By no stretch of imagination am I a frontend person. Graphic design is not my passion. I even got
the colour blindness perk. I do like this little website I’ve written over the years and I kind
of want it to look nice. And while the design I had created a few years ago still works, it’s also
dependent on an outdated version of Jekyll, and it has a few technical issues. So please, follow a
long, as a DevOps engineer tries to explain how to make a nice-looking website.</p>

<p>My website is built on <a href="https://getbootstrap.com/">Bootstrap</a>. Bootstrap is very nice because it makes it much easier to get an
acceptable design. It does use a bit more javascript than I’d like to The former is nice because it
does most of the design for me, but the latter is open for debate. Also, the colour scheme could use
some work.</p>

<figure>

  <p class="figure-img"><img src="/assets/2022/redesign/before.png" alt="Before all changes" /></p>

  <figcaption class="figure-caption">
    <p>The website before I got started on upgrading</p>
  </figcaption>

</figure>

<p>So in no particular order, let’s see what I actually discovered along the way and what things turned
out to be useful.</p>

<h2 id="updating-bootstrap-46-to-52">Updating Bootstrap 4.6 to 5.2</h2>

<p>Bootstrap has both changed a lot and not changed at all, which made the upgrade somewhat tedious.
You can read the <a href="https://getbootstrap.com/docs/5.2/migration/#dependencies">Bootstrap changelog</a> for more details. For the most the most part, things were
alright. A non-exhaustive list of things that bit me is</p>

<ul>
  <li>
    <p>The official Sass compiler was changed from <code class="language-plaintext highlighter-rouge">libsass</code> to Dart Sass. You can still compile
Bootstrap with libsass, but now you will notice the bugs in the compiler that Bootstrap no longer
works around. For example, <code class="language-plaintext highlighter-rouge">libsass</code> handles negative numbers incorrectly causing them to
sometimes not be equal to themselves. It only causes minor issues like the pagination number boxes
being rounded when they shouldn’t, so if you want to avoid Dart you can still go for it.</p>
  </li>
  <li>
    <p>Many CSS classes have moved from outer element to the inner one. For example: items in the
navigation bar in 4.6 would apply the <code class="language-plaintext highlighter-rouge">.active</code> class to the outer <code class="language-plaintext highlighter-rouge">.nav-item</code> element, but
Bootstrap 5 requires you to apply it to the <code class="language-plaintext highlighter-rouge">.nav-link</code> element instead.</p>
  </li>
</ul>

<p>Actually putting this all together is not too hard. Most things actually translated quite naturally.
As part of updating the styling, I found that it was easier to remove my custom components and
replace them with modified Bootstrap components.</p>

<p>Speaking of modifications: previously this site used the <a href="https://usebootstrap.com/theme/superhero">Superhero Bootstrap theme</a> which worked
well enough but requires a bunch of scaffolding code and doesn’t really integrate into Bootrap, but
rather forces itself through. This has funny results like <code class="language-plaintext highlighter-rouge">$blue</code> not being blue at all, though most
of the time it results in redundant code. Theming is now completely done with variable overrides,
and just 30 lines of overrides (including comments and whitespace) is enough to somewhat
consistently override default colours and “work around” assumptions of contrast between them.</p>

<p>The basics for this theming can be as simple as the snippet below. The only hard thing about it was
choosing a proper color scheme. I found <a href="https://coolors.co/114b5f-456990-e4fde1-f45b69-6b2737">Coolors</a> helpful to compensate for my less-than-stellar
design skills. The linked colour scheme is not coincidentally a colour scheme I liked but didn’t
actually go for. By all means use it.</p>

<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="k">@import</span> <span class="s2">"bootstrap/functions"</span><span class="p">;</span>

<span class="c1">// Don't actually use these colors</span>
<span class="nv">$body-bg</span><span class="p">:</span> <span class="no">purple</span><span class="p">;</span>
<span class="nv">$body-color</span><span class="p">:</span> <span class="no">azure</span><span class="p">;</span>
<span class="nv">$primary</span><span class="p">:</span> <span class="no">ivory</span><span class="p">;</span>
<span class="nv">$secondary</span><span class="p">:</span> <span class="no">navy</span><span class="p">;</span>
<span class="nv">$dark</span><span class="p">:</span> <span class="no">thistle</span><span class="p">;</span>

<span class="c1">// Import the rest of Bootstrap</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="moving-the-build-to-github-actions">Moving the build to Github Actions</h2>

<p>While I was working on the the big refactor, Github <a href="https://github.com/github/pages-gem/issues/651#issuecomment-1156978042">first teased</a>, then <a href="https://github.blog/changelog/2022-07-27-github-pages-custom-github-actions-workflows-beta/">finally
announced</a> a solution to the much requested Jekyll 4 support in Github Pages. Seeing
as I’ve been waiting for this feature ever since Jekyll came out<sup id="fnref:patience"><a href="#fn:patience" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> I wasn’t really
expecting anything, but it is now possible to manually build your site using Github Actions. That
means that we can now have full control over the build process and can do anything we want.</p>

<p>The <a href="https://github.com/actions/starter-workflows/tree/main/pages">example workflows Github has provided</a> are actually very simple and you can
mostly drop them in. I had <a href="https://github.com/actions/starter-workflows/issues/1673">one issue</a> with them, which could be resolved by
removing the <code class="language-plaintext highlighter-rouge">--baseurl</code> override, but this has since been resolved. Great issue response, guys!</p>

<p>It turns out that I don’t really want all that much. The only immediate thorn in my side that I
could fix was to update the default Sass compiler. Jekyll ships with <code class="language-plaintext highlighter-rouge">sassc</code> by default, which
doesn’t handle negative numbers properly and causes a few miscompilations with Bootstrap. Luckily,
it also supports using the reference Dart Sass compiler. All that’s needed is adding the
<code class="language-plaintext highlighter-rouge">sass-embedded</code> gem and overriding the compiler preference:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1"># _config.yml</span>
<span class="na">sass</span><span class="pi">:</span>
  <span class="na">implementation</span><span class="pi">:</span> <span class="s">sass-embedded</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="replacing-the-math-renderer">Replacing the math renderer</h2>

<p>My posts occasionally contain some maths and because I have been properly taught at some point in my
life, I like to typeset that in <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>LaTeX</mtext></mrow><annotation encoding="application/x-tex">\LaTeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em;"></span><span class="mord text"><span class="mord textrm">L</span><span class="mspace" style="margin-right:-0.36em;"></span><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6833em;"><span style="top:-2.905em;"><span class="pstrut" style="height:2.7em;"></span><span class="mord"><span class="mord textrm mtight sizing reset-size6 size3">A</span></span></span></span></span></span><span class="mspace" style="margin-right:-0.15em;"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em;"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em;"><span style="top:-2.7845em;"><span class="pstrut" style="height:3em;"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em;"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em;"></span><span class="mord textrm">X</span></span></span></span></span></span>.<sup id="fnref:stockholm"><a href="#fn:stockholm" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> Regrettably, support for Latex in HTML is
still not really a thing so you need to render it one way or another.</p>

<p>Kramdown by default comes with “<a href="https://www.mathjax.org/">MathJax</a> support” but that mostly means that instances of <code class="language-plaintext highlighter-rouge">$$some
maths$$</code> are replaced with <code class="language-plaintext highlighter-rouge">\(some maths\)</code>. No actual rendering is done while building the site. To
add the actual rendering, you are required to load quite some additional JavaScript files, which
then in turn marks up the text and loads some more fonts for the true Latex look and feel. This is
kind of slow and changes the page layout, so you get that much maligned <a href="https://web.dev/optimize-cls">layout shift effect</a>.</p>

<p>You are familiar with this effect, but you might not know it was called like that. For a refresher,
try clicking the green button in the demo below.</p>

<div class="card mb-3">
  <div class="card-img-top ratio ratio-21x9">
    <iframe src="/assets/2022/redesign/cls.html"></iframe>
  </div>
  <div class="card-footer">
    <noscript>
      <div class="alert alert-warning">
        <i class="fa-solid fa-triangle-exclamation"></i> Demos use javascript. Please enable it to view the demo.
      </div>
    </noscript>
    <a class="card-link" href="/assets/2022/redesign/cls.html" target="_blank">Larger version <i class="fa-solid fa-arrow-up-right-from-square"></i></a>
  </div>
</div>

<p>All in all, I didn’t really like what MathJax did to the site. Luckily, thanks to moving to Github
Actions, I could now add my own dependencies to the build. Which brings me to <a href="https://katex.org/">KaTeX</a>. KaTeX is also
a JavaScript-based TeX renderer, but unlike MathJax, it can render the math server-side. The final
page then only requires some CSS and webfonts to render correctly. The webfonts are technically even
optional, though everything looks slightly off without them.</p>

<p>There is <a href="https://github.com/kramdown/math-katex">an official plugin for Kramdown</a> that makes it use KaTex. You can simply
add the gem to your build, and enable with by adding the following to your <code class="language-plaintext highlighter-rouge">_config.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="na">kramdown</span><span class="pi">:</span>
  <span class="na">math</span><span class="pi">:</span> <span class="s">katex</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Unfortunately that doesn’t include the KaTeX CSS yet, so we still need to add that to the site. The
recommended method appears to be to use the jsDelivr CDN, but <a href="https://httptoolkit.tech/blog/public-cdn-risks/">public CDNs aren’t that great of an
idea</a> so I didn’t want to do that. Instead I wrote my own Jekyll plugin to
automatically copy the relevant files from the KaTeX gem. This plugin can probably be done better by
someone who actually knows how to write Ruby, but it gets the job done:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
</pre></td><td class="rouge-code"><pre><span class="nb">require</span> <span class="s1">'katex'</span>
<span class="nb">require</span> <span class="s1">'find'</span>
<span class="nb">require</span> <span class="s1">'pathname'</span>

<span class="k">module</span> <span class="nn">Jekyll</span>
    <span class="k">module</span> <span class="nn">KatexBundle</span>
        <span class="k">class</span> <span class="nc">KatexResource</span> <span class="o">&lt;</span> <span class="no">Jekyll</span><span class="o">::</span><span class="no">StaticFile</span>
            <span class="k">def</span> <span class="nf">destination</span><span class="p">(</span><span class="n">dest</span><span class="p">)</span>
                <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">dest</span><span class="p">,</span> <span class="s1">'assets'</span><span class="p">,</span> <span class="s1">'katex'</span><span class="p">,</span> <span class="vi">@dir</span><span class="p">,</span> <span class="vi">@name</span><span class="p">)</span>
            <span class="k">end</span>
        <span class="k">end</span>

        <span class="k">class</span> <span class="nc">Generator</span> <span class="o">&lt;</span> <span class="no">Jekyll</span><span class="o">::</span><span class="no">Generator</span>
            <span class="k">def</span> <span class="nf">generate</span><span class="p">(</span><span class="n">site</span><span class="p">)</span>
                <span class="vi">@site</span> <span class="o">=</span> <span class="n">site</span>

                <span class="vi">@site</span><span class="p">.</span><span class="nf">static_files</span> <span class="o">&lt;&lt;</span> <span class="n">css_file</span>
                <span class="vi">@site</span><span class="p">.</span><span class="nf">static_files</span><span class="p">.</span><span class="nf">concat</span> <span class="n">font_files</span>
            <span class="k">end</span>

            <span class="kp">private</span>

            <span class="k">def</span> <span class="nf">css_file</span><span class="p">()</span>
                <span class="n">path</span> <span class="o">=</span> <span class="no">Pathname</span><span class="p">.</span><span class="nf">new</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="no">Katex</span><span class="p">.</span><span class="nf">gem_path</span><span class="p">,</span> <span class="s1">'vendor'</span><span class="p">,</span> <span class="s1">'katex'</span><span class="p">,</span> <span class="s1">'stylesheets'</span><span class="p">,</span> <span class="s1">'katex.css'</span><span class="p">)</span>

                <span class="n">source</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="nf">dirname</span><span class="p">()</span>
                <span class="n">file</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="nf">basename</span><span class="p">()</span>

                <span class="no">KatexResource</span><span class="p">.</span><span class="nf">new</span> <span class="vi">@site</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">file</span>
            <span class="k">end</span>

            <span class="k">def</span> <span class="nf">font_files</span><span class="p">()</span>
                <span class="n">assets_dir</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="no">Katex</span><span class="p">.</span><span class="nf">gem_path</span><span class="p">,</span> <span class="s1">'vendor'</span><span class="p">,</span> <span class="s1">'katex'</span><span class="p">)</span>
                <span class="n">fonts_dir</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">assets_dir</span><span class="p">,</span> <span class="s1">'fonts'</span><span class="p">)</span>

                <span class="n">fonts</span> <span class="o">=</span> <span class="p">[]</span>

                <span class="no">Find</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="n">fonts_dir</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">path</span><span class="o">|</span>
                    <span class="k">next</span> <span class="k">if</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>

                    <span class="n">path</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="n">fonts_dir</span><span class="p">,</span> <span class="s1">'fonts'</span><span class="p">)</span>
                    <span class="n">dir</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
                    <span class="n">file</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">basename</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>

                    <span class="n">static_file</span> <span class="o">=</span> <span class="no">KatexResource</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="vi">@site</span><span class="p">,</span> <span class="n">assets_dir</span><span class="p">,</span> <span class="n">dir</span><span class="p">,</span> <span class="n">file</span><span class="p">)</span>

                    <span class="n">fonts</span> <span class="o">&lt;&lt;</span> <span class="n">static_file</span>
                <span class="k">end</span>

                <span class="n">fonts</span>
            <span class="k">end</span>
        <span class="k">end</span>
    <span class="k">end</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In the future I may decide to release this as a standalone plugin. The KaTeX gem also appears to
support sprockets, so maybe I’ll try to get that to work instead. The <a href="https://github.com/envygeeks/jekyll-assets">Jekyll Assets</a>
plugin looks promising.</p>

<h2 id="moving-on">Moving on</h2>

<p>The main update is now a few months behind me and things work well enough. Now back to non-radical,
incremental change. Or maybe I’ll rewrite it in <a href="https://cobalt-org.github.io/">Cobalt</a> or <a href="https://www.getzola.org/">Zola</a> in a few months. Who knows? For
now, the site can look like this for a while.</p>

<figure>

  <p class="figure-img"><img src="/assets/2022/redesign/after.png" alt="Website after the update" /></p>

  <figcaption class="figure-caption">
    <p>How it looks after applying all of this</p>
  </figcaption>

</figure>

<footer>

  <h2 id="acknowledgements">Acknowledgements</h2>

  <ul>
    <li>Cover image by <a href="https://pixabay.com/users/stocksnap-894430/">StockSnap</a></li>
    <li>Color scheme by <a href="https://coolors.co/114b5f-456990-e4fde1-f45b69-6b2737">Coolors</a></li>
  </ul>

</footer>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:patience">
      <p>I looked up when I first got a notification from being subscribed and it apparently was
September 24th, 2019, <a href="#fnref:patience" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
    <li id="fn:stockholm">
      <p>It’s entirely possible that this is just because I’ve been forced to use it but you
can’t deny that the result looks beautiful. <a href="#fnref:stockholm" class="reversefootnote" role="doc-backlink">&#8617;&#65038;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Bert Peters</name></author><category term="jekyll" /><category term="tutorial" /><summary type="html"><![CDATA[By no stretch of imagination am I a frontend person. Graphic design is not my passion. I even got the colour blindness perk. I do like this little website I’ve written over the years and I kind of want it to look nice. And while the design I had created a few years ago still works, it’s also dependent on an outdated version of Jekyll, and it has a few technical issues. So please, follow a long, as a DevOps engineer tries to explain how to make a nice-looking website.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://bertptrs.nl/assets/2022/redesign/design-example.jpeg" /><media:content medium="image" url="https://bertptrs.nl/assets/2022/redesign/design-example.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>