<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>joe.neeman</title>
    <link href="https://joe.neeman.me/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://joe.neeman.me"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2025-10-19T00:00:00+00:00</updated>
    <id>https://joe.neeman.me/atom.xml</id>
    <entry xml:lang="en">
        <title>Linesweeper: a new library for boolean path ops</title>
        <published>2025-10-19T00:00:00+00:00</published>
        <updated>2025-10-19T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/linesweeper/" type="text/html"/>
        <id>https://joe.neeman.me/posts/linesweeper/</id>
        
        <content type="html">&lt;p&gt;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;linesweeper&quot;&gt;Linesweeper&lt;&#x2F;a&gt; is a library that
takes shapes (defined by Bézier curves) as input, and computes their union,
intersection, or other related operation (let&#x27;s call these operations &amp;quot;boolean ops&amp;quot;).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;logo.svg&quot; alt=&quot;An example of two overlapping shapes, decomposed into intersection and set differences&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;No big deal, really: this is a super common operation in vector graphics,
and so programs like Illustrator and Inkscape have been able to do
it for decades. There are multiple open-source implementations available,
for example in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.cgal.org&#x2F;latest&#x2F;Boolean_set_operations_2&#x2F;index.html#Chapter_2D_Regularized_Boolean_Set-Operations&quot;&gt;CGAL&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;paperjs&#x2F;paper.js&quot;&gt;Paper.js&lt;&#x2F;a&gt;,
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;inkscape&#x2F;lib2geom&quot;&gt;lib2geom&lt;&#x2F;a&gt; (as used by Inkscape), or
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;cubicbezier&quot;&gt;Geom2D.CubicBezier&lt;&#x2F;a&gt; (as used by &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;diagrams.github.io&#x2F;&quot;&gt;Diagrams&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;But I had some reasons to try my own thing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;None of the existing implementations were easy to use in Rust, and I think there&#x27;s some
demand for a good Rust implementation; it&#x27;s a popular feature request for kurbo,
for example.&lt;&#x2F;li&gt;
&lt;li&gt;I wanted access to certain low-level operations that aren&#x27;t exposed by existing packages.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s surprisingly hard to do correctly and robustly, and I wanted to try a new approach.&lt;&#x2F;li&gt;
&lt;li&gt;Sometimes it&#x27;s just fun to do something yourself.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think these are all valid reasons, but I want to really focus on the second-last one.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-makes-it-hard&quot;&gt;What makes it hard?&lt;&#x2F;h1&gt;
&lt;p&gt;The basic ideas aren&#x27;t too hard to explain: as an example, suppose we want the union
of two shapes.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;shapes_for_union.svg&quot; alt=&quot;two shapes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first step is to compute all the intersection points of the shapes&#x27; boundaries.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;shapes_for_union_with_points.svg&quot; alt=&quot;two shapes with their intersections highlighted&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then we&#x27;ll use these intersection points to determine the boundary of the union. Starting
from the top, we&#x27;ll &amp;quot;trace&amp;quot; the boundary, keeping the union to our left as we go (either
way would work, as long as we&#x27;re consistent).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;shapes_for_union_tracing_1.svg&quot; alt=&quot;two shapes with an arrow showing how we&#x27;re tracing the boundary&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Whenever we get to an intersection point, we turn right. There could be lots of choices; we take
the right-most one.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;shapes_for_union_tracing_2.svg&quot; alt=&quot;two shapes with an arrow showing how we&#x27;re tracing the boundary&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And that&#x27;s all! Once we get back to our starting segment, we&#x27;ve traced out the union.
(Ok, if the original sets had holes then there&#x27;s some more work to be done to find holes
in the union. But that&#x27;s pretty similar to the steps we&#x27;ve already done.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;shapes_for_union_tracing_3.svg&quot; alt=&quot;two shapes with an arrow showing how we&#x27;re tracing the boundary&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The exact details of the tracing algorithm aren&#x27;t that important here, but the key takeaway
is that it&#x27;s pretty simple once you know two important things: the locations of all
the intersections of all the boundary segments, and &amp;quot;how&amp;quot; those boundary segments meet there
(like, what&#x27;s the clockwise order of the segments at an intersection point).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numerical-issues&quot;&gt;Numerical issues&lt;&#x2F;h2&gt;
&lt;p&gt;When you actually try to implement this, finding the intersections already presents some problems. We like to
use floating point numbers for problems like this because they&#x27;re pretty accurate and very
fast on modern computers. But floating-point calculations invariably involve approximations
and they can sometimes be wrong. For example: does the line segment from
$(0, 0)$ to $(1, 3)$ intersect the line segment from $(2, 2)$ to
$(0.5, 1.5) + (3, 10) \times 2^{-53}$? It does, in fact, but a numerical
implementation might claim that it doesn&#x27;t. (This isn&#x27;t just hypothetical:
I came up with this example by exhaustively searching for line segment
intersections that &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;kurbo&#x2F;latest&#x2F;kurbo&#x2F;enum.PathSeg.html#method.intersect_line&quot;&gt;kurbo&lt;&#x2F;a&gt; gets wrong.)&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re only comparing individual segments, you might not care: you&#x27;re doing approximations
and you got an approximate answer. But when you have a path made up with multiple segments,
small errors on individual segments can conspire to mess things up on a larger scale.
In the picture below, if we fail
to detect that segment A intersects with segment B (which is defensible) and we &lt;em&gt;also&lt;&#x2F;em&gt; fail to
detect that it intersects
with segment C (also reasonable), we might fail to notice that these two &lt;em&gt;paths&lt;&#x2F;em&gt; intersect,
and that&#x27;s definitely wrong.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;intersecting-paths.svg&quot; alt=&quot;Intersecting paths&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can try to fix this by tweaking your intersection-finding code to also report
almost-intersections, but it can be tricky to &lt;em&gt;consistently&lt;&#x2F;em&gt; turn almost-intersections
into intersections across different path segments. For example, in the picture above,
it doesn&#x27;t matter exactly which intersections we detect, but it&#x27;s important that
we see the B-C path as crossing the A segment exactly once.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;existing-approaches&quot;&gt;Existing approaches&lt;&#x2F;h2&gt;
&lt;p&gt;There are a few approaches I know for handling these numerical issues.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use exact math. This is relatively easy if you only support paths made out of straight lines,
because straight lines between integer endpoints can only intersect at rational coordinates.
It&#x27;s more challenging -- but still possible -- to support paths made out of Bézier curves:
you &amp;quot;just&amp;quot; need to compute with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Algebraic_number&quot;&gt;algebraic numbers&lt;&#x2F;a&gt;.
This is the approach used by CGAL. The main disadvantage of exact math is that
it&#x27;s slow. But also, if you want to get the answer out in floating point then
you need a rounding step at the end anyway.&lt;&#x2F;li&gt;
&lt;li&gt;Use exact math, but only for &lt;em&gt;detecting&lt;&#x2F;em&gt; intersections. This is commonly used for handling
paths made out of straight lines, because there&#x27;s an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.cs.cmu.edu&#x2F;~quake&#x2F;robust.html&quot;&gt;algorithm&lt;&#x2F;a&gt;
for it that&#x27;s much faster than generic exact arithmetic. I don&#x27;t know of anyone doing this
for paths with curved segments.&lt;&#x2F;li&gt;
&lt;li&gt;Use approximate math, and try to handle all the corner cases. This is what most implementations
do, but it feels a bit unsatisfying to me.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;linesweeper-s-goals&quot;&gt;Linesweeper&#x27;s goals&lt;&#x2F;h1&gt;
&lt;p&gt;So what do I want out of this new boolean ops algorithm?
The big one is this: it should be correct &lt;em&gt;by design&lt;&#x2F;em&gt;, while
avoiding exact math. I want to use inexact floating point
arithmetic (for speed), and I want our algorithm to be robust
to rounding errors.&lt;&#x2F;p&gt;
&lt;p&gt;I also want a reasonably strong definition of correctness. I&#x27;ll
allow the algorithm to perturb the input curves (this is unavoidable,
as you can&#x27;t represent the intersection points otherwise), but after
that perturbation I want the output to be exactly correct: all the output
curves should intersect one another only at endpoints:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;allowed-perturbation.svg&quot; alt=&quot;Allowed perturbations&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the picture above, I&#x27;m taking the union of the orange and blue shapes
at the top left. The two green outcomes are acceptable: the two inputs
can be perturbed to not touch, in which case the union has two disjoint parts.
Or they can be perturbed to overlap, in which case the union gets fused
into a single piece. The red outcome, however, is forbidden.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to being correct, I&#x27;d like the algorithm to be fast. After all,
that&#x27;s the main point of avoiding exact arithmetic. At this stage of development,
I&#x27;m not too worried about optimizing constant factors in the implementation,
but I want it to scale well to higher accuracy and larger inputs.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-classical-sweep-line-algorithm&quot;&gt;The classical sweep-line algorithm&lt;&#x2F;h1&gt;
&lt;p&gt;The first point to address is the search for all intersections between paths.
There&#x27;s a classical, simple, and efficient algorithm for this: the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bentley%E2%80%93Ottmann_algorithm&quot;&gt;Bentley-Ottmann&lt;&#x2F;a&gt; sweep line algorithm. This will be the basis for
our robust intersection finder, so let&#x27;s begin with a quick outline of the
classical algorithm, which was originally described for straight line segments
using exact arithmetic.&lt;&#x2F;p&gt;
&lt;p&gt;The Bentley-Ottmann algorithm sweeps a horizontal line (the &amp;quot;sweep line&amp;quot;) from top to bottom,
keeping a list (ordered from left to right) of the path segments that cross it.
As the sweep line advances downwards, there are three interesting things that can happen:
it can touch a new segment that wasn&#x27;t already in the sweep line (we say that the segment &amp;quot;enters&amp;quot;
the sweep line), it can move past the end of a segment (we say that the segment &amp;quot;exits&amp;quot; the sweep line),
or it can pass the intersection point of two segments.&lt;&#x2F;p&gt;
&lt;p&gt;When a segment enters the sweep line, we insert it at the appropriate index&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#data-structure&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Then we look
at the neighboring segments to the left and right to see if they intersect with the new segment.
If so, we make a note of the intersection point so we can handle it when the sweep line passes it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;bo-entrance.svg&quot; alt=&quot;first-bo-example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For example, here the sweep line contains (A, B). Then C enters and we insert it at the
appropriate place (after B), and we note that it will intersect with C pretty soon (circled
in black). We don&#x27;t
yet compare C with A, though.&lt;&#x2F;p&gt;
&lt;p&gt;When two segments in the sweep line cross one another, we swap their positions
and then re-check them for intersections with their new neighbors.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;bo-cross.svg&quot; alt=&quot;bo-cross-example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For example, here the sweep line is at the previously-recorded crossing of B and C.
So we swap their order -- the sweep line goes from (A, B, C, D) to (A, C, B, D).
Then we compare both B and C against their new neighbors, and make a note that C will
cross A at some point (circled in black).&lt;&#x2F;p&gt;
&lt;p&gt;When a segment exits the sweep line, we remove it from the sweep line data structure, and then
we check whether its two neighbors intersect each other (and make a note of the intersection point
if they do).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;bo-exit.svg&quot; alt=&quot;bo-exit-example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For example, here both A and B exit at the sweep line. After that, C and D are neighbors
so we compare them and note that they&#x27;ll cross one another soon.&lt;&#x2F;p&gt;
&lt;p&gt;The key to this algorithm&#x27;s efficiency is that for reasonable inputs, it
avoids comparing most pairs of segments: each segment in the sweep line is only
compared to its left and right neighbors.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-two-phase-strategy&quot;&gt;A two-phase strategy&lt;&#x2F;h1&gt;
&lt;p&gt;The first novel (to me, at least) part of our robust sweep line algorithm is that
it comes in two parts: first, we run a sweep line algorithm to determine the ordering
of all our segments. Only once the ordering is decided do we fix the positions.
This two-phase split sounds minor, but it makes
corner cases much simpler: whenever you assign positions to segments, you
perturb them. And whenever you perturb them, you might affect the validity of
things you&#x27;ve already calculated.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#cautionary example&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; By looking after the ordering first,
and then later assigning the positions all at once, you can avoid this problem.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-ordering-phase&quot;&gt;The ordering phase&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a little example of what the ordering phase does: we have four segments (A, B, C, D).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;sweep-example.svg&quot; alt=&quot;sweep example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And as we sweep the sweep line down the picture, there are four heights where the ordering changes.
Initially, the ordering is (A, B). At height a, it switches to (A, B, C, D). At height b,
it switches to (A, C, B, D), and height c, it switches back to (A, B, C, D), and at height
d it changes to (C, D). This sequence of orderings actually encodes all the information about
entrances, exits, and intersections: at height a, the order changed from (A, B) to (A, B, C, D)
and so we know that C and D entered. At height b, the order changed from (A, B, C, D) to
(A, C, B, D), so we know that B and C crossed one another. Even though we haven&#x27;t figured
out horizontal positions for the various crossings, this is already enough information
to do the &lt;em&gt;topological&lt;&#x2F;em&gt; part of boolean ops, by which I mean that you can use the algorithm
at the beginning of this post to figure out that the union of these two shapes
is traced out by following A down to height d, and then B up to height c, and then C down to the end, and then D up
to height a, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;They key primitive for the ordering phase is an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;linesweeper&#x2F;blob&#x2F;b7986c6f36b3c3a90227e96ad4cdd1639bb36fdc&#x2F;src&#x2F;curve&#x2F;mod.rs#L1171&quot;&gt;algorithm&lt;&#x2F;a&gt; that takes two (monotonic in y)
curves and figures out their horizontal order. For each vertical range, the algorithm is
allowed to output &amp;quot;left&amp;quot;, &amp;quot;right&amp;quot;, or &amp;quot;close.&amp;quot; So in the following example,
it might say that A is to the left up until height a, and then they&#x27;re close up until height b,
and then A is to the right after that.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;order-example.svg&quot; alt=&quot;order example&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This pair-wise curve ordering primitive was &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;linebender&#x2F;kurbo&#x2F;pull&#x2F;258&quot;&gt;proposed&lt;&#x2F;a&gt; by Raph Levien a couple
years ago, and it&#x27;s such an important part of our robustness story that I&#x27;ll put this
in bold for the skimmers to see: &lt;strong&gt;the key to robustness is to look for &lt;em&gt;orderings&lt;&#x2F;em&gt; instead
of intersections&lt;&#x2F;strong&gt;. Unlike the
intersection-finding approach that is usually used, this approach doesn&#x27;t get confused by
near-intersections near the endpoints of segments.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;intersecting-paths.svg&quot; alt=&quot;Intersecting paths&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Like, in this example from before, the ordering approach just says that B starts out to the left of A,
and then they get close, and then C is close to A, and then C is to A&#x27;s right. It doesn&#x27;t even try
to figure out which of B or C intersects A, because we just don&#x27;t care.&lt;&#x2F;p&gt;
&lt;p&gt;In order for this primitive to be a useful building block, we impose some requirements. Without getting too
much into the technical details, if the curves are very close we require that the answer is &amp;quot;close,&amp;quot;
and if the curves are clearly ordered, we require the answer to be correct. In intermediate cases,
one of two answers is allowed. For example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;order-example-guarantees.svg&quot; alt=&quot;order example guarantees&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Above height a, the comparison &lt;em&gt;must&lt;&#x2F;em&gt; say that A is on the left. Within the a region, it can say either
&amp;quot;left&amp;quot; or &amp;quot;close&amp;quot; (and is even allowed to alternate between the two). Between heights a and b,
it &lt;em&gt;must&lt;&#x2F;em&gt; say &amp;quot;close&amp;quot;. Within the b region, it can say either &amp;quot;right&amp;quot; or &amp;quot;close&amp;quot;, and below b
it &lt;em&gt;must&lt;&#x2F;em&gt; say &amp;quot;right&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;A brief interlude here, to elaborate on why I consider this algorithm correct &amp;quot;by design&amp;quot;
and how that differs from being correct by trying really hard to handle all corner cases:
once we have the comparison guarantees above, all of the fuzziness and rounding in the
floating point calculations is gone from the algorithm. We have our ternary categorization
(&amp;quot;left&amp;quot;, &amp;quot;right&amp;quot;, or &amp;quot;close&amp;quot;) and the rest of the algorithm&#x27;s logic is based on
these discrete categories. I think this separation of concerns makes it easier to
get things right, and also to test that they&#x27;re right.&lt;&#x2F;p&gt;
&lt;p&gt;Back from the interlude, we use this approximate pair-wise comparison to build
a sweep line algorithm that guarantees approximate ordering: whenever curve A is
definitely to the left of curve B according to our approximate comparison then
it should precede B in the sweep line. There are a few tricky parts to the sweep
line algorithm, because&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we need the ordering guarantees to hold for &lt;em&gt;all&lt;&#x2F;em&gt; pairs of curves in the sweep line,
not just adjacent pairs of curves; and&lt;&#x2F;li&gt;
&lt;li&gt;we want to avoid comparing all pairs of curves in the sweep line, like how the
classical Bentley-Ottmann algorithm only ever compares adjacent curves.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here&#x27;s an example of a tricky case:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;order-example-guarantees-intransitive.svg&quot; alt=&quot;order example guarantees intransitive&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Initially, (A, B, C) is the only valid sweep line order. At the marked height,
our pairwise comparison is still allowed to say that A is close to B and B is
close to C. If we only look at those adjacent comparisons, we might think that
(A, B, C) is still a valid sweep line at that height. But clearly that isn&#x27;t the
case, since C is left of A.&lt;&#x2F;p&gt;
&lt;p&gt;This example shows that (unlike Bentley-Ottmann) our approximate sweep line
needs more than just nearest-neighbor comparisons: when there are multiple
nearby neighbors, it keeps scanning until it sees a gap large enough to know
that it doesn&#x27;t need to look any further. This &amp;quot;large enough gap&amp;quot; test is implemented
with the same pair-wise curve comparison algorithm, just with a larger threshold
for the meaning of &amp;quot;left&amp;quot; and &amp;quot;right&amp;quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-positioning-phase&quot;&gt;The positioning phase&lt;&#x2F;h2&gt;
&lt;p&gt;In the second phase, we physically lay out our segments, while respecting
the orders that the first phase chose for us. It turns out (surprisingly
to me, at first) that producing Bézier curves with a guaranteed horizontal
order is easier than checking the order of some given curves: the latter I
can only do approximately, but the former I can do exactly. The trick
is that if you have two Bézier curves and their control points have the
same $y$ coordinates and ordered $x$ coordinates, the curves are guaranteed
to also be ordered. In the picture below, we can tell that curve A is to
the right of curve B just by comparing their control points (the black
control points are common to both curves).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;control-point-order.svg&quot; alt=&quot;control-point-order&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Therefore our strategy for outputting correctly ordered segments is to simultaneously
approximate all segments with Bézier curves that all share the same control
point $y$ coordinates. If the approximation isn&#x27;t good enough, subdivide and try
again. If the approximation is good enough, apply some maxes and mins to the
control point $x$ coordinates to ensure the correct order.&lt;&#x2F;p&gt;
&lt;p&gt;The most complex part of this scheme is the part that tries to avoid doing it,
because we&#x27;d prefer not to approximate and subdivide if we don&#x27;t have to.
So we conservatively detect regions where the order needs to be enforced. Outside
of those regions, we simply copy the input segments to the output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;linesweeper&#x2F;order-in-strips.svg&quot; alt=&quot;order-in-strips&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this example, the three curves are close to one another at the top, so in the
top rectangular region we&#x27;ll have to approximate and subdivide. But pretty soon
&lt;code&gt;C&lt;&#x2F;code&gt; moves far away, so in the second rectangular region we&#x27;ll only approximate
and subdivide &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;B&lt;&#x2F;code&gt;. Near the bottom, we&#x27;ll approximate and subdivide &lt;code&gt;A&lt;&#x2F;code&gt;
and &lt;code&gt;C&lt;&#x2F;code&gt; while leaving &lt;code&gt;B&lt;&#x2F;code&gt; alone.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;where-we-re-at&quot;&gt;Where we&#x27;re at&lt;&#x2F;h1&gt;
&lt;p&gt;The algorithm described above is implemented (in Rust)
in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;linesweeper&quot;&gt;linesweeper&lt;&#x2F;a&gt; crate, which recently
released verson &lt;code&gt;0.1.0&lt;&#x2F;code&gt;. That version number probably gives the right idea
of the implementation&#x27;s maturity: it should basically work, but there are still
lots of improvements to be made to correctness, performance, and clarity
of implementation. But you can give it a try and report issues! If you&#x27;d like
it to run particularly slowly and crash a lot, you can use it with the &lt;code&gt;slow-asserts&lt;&#x2F;code&gt;
feature, which exhaustively checks all the ordering invariants that we&#x27;re
supposed to uphold.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;acknowledgement&quot;&gt;Acknowledgement&lt;&#x2F;h1&gt;
&lt;p&gt;None of this would have been done without the advice and encouragement of the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;linebender.org&quot;&gt;linebender&lt;&#x2F;a&gt; community, especially that of Raph Levien.
In particular, the implementation of curve comparison is heavily based on
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;linebender&#x2F;kurbo&#x2F;pull&#x2F;258&quot;&gt;his code&lt;&#x2F;a&gt;, and I learned about the
approach to guaranteed output ordering from conversations with him on the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xi.zulipchat.com&#x2F;&quot;&gt;linebender zulip&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;data-structure&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;using some kind of tree structure for fast searching and insertion&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;cautionary example&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;For a cautionary example, the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;geo&quot;&gt;geo&lt;&#x2F;a&gt;
was using a sweep line algorithm that had a bunch of bugs caused by perturbations
unexpectedly breaking their invariants. These were eventually fixed by ditching
their custom implementation and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;georust&#x2F;geo&#x2F;commit&#x2F;4915dfc7db11cd8e94e3c89329295a8d5db72b77&quot;&gt;switching&lt;&#x2F;a&gt; to the &lt;code&gt;i_overlay&lt;&#x2F;code&gt; crate, which uses
a variant of the exact math approach (implemented in fixed precision for speed).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Prospero challenge</title>
        <published>2025-06-09T00:00:00+00:00</published>
        <updated>2025-06-09T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/prospero/" type="text/html"/>
        <id>https://joe.neeman.me/posts/prospero/</id>
        
        <content type="html">&lt;p&gt;Matt Keeter posted his &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mattkeeter.com&#x2F;projects&#x2F;prospero&#x2F;&quot;&gt;Prospero challenge&lt;&#x2F;a&gt; in March, and for a couple of months
I did an excellent job of focusing on my other free-time projects and resisting
the urge to jump in. That ended last week.&lt;&#x2F;p&gt;
&lt;p&gt;For a proper description of the problem, see the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mattkeeter.com&#x2F;projects&#x2F;prospero&#x2F;&quot;&gt;original post&lt;&#x2F;a&gt;.
But the basic idea is that we have a giant list of almost 8000 instructions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; class=&quot;language-text z-code&quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;_0 const 2.95
_1 var-x
_2 const 8.13008
_3 mul _1 _2
_4 add _0 _3
... and 7861 more
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and we want to execute them as fast as possible, many times in parallel.
There are already a bunch of great write-ups linked from the main Prospero challenge
page, and most of them fall into two categories: &lt;em&gt;interpreters&lt;&#x2F;em&gt;,
programs that iterate over the instructions to compute the result, and &lt;em&gt;JIT
compilers&lt;&#x2F;em&gt;, programs that iterate over the instructions and emit machine code, which
then gets executed to produce the result.
One interesting solution emitted CUDA source code instead of machine code.
Then it compiled the CUDA source and executed it on a GPU. The execution time was
super fast, but the compilation step was very slow.&lt;&#x2F;p&gt;
&lt;p&gt;This post is about a sort of a JIT compiler, but one that outputs code for the GPU
instead.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#process&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; The first challenge is to figure out what kind of code to output.
Most CPUs have a standardized instruction set (either ARM or x86_64 for most desktop
CPUs), but GPUs don&#x27;t: every GPU generation from every manufacturer does its own thing.
Nvidia has &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.nvidia.com&#x2F;cuda&#x2F;parallel-thread-execution&#x2F;index.html&quot;&gt;PTX&lt;&#x2F;a&gt;, an instruction set that&#x27;s portable among their own GPUs, but I don&#x27;t
have an Nvidia GPU.&lt;&#x2F;p&gt;
&lt;p&gt;On non-Nvidia GPUs, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Standard_Portable_Intermediate_Representation&quot;&gt;SPIR-V&lt;&#x2F;a&gt; appears to be the most useful compilation target. It&#x27;s
the intermediate representation used by Vulkan, and so any Vulkan video driver knows
how to compile SPIR-V into the GPU&#x27;s native instruction set. Hopefully those compilers
are fast! &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#lower&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;emitting-spir-v&quot;&gt;Emitting SPIR-V&lt;&#x2F;h1&gt;
&lt;p&gt;I was relieved to discover that there&#x27;s a crate (&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gfx-rs&#x2F;rspirv&quot;&gt;&lt;code&gt;rspirv&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;) for emitting
binary SPIR-V code in Rust. They even have a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;rspirv&#x2F;latest&#x2F;rspirv&#x2F;dr&#x2F;struct.Builder.html&quot;&gt;builder&lt;&#x2F;a&gt; with a convenient interface
for building SPIR-V modules! Well, sort of convenient: SPIR-V is quite low-level
and explicit, and it demands that you bind variables to everything that you
want to refer to. For example, you can&#x27;t just say that you want to declare
a &lt;code&gt;void&lt;&#x2F;code&gt; function returning &lt;code&gt;void&lt;&#x2F;code&gt;. Instead, you have to first bind the &lt;code&gt;void&lt;&#x2F;code&gt; type
to a variable, then bind the &lt;code&gt;void-function-returning-void&lt;&#x2F;code&gt; type to a variable,
and then finally you can declare your function. In &lt;code&gt;rspirv&lt;&#x2F;code&gt; it looks like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; b &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;rpsirv&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-path z-rust&quot;&gt;dr&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-path z-rust&quot;&gt;Builder&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;new&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; void &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;type_void&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;                      &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; declare the void type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; voidf &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;type_function&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;void&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-macro z-rust&quot;&gt;vec!&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;void&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; declare the void -&amp;gt; void type
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; f &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;begin_function&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;void&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-type z-rust&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;FunctionControl&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;None&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; voidf&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want to add &lt;code&gt;1.0&lt;&#x2F;code&gt; to a floating point number, first you need to declare the
floating point type, then the floating point constant &lt;code&gt;1.0&lt;&#x2F;code&gt;, and only then can
you create the addition instruction:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; float &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;type_float&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;32&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; float_1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;constant_bit32&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;float&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;bytemuck&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;cast&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;1.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-numeric z-rust&quot;&gt;f32&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; result &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;f_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;float&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-type z-rust&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; float_1&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; x&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To make things more fun, &lt;code&gt;void&lt;&#x2F;code&gt;, &lt;code&gt;voidf&lt;&#x2F;code&gt;, &lt;code&gt;f&lt;&#x2F;code&gt;, &lt;code&gt;float&lt;&#x2F;code&gt;, and &lt;code&gt;float_1&lt;&#x2F;code&gt; are represented
in Rust by the same &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;spirv&#x2F;0.3.0+sdk-1.3.268.0&#x2F;spirv&#x2F;type.Word.html&quot;&gt;type&lt;&#x2F;a&gt;, so there&#x27;s nothing to prevent you from accidentally
trying to add some thing to the float &lt;em&gt;type&lt;&#x2F;em&gt; instead of the floating-point constant
&lt;code&gt;1.0&lt;&#x2F;code&gt;. If you mess something up then the Vulkan runtime will helpfully
segfault without telling you where the problem is.&lt;&#x2F;p&gt;
&lt;p&gt;To actually get things working, I made heavy usage of the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;KhronosGroup&#x2F;SPIRV-Tools&quot;&gt;&lt;code&gt;spirv-val&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; tool
to debug problems with my generated code. To figure out how to express things
in SPIR-V, I also looked at a bunch of generated disassembly from the reference
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;KhronosGroup&#x2F;glslang&quot;&gt;&lt;code&gt;glslang&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; compiler, without which I never would have figured out how
to access the built-in &lt;code&gt;gl_GlobalInvocationId&lt;&#x2F;code&gt; variables or how to write to
the output buffer.&lt;&#x2F;p&gt;
&lt;p&gt;Once I&#x27;d figured out the basics, translating instructions to SPIR-V was pretty
straightforward. The main loop looks something like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;  &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Map the line names in the input file to their SPIR-V variables
&lt;&#x2F;span&gt;  &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; vars &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;HashMap&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-generic z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-begin z-rust&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;str&lt;&#x2F;span&gt;, Word&lt;span class=&quot;z-punctuation z-definition z-generic z-end z-rust&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-path z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;new&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; line &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; insts &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
      &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Skip comments
&lt;&#x2F;span&gt;      &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;if&lt;&#x2F;span&gt; line&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;starts_with&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-quoted z-single z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-rust&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;#&lt;span class=&quot;z-punctuation z-definition z-string z-end z-rust&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
          &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;continue&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
      &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
      &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; parts &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; line&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;split_whitespace&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
      &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; name &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; parts&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
      &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; op &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; parts&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
      &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; var &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;match&lt;&#x2F;span&gt; op &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
          &lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;const&lt;span class=&quot;z-punctuation z-definition z-string z-end z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
              &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; val&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; parts&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;parse&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
              b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;constant_bit32&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ty_float&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;bytemuck&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;cast&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;val&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
          &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
          &lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;add&lt;span class=&quot;z-punctuation z-definition z-string z-end z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
              &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; op0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; vars&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-bitwise z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;parts&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
              &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; op1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; vars&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-bitwise z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;parts&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;next&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
              b&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;f_add&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ty_float&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-type z-rust&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; op0&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; op1&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
          &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
          &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; ... and the rest of the ops
&lt;&#x2F;span&gt;      &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

      vars&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;insert&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;name&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; var&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;


&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;does-it-go-fast&quot;&gt;Does it go fast?&lt;&#x2F;h1&gt;
&lt;p&gt;Sort of, yes! The binary as a whole isn&#x27;t super quick, because initializing the GPU
resources takes a little while. But after about 50ms of setup time, it took about 12ms
to read the input data, create and assemble the binary SPIR-V code, load it on the GPU,
and render out a 1024×1024 image. We can break down that 12ms further:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;stage&lt;&#x2F;th&gt;&lt;th&gt;time (ms)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;shader creation&lt;&#x2F;td&gt;&lt;td&gt;1.5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;shader loading&lt;&#x2F;td&gt;&lt;td&gt;3.7&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;dispatch&lt;&#x2F;td&gt;&lt;td&gt;6.8&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Here, &amp;quot;shader creation&amp;quot; includes the time to parse the input file and call &lt;code&gt;b.begin_function&lt;&#x2F;code&gt;
and so on. &amp;quot;Shader loading&amp;quot; is just the call to &lt;code&gt;vulkano::shader::ShaderModule::new&lt;&#x2F;code&gt;, which I
think is where shader compilation happens,&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#caching&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and &amp;quot;dispatch&amp;quot; consists of running the shader
and reading back the filled buffer.
As a point of comparison, Ken Micklas&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;tech.kmicklas.com&#x2F;posts&#x2F;prospero&#x2F;&quot;&gt;vectorized JIT&lt;&#x2F;a&gt; implementation took 38ms on my machine.
So 12ms seems pretty good, especially if we can further reduce the shader creation and loading
time by targeting a lower-level representation.&lt;&#x2F;p&gt;
&lt;p&gt;Increasing the problem size scales a little better than I&#x27;d expect: doubling the sizes
quadruples the number of pixels, but the runtime seems to consistently rise by less than
a factor of four. I suspect this is caused by the GPU clocking up for the longer computations.
I tried forcing my GPU to max power&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#max power&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and it sped things up quite a lot --
1024×1024 went from 6.8ms to 2.6ms -- but it also consistently crashed my machine at
size 8192. So all these measurements are with the default power settings.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;prospero&#x2F;timings.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The main take-away here is that 1024×1024 is about the size where setup time
and execution time are roughly comparable. Much larger than that, and the execution
time dominates.&lt;&#x2F;p&gt;
&lt;p&gt;The source code of this implementation is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;jneem&#x2F;prospero-gpu&#x2F;src&#x2F;branch&#x2F;main&#x2F;src&#x2F;bin&#x2F;spirv-runner.rs&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-next&quot;&gt;What next?&lt;&#x2F;h1&gt;
&lt;p&gt;We&#x27;ve only explored brute-force computations so far, but apparently the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.mattkeeter.com&#x2F;projects&#x2F;fidget&#x2F;&quot;&gt;really fast&lt;&#x2F;a&gt;
and scalable way to do this sort of thing is via interval arithmetic and expression
simplification. I&#x27;d like to explore these techniques on the GPU, where we might
face different tradeoffs with setup time and execution time: we&#x27;ll have to compile
a new shader for each simplified expression, so it probably makes sense to do
bigger batches and less simplification.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;process&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;I actually started out by trying to write a fast GPU interpreter,
but it was hard. I may write about some of the issues that I had and the benchmarks
that I run, but it&#x27;s more fun to first write about the part that worked.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;lower&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;If we&#x27;re willing to give up platform portability, we could
look into Mesa -- the Linux graphics framework -- more directly. For modern AMD
cards, their &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.mesa3d.org&#x2F;drivers&#x2F;radv.html#shader-compilation&quot;&gt;shader compilation process&lt;&#x2F;a&gt; has two stages: compilation from SPIR-V
to NIR (their native intermediate representation), and then compilation to GPU
machine code. It would be interesting to see if we could hook in to Mesa at one
of those two stages instead of going through SPIR-V.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;caching&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Apparently, Mesa sometimes caches shader compilation, so I ran this with
&lt;code&gt;MESA_SHADER_CACHE_DISABLE=true&lt;&#x2F;code&gt; but didn&#x27;t notice much difference. Maybe our shader doesn&#x27;t
trigger Mesa&#x27;s caching heuristics?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;max power&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;code&gt;echo high | sudo tee &#x2F;sys&#x2F;class&#x2F;drm&#x2F;card1&#x2F;device&#x2F;power_dpm_force_performance_level&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Sleep and power usage</title>
        <published>2023-11-17T00:00:00+00:00</published>
        <updated>2023-11-17T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/sleep/" type="text/html"/>
        <id>https://joe.neeman.me/posts/sleep/</id>
        
        <content type="html">&lt;p&gt;So I have a bunch of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_mini.html&quot;&gt;these&lt;&#x2F;a&gt; dev boards and
I&#x27;d like to try using some of them as battery-powered remote sensors
(I&#x27;m not sure yet exactly what I want to sense; maybe temperature would be a good start).
I&#x27;d like it them to last for at least a couple of weeks on a battery charge, so let&#x27;s try to
figure out what my options are.&lt;&#x2F;p&gt;
&lt;p&gt;First up, roughly how much charge can we expect our battery to have? Adafruit sells rechargeable 3.7v batteries with capacities
ranging from 350mAh (for &lt;span&gt;$&lt;&#x2F;span&gt;7) to 10Ah (for &lt;span&gt;$&lt;&#x2F;span&gt;30). That&#x27;s a big range, so let&#x27;s just say &amp;quot;within an order of magnitude of 1Ah&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Next up, how much power does our chip use? We can get a very rough first guess
from the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.espressif.com&#x2F;sites&#x2F;default&#x2F;files&#x2F;documentation&#x2F;esp32-c3_datasheet_en.pdf&quot;&gt;datasheet&lt;&#x2F;a&gt;.
The current consumption is listed in Section 4.6.1:
while transmitting over wifi the current can go as high as 335 mA, which would
drain our 1Ah battery in 3 hours. At the other extreme, there is a &amp;quot;deep sleep&amp;quot;
mode that is supposed to use only 5µA. At this current, our 1Ah battery would
last more than 22 years.&lt;&#x2F;p&gt;
&lt;p&gt;Those initial estimates leave a big range, and they aren&#x27;t likely to be accurate
for my usage, because those figures are just for the chip itself. Since I&#x27;m
not (yet) hardcore enough to design my own PCBs, my chips are embedded in a
development board full of other components that also use power. Since I couldn&#x27;t
find anything about power consumption on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_mini.html&quot;&gt;board manufacturer&#x27;s page&lt;&#x2F;a&gt;,
I guess I&#x27;ll just have to measure it myself.&lt;&#x2F;p&gt;
&lt;p&gt;At this point I need to confess that I was having so much fun with my Lolin C3 Mini that I went
out and bought another couple of dev boards to see how they compare: I got some
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_pico.html&quot;&gt;Lolin C3 Picos&lt;&#x2F;a&gt; -- which are slightly
smaller than the minis and have some battery charging circuitry and a few
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.seeedstudio.com&#x2F;Seeed-XIAO-ESP32C3-p-5431.html&quot;&gt;Seeed Studio XIAOs&lt;&#x2F;a&gt;, which have
two fewer pins and are noticeably smaller than the Lolins unless you count their largish (but detachable)
external antenna. Here you can see the three of them side-by-side, with my hand for scale.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;sleep&#x2F;three-boards.webp&quot; alt=&quot;Lolin C3 mini, Lolin C3 pico, and Seeed Studio XIAO&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sleep-modes&quot;&gt;Sleep modes&lt;&#x2F;h2&gt;
&lt;p&gt;The esp32c3 has two &amp;quot;&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.espressif.com&#x2F;projects&#x2F;esp-idf&#x2F;en&#x2F;latest&#x2F;esp32c3&#x2F;api-reference&#x2F;system&#x2F;sleep_modes.html#sleep-modes&quot;&gt;sleep modes&lt;&#x2F;a&gt;,&amp;quot;
called &amp;quot;light sleep&amp;quot; and &amp;quot;deep sleep.&amp;quot; Light sleep behaves like I would expect
sleep to behave: it puts the device into a low-power state, and then after some
wakeup-trigger event it wakes up in the same state it was before going to sleep.
Deep sleep also puts the device into a low-power state and waits for some wakeup-trigger
event, but it&#x27;s a bit more like a &amp;quot;reboot&amp;quot; in that when it starts back up again,
it starts back from the beginning.&lt;&#x2F;p&gt;
&lt;p&gt;Both sleep modes can be configured to wake up after a certain amount of time,
or when a specific GPIO pin gets driven high or low. So I wrote a quick test program
that runs for a little while, turns out the wifi for a little while, light-sleeps
for a little while, and deep-sleeps for a little while. I powered it from a 5v power
source and measured the power draw in the various different modes. I also varied
the CPU frequency and tried running a bit of math code in a loop to see if the
processor consumed more power.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Board&lt;&#x2F;th&gt;&lt;th&gt;idle@80MHz&lt;&#x2F;th&gt;&lt;th&gt;work@80MHz&lt;&#x2F;th&gt;&lt;th&gt;idle@160MHz&lt;&#x2F;th&gt;&lt;th&gt;work@160MHz&lt;&#x2F;th&gt;&lt;th&gt;wifi&lt;&#x2F;th&gt;&lt;th&gt;light sleep&lt;&#x2F;th&gt;&lt;th&gt;deep sleep&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;lolin mini&lt;&#x2F;td&gt;&lt;td&gt;20mA&lt;&#x2F;td&gt;&lt;td&gt;21mA&lt;&#x2F;td&gt;&lt;td&gt;27mA&lt;&#x2F;td&gt;&lt;td&gt;31mA&lt;&#x2F;td&gt;&lt;td&gt;101mA&lt;&#x2F;td&gt;&lt;td&gt;2.4mA&lt;&#x2F;td&gt;&lt;td&gt;400µA&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;lolin pico&lt;&#x2F;td&gt;&lt;td&gt;21mA&lt;&#x2F;td&gt;&lt;td&gt;22mA&lt;&#x2F;td&gt;&lt;td&gt;27mA&lt;&#x2F;td&gt;&lt;td&gt;31mA&lt;&#x2F;td&gt;&lt;td&gt;100mA&lt;&#x2F;td&gt;&lt;td&gt;2.6mA&lt;&#x2F;td&gt;&lt;td&gt;437µA&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;seeed xiao&lt;&#x2F;td&gt;&lt;td&gt;21mA&lt;&#x2F;td&gt;&lt;td&gt;23mA&lt;&#x2F;td&gt;&lt;td&gt;29mA&lt;&#x2F;td&gt;&lt;td&gt;32mA&lt;&#x2F;td&gt;&lt;td&gt;102mA&lt;&#x2F;td&gt;&lt;td&gt;2.5mA&lt;&#x2F;td&gt;&lt;td&gt;175µA&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Take-aways include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;running at 160MHz takes roughly 50% more power than running at 80MHz&lt;&#x2F;li&gt;
&lt;li&gt;doing work on the CPU uses a little more power than idling it&lt;&#x2F;li&gt;
&lt;li&gt;the three boards use about the same power except during deep sleep, where the Seeed XIAO is the most
efficient by a long way. Maybe this is because it lacks an RGB LED?&lt;&#x2F;li&gt;
&lt;li&gt;we aren&#x27;t getting anywhere near the 5µA deep sleep current promised by the datasheet. But still,
deep sleep takes about 500x less power than WiFi.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rtt and defmt</title>
        <published>2023-11-04T00:00:00+00:00</published>
        <updated>2023-11-04T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/rtt/" type="text/html"/>
        <id>https://joe.neeman.me/posts/rtt/</id>
        
        <content type="html">&lt;p&gt;In the first post, we printed a &amp;quot;Hello world&amp;quot; message to our computer using the &lt;code&gt;esp_println::println!&lt;&#x2F;code&gt;
macro. But there&#x27;s another (better? I&#x27;m not sure. But at least, different and therefore worth trying) way
to make code running on our &lt;code&gt;esp32c3&lt;&#x2F;code&gt; show up on our screen.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.segger.com&#x2F;RTT&quot;&gt;Real Time Transfer&lt;&#x2F;a&gt; (RTT) protocol is a simple method of transferring
data from an embedded device to the host system. It works by sort of not transferring data at all: it just
writes buffers in the embedded device&#x27;s memory. So then how does that data make it to the host? Well, the
host connects to the device using a debug probe that&#x27;s capable of reading to and writing from the device&#x27;s
memory. As the embedded device is writing its data to memory, the debug probe is reading it out to the host
and then modifying the device&#x27;s memory to tell it that the data&#x27;s been read.&lt;&#x2F;p&gt;
&lt;p&gt;The advantage of this is that the data transfer code on the device is simple, fast, and small: just write
a few bytes to memory.&lt;&#x2F;p&gt;
&lt;p&gt;There are several device-side implementations of RTT in rust. &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;defmt-rtt&quot;&gt;&lt;code&gt;defmt-rtt&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;rtt-target&quot;&gt;&lt;code&gt;rtt-target&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; appear to be two of the more popular options, but
the &lt;code&gt;esp_println&lt;&#x2F;code&gt; crate we were using before also has support for RTT as long as you set its features correctly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;probe-rs&quot;&gt;probe-rs&lt;&#x2F;h2&gt;
&lt;p&gt;There are a few options for RTT on the device side, but on the host side the only implementation
I could find was the one in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;probe.rs&#x2F;&quot;&gt;&lt;code&gt;probe-rs&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; family of tools.
When I first started writing this post a couple of months ago, the released version of &lt;code&gt;probe-rs&lt;&#x2F;code&gt; didn&#x27;t
work out-of-the-box with the binaries that &lt;code&gt;esp-hal&lt;&#x2F;code&gt; produces by default. (The &lt;code&gt;esp32c3&lt;&#x2F;code&gt; supports two
different binary formats; details &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.espressif.com&#x2F;projects&#x2F;esptool&#x2F;en&#x2F;latest&#x2F;esp32c3&#x2F;advanced-topics&#x2F;firmware-image-format.html&quot;&gt;here&lt;&#x2F;a&gt;.
The latest release -- &lt;code&gt;0.21.1&lt;&#x2F;code&gt; as of me writing this -- of &lt;code&gt;probe-rs&lt;&#x2F;code&gt; works, though, as long as you pass
the &lt;code&gt;--format idf&lt;&#x2F;code&gt; argument: here we can run one of the led-blinking examples from the previous post.&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;❯ probe-rs run --chip esp32c3 --format idf target&#x2F;riscv32imc-unknown-none-elf&#x2F;debug&#x2F;rmt
DEBUG probe_rs::architecture::riscv: Before requesting halt, the Dmcontrol register value was: Dmcontrol { .0: 1, hartreset: false, hasel: false, hartsello: 0, hartselhi: 0, ndmreset: false, dmactive: true }
     Erasing sectors ⠁ [00:00:00] [##################################################################]      0 B&#x2F;     0 B @      0 B&#x2F;s (eta 0s )
 Programming pages   ⠁ [00:00:00] [##################################################################]      0 B&#x2F;     0 B @      0 B&#x2F;s (eta 0s )DEBUG probe_rs::architecture::riscv: Before requesting halt, the Dmcontrol register value was: Dmcontrol { .0: 1, hartreset: false, hasel: fals     Erasing sectors ✔ [00:00:01] [#############################################################] 116.00 KiB&#x2F;116.00 KiB @ 58.25 KiB&#x2F;s (eta 0s )
 Programming pages   ✔ [00:00:04] [#############################################################] 112.00 KiB&#x2F;112.00 KiB @ 26.82 KiB&#x2F;s (eta 0s )    Finished in 6.181s
ERROR probe_rs::cmd::run: Failed to enable_vector_catch: NotImplemented(&amp;quot;vector catch&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Despite the scary-looking error message, it does run and blink the LED.
(Regarding the spurious debug messages, I&#x27;ll spare you the account of my late-night debugging and
just link to the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tokio-rs&#x2F;tracing&#x2F;issues&#x2F;2704&quot;&gt;&lt;code&gt;tracing_subscriber&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; issue report that came out of it.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rtt-on-the-device-using-esp-println&quot;&gt;RTT on the device using esp-println&lt;&#x2F;h2&gt;
&lt;p&gt;Since we&#x27;ve been using the &lt;code&gt;esp-println&lt;&#x2F;code&gt; crate already, the easiest way to get RTT working on the device is just to twiddle
some features: remove the default &lt;code&gt;uart&lt;&#x2F;code&gt; feature from &lt;code&gt;esp-println&lt;&#x2F;code&gt; and replace it with the &lt;code&gt;rtt&lt;&#x2F;code&gt; feature (and if using
&lt;code&gt;esp-backtrace&lt;&#x2F;code&gt;, remove its &lt;code&gt;print-uart&lt;&#x2F;code&gt; feature and replace it with &lt;code&gt;print-rtt&lt;&#x2F;code&gt;). There&#x27;s a full runnable example, complete
with &lt;code&gt;.cargo&#x2F;config.toml&lt;&#x2F;code&gt; and everything, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;esp-examples&#x2F;tree&#x2F;main&#x2F;rtt-esp-println&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rtt-on-the-device-using-defmt&quot;&gt;RTT on the device using defmt&lt;&#x2F;h2&gt;
&lt;p&gt;One of the exciting parts about using RTT is that it unlocks using [defmt]
(https:&#x2F;&#x2F;defmt.ferrous-systems.com&#x2F;), the &amp;quot;deferred&amp;quot; formatting system that
makes formatting small and fast by doing the formatting on the host instead of
the device: when you run &lt;code&gt;println!(&amp;quot;{}, ah ha ha&amp;quot;, x)&lt;&#x2F;code&gt; using, say, &lt;code&gt;esp-println&lt;&#x2F;code&gt;,
it compiles down to code that formats the integer &lt;code&gt;x&lt;&#x2F;code&gt;, builds the string with that
formatting in it, and sends that string back to the host somehow. When you run
the same code using &lt;code&gt;defmt::println!&lt;&#x2F;code&gt;, it sends back the integer &lt;code&gt;x&lt;&#x2F;code&gt; without formatting
it, and all the formatting and string concatenation is done on the host.
This removes the formatting code from the embedded device, and it also reduces the
amount of data that has to go from the device to the host.&lt;&#x2F;p&gt;
&lt;p&gt;So let&#x27;s give it a try! We need to add in the defmt linker script in &lt;code&gt;.cargo&#x2F;config.toml&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; class=&quot;language-toml z-code&quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;z-source z-toml&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-toml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-toml&quot;&gt;#&lt;&#x2F;span&gt; ...&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-table z-begin z-toml&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-tag z-table z-toml&quot;&gt;&lt;span class=&quot;z-entity z-name z-table z-toml&quot;&gt;build&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-table z-end z-toml&quot;&gt;]&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-meta z-tag z-key z-toml&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-toml&quot;&gt;rustflags&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-key-value z-toml&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-array z-begin z-toml&quot;&gt;[&lt;&#x2F;span&gt;
     &lt;span class=&quot;z-string z-quoted z-double z-basic z-toml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-toml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;-C&lt;span class=&quot;z-punctuation z-definition z-string z-end z-toml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-array z-toml&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-basic z-toml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-toml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;link-arg=-Tdefmt.x&lt;span class=&quot;z-punctuation z-definition z-string z-end z-toml&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-array z-toml&quot;&gt;,&lt;&#x2F;span&gt;
     &lt;span class=&quot;z-comment z-line z-number-sign z-toml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-toml&quot;&gt;#&lt;&#x2F;span&gt; ... whatever was in rustflags before&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-array z-end z-toml&quot;&gt;]&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we need to open our &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; and replace the &lt;code&gt;esp-println&lt;&#x2F;code&gt; dependency by &lt;code&gt;defmt&lt;&#x2F;code&gt;, as they can&#x27;t really coexist. More precisely,
we can&#x27;t use &lt;code&gt;defmt&lt;&#x2F;code&gt; with &lt;code&gt;rtt&lt;&#x2F;code&gt; support at the same time as we use &lt;code&gt;esp-println&lt;&#x2F;code&gt; with &lt;code&gt;rtt&lt;&#x2F;code&gt; support
(the binary will fail to link, as both crates expect to own the &lt;code&gt;rtt&lt;&#x2F;code&gt; block of memory).
We &lt;em&gt;can&lt;&#x2F;em&gt; use &lt;code&gt;defmt&lt;&#x2F;code&gt; with &lt;code&gt;rtt&lt;&#x2F;code&gt; support and &lt;code&gt;esp-println&lt;&#x2F;code&gt; with some other method of printing. But then
we can only see one of the two outputs -- as far as I could tell, there&#x27;s nothing on the host end
that will print them both.&lt;&#x2F;p&gt;
&lt;p&gt;We also need to remove &lt;code&gt;esp-backtrace&lt;&#x2F;code&gt;, since it only supports printing things using &lt;code&gt;esp-println&lt;&#x2F;code&gt;.
This also means we&#x27;ll have to add our own backtrace implementation. Fortunately, it isn&#x27;t too
difficult to just copy one of the implementations out there. The full runnable example is
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;esp-examples&#x2F;tree&#x2F;main&#x2F;rtt-defmt&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gripes-with-probe-rs&quot;&gt;Gripes with probe-rs&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s great that &lt;code&gt;probe-rs&lt;&#x2F;code&gt; and &lt;code&gt;defmt&lt;&#x2F;code&gt; support the &lt;code&gt;esp32c3&lt;&#x2F;code&gt; with minimal setup pain, but I did have
a couple of gripes that will keep me using &lt;code&gt;espflash&lt;&#x2F;code&gt; for now:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;probe-rs&lt;&#x2F;code&gt; is slower to flash: this &lt;code&gt;rtt-defmt&lt;&#x2F;code&gt; binary in the previous example takes about 2 seconds to
flash with &lt;code&gt;espflash&lt;&#x2F;code&gt;, but about 6 seconds to flash with &lt;code&gt;probe-rs&lt;&#x2F;code&gt;. Not such a big deal for this example,
but it&#x27;s more painful for larger programs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;there&#x27;s one situation that I often run into where &lt;code&gt;espflash&lt;&#x2F;code&gt; has a decent error message but &lt;code&gt;probe-rs&lt;&#x2F;code&gt; doesn&#x27;t:
if I put my board into boot-from-flash mode by holding down the button on the right and the tapping the reset button
(more on this in a future post) then on the next &lt;code&gt;cargo run&lt;&#x2F;code&gt; it won&#x27;t actually boot up.
When using &lt;code&gt;espflash&lt;&#x2F;code&gt; you get a somewhat cryptic error message in this situation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x15 (USB_UART_CHIP_RESET),boot:0x7 (DOWNLOAD(USB&#x2F;UART0&#x2F;1))
Saved PC:0x40380836
0x40380836 - _start_trap_rust_hal
    at &#x2F;home&#x2F;me&#x2F;.cargo&#x2F;registry&#x2F;src&#x2F;index.crates.io-6f17d22bba15001f&#x2F;esp-hal-common-0.13.1&#x2F;src&#x2F;interrupt&#x2F;riscv.rs:433
waiting for download
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At least it&#x27;s an error message, though. &lt;code&gt;probe-rs&lt;&#x2F;code&gt; just sits there saying nothing.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Blinky</title>
        <published>2023-09-03T00:00:00+00:00</published>
        <updated>2023-09-03T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/blinky/" type="text/html"/>
        <id>https://joe.neeman.me/posts/blinky/</id>
        
        <content type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;small-thing&#x2F;&quot;&gt;Last time&lt;&#x2F;a&gt; we left off by failing to get the integrated LED to light up.
We were following the &amp;quot;blinky&amp;quot; example in
&lt;code&gt;esp32c3-hal&lt;&#x2F;code&gt;, which is pretty simple: the main loop looks just toggles the pin&#x27;s voltage from
low to high and back again, one cycle per second. There&#x27;s not much that can go wrong besides choosing the wrong pin,
and we double-checked that already: pin 7 is the one hooked up to our LED.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;c3_mini_circled_led.webp&quot; alt=&quot;The Lolin C3 mini pin diagram, with pin 7 circled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That pin diagram gives us our first hint for what&#x27;s gone wrong: pin 7 is marked as an &lt;code&gt;RGB_LED&lt;&#x2F;code&gt; instead of just a LED.
(There&#x27;s also a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_mini_1_0_0.html&quot;&gt;Lolin C3 mini v1.0.0&lt;&#x2F;a&gt; that has a normal LED, but we&#x27;re using the v2.1.0.)
What&#x27;s an RGB LED? A google search turned up LEDs with &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.sparkfun.com&#x2F;products&#x2F;105&quot;&gt;four pins&lt;&#x2F;a&gt;: a common ground and then
one pin for each color. But our RGB LED has only one pin besides the ground, so we must have something different.&lt;&#x2F;p&gt;
&lt;p&gt;Our next hint comes from our board&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;_static&#x2F;files&#x2F;sch_c3_mini_v2.1.0.pdf&quot;&gt;schematics&lt;&#x2F;a&gt;,
which shows IO 7 connected to something that&#x27;s labelled &lt;code&gt;WS2812B-3535&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;schematic-circled.webp&quot; alt=&quot;The Lolin C3 mini schematic, with the WS2812B-3535 circled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A quick google turns up a &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;ws2812b-spec.pdf&quot;&gt;datasheet&lt;&#x2F;a&gt;.
Instead of a mere analog LED,
it appears we have an &amp;quot;Intelligent control LED integrated light source,&amp;quot; which we control using a simple communication
protocol described in that linked datasheet. The main parts are this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;ws_2812_diag1.webp&quot; alt=&quot;A diagram from the WS2812 datasheet&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;which says we need to send 24 bits of color data by transmitting (in big-endian order) a byte of green, a byte of
red, and then a byte of blue. (I&#x27;m pretty sure the diagram has three blocks of 24 bits each because it&#x27;s showing
how you would transfer data if you had three LEDs wired up in series. We only have one LED, so we just need one
block of 24 bits, followed by the reset code.) And then this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;ws_2812_diag2.webp&quot; alt=&quot;Another diagram from the WS2812 datasheet&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;tells us how to send each bit: a 1 bit is sent by setting the pin high for 850ns and then low for 400ns, and a 0 bit
is sent by setting the pin high for 400ns and then high for 850ns. After sending all 24 bits, we finish up by setting
the pin low for 50µs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bit-banging-the-ws2812&quot;&gt;Bit-banging the WS2812&lt;&#x2F;h2&gt;
&lt;p&gt;Since the procotol is simple, we can implement it bare-hands:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Transmit a 1 to the LED.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-name z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
  io7&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;set_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  delay&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;delay_ns&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;850&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  io7&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;set_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  delay&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;delay_ns&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;400&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Transmit a 0 to the LED.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-name z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
  io7&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;set_high&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  delay&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;delay_ns&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;400&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  io7&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;set_low&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
  delay&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;delay_ns&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;850&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Send G = 0, R = 255, B = 0.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;one&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-support z-function z-rust&quot;&gt;zero&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; 40µs of &amp;quot;low&amp;quot; to finish.
&lt;&#x2F;span&gt;delay&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;delay_us&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;40&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Unfortunately, we can&#x27;t do this because there is no &lt;code&gt;delay_ns&lt;&#x2F;code&gt; function:
&lt;code&gt;esp32c3&lt;&#x2F;code&gt;&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;esp32c3-hal&#x2F;latest&#x2F;esp32c3_hal&#x2F;struct.Delay.html&quot;&gt;&lt;code&gt;Delay&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; only supports microsecond
resolution. I&#x27;m not completely sure why, because &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&#x2F;blob&#x2F;0c47ceda3afbc71dc2f540589811257eab51199f&#x2F;esp-hal-common&#x2F;src&#x2F;delay.rs#L72&quot;&gt;apparently&lt;&#x2F;a&gt;
the underlying timer runs at 16MHz, but anyway there are less brute-force ways to speak to our WS2812B.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-spi-protocol&quot;&gt;The SPI protocol&lt;&#x2F;h2&gt;
&lt;p&gt;I learned about this first method from the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;smart-leds-rs&#x2F;ws2812-spi-rs&quot;&gt;ws2812-spi-rs&lt;&#x2F;a&gt; crate,
which I found by searching for &amp;quot;ws2812&amp;quot; on crates.io. The idea is that the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Serial_Peripheral_Interface&quot;&gt;Serial Peripheral Interface&lt;&#x2F;a&gt;
is close enough to what we want that we can manipulate it into talking to our LED.
The SPI protocol has a fixed clock speed and it sends one bit per clock tick by setting a pin high for a 1 or low for a 0.
In contrast, each bit in the WS2812&#x27;s protocol needs the pin to be high and then low (for durations depending on the
bit we&#x27;re sending).
For example, the nibble &lt;code&gt;1101&lt;&#x2F;code&gt; gets represented like this in our two protocols:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;spi_ws2812.webp&quot; alt=&quot;Diagram of SPI and WS2812 waveforms coming from the nibble 1101&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Clearly they aren&#x27;t the &lt;em&gt;same&lt;&#x2F;em&gt; protocol, but just from looking at the diagram you can see how the WS2812&#x27;s protocol
can be emulated using SPI: we set the SPI frequency higher; then we send a WS2812 1 by sending several
SPI 1s followed by an SPI 0, and we send a WS2812 0 by sending an SPI 1 followed by several SPI 0s.&lt;&#x2F;p&gt;
&lt;p&gt;We have to do a little math to get the timings to line up. First up, I lied a little bit earlier: the WS2812&#x27;s timings
don&#x27;t need to be exactly 850ns for the long pulse and 400ns for the short pulse: you get ±150ns of slack on both of those timings.
So if we set the SPI clock to 3.33 MHz then one clock cycle (300ns) is suitable for a short pulse and three clock cycles (900ns) is
suitable for a long pulse. Then we can sent a WS2812 1 by sending &lt;code&gt;1110&lt;&#x2F;code&gt; on this faster SPI interface,
and a WS2812 0 by sending &lt;code&gt;1000&lt;&#x2F;code&gt;. For example, to send the nibble &lt;code&gt;1101&lt;&#x2F;code&gt; as above, we replace each 1 by &lt;code&gt;1110&lt;&#x2F;code&gt; and each &lt;code&gt;0&lt;&#x2F;code&gt;
by &lt;code&gt;1000&lt;&#x2F;code&gt; to get &lt;code&gt;1110111010001110&lt;&#x2F;code&gt; and then we send that along our faster SPI interface like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;blinky&#x2F;spi_faster.webp&quot; alt=&quot;Diagram the SPI waveform for 1110111010001110&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And it works! I implemented it &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;esp-examples&#x2F;blob&#x2F;main&#x2F;blinky&#x2F;src&#x2F;bin&#x2F;spi.rs&quot;&gt;here&lt;&#x2F;a&gt;; running
&lt;code&gt;cargo run --bin spi&lt;&#x2F;code&gt; finally gives me a life sign from the onboard LED.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-rmt-peripheral&quot;&gt;The RMT peripheral&lt;&#x2F;h2&gt;
&lt;p&gt;I didn&#x27;t realize when I started, but the &lt;code&gt;esp32c3-hal&lt;&#x2F;code&gt; has an example for us after all: it&#x27;s called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&#x2F;blob&#x2F;0c47ceda3afbc71dc2f540589811257eab51199f&#x2F;esp32c3-hal&#x2F;examples&#x2F;hello_rgb.rs&quot;&gt;&lt;code&gt;hello_rgb&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
It talks the WS2812&#x27;s protocol using (by way of the &lt;code&gt;esp_hal_smartled&lt;&#x2F;code&gt; crate) the &amp;quot;Remote Control Transceiver (RMT)&amp;quot; peripheral.
According to &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.espressif.com&#x2F;projects&#x2F;esp-idf&#x2F;en&#x2F;latest&#x2F;esp32c3&#x2F;api-reference&#x2F;peripherals&#x2F;rmt.html&quot;&gt;Espressif&#x27;s documentation&lt;&#x2F;a&gt;,
the RMT was originally designed as an infrared transceiver (hence the name), but has grown to be
&amp;quot;a versatile and general-purpose transceiver, transmitting or receiving many other types of signals.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;One nice thing about RMT is that it has built-in support for the sort of long-short&#x2F;short-long pulses we want, without
needing to fake them: the &lt;code&gt;esp32c3_hal::rmt&lt;&#x2F;code&gt; module provides a &lt;code&gt;PulseCode&lt;&#x2F;code&gt; struct that can express our protocol
like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; The pulse for a 1 bit in WS2812&amp;#39;s protocol:
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; one &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; PulseCode &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
  level1&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-rust&quot;&gt;true&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;  &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Start by setting the pin high...
&lt;&#x2F;span&gt;  length1&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;68&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;   &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; for 68 cycles, which is 850ns at the default 80 MHz clock speed.
&lt;&#x2F;span&gt;  level2&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-rust&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Then set the pin low...
&lt;&#x2F;span&gt;  length2&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;32&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;   &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; for 32 cycles, which is 400ns.
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; zero &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; PulseCode &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; similar &lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Just zero for 4000 cycles.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; reset &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; PulseCode &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
  level1&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-rust&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
  length1&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
  level2&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-language z-rust&quot;&gt;false&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
  length2&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4000&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I wrote a small example &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;esp-examples&#x2F;blob&#x2F;main&#x2F;blinky&#x2F;src&#x2F;bin&#x2F;rmt.rs&quot;&gt;here&lt;&#x2F;a&gt;
that uses the RMT peripheral to talk to our onboard LED. It works just as well as the SPI example.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;which-peripheral-should-you-use&quot;&gt;Which peripheral should you use?&lt;&#x2F;h2&gt;
&lt;p&gt;You can use either the RMT or the SPI peripheral to communicate with the Lolin C3 Mini v2.1.0&#x27;s onboard LED.
But which way is better? The honest answer is &amp;quot;I don&#x27;t know,&amp;quot; but here are a few relevant factors:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The ESP32-C3 has only one general-purpose SPI controller, according to
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.espressif.com&#x2F;projects&#x2F;esp-idf&#x2F;en&#x2F;v5.1.1&#x2F;esp32c3&#x2F;api-reference&#x2F;peripherals&#x2F;spi_master.html&quot;&gt;this&lt;&#x2F;a&gt;.
So if you need the SPI bus for a non-LED purpose, you&#x27;ll need to use RMT for the LED.
There are &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;esp32c3-hal&#x2F;latest&#x2F;esp32c3_hal&#x2F;rmt&#x2F;index.html&quot;&gt;four&lt;&#x2F;a&gt; RMT channels, two for sending
and two for receiving. So the SPI controller seems to be the more precious resource.&lt;&#x2F;li&gt;
&lt;li&gt;The in-memory encoding of the signals is more efficient for SPI: we used four bits in-memory to encode one
bit of protocol (we could have made do with three bits but four was easier). The RMT encoding uses a &lt;code&gt;u32&lt;&#x2F;code&gt; (four &lt;em&gt;bytes&lt;&#x2F;em&gt;)
for each bit of protocol. Given that we only 24 bits to send, this discrepency probably doesn&#x27;t matter.
But if we had a lot of LEDs to control, maybe it would make a difference.&lt;&#x2F;li&gt;
&lt;li&gt;The SPI method is less &amp;quot;blocking:&amp;quot; there is support (which I might write about later) for async operations
on the SPI bus, while the RMT code in &lt;code&gt;esp-hal&lt;&#x2F;code&gt; does a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&#x2F;blob&#x2F;2f5ebad9fe9f8f7186a43e16b29e578b22eac47f&#x2F;esp-hal-common&#x2F;src&#x2F;rmt.rs#L337&quot;&gt;busy loop&lt;&#x2F;a&gt;
until sending is complete.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;esp-hal-smartled&lt;&#x2F;code&gt; crate uses RMT, so you&#x27;ll need to use RMT also if you want to use their utilities.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>About</title>
        <published>2023-08-19T00:00:00+00:00</published>
        <updated>2023-08-19T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/about/" type="text/html"/>
        <id>https://joe.neeman.me/about/</id>
        
        <content type="html">&lt;p&gt;Hello.&lt;&#x2F;p&gt;
&lt;p&gt;My name is Joe.&lt;&#x2F;p&gt;
&lt;p&gt;I work in a SimpleButtonFactoryAwareAspectInstanceFactory.&lt;&#x2F;p&gt;
&lt;p&gt;Ok, not really, but I do write software for a living. Once in a
great while, I also write &lt;em&gt;about&lt;&#x2F;em&gt; software. If that&#x27;s what you&#x27;re here
for, check out my &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;&quot;&gt;blog&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In a previous career, I published papers on probability, geometry and
computer science. Those papers are all available on the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;a&#x2F;neeman_j_1.html&quot;&gt;arXiv&lt;&#x2F;a&gt;, and there&#x27;s also a
summary of some of my research themes &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;math&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Going small: my adventures with an esp32c3</title>
        <published>2023-08-19T00:00:00+00:00</published>
        <updated>2023-08-19T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/small-thing/" type="text/html"/>
        <id>https://joe.neeman.me/posts/small-thing/</id>
        
        <content type="html">&lt;p&gt;I&#x27;m a software developer most of the time, but recently -- originally inspired
by the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.brachiograph.art&#x2F;en&#x2F;latest&#x2F;&quot;&gt;brachiograph&lt;&#x2F;a&gt;, which is amazing
and you should make one -- I decided to dabble in embedded devices.
This is a collection of various random things I&#x27;m learning as I go.
Who is it written for? I&#x27;m not really sure. If you&#x27;re also dabbling in the area,
you might find some useful tidbits. If you already know what&#x27;s going on, maybe
you&#x27;ll be amused by my struggles.&lt;&#x2F;p&gt;
&lt;p&gt;The goal here is learning and fun; productivity, reliability, maturity of tooling
are all optional. In particular:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;everything will be in rust: I&#x27;ll be writing in rust and using rust-native tooling.
I enjoy writing rust, so that&#x27;s what it will be.&lt;&#x2F;li&gt;
&lt;li&gt;RISC-V will be the target platform. People seem excited about it, so let&#x27;s see what it&#x27;s about.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After a bit of googling, I landed on the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.espressif.com&#x2F;en&#x2F;products&#x2F;socs&#x2F;esp32-c3&quot;&gt;ESP32-C3&lt;&#x2F;a&gt;. It
has wifi and bluetooth built in, and there seems to be an active rust community in
the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&quot;&gt;esp-hal&lt;&#x2F;a&gt; project. Including, if I&#x27;m not mistaken, some employees of the
manufacturer.
I&#x27;ve chosen the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_mini.html&quot;&gt;Lolin C3 mini&lt;&#x2F;a&gt; as my dev board, mostly because it&#x27;s
cheaply available on &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.aliexpress.us&#x2F;item&#x2F;3256804553736450.html?spm=a2g0o.cart.0.0.2ef938da0tl9KC&amp;amp;mp=1&amp;amp;gatewayAdapt=glo2usa&amp;amp;_randl_shipto=US&quot;&gt;AliExpress&lt;&#x2F;a&gt;.
This is what it looks like:
&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;small-thing&#x2F;lolin-c3-mini.webp&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There are some &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;_static&#x2F;files&#x2F;sch_c3_mini_v2.1.0.pdf&quot;&gt;schematics&lt;&#x2F;a&gt; available
and a link to the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.espressif.com&#x2F;sites&#x2F;default&#x2F;files&#x2F;documentation&#x2F;esp32-c3_datasheet_en.pdf&quot;&gt;ESP32-C3 datasheet&lt;&#x2F;a&gt;.
At some point I&#x27;ll need to figure out what those things mean.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;first-look-at-the-docs&quot;&gt;First look at the docs&lt;&#x2F;h1&gt;
&lt;p&gt;There are a number of good sources for documentation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;esp-rs.github.io&#x2F;book&#x2F;introduction.html&quot;&gt;Rust on ESP&lt;&#x2F;a&gt; has some
useful instructions on how to set up projects. I learned that we have two
possibilities: we can use [ESP-IDF](https:&#x2F;&#x2F; docs.espressif.com&#x2F;projects&#x2F;esp-
idf&#x2F;en&#x2F;latest&#x2F;esp32c3&#x2F;get-started&#x2F;index.html), Espressif&#x27;s SDK, via the &lt;code&gt;esp- idf-sys&lt;&#x2F;code&gt; crate. This gives us support for rust&#x27;s &lt;code&gt;std&lt;&#x2F;code&gt; library. Or we can go
bare-metal, using the &lt;code&gt;esp32c3-hal&lt;&#x2F;code&gt; crate. I couldn&#x27;t find any analysis of the
advantages or disadvantages of these two choices, so let&#x27;s start with the bare
metal version.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s some tutorial-style documentation in the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;esp-rs.github.io&#x2F;no_std-training&#x2F;&quot;&gt;Embedded Rust (no_std) on
Espressif&lt;&#x2F;a&gt;. book. It assumes you&#x27;re
using a specific board, though. Maybe I should have bought that one instead?&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rust-embedded.org&#x2F;embedonomicon&#x2F;preface.html&quot;&gt;Embedonomicon&lt;&#x2F;a&gt; seems to
be all about ARM processors, but there are a bunch of low-level details there
that might be important to know. Like linker scripts. I know nothing about linker scripts.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ve had less success with the [API documentation](https:&#x2F;&#x2F;docs.rs&#x2F;esp32c3-
hal&#x2F;0.12.0&#x2F;esp32c3_hal&#x2F;index.html) of the &lt;code&gt;esp32c3_hal&lt;&#x2F;code&gt; crate. The module
documentation is good, but I&#x27;m a bit stumped by pages like [this](https:&#x2F;&#x2F;
docs.rs&#x2F;esp32c3-hal&#x2F;0.12.0&#x2F;esp32c3_hal&#x2F;struct.Rmt.html) one. (What can I do
with the channels? &lt;code&gt;Channel0Creator&amp;lt;0&amp;gt;&lt;&#x2F;code&gt; doesn&#x27;t seem to be public...) The day
is saved, however, by the [examples](https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&#x2F;tree&#x2F;
main&#x2F;esp32c3-hal&#x2F;examples): there&#x27;s a ton of them, covering a lot of different
stuff. And they&#x27;re kept up-to-date, which is amazing because this stuff is under
heavy development.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;first-look-at-the-board&quot;&gt;First look at the board&lt;&#x2F;h1&gt;
&lt;p&gt;The biggest thing on this small board is the USB-C connector in the middle of the bottom edge.
To its right is an LED, marked with a &lt;code&gt;7&lt;&#x2F;code&gt;. The two bottom corners are taken up by two small buttons;
the one on the left is marked &lt;code&gt;RST&lt;&#x2F;code&gt; and the one on the right is marked &lt;code&gt;9&lt;&#x2F;code&gt;. More on those later.&lt;&#x2F;p&gt;
&lt;p&gt;The ESP32-C3 comes with integrated USB-to-JTAG support, meaning that you can flash and debug it without any
extra hardware. In this board, this functionality is hooked up
directly to the USB port, so you can get started with an ordinary USB-C cable.
(We&#x27;ll see later what implications this has if you want to use the USB port for other stuff.)
The board is recognized automatically when I hook it up to my computer with a USB-C cable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;❯ dmesg
&amp;lt;...&amp;gt;
usb 1-2.1: New USB device found, idVendor=303a, idProduct=1001, bcdDevice= 1.01
usb 1-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-2.1: Product: USB JTAG&#x2F;serial debug unit
usb 1-2.1: Manufacturer: Espressif
&amp;lt;...&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;permission-to-flash&quot;&gt;Permission to flash&lt;&#x2F;h1&gt;
&lt;p&gt;Before we can actually write to the flash chip, we&#x27;ll need to convince Linux to give us the necessary permissions
on the USB device. &lt;code&gt;probe-rs&lt;&#x2F;code&gt; has some &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;probe.rs&#x2F;docs&#x2F;getting-started&#x2F;probe-setup&#x2F;&quot;&gt;docs&lt;&#x2F;a&gt; for setting
this up on &amp;quot;normal&amp;quot; Linux distributions. I&#x27;m using NixOS, though, which always has to be a little special.
To configure the flasher&#x27;s permissions on NixOS, add the following to your system configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; class=&quot;language-nix z-code&quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;z-source z-nix&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-nix&quot;&gt;# Note that I&amp;#39;m not using `services.udev.extraRules`, because those rules go&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-nix&quot;&gt;# in `99-local.rules` and `uaccess` needs to be set before that. I learned this&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-nix&quot;&gt;# from https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues&#x2F;210856; see also&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-nix&quot;&gt;# https:&#x2F;&#x2F;enotty.pipebreaker.pl&#x2F;2012&#x2F;05&#x2F;23&#x2F;linux-automatic-user-acl-management&#x2F; for&lt;&#x2F;span&gt;
&lt;span class=&quot;z-comment z-line z-number-sign z-nix&quot;&gt;# more about how the uaccess tag works.&lt;&#x2F;span&gt;
&lt;span class=&quot;z-variable z-parameter z-name z-nix&quot;&gt;services&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-nix&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-name z-nix&quot;&gt;udev&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-nix&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-name z-nix&quot;&gt;packages&lt;&#x2F;span&gt; &lt;span class=&quot;z-invalid z-illegal&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-list z-nix&quot;&gt;[&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-expression z-nix&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-name z-nix&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-nix&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-name z-nix&quot;&gt;writeTextFile&lt;&#x2F;span&gt; &lt;span class=&quot;z-punctuation z-definition z-attrset-or-function z-nix&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-entity z-other z-attribute-name z-multipart z-nix&quot;&gt;name&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-bind z-nix&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-nix&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-double z-start z-nix&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;embedded-udev-rules&lt;span class=&quot;z-punctuation z-definition z-string z-double z-end z-nix&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-bind z-nix&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-entity z-other z-attribute-name z-multipart z-nix&quot;&gt;text&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-bind z-nix&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-other z-nix&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-other z-start z-nix&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;
      ATTRS{idVendor}==&amp;quot;303a&amp;quot;, ATTRS{idProduct}==&amp;quot;1001&amp;quot;, TAG+=&amp;quot;uaccess&amp;quot;
    &lt;span class=&quot;z-punctuation z-definition z-string z-other z-end z-nix&quot;&gt;&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-bind z-nix&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-entity z-other z-attribute-name z-multipart z-nix&quot;&gt;destination&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-bind z-nix&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-nix&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-double z-start z-nix&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&#x2F;etc&#x2F;udev&#x2F;rules.d&#x2F;69-probe.rules&lt;span class=&quot;z-punctuation z-definition z-string z-double z-end z-nix&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-bind z-nix&quot;&gt;;&lt;&#x2F;span&gt;
  &lt;span class=&quot;z-punctuation z-definition z-attrset z-nix&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-expression z-nix&quot;&gt;)&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-definition z-list z-nix&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I made a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;probe-rs-rules&quot;&gt;NixOS module&lt;&#x2F;a&gt; for adding these permissions.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;hello-world&quot;&gt;Hello, world!&lt;&#x2F;h1&gt;
&lt;p&gt;If you&#x27;re running Nix, you can get everything you need from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;esp-template&#x2F;&quot;&gt;this&lt;&#x2F;a&gt; template.
It contains a flake for setting up a rust toolchain and &lt;code&gt;espflash&lt;&#x2F;code&gt; (a tool for flashing stuff to our esp).
It also has a &lt;code&gt;.cargo&#x2F;config.toml&lt;&#x2F;code&gt; that I lifted from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-template&quot;&gt;here&lt;&#x2F;a&gt; and a
&lt;code&gt;.helix&#x2F;languages.toml&lt;&#x2F;code&gt; that gets rid of some spurious diagnostics (if you use helix).&lt;&#x2F;p&gt;
&lt;p&gt;Entering the Nix shell however you like to do that (I use direnv) and running &lt;code&gt;cargo run&lt;&#x2F;code&gt; should be all
you need to do. (The &lt;code&gt;.cargo&#x2F;config.toml&lt;&#x2F;code&gt; file has some configuration to make &lt;code&gt;cargo run&lt;&#x2F;code&gt; flash the board using
&lt;code&gt;espflash&lt;&#x2F;code&gt;.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;❯ cargo run
Serial port: &#x2F;dev&#x2F;ttyACM0
Connecting...

Chip type:         ESP32-C3 (revision 3)
Crystal frequency: 40MHz
Flash size:        4MB
Features:          WiFi
MAC address:       68:67:25:b7:49:18
App&#x2F;part. size:    185920&#x2F;4128768 bytes, 4.50%
&amp;lt;...&amp;gt;
Hello ESP32-C3!
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;blinky&quot;&gt;Blinky&lt;&#x2F;h1&gt;
&lt;p&gt;Next up: make the little led flash. Flashing leds are great because unlike the &amp;quot;hello world&amp;quot; example, you
really see that something is happening physically on the device.
Fortunately, &lt;code&gt;esp-hal&lt;&#x2F;code&gt; has an &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esp-rs&#x2F;esp-hal&#x2F;blob&#x2F;696b21bd92737f8e220a1b40bad448abecd090d3&#x2F;esp32c3-hal&#x2F;examples&#x2F;blinky.rs&quot;&gt;example&lt;&#x2F;a&gt;
for this. So let&#x27;s copy it over to our &lt;code&gt;esp-template&lt;&#x2F;code&gt; and run it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;❯ cargo run --bin blinky
Serial port: &#x2F;dev&#x2F;ttyACM0
Connecting...

&amp;lt;...&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok, it runs but I don&#x27;t see blinking. Let&#x27;s look at the file we copied over:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-comment z-line z-documentation z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;!&lt;&#x2F;span&gt; Blinks an LED
&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-documentation z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;!&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-documentation z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;!&lt;&#x2F;span&gt; This assumes that a LED is connected to the pin assigned to `led`. (GPIO5)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ah, but our board&#x27;s &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.wemos.cc&#x2F;en&#x2F;latest&#x2F;c3&#x2F;c3_mini.html&quot;&gt;documentation&lt;&#x2F;a&gt; says that
the LED is on GPIO7, not 5. Changing the 5 to a 7, and...&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;❯ cargo run --bin blinky
Serial port: &#x2F;dev&#x2F;ttyACM0
Connecting...

&amp;lt;...&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Still nothing. To be continued...&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>nnnoiseless: porting audio code from C to rust</title>
        <published>2020-07-12T00:00:00+00:00</published>
        <updated>2020-07-12T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/nnnoiseless/" type="text/html"/>
        <id>https://joe.neeman.me/posts/nnnoiseless/</id>
        
        <content type="html">&lt;p&gt;I ported a C library to rust last week, and it went pretty smoothly. This is
the story, and &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;nnnoiseless&quot;&gt;here&lt;&#x2F;a&gt; is the repo.&lt;&#x2F;p&gt;
&lt;p&gt;The library in question is &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;xiph&#x2F;rnnoise&quot;&gt;RNNoise&lt;&#x2F;a&gt;, a
library for removing noise from audio. It works well, it runs fast, and best of
all it has no knobs that you need to tune. There&#x27;s even a &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;RustAudio&#x2F;rnnoise-c&quot;&gt;rust
binding&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So why bother porting it?
Well, I need to patch it so that it would compile with MSVC, but my PR went
unnoticed for a month. I thought about maintaining my own fork, but it&#x27;s been
more than 10 years since I last wrote anything in C or C++.
And that&#x27;s how I ended up porting RNNoise to rust. It probably wasn&#x27;t the most
efficient use of my time, but I had fun and learned something.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot of information out there about porting C to rust, but the most
useful resource for me was the fantastic
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;carols10cents&#x2F;rust-out-your-c-talk&quot;&gt;talk&lt;&#x2F;a&gt; by Carol (Nichols
|| Goulding). It lays out a simple process for porting one function
at a time: first, you set up the cargo to compile as a static library and you
set up the C build system to link that static library into the C library
(see the slides for the relevant Makefile and Cargo.toml snippets).
Then you can port one function at time: the C code goes like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c z-code&quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;z-source z-c&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-c&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier z-c&quot;&gt;extern&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-c&quot;&gt;void&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-variable z-function z-c&quot;&gt;_celt_lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-c&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;opus_val16 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;_lpc&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-c&quot;&gt;const&lt;&#x2F;span&gt; opus_val32 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;ac&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-c&quot;&gt;int&lt;&#x2F;span&gt; p&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-c&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-c&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-c&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-c&quot;&gt;void&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-variable z-function z-c&quot;&gt;__celt_lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-c&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;opus_val16 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;_lpc&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-c&quot;&gt;const&lt;&#x2F;span&gt; opus_val32 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;ac&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-c&quot;&gt;int&lt;&#x2F;span&gt; p&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-c&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-c&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-c&quot;&gt;void&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-variable z-function z-c&quot;&gt;_celt_lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-c&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;opus_val16 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;_lpc&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-c&quot;&gt;const&lt;&#x2F;span&gt; opus_val32 &lt;span class=&quot;z-keyword z-operator z-c&quot;&gt;*&lt;&#x2F;span&gt;ac&lt;span class=&quot;z-punctuation z-separator z-c&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-c&quot;&gt;int&lt;&#x2F;span&gt; p&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function-call z-c&quot;&gt;&lt;span class=&quot;z-meta z-group z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-c&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-block z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-c&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-comment z-block z-c&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-c&quot;&gt;&#x2F;*&lt;&#x2F;span&gt; C body of _celt_lpc &lt;span class=&quot;z-punctuation z-definition z-comment z-c&quot;&gt;*&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-punctuation z-section z-block z-end z-c&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and the rust code goes like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-annotation z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-annotation z-rust&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-annotation z-rust&quot;&gt;no_mangle&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;pub&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-other z-rust&quot;&gt;extern&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-quoted z-double z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-begin z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;C&lt;span class=&quot;z-punctuation z-definition z-string z-end z-rust&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-rust&quot;&gt;fn&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-rust&quot;&gt;_celt_lpc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;, &lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;ac&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*const&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;, &lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; c_int&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;    &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;unsafe&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;        &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; lpc_slice &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;std&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-path z-rust&quot;&gt;slice&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;from_raw_parts_mut&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;lpc&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; p &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;usize&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;        &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; ac_slice &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-path z-rust&quot;&gt;std&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-path z-rust&quot;&gt;slice&lt;span class=&quot;z-punctuation z-accessor z-rust&quot;&gt;::&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;from_raw_parts&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ac&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; p &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;usize&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;        &lt;span class=&quot;z-support z-function z-rust&quot;&gt;rs_celt_lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;lpc_slice&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; ac_slice&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-rust&quot;&gt;fn&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-rust&quot;&gt;rs_celt_lpc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;lpc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; [&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;], &lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;ac&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;[&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; rust body of celt_lpc
&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;ve watched the talk (which you should), you might notice that this is a
tiny bit different from what they recommend: I&#x27;ve renamed the original C
function instead of deleting it. I found that this helped me narrow down porting
mistakes, because it made it easy to switch back and forth between the C and
rust implementations.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pain-points&quot;&gt;Pain points&lt;&#x2F;h1&gt;
&lt;p&gt;Most of the porting process was mechanical and easy. One of the less fun parts was
porting code involving C structs. RNNoise has structs that (when ported to
rust) look like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-annotation z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-annotation z-rust&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-annotation z-rust&quot;&gt;repr&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-annotation z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-annotation z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;C&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-annotation z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-struct z-rust&quot;&gt;struct&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-entity z-name z-struct z-rust&quot;&gt;RnnState&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*const&lt;&#x2F;span&gt; RnnModel,
    &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; Various buffers, whose sizes are determined by some subfields of `model`.
&lt;&#x2F;span&gt;    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;vad_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;,
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;noise_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;,
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;denoise_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;*mut&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An idomatic rust version might look something like&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-struct z-rust&quot;&gt;struct&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-entity z-name z-struct z-rust&quot;&gt;RnnState&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-struct z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier z-lifetime z-rust&quot;&gt;&amp;#39;static&lt;&#x2F;span&gt; RnnModel,
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;vad_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-generic z-rust&quot;&gt;&lt;span class=&quot;z-support z-type z-rust&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-begin z-rust&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-end z-rust&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;,
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;noise_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-generic z-rust&quot;&gt;&lt;span class=&quot;z-support z-type z-rust&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-begin z-rust&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-end z-rust&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;,
    &lt;span class=&quot;z-variable z-other z-member z-rust&quot;&gt;denoise_gru_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-type z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-generic z-rust&quot;&gt;&lt;span class=&quot;z-support z-type z-rust&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-begin z-rust&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-end z-rust&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;,
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;but this isn&#x27;t layout-compatible with the original C version, and so I need to
stick with the original struct for as long as &lt;code&gt;RnnState&lt;&#x2F;code&gt; is being accessed by
both C and rust code. This increases the amount of &lt;code&gt;unsafe&lt;&#x2F;code&gt; sprinkled around
the rust code, and it was also the source of an annoying bug of the sort that I
thought I had left behind by moving to rust.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-heisenbug&quot;&gt;A Heisenbug&lt;&#x2F;h1&gt;
&lt;p&gt;At some point during the porting process, my tests started failing in release mode,
but not in debug mode. Most likely some undefined behavior triggered by my amateurish
attempts at unsafe code, but I couldn&#x27;t quickly spot the problem and the prospect of
a more careful round of debugging didn&#x27;t spark a whole lot of joy. So I did something
that I never would have dared to do in my C&#x2F;C++ days: I ignored the problem and kept
porting; after all, the tests were still working in debug mode. And sure enough,
a few more ported functions later and &lt;code&gt;rustc&lt;&#x2F;code&gt; found the problem for me: in a function
taking a &lt;code&gt;&amp;amp;RnnState&lt;&#x2F;code&gt; parameter, I was modifying data in the &lt;code&gt;vad_gru_state&lt;&#x2F;code&gt; buffer.
Since I was using unsafe code, &lt;code&gt;rustc&lt;&#x2F;code&gt; didn&#x27;t complain at first. But once I ported
the &lt;code&gt;RnnState&lt;&#x2F;code&gt; struct to safe and idiomatic rust, the compiler flagged the problem
immediately.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;performance&quot;&gt;Performance&lt;&#x2F;h1&gt;
&lt;p&gt;After getting everything to 100% safe (if not particulary idiomatic) rust, it was time
to check whether performance had suffered.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;..&#x2F;ported_benchmark.svg&quot; alt=&quot;initial benchmark&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yes, apparently, by about 50%. The most obvious culprit was bounds checking: there was
a lot of indexing in the C code, and some of it wasn&#x27;t trivial to convert to a more
rust-friendly, iterator-based version. First priority was the neural network evaluation:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; m &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; At most 114.
&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; n &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;...&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt; &lt;span class=&quot;z-comment z-line z-double-slash z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-rust&quot;&gt;&#x2F;&#x2F;&lt;&#x2F;span&gt; At most 96.
&lt;&#x2F;span&gt;
&lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; i &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;n &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; output&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; layer&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;bias&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; j &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;m &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
        output&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; layer&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;input_weights&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;j &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; n &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; input&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;j&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I can already see you shaking your head. I&#x27;m doing naive matrix-vector multiplication
with a 100x100ish matrix in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Row-_and_column-major_order&quot;&gt;column-major format&lt;&#x2F;a&gt;?
Not only is this costing me bounds checks, it&#x27;s terrible for memory locality.
Swapping the weights storage from column- to row-major order only made things
about 1.5% faster, but more importantly it made the whole thing iterator-friendly.
Converting to zips and sums bought another 15%, leaving me only about 25-30% slower
than the C code.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; i &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;n &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; output&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt;
        layer&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;bias&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; 
        layer&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;input_weights&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; m&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; m&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
            &lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
            &lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zip&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;input&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
            &lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;x &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
            &lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For my next optimization opportunity, I moved on to the function
that
computes &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cross-correlation&quot;&gt;cross-correlations&lt;&#x2F;a&gt;.
The un-optimized version of this function looks like&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-storage z-type z-function z-rust&quot;&gt;fn&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-function z-rust&quot;&gt;pitch_xcorr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;xs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;[&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;], &lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;ys&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;[&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;], &lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;xcorr&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; [&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; i &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;xcorr&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
        xcorr&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; xs&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zip&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-bitwise z-rust&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;x &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;but the C code contained a massive, manually-unrolled version. I&#x27;d skipped
it while porting, but maybe I&#x27;d gain something from porting it over. Here&#x27;s
an abbreviated version of the optimized function, assuming that all
lengths are a multiple of 4 (the real code also handles the case that they aren&#x27;t).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; i &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;xcorr&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;step_by&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; c0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; c1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; c2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; c3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; y0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; y1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; y2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; y3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

    &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;x&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; xs&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;chunks_exact&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zip&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ys&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;i &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-operator z-range z-rust&quot;&gt;..&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;chunks_exact&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
        c0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y0&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y1&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y2&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y3&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

        y0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y1&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y2&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y3&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y0&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

        y1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y2&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y3&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y0&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y1&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

        y2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y3&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y0&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y1&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        c3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y2&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

        y3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Basically, both inner and outer loops have been unrolled four times, and I&#x27;ve
exploited the inner loop&#x27;s unrolling to optimize the memory access pattern.
Thanks to the amazing &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gnzlbg&#x2F;cargo-asm&quot;&gt;&lt;code&gt;cargo asm&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, I
can happily report that there&#x27;s no bounds-checking in the inner loop and that
all the arithmetic has been &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Automatic_vectorization&quot;&gt;auto-vectorized&lt;&#x2F;a&gt;
to work four &lt;code&gt;f32&lt;&#x2F;code&gt;s at a time. (Maybe it would get even faster if I unrolled 8 times and
compiled with AVX enabled; I haven&#x27;t tried that yet.)&lt;&#x2F;p&gt;
&lt;p&gt;This change more than doubled the speed of &lt;code&gt;pitch_xcorr&lt;&#x2F;code&gt;, and gained me about 10% overall.
More importantly, it showed me how to coerce the compiler into auto-vectorizing something
that it hadn&#x27;t auto-vectorized before. I went back to the neural network code and
replaced things like&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;xs&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zip&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ys&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;map&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-begin z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;&lt;span class=&quot;z-meta z-function z-parameters z-rust&quot;&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; &amp;amp;&lt;span class=&quot;z-variable z-parameter z-rust&quot;&gt;y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-parameters z-end z-rust&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-function z-closure z-rust&quot;&gt;x &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;f32&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;with things like&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; sum0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; sum1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; sum2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;span class=&quot;z-storage z-type z-rust&quot;&gt;let&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;mut&lt;&#x2F;span&gt; sum3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0.&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-float z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;

    &lt;span class=&quot;z-keyword z-control z-rust&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;x&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; y&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-rust&quot;&gt;in&lt;&#x2F;span&gt; xs&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;chunks_exact&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;zip&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;ys&lt;span class=&quot;z-punctuation z-accessor z-dot z-rust&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-function z-rust&quot;&gt;chunks_exact&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
        sum0 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        sum1 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        sum2 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
        sum3 &lt;span class=&quot;z-keyword z-operator z-assignment z-rust&quot;&gt;+=&lt;&#x2F;span&gt; x&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;*&lt;&#x2F;span&gt; y&lt;span class=&quot;z-meta z-group z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-group z-begin z-rust&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-numeric z-integer z-decimal z-rust&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-section z-group z-end z-rust&quot;&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-terminator z-rust&quot;&gt;;&lt;&#x2F;span&gt;
    &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
    sum0 &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; sum1 &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; sum2 &lt;span class=&quot;z-keyword z-operator z-arithmetic z-rust&quot;&gt;+&lt;&#x2F;span&gt; sum3
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;for another 20% improvement.&lt;&#x2F;p&gt;
&lt;p&gt;Current score: the rust version (still 100% safe) is about 15% faster, and there&#x27;s probably plenty more
still on the table.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;..&#x2F;ported_benchmark_after.svg&quot; alt=&quot;final benchmark&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The performance lesson I learned from this is that bounds checking can be expensive in numerical code
and iterator-style code can help a bit, but if you really want faster numerical code then you need
to write in a style that the auto-vectorizer likes. (Or you could use the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;core&#x2F;arch&#x2F;index.html&quot;&gt;SIMD intrinsics&lt;&#x2F;a&gt;
directly, but that&#x27;s another story.)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;a-huge-thank-you-to-cargo&quot;&gt;A huge thank you to &lt;code&gt;cargo&lt;&#x2F;code&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Like I wrote above, it&#x27;s been a while since I did any C&#x2F;C++, and because of that I&#x27;ve started to take tools
like cargo for granted. This little porting project brought back some memories, mostly because about half of the
code in RNNoise was actually &amp;quot;vendored&amp;quot; from &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;gitlab.xiph.org&#x2F;xiph&#x2F;opus&quot;&gt;opus&lt;&#x2F;a&gt;. I put &amp;quot;vendored&amp;quot;
in quotes because I usually think of vendoring as involving a subdirectory (maybe even a git submodule if
I&#x27;m lucky) with its own build artifacts. That&#x27;s not what&#x27;s going on here, though; I&#x27;m just talking about files
that were copied from the source directory of one project to the source directory of another, complete with
never-used functions and never-def&#x27;ed ifdefs. The thing is, though, that I understand exactly why they did it:
it&#x27;s by far the easiest way to share code between C projects. So I just want to finish by saying a big &amp;quot;thank you&amp;quot;
to &lt;code&gt;cargo&lt;&#x2F;code&gt; and &lt;code&gt;crates.io&lt;&#x2F;code&gt; for making me not have to deal with C dependency management any more.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Gaussian bubble clusters</title>
        <published>2020-06-15T00:00:00+00:00</published>
        <updated>2020-06-15T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/math/double-bubble/" type="text/html"/>
        <id>https://joe.neeman.me/math/double-bubble/</id>
        
        <summary type="html">&lt;p&gt;Suppose I ask you to divide \(\mathbb{R}^n\) into two pieces of fixed Gaussian
measure so that the surface area of the boundary is as small as possible.  The
&lt;em&gt;Gaussian isoperimetric inequality&lt;&#x2F;em&gt; states that the best way to do it is by
cutting \(\mathbb{R}^n\) with a hyperplane:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;..&#x2F;halfspace.svg&quot; alt=&quot;Gaussian half-space&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now what if I ask for &lt;em&gt;three&lt;&#x2F;em&gt; parts instead of two? </summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Gaussian noise sensitivity</title>
        <published>2020-06-15T00:00:00+00:00</published>
        <updated>2020-06-15T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/math/noise-sensitivity/" type="text/html"/>
        <id>https://joe.neeman.me/math/noise-sensitivity/</id>
        
        <summary type="html">&lt;p&gt;Suppose that \(X\) and \(Y\) are positively correlated standard Gaussian
vectors in \(\mathbb{R}^n\). Define the noise sensitivity of \(A \subset
\mathbb{R}^n\) to be the probability that \(X \in A\) and \(Y \not \in
A\). Borell proved that for any \(a \in (0,1)\), half-spaces minimize the
noise sensitivity subject to the constraint \(\mathrm{Pr}(X \in A)=a\).
This inequality can be seen as a strengthening of the Gaussian isoperimetric
inequality: in the limit as the correlation goes to one the noise sensitivity
is closely related to the surface area, because if \(X \in A\)
and \(Y \not \in A\) are close together then they&#x27;re probably both
close to the boundary of \(A\). From a
more applied point of view, Borell&#x27;s inequality and its discrete relatives
played a surprising and crucial role in studying hardness of approximation in
theoretical computer science.  </summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Patches, Part 5: Pseudo-edges</title>
        <published>2019-05-07T00:00:00+00:00</published>
        <updated>2019-05-07T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/pseudo/" type="text/html"/>
        <id>https://joe.neeman.me/posts/pseudo/</id>
        
        <content type="html">&lt;p&gt;This is the fifth (and final planned) post in a series on some new ideas
in version control. To start at the beginning,
&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;&quot;&gt;go here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The goal of this post is to describe pseudo-edges: what they are, how to
compute them efficiently, and how to update them efficiently upon small
changes. To recall the important points from the
&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;ids&#x2F;&quot;&gt;last post&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We (pretend, for now, that we) represent the state of the repository as a
graph in memory: one node for every line, with a directed edges that enforce
ordering constraints between two lines. Each line has a flag that says
whether it is deleted or not.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;em&gt;current output&lt;&#x2F;em&gt; of the repository consists of just those nodes that
are not deleted, and there is an ordering constraint between two nodes if
there is a path in the graph between them, &lt;em&gt;but note that the path is allowed
to go through deleted nodes&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Applying a patch to the repository is very efficient: the complexity of applying
a patch is proportional to the number of changes it makes.&lt;&#x2F;li&gt;
&lt;li&gt;Rendering a the current output to a file is potentially very expensive: its
complexity requires traversing the entire graph, &lt;em&gt;including nodes that are
marked as deleted&lt;&#x2F;em&gt;. To the extent we can, we&#x27;d like to reduce this
complexity to the number of live nodes in the graph.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main idea for solving this is to add &amp;quot;pseudo-edges&amp;quot; to the graph: for every
path that connects two live nodes through a sequence of deleted nodes, add
a corresponding edge to the graph. Once this is done, we can render the current
output without traversing the deleted parts of the graph, because every
ordering contraint that used to depend on some deleted parts is now represented
by some pseudo-edge. Here&#x27;s an example: the deleted nodes are in gray, and the
pseudo-edge that they induce is the dashed arrow.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_1.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We haven&#x27;t really solved anything yet, though: once we have the pseudo-edges,
we can efficiently render the output, but how do we compute the pseudo-edges?
The naive algorithm (look at every pair of live nodes, and check if they&#x27;re
connected by a path of deleted nodes) still depends on the number of deleted
nodes. Clearly, what we need is some sort of &lt;em&gt;incremental&lt;&#x2F;em&gt; way to update the
pseudo-edges.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;deferring-pseudo-edges&quot;&gt;Deferring pseudo-edges&lt;&#x2F;h1&gt;
&lt;p&gt;The easiest way that we can reduce the amount of time required for computing
pseudo-edges is simply to do it rarely. Specifically, remember that
applying a patch can be very fast, and that pseudo-edges only need to be
computed when outputting a file. So, obviously, we should only update the
pseudo-edges when it&#x27;s time to actually output the file. This sounds trivial,
but it can actually be significant. Imagine, for example, that you&#x27;re cloning a
repository that has a long history; let&#x27;s say it has &lt;code&gt;n&lt;&#x2F;code&gt; patches, each of which
has a constant size, and let&#x27;s assume that computing pseudo-edges takes time
&lt;code&gt;O(m)&lt;&#x2F;code&gt;, where &lt;code&gt;m&lt;&#x2F;code&gt; is the size of the history. Cloning a repository involves
downloading all of those patches, and then applying them one-by-one. If we
recompute the pseudo-edges after every patch application, the total amount of
time required to clone the repository is &lt;code&gt;O(n^2)&lt;&#x2F;code&gt;; if we apply all the patches
first and only compute the pseudo-edges at the end, the total time is &lt;code&gt;O(n)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can see how &lt;code&gt;ojo&lt;&#x2F;code&gt; implements this deferred pseudo-edge computation
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;lib.rs#L391&quot;&gt;here&lt;&#x2F;a&gt;:
first, it applies all of the patches; then it
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;lib.rs#L416&quot;&gt;recomputes the pseudo-edges&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;connected-deleted-components&quot;&gt;Connected deleted components&lt;&#x2F;h1&gt;
&lt;p&gt;Deferring the pseudo-edge computation certainly helps, but we&#x27;d also like to
speed up the computation itself. The main idea is to avoid unnecessary
recomputation by only examining parts of the graph that might have actually
changed.  At this point, I need to admit that I don&#x27;t know whether what I&#x27;m
about to propose is the best way of updating the pseudo-edges. In particular,
its efficiency rests on a bunch of assumptions about what sort of graphs we&#x27;re
likely to encounter. I haven&#x27;t made any attempt to test these assumptions on
actual large repositories (although that&#x27;s something I&#x27;d like to try in the
future).&lt;&#x2F;p&gt;
&lt;p&gt;The main assumption is that while there may be many deleted nodes, they tend to
be collected into a large number of connected components, each of which tends
to be small. What&#x27;s more, each patch (I&#x27;ll assume) tends to only affect a small
number of these connected components. In other words, the plan will be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;keep track (incrementally) of connected components made up of deleted nodes,&lt;&#x2F;li&gt;
&lt;li&gt;when applying or reverting a patch, figure out which connected components
were touched, and only recompute paths among the live nodes that are on the
boundary of one of the dirty connected components.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Before talking about algorithms, here are some pictures that should help
unpack what it is that I actually mean. Here is a graph containing three
connected components of deleted nodes (represented by the rounded rectangles):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_2.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I delete node &lt;code&gt;h&lt;&#x2F;code&gt;, it gets added to one of the connected components,
and I can update relevant pseudo-edges without looking at the other two connected
components:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_3.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If I delete node &lt;code&gt;d&lt;&#x2F;code&gt; then it will cause all of the connected components to
merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_4.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This isn&#x27;t hard to handle, it just means that we should run our
pseudo-edge-checking algorithm on the merged component.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;maintaining-the-components&quot;&gt;Maintaining the components&lt;&#x2F;h1&gt;
&lt;p&gt;To maintain the partition of deleted nodes into connected components, we use a
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Disjoint-set_data_structure&quot;&gt;disjoint-set&lt;&#x2F;a&gt; data
structure.
This is very fast (pretty close to constant time) when applying patches,
because applying patches can only enlarge deleted components.  It&#x27;s slower when
reverting patches, because the disjoint-set algorithm doesn&#x27;t allow splitting:
when reverting patches, connected components could split into smaller ones.
Our approach is to defer the splitting: we just mark the original connected component
as dirty. When it comes time to compute the pseudo-edges, we explore the original
component, and figure out what the new connected pieces are.&lt;&#x2F;p&gt;
&lt;p&gt;The disjoint-set data structure is implemented in the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;tree&#x2F;master&#x2F;partition&quot;&gt;&lt;code&gt;ojo_partition&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;
subcrate. It appears in the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;storage&#x2F;graggle.rs#L117&quot;&gt;&lt;code&gt;Graggle&lt;&#x2F;code&gt; struct&lt;&#x2F;a&gt;;
note also the &lt;code&gt;dirty_reps&lt;&#x2F;code&gt; member: that&#x27;s for keeping track of which parts in
the partition have been modified by a patch and require recomputing
pseudo-edges.&lt;&#x2F;p&gt;
&lt;p&gt;We recompute the components
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;storage&#x2F;graggle.rs#L417&quot;&gt;here&lt;&#x2F;a&gt;.
Specifically, we consider the subgraph consisting only of nodes that belong
to one of the dirty connected components. We run Tarjan&#x27;s algorithm on that
subgraph to find out what the new connected components are. On each of those
components, we recompute the pseudo-edges.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;recomputing-the-pseudo-edges&quot;&gt;Recomputing the pseudo-edges&lt;&#x2F;h1&gt;
&lt;p&gt;The algorithm for this is: after deleting the node, look at the deleted connected
component that it belongs to, including the &amp;quot;boundary&amp;quot; consisting of live nodes:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_5.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using depth-first search, check which of the live boundary nodes (in this case,
just &lt;code&gt;a&lt;&#x2F;code&gt; and &lt;code&gt;i&lt;&#x2F;code&gt;) are connected by a path within that component (in this case,
they are). If so, add a pseudo-edge.  The complexity of this algorithm is
&lt;code&gt;O(nm)&lt;&#x2F;code&gt;, where &lt;code&gt;n&lt;&#x2F;code&gt; is the number of boundary nodes, and &lt;code&gt;m&lt;&#x2F;code&gt; is the total number
of nodes in the component, including the boundary (because we need to run &lt;code&gt;n&lt;&#x2F;code&gt;
DFSes, and each one takes &lt;code&gt;O(m)&lt;&#x2F;code&gt; time). The hope here is that &lt;code&gt;m&lt;&#x2F;code&gt; and &lt;code&gt;n&lt;&#x2F;code&gt; are
small, even for large histories.  For example, I hope that &lt;code&gt;n&lt;&#x2F;code&gt; is almost always
2; at least, this is the case if the final live graph is totally ordered.&lt;&#x2F;p&gt;
&lt;p&gt;This algorithm is implemented
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;storage&#x2F;graggle.rs#L479&quot;&gt;&lt;code&gt;here&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;unapplying-and-pseudo-edge-reasons&quot;&gt;Unapplying, and pseudo-edge reasons&lt;&#x2F;h1&gt;
&lt;p&gt;There&#x27;s one more wrinkle in the pseudo-edge computation, and it has to do with
reverting patches: if applying a patch created a pseudo-edge, removing a patch
might cause that pseudo-edge to get deleted. But we have to be very careful
when doing so, because a pseudo-edge might have multiple reasons for existing.
You can see why in this example from before:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_6.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The pseudo-edge from &lt;code&gt;a&lt;&#x2F;code&gt; to &lt;code&gt;d&lt;&#x2F;code&gt; is caused independently by the both the
&lt;code&gt;b -&amp;gt; c&lt;&#x2F;code&gt; component and the &lt;code&gt;cy -&amp;gt; cl -&amp;gt; e&lt;&#x2F;code&gt; component. If by unapplying
some patch we destroy the &lt;code&gt;b -&amp;gt; c&lt;&#x2F;code&gt; component but leave the &lt;code&gt;cy -&amp;gt; cl -&amp;gt; e&lt;&#x2F;code&gt;
component untouched, we have to be sure not to delete the pseudo-edge from
&lt;code&gt;a&lt;&#x2F;code&gt; to &lt;code&gt;d&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The solution to this is to track to &amp;quot;reasons&amp;quot; for pseudo-edges, where each
&amp;quot;reason&amp;quot; is a deleted connected component. This is a many-to-many mapping
between connected deleted components and pseudo-edges, and it&#x27;s stored in the
&lt;code&gt;pseudo_edge_reasons&lt;&#x2F;code&gt; and &lt;code&gt;reason_pseudo_edges&lt;&#x2F;code&gt; members of the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;storage&#x2F;graggle.rs#L117&quot;&gt;&lt;code&gt;GraggleData&lt;&#x2F;code&gt; struct&lt;&#x2F;a&gt;.
Once we store pseudo-edge reasons, it&#x27;s easy to figure out when a pseudo-edge
needs deleting: whenever its
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;a02daf4df387b407f6c8d0b7237472e5960344dd&#x2F;libojo&#x2F;src&#x2F;storage&#x2F;graggle.rs#L372&quot;&gt;last reason becomes obsolete&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pseudo-edge-spamming-an-optimization&quot;&gt;Pseudo-edge spamming: an optimization&lt;&#x2F;h1&gt;
&lt;p&gt;We&#x27;ve finished describing &lt;code&gt;ojo&lt;&#x2F;code&gt;&#x27;s algorithm for keeping pseudo-edges up to date,
but there&#x27;s stil room for improvement. Here, I&#x27;ll describe a potential optimization
that I haven&#x27;t implemented yet. It&#x27;s based on a simple, but non-quite-correct,
algorithm for adding pseudo-edges incrementally:
every time you mark a node as deleted, add a pseudo-edge
from each of its in-neighbors to each of its out-neighbors. I call this
&amp;quot;pseudo-edge spamming&amp;quot; because it just eagerly throws in as many pseudo-edges
as needed.  In pictures, if we have this graph&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_7.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and we delete the &amp;quot;deleted&amp;quot; line, then we&#x27;ll add a pseudo-edge from the
in-neighbor of &amp;quot;deleted&amp;quot; (namely, &amp;quot;first&amp;quot;) to the out-neighbor of &amp;quot;deleted&amp;quot;
(namely, &amp;quot;last&amp;quot;).&lt;&#x2F;p&gt;
&lt;p&gt;This algorithm has two problems. The first is that it isn&#x27;t complete: you
might also need to add pseudo-edges when adding an edge where at least
one end is deleted. Consider this example, where our graph consists
of two disconnected parts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pseudo&#x2F;ryjikcjj21z500n2jn76rk1qqxwmk1cn-pseudo_tikz_block_8.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we add an edge from &amp;quot;deleted 1&amp;quot; to &amp;quot;deleted 2&amp;quot;, clearly we also need to add
a pseudo-edge between each of the &amp;quot;first&amp;quot; nodes and each of the &amp;quot;last&amp;quot; nodes.
In order to handle this case, we really do need to explore the deleted connected
component (which could be slow).&lt;&#x2F;p&gt;
&lt;p&gt;The second problem with our pseudo-edge spamming algorithm is that it
doesn&#x27;t handle &lt;em&gt;reverting&lt;&#x2F;em&gt; patches: it only describes how to add pseudo-edges,
not delete them.&lt;&#x2F;p&gt;
&lt;p&gt;The nice thing about pseudo-edge spamming is that even if it isn&#x27;t completely
correct, it can be used as a fast-path in the correct algorithm: when applying
a patch, if it modifies the boundary of a deleted connected component that
isn&#x27;t already dirty, use pseudo-edge spamming to update the pseudo-edges
(and don&#x27;t mark the component as dirty). In every other case, fall back to
the previous algorithm.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Patches, Part 4: Line IDs</title>
        <published>2019-02-25T00:00:00+00:00</published>
        <updated>2019-02-25T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/ids/" type="text/html"/>
        <id>https://joe.neeman.me/posts/ids/</id>
        
        <content type="html">&lt;p&gt;I&#x27;ve written quite a bit about the &lt;em&gt;theory&lt;&#x2F;em&gt; of patches and merging, but nothing
yet about how to actually implement anything efficiently. That will be the
subject of this post, and probably some future posts too.  Algorithms and
efficiency are not really discussed in the original
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1311.3903&quot;&gt;paper&lt;&#x2F;a&gt;, so most of this material I learned
from reading the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pijul.com&quot;&gt;pijul&lt;&#x2F;a&gt; source code. Having said that,
my main focus here is on broader ideas and algorithms, and so you shouldn&#x27;t
assume that anything written here is an accurate reflection of pijul
(plus, my pijul knowledge is about 2 years out of date by now).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;three-sizes&quot;&gt;Three sizes&lt;&#x2F;h1&gt;
&lt;p&gt;Before getting to the juicy details, we have to decide what it means for things
to be fast. In a VCS, there are three different size scales that we need
to think about. From smallest to biggest, we have:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The size of the &lt;em&gt;change&lt;&#x2F;em&gt;. Like, if we&#x27;re changing one line in a giant file,
then the size of the change is just the length of the one line that we&#x27;re
changing.&lt;&#x2F;li&gt;
&lt;li&gt;The size of the &lt;em&gt;current output&lt;&#x2F;em&gt;. In the case of ojo (which just tracks
a single file), this is just the size of the file.&lt;&#x2F;li&gt;
&lt;li&gt;The size of the &lt;em&gt;history&lt;&#x2F;em&gt;. This includes everything that has ever been in
the repository, so if the repository has been active for years then the size
of the history could be much bigger than the current size.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The first obvious requirement is that the size of a patch should be
proportional to the size of the change. This sounds almost too obvious to
mention, but remember the definition of a patch from
&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;#graggles&quot;&gt;here&lt;&#x2F;a&gt;: a
patch consists of a source file, a target file, and a function from one to the
other that has certain additional properties. If we were to naively translate
this definition into code, the size of a patch would be proportional to the
size of the entire file.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, this is a solved problem in the world of UNIX-style diffs (which I
mentioned all the way back in the
&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;&quot;&gt;first post&lt;&#x2F;a&gt;).
The problem is to adapt the diff approach to our mathematical patch framework;
for example, the fact that our files need not even be ordered means that it
doesn&#x27;t make sense to talk about inserting a line &amp;quot;after line 62.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;The key to solving this turns out to be to unique IDs: give every line in the
entire history of the repository a unique ID. This isn&#x27;t even very difficult:
we can give every patch in the history of the repository a unique ID by hashing
its contents. For each patch, we can enumerate the lines that it adds and then
for the rest of time, we can uniquely refer to those lines like &amp;quot;the third line
added by patch &lt;code&gt;Ar8f&lt;&#x2F;code&gt;.&amp;quot;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;representing-patches&quot;&gt;Representing patches&lt;&#x2F;h1&gt;
&lt;p&gt;Once we&#x27;ve added unique IDs to every line, it becomes pretty easy to encode
patches compactly. For example, suppose we want to describe this patch:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;ids&#x2F;hw4yarwy0fv5jvlc7hqsgprn69p1cz0y-ids_tikz_block_1.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here, &lt;code&gt;dA&lt;&#x2F;code&gt; is the unique ID of the patch that introduced the to-do, shoes, and
garbage lines, and &lt;code&gt;x5&lt;&#x2F;code&gt; is the unique ID of the patch that we want to describe.
Anyway, the patch is now easy to describe by using the unique IDs to specify
what we want to do: delete the line with ID &lt;code&gt;dA&#x2F;1&lt;&#x2F;code&gt;, add the line with ID &lt;code&gt;x5&#x2F;0&lt;&#x2F;code&gt;
and contents &amp;quot;work&amp;quot;, and add an edge from the line &lt;code&gt;dA&#x2F;2&lt;&#x2F;code&gt; to the line
&lt;code&gt;x5&#x2F;0&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s have a quick look at how this is implemented in ojo, by taking a peek
at the &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libojo&#x2F;0.1.0&#x2F;libojo&#x2F;&quot;&gt;API docs&lt;&#x2F;a&gt;.  Patches, funnily
enough, are represented by the
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libojo&#x2F;0.1.0&#x2F;libojo&#x2F;struct.Patch.html&quot;&gt;&lt;code&gt;Patch&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; struct, which
basically consists of metadata (author, commit message, timestamp) and a list
of &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;libojo&#x2F;0.1.0&#x2F;libojo&#x2F;enum.Change.html&quot;&gt;&lt;code&gt;Change&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;s. The
&lt;code&gt;Change&lt;&#x2F;code&gt;s are the most interesting part, and they look like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; class=&quot;language-rust z-code&quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;z-source z-rust&quot;&gt;&lt;span class=&quot;z-meta z-enum z-rust&quot;&gt;&lt;span class=&quot;z-storage z-modifier z-rust&quot;&gt;pub&lt;&#x2F;span&gt; &lt;span class=&quot;z-storage z-type z-enum z-rust&quot;&gt;enum&lt;&#x2F;span&gt; &lt;span class=&quot;z-entity z-name z-enum z-rust&quot;&gt;Change&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt;
    NewNode &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt; id&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; NodeId&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; contents&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-meta z-generic z-rust&quot;&gt;&lt;span class=&quot;z-support z-type z-rust&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-begin z-rust&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type z-rust&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-generic z-end z-rust&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt; &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
    DeleteNode &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt; id&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; NodeId &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
    NewEdge &lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-begin z-rust&quot;&gt;{&lt;&#x2F;span&gt; src&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; NodeId&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt; dest&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;:&lt;&#x2F;span&gt; NodeId &lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-rust&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-meta z-block z-rust&quot;&gt;&lt;span class=&quot;z-punctuation z-section z-block z-end z-rust&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In other words, the example that we saw above is basically all there is to it,
as far as patches go.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to see what actual patches look like in actual usage, you can do
that too because ojo keeps all of its data in human-readable text. After installing
ojo (with &lt;code&gt;cargo install ojo&lt;&#x2F;code&gt;), you can create a new repository (with &lt;code&gt;ojo init&lt;&#x2F;code&gt;), edit the file &lt;code&gt;ojo_file.txt&lt;&#x2F;code&gt; with your favorite editor, and then:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; class=&quot;language-console z-code&quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ ojo patch create -m &amp;quot;Initial commit&amp;quot; -a Me
Created patch PMyANESmvMQ8WR8ccSKpnH8pLc-uyt0jzGkauJBWeqx4=
$ ojo patch export -o out.txt PSc97nCk9oRrRl-2IW3H8TYVtA0hArdVtj5F0f4YSqqs=
Successfully wrote the file &amp;#39;out.txt&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now look in &lt;code&gt;out.txt&lt;&#x2F;code&gt; to see your &lt;code&gt;NewNode&lt;&#x2F;code&gt;s and &lt;code&gt;NewEdge&lt;&#x2F;code&gt;s in all their glory.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;antiquing&quot;&gt;Antiquing&lt;&#x2F;h1&gt;
&lt;p&gt;I introduced unique IDs as a way to achieve compact representations of patches,
but it turns out that they also solve a problem that I promised to explain
&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;&quot;&gt;two years ago&lt;&#x2F;a&gt;:
how do I compute the &amp;quot;most antique&amp;quot;
version of a patch? Or equivalently, if I have some patch but I want to apply
it to a slightly different repository, how do I know whether I can do that?
With our description of patches above, this is completely trivial: a patch can
only add lines, delete lines, or add edges. Adding lines is always valid, no
matter what the repository contains. Deleting lines and adding edges can be
done if and only if the lines to delete, or the lines to connect, exist in the
repository. Since lines have unique IDs, checking this is unambiguous.
Actually, it&#x27;s really easy because the line IDs are tied to the patch that
introduced them: a patch can be applied if and only if all the patch IDs that
it refers to have already been applied. For obvious reasons, we refer to these
as &amp;quot;dependencies&amp;quot;: the dependencies of a patch are all the other patches that
it refers to in &lt;code&gt;DeleteNode&lt;&#x2F;code&gt; and &lt;code&gt;NewEdge&lt;&#x2F;code&gt; commands. You can see this in action
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&#x2F;blob&#x2F;c0eac6d5248e6ef4f811b72819794786b54f09a4&#x2F;libojo&#x2F;src&#x2F;patch.rs#L203&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;By the way, this method will always give a minimal set of dependencies (in
other words, the most antique version of a patch), but it isn&#x27;t necessarily
the right thing to do. For example, if a patch deletes a line then it seems
reasonable for it to also depend on the lines &lt;em&gt;adjacent&lt;&#x2F;em&gt; to the deleted line.
Ojo might do this in the future, but for now it sticks to the minimal
dependencies.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;applying-patches&quot;&gt;Applying patches&lt;&#x2F;h1&gt;
&lt;p&gt;Now that we know how to compactly represent patches, how quickly can we apply
them?  To get really into detail here, we&#x27;d need to talk about how the state of
the repository is represented on disk (which is an interesting topic on its
own, but a bit out of scope for this post). Let&#x27;s just pretend for now that the
current state of the repository is stored as a graph in memory, using some
general-purpose crate
(like, say, &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;petgraph&#x2F;0.4.13&#x2F;petgraph&#x2F;&quot;&gt;&lt;code&gt;petgraph&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;).
Each node in the graph needs to store the contents of the corresponding line,
as well as a &amp;quot;tombstone&amp;quot; saying whether it has been deleted
(see &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jneem.github.io&#x2F;merging&#x2F;&quot;&gt;the first post&lt;&#x2F;a&gt;). Assuming we can
add nodes and edges in constant time (like, say, in &lt;code&gt;petgraph&lt;&#x2F;code&gt;), applying
a single change is a constant time operation. That means the time it takes
to apply the whole patch is proportional to the number of changes.
That&#x27;s the best we could hope for, so we&#x27;re done, right? What was even the
point of the part about three size scales?&lt;&#x2F;p&gt;
&lt;h1 id=&quot;revenge-of-the-ghosts&quot;&gt;Revenge of the ghosts&lt;&#x2F;h1&gt;
&lt;p&gt;Imagine you have a file that contains three lines:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; class=&quot;language-text z-code&quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;first line
second line
last line
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;but behind the scenes, there are a bunch of lines that used to be there. So
ojo&#x27;s representation of your file might look like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;ids&#x2F;hw4yarwy0fv5jvlc7hqsgprn69p1cz0y-ids_tikz_block_2.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s imagine that we delete &amp;quot;second line.&amp;quot; The patch to do this consists
of a single &lt;code&gt;DeleteLine&lt;&#x2F;code&gt; command, and it takes almost no time to apply:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;ids&#x2F;hw4yarwy0fv5jvlc7hqsgprn69p1cz0y-ids_tikz_block_3.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have this internal representation, ojo needs to create a file
on disk showing the new state. That is, we want to somehow go from the internal
representation above to the file&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; class=&quot;language-text z-code&quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;first line
last line
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Do you see the problem? Even though the output file is only two lines long, in
order to produce it we need to visit all of the lines that used to be there but
have since been deleted. In other words, we can apply patches quickly (in
timescale 1), but rendering the output file is slow (in timescale 3). For a
real VCS that tracks decade-old repositories, that clearly isn&#x27;t going to fly.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;pseudo-edges&quot;&gt;Pseudo-edges&lt;&#x2F;h1&gt;
&lt;p&gt;There are several ingredients that go into supporting fast rendering of output
files (fast here means &amp;quot;timescale 2, most of the time&amp;quot;, which is the best that
we can hope for). Those are going to be the subject of the next post. So that
you have something to think about until then, let me get you started: the key
idea is to introduce &amp;quot;pseudo-edges,&amp;quot; which are edges that we insert on our own
in order to allow us to &amp;quot;skip&amp;quot; large regions of deleted lines. In the example
above, the goal is to actually generate this graph:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;ids&#x2F;hw4yarwy0fv5jvlc7hqsgprn69p1cz0y-ids_tikz_block_4.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;These extra edges will allow us to quickly render output files, but they open
up a new can of worms (and were the source of several subtle bugs in &lt;code&gt;pijul&lt;&#x2F;code&gt;
last time I used it (i.e. two years ago)): how do we know when to add (or
remove, or update) pseudo-edges? Keep in mind that we aren&#x27;t willing to
traverse the entire graph to compute the pseudo-edges, because that would
defeat the purpose.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Patches, Part 3: Graggles can have cycles</title>
        <published>2019-02-19T00:00:00+00:00</published>
        <updated>2019-02-19T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/cycles/" type="text/html"/>
        <id>https://joe.neeman.me/posts/cycles/</id>
        
        <content type="html">&lt;p&gt;Almost two years ago, I promised a series of three posts about version control.
The first two
(&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;
and &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;)
introduced a new (at the time)
framework for version control. The third post, which I never finished, was
going to talk about the datastructures and algorithms used in
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pijul.com&quot;&gt;pijul&lt;&#x2F;a&gt;, a version control system built around that new
framework. The problem is that pijul is a complex piece of software, and so I
had lots of trouble wrapping my head around it.&lt;&#x2F;p&gt;
&lt;p&gt;Two years later, I&#x27;m finally ready to continue with this series of posts (but
having learned from my earlier mistakes, I&#x27;m not going to predict the total
number of posts ahead of time). In the meantime, I&#x27;ve written my own toy
version control system (VCS) to help me understand what&#x27;s going on. It&#x27;s called
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jneem&#x2F;ojo&quot;&gt;&lt;code&gt;ojo&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, and it&#x27;s extremely primitive: to start
with, it can only track a single file. However, it is (just barely)
sophisticated enough to demonstrate the important ideas. I&#x27;m also doing my best
to make the code is clear and well-documented.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;graggles-can-have-cycles&quot;&gt;Graggles can have cycles&lt;&#x2F;h1&gt;
&lt;p&gt;As I try and ease back into this whole blogging business, let me just start
with a short answer for something that several people have asked me (and which
also confused me at some point). Graggles (which, as described in the earlier
posts are a kind of generalized file in which the lines are not necessarily
ordered, but instead form a directed graph) are not
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Directed_acyclic_graph&quot;&gt;DAGs&lt;&#x2F;a&gt;; that is, they can
have cycles. To see why, suppose we start out with this graggle&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;cycles&#x2F;dc9av5n9ir6s5xmwrgqwsxw5c5xqi871-cycles_tikz_block_1.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The reason this thing isn&#x27;t a file is because there&#x27;s no prescribed order
between the &amp;quot;shoes&amp;quot; line and the &amp;quot;garbage&amp;quot; line. Now suppose that my wife and I
independently flatten this graggle, but in different ways (because apparently
she doesn&#x27;t care if I get my feet wet).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;cycles&#x2F;dc9av5n9ir6s5xmwrgqwsxw5c5xqi871-cycles_tikz_block_2.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Merging these two flattenings will produce the following graggle:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;cycles&#x2F;dc9av5n9ir6s5xmwrgqwsxw5c5xqi871-cycles_tikz_block_3.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Notice the cycle between &amp;quot;shoes&amp;quot; and &amp;quot;garbage!&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;Although I was surprised when I first noticed that graggles could have cycles, if
you think about it a bit more then it makes a lot of sense: one graggle put a
&amp;quot;garbage&amp;quot; dependency on &amp;quot;shoes&amp;quot; and the other put a &amp;quot;shoe&amp;quot; dependency on &amp;quot;garbage,&amp;quot;
and so when you merge them a cycle naturally pops out.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Patches, Part 2: Merging, patches, and pijul</title>
        <published>2017-05-13T00:00:00+00:00</published>
        <updated>2017-05-13T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/pijul/" type="text/html"/>
        <id>https://joe.neeman.me/posts/pijul/</id>
        
        <content type="html">&lt;p&gt;In the &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;&quot;&gt;last post&lt;&#x2F;a&gt;,
I talked about a mathematical
framework for a version control system (VCS) without merge conflicts. In this
post I&#x27;ll explore &lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;pijul.com&quot;&gt;pijul&lt;&#x2F;a&gt;, which is a VCS based on a similar system.
Note that pijul is under heavy development; this post is based on a development
snapshot (I almost called it a &amp;quot;git&amp;quot; snapshot by mistake), and might be out of
date by the time you read it.&lt;&#x2F;p&gt;
&lt;p&gt;The main goal of this post is to describe how pijul handles what other VCSes
call conflicts. We&#x27;ll see some examples where pijul&#x27;s approach works better than
git&#x27;s, and I&#x27;ll discuss why.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;some-basics&quot;&gt;Some basics&lt;&#x2F;h1&gt;
&lt;p&gt;I don&#x27;t want to write a full pijul tutorial here, but I do need to mention
the basic commands if you&#x27;re to have any hope of understanding the rest
of the post. Fortunately, pijul commands have pretty close analogues in
other VCSes.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pijul init&lt;&#x2F;code&gt; creates a pijul repository, much like &lt;code&gt;git init&lt;&#x2F;code&gt; or &lt;code&gt;hg init&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pijul add&lt;&#x2F;code&gt; tells pijul that it should start tracking a file, much like &lt;code&gt;git add&lt;&#x2F;code&gt; or &lt;code&gt;hg add&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pijul record&lt;&#x2F;code&gt; looks for changes in the working directory and records a patch
with those changes, so it&#x27;s similar to &lt;code&gt;git commit&lt;&#x2F;code&gt; or &lt;code&gt;hg commit&lt;&#x2F;code&gt;. Unlike
those two (and much like &lt;code&gt;darcs record&lt;&#x2F;code&gt;), &lt;code&gt;pijul record&lt;&#x2F;code&gt; asks a million
questions before doing anything; you probably want to use the &lt;code&gt;-a&lt;&#x2F;code&gt; option to
stop it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pijul fork&lt;&#x2F;code&gt; creates a new branch, like &lt;code&gt;git branch&lt;&#x2F;code&gt;. Unlike &lt;code&gt;git branch&lt;&#x2F;code&gt;,
which creates a copy of the current branch, &lt;code&gt;pijul fork&lt;&#x2F;code&gt; defaults to creating a copy of
the master branch. (This is a bug, apparently.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pijul apply&lt;&#x2F;code&gt; adds a patch to the current branch, like &lt;code&gt;git cherry-pick&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pijul pull&lt;&#x2F;code&gt; fetches and merges another branch into your current branch.
The other branch could be a remote branch, but it could also just be a
branch in the local repository.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;dealing-with-conflicts&quot;&gt;Dealing with conflicts&lt;&#x2F;h1&gt;
&lt;p&gt;As I explained in the last post, pijul differs from other VCSes by not having
merge conflicts. Instead, it has (what I call) &lt;em&gt;graggles&lt;&#x2F;em&gt;, which are different
from files in that their lines form a directed acyclic graph instead of
a totally ordered list. The thing about graggles is that you can&#x27;t really work
with them (for example, by opening them in an editor), so pijul doesn&#x27;t let you
actually see the graggles: it stores them as graggles internally, but renders them
as files for you to edit. As an example, we&#x27;ll create a graggle by asking pijul
to perform the following merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_1.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here are the pijul commands to do this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ pijul init

# Create the initial file and record it.
$ cat &amp;gt; todo.txt &amp;lt;&amp;lt; EOF
&amp;gt; to-do
&amp;gt; * work
&amp;gt; EOF
$ pijul add todo.txt
$ pijul record -a -m todo

# Switch to a new branch and add the shoes line.
$ pijul fork --branch=master shoes
$ sed -i &amp;#39;2i* shoes&amp;#39; todo.txt
$ pijul record -a -m shoes

# Switch to a third branch and add the garbage line.
$ pijul fork --branch=master garbage
$ sed -i &amp;#39;2i* garbage&amp;#39; todo.txt
$ pijul record -a -m garbage

# Now merge in the &amp;quot;shoes&amp;quot; change to the &amp;quot;garbage&amp;quot; branch.
$ pijul pull . --from-branch shoes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first thing to notice after running those commands is that pijul
doesn&#x27;t complain about any conflicts (this is not intentional; it&#x27;s
a known issue).
Anyway, if you run
the above commands then the final, merged version of &lt;code&gt;todo.txt&lt;&#x2F;code&gt; will
look like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_2.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s... a little disappointing, maybe, especially since pijul was supposed to
free us from merge conflicts, and this looks a lot like a merge conflict. The
point, though, is that pijul has to somehow produce a file -- one that the
operating system and your editor can understand -- from the graggle that it
maintains internally. The output format just happens to look a bit like what
other VCSes output when they need you to resolve a merge conflict.&lt;&#x2F;p&gt;
&lt;p&gt;As it stands, pijul doesn&#x27;t have a very user-friendly way to actually see
its internal graggles. But with a little effort, you can figure it out. The
secret is the command&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;RUST_LOG=&amp;quot;libpijul::backend=debug&amp;quot; pijul info --debug
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For every branch, this will create a file named &lt;code&gt;debug_&amp;lt;branchname&amp;gt;&lt;&#x2F;code&gt; which
describes, in graphviz&#x27;s &lt;code&gt;dot&lt;&#x2F;code&gt; format, the graggles contained in that branch.
That file&#x27;s a bit hard to read since it doesn&#x27;t directly tell you the actual
contents of any line; in place of, for example, &amp;quot;to-do&amp;quot;, it just has
a giant hex string corresponding to pijul&#x27;s internal identifiers for that line.
To decode everything, you&#x27;ll need to look at the terminal output of that
pijul command above. Part of it should look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;DEBUG:libpijul::backend::dump: ============= dumping Contents
DEBUG:libpijul::backend::dump: &amp;gt; Key { patch: PatchId 0x0414005c0c2122ca, line: LineId(0x0200000000000000) } Value (0) { value: [Ok(&amp;quot;&amp;quot;)] }
DEBUG:libpijul::backend::dump: &amp;gt; Key { patch: PatchId 0x0414005c0c2122ca, line: LineId(0x0300000000000000) } Value (12) { value: [Ok(&amp;quot;to-do\n&amp;quot;)] }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By cross-referencing that output with the contents of &lt;code&gt;debug_&amp;lt;branchname&amp;gt;&lt;&#x2F;code&gt;,
you can reconstruct pijul&#x27;s internal graggles.
Just this once, I&#x27;ve done it for you, and the result is exactly as it should be:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_3.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-should-i-do-with-a-conflict&quot;&gt;What should I do with a conflict?&lt;&#x2F;h2&gt;
&lt;p&gt;Since pijul will happily work with graggles internally, you could in principle
ignore a conflict and work on other things. That&#x27;s probably a bad idea for
several reasons (for starters, there are no good tools for working with graggles,
and their presence will probably break your build). So here&#x27;s my unsolicited
opinion: when you have a conflict, you should resolve it ASAP.
In the example above, all we need to do is remove the &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;&#x2F;code&gt; lines
and then record the changes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ sed -i 3D;5D todo.txt
$ pijul record -a -m resolve
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To back up my recommendation for immediate flattening, I&#x27;ll give an example
where pijul&#x27;s graggle-to-file rendering is lossy. Here are two different graggles:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_4.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But pijul renders both in the same way:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_5.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is a perfectly good representation of the graggle on the right, but it loses
information from the one on the left (such as the fact that both &amp;quot;home&amp;quot; lines
are the same, and the fact that &amp;quot;shop&amp;quot; and &amp;quot;home&amp;quot; don&#x27;t have a prescribed
order). The good news here is that as long as your graggle came from merging two
&lt;em&gt;files&lt;&#x2F;em&gt;, then pijul&#x27;s rendering is lossless. That means you can avoid the
problem by flattening your graggles to files after every merge (i.e., by
resolving your merge conflicts immediately).
Like &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;www.livescience.com&#x2F;33995-cockroaches.html&quot;&gt;cockroaches&lt;&#x2F;a&gt;,
graggles are important for the ecosystem as a whole, but you should still flatten them
as soon as they appear.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;case-study-1-reverting-an-old-commit&quot;&gt;Case study 1: reverting an old commit&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s (unfortunately) common to discover that an old commit introduced
a show-stopper bug. On the bright side, every VCS worth its salt has some way
of undoing the problematic commit without throwing away everything else you&#x27;ve
written since then. But if the problematic commit predates a merge conflict,
undoing it can be painful.&lt;&#x2F;p&gt;
&lt;p&gt;As an illustration of what pijul brings to the table, we&#x27;ll look at
a situation where pijul&#x27;s conflict-avoidance saves the day (at least,
compared to git; darcs also does ok here).
We&#x27;ll start with the example merge from before, including
our manual graggle resolution:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_6.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then we&#x27;ll ask pijul to revert the &amp;quot;shoes&amp;quot; patch:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ pijul unrecord --patch=&amp;lt;hash-of-shoes-patch&amp;gt;
$ pijul revert
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result? We didn&#x27;t have any conflicts while reverting the old patch,
and the final file is exactly what we expected:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_7.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s try the same thing with git:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ git init

# Create the initial file and record it.
$ cat &amp;gt; todo.txt &amp;lt;&amp;lt; EOF
&amp;gt; to-do
&amp;gt; * work
&amp;gt; EOF
$ git add todo.txt
$ git commit -a -m todo

# Switch to a new branch and add the shoes line.
$ git checkout -b shoes
$ sed -i &amp;#39;2i* shoes&amp;#39; todo.txt
$ git commit -a -m shoes

# Switch to a third branch and add the garbage line.
$ git checkout -b garbage master
$ sed -i &amp;#39;2i* garbage&amp;#39; todo.txt
$ git commit -a -m garbage

# Now merge in the &amp;quot;shoes&amp;quot; change to the &amp;quot;garbage&amp;quot; branch.
$ git merge shoes
Auto-merging todo.txt
CONFLICT (content): Merge conflict in todo.txt
Automatic merge failed; fix conflicts and then commit the result.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That was expected: there&#x27;s a conflict, so we have to resolve it. So I edited
&lt;code&gt;todo.txt&lt;&#x2F;code&gt; and manually resolved the conflict. Then,&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;# Commit the manual resolution.
$ git commit -a -m merge
# Try to revert the shoes patch.
$ git revert &amp;lt;hash-of-shoes-patch&amp;gt;
error: could not revert 4dcf1ae... shoes
hint: after resolving the conflicts, mark the corrected paths
hint: with &amp;#39;git add &amp;lt;paths&amp;gt;&amp;#39; or &amp;#39;git rm &amp;lt;paths&amp;gt;&amp;#39;
hint: and commit the result with &amp;#39;git commit&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since git can&#x27;t &amp;quot;see through&amp;quot; my manual merge resolution, it can&#x27;t handle
reverting the patch by itself. I have to manually resolve the conflicting
patches both when applying and reverting.&lt;&#x2F;p&gt;
&lt;p&gt;I won&#x27;t bore you with long command listings for other VCSes, but you can test
them out yourself! I&#x27;ve tried mercurial (which does about the same as git in
this example) and darcs (which does about the same as pijul in this example).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-little-warning-about-pijul-unrecord&quot;&gt;A little warning about &lt;code&gt;pijul unrecord&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m doing my best to present roughly equivalent command sequences for pijul and
git, but there&#x27;s something important you should know about the difference
between &lt;code&gt;pijul unrecord&lt;&#x2F;code&gt; and &lt;code&gt;git revert&lt;&#x2F;code&gt;: &lt;code&gt;pijul unrecord&lt;&#x2F;code&gt; modifies the
history of the repository, as though the unrecorded patch never existed. In
this way, &lt;code&gt;pijul unrecord&lt;&#x2F;code&gt; is a bit like a selective version of &lt;code&gt;git reset&lt;&#x2F;code&gt;.
This is probably not the functionality that you want, especially if you&#x27;re
working on a public repository. Pijul actually does have the internal
capability to do something closer to &lt;code&gt;git revert&lt;&#x2F;code&gt; (i.e., undo a patch while
keeping it in the history), but it isn&#x27;t yet user-accessible.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;sets-of-patches&quot;&gt;Sets of patches&lt;&#x2F;h1&gt;
&lt;p&gt;The time has come again to throw around some fancy math words. First,
&lt;em&gt;associativity&lt;&#x2F;em&gt;. As you might remember, a binary operator (call it &lt;code&gt;+&lt;&#x2F;code&gt;)
is associative if &lt;code&gt;(x + y) + z = x + (y + z)&lt;&#x2F;code&gt; for any &lt;code&gt;x&lt;&#x2F;code&gt;, &lt;code&gt;y&lt;&#x2F;code&gt;, and &lt;code&gt;z&lt;&#x2F;code&gt;.
The great thing about associative operators is that you never need
parentheses: you can just write &lt;code&gt;x + y + z&lt;&#x2F;code&gt; and there&#x27;s no ambiguity.
Associativity automatically extends to more than three things: there&#x27;s also
no ambiguity with &lt;code&gt;w + x + y + z&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The previous paragraph is relevant to patches because perfect merging
is associative, in the following sense: if I have multiple patches
(let&#x27;s say three to keep the diagrams manageable) then there&#x27;s a unique
way to perfectly merge them all together. That three-way merge
can be written as combinations of two-way merges in multiple different
ways, but every way that I write it gives the same result. Let&#x27;s have some pictures.
Here are my three patches:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_8.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And here&#x27;s one way I could merge them all together:
first, merge patches &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_9.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then, merge patches &lt;code&gt;pm&lt;&#x2F;code&gt; (remember, that&#x27;s the patch I get from applying &lt;code&gt;p&lt;&#x2F;code&gt; and then &lt;code&gt;m&lt;&#x2F;code&gt;,
which in the diagram above is the same as &lt;code&gt;qn&lt;&#x2F;code&gt;) and &lt;code&gt;r&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_10.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Another way would be to first merge &lt;code&gt;q&lt;&#x2F;code&gt; and &lt;code&gt;r&lt;&#x2F;code&gt;, and then merge &lt;code&gt;p&lt;&#x2F;code&gt; in to the result:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_11.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yet a third way would be to merge &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt;, then merge &lt;code&gt;q&lt;&#x2F;code&gt; and &lt;code&gt;r&lt;&#x2F;code&gt;, and finally merge
the results of those merges. This one gives a nice, symmetric picture:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_12.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The great thing about our mathematical foundation from the previous post is
that &lt;em&gt;all these merges produce the same result&lt;&#x2F;em&gt;. And I don&#x27;t just mean that
they give the same final file: they also result in the same patches, meaning
that everyone will always agree on which lines in the final file came from
where. There isn&#x27;t even anything special about the initial configuration (three
patches coming out of a single file). I could start with an arbitrarily complex
history, and there would be an unambiguous way to merge together all of the
patches that it contains. In this sense, we can say that the current state of
a pijul branch is determined by a set of patches; this is in contrast to most
existing VCSes, where the order in which patches are merged also matters.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reordering-and-antiquing-patches&quot;&gt;Reordering and antiquing patches&lt;&#x2F;h2&gt;
&lt;p&gt;One of the things you might have heard about pijul is that it can reorder
patches (i.e. that they are commutative). This is not 100% accurate, and it
might also be a bit confusing if you paid attention in my last post. That&#x27;s
because a patch, according to the definition I gave before, &lt;em&gt;includes its input
file&lt;&#x2F;em&gt;. So if you have a patch &lt;code&gt;p&lt;&#x2F;code&gt; that turns file &lt;code&gt;A&lt;&#x2F;code&gt; into file &lt;code&gt;B&lt;&#x2F;code&gt; and a patch
&lt;code&gt;q&lt;&#x2F;code&gt; that turns file &lt;code&gt;B&lt;&#x2F;code&gt; into file &lt;code&gt;C&lt;&#x2F;code&gt;, then it makes sense to apply &lt;code&gt;p&lt;&#x2F;code&gt; and
then &lt;code&gt;r&lt;&#x2F;code&gt; but not the other way around. It turns out that pijul has a nice trick
up its sleeve, which allows you to reorder patches as long as they don&#x27;t
&amp;quot;depend&amp;quot; (and I&#x27;ll explain what that means precisely) on each other.&lt;&#x2F;p&gt;
&lt;p&gt;The key idea behind reordering patches is something I call &amp;quot;antiquing.&amp;quot;
Consider the following sequenced patches:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_13.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;According to how we defined patches, the second patch (let&#x27;s call it the
garbage patch) has to be applied after the first one (the shoes patch). On the
other hand, it&#x27;s pretty obvious just by staring at them that the garbage patch
doesn&#x27;t depend on the shoes patch. In particular, the following parallel
patches convey exactly the same information, without the dependencies:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_14.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;How do I know for sure that they convey the same information? Because if we take
the perfect merge of the diagram above then we get back the original sequenced
diagram by following the top path in the merge!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_15.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This example motivates the following definition: given a pair of patches
&lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt; in sequence:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_16.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;we say that &lt;code&gt;q&lt;&#x2F;code&gt; can be &lt;em&gt;antiqued&lt;&#x2F;em&gt; if there exists some patch &lt;code&gt;a(q)&lt;&#x2F;code&gt; starting at &lt;code&gt;O&lt;&#x2F;code&gt;
such that the perfect merge between &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;a(q)&lt;&#x2F;code&gt; involves &lt;code&gt;q&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_17.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In a case like this, we can just forget about &lt;code&gt;q&lt;&#x2F;code&gt; entirely, since &lt;code&gt;a(q)&lt;&#x2F;code&gt; carries
the same information. I call it antiquing because it&#x27;s like making &lt;code&gt;q&lt;&#x2F;code&gt; look
older than it really is.&lt;&#x2F;p&gt;
&lt;p&gt;One great thing about the &amp;quot;sets of patches&amp;quot; thing above is that it let us
easily generalize antiquing from pairs of patches to arbitrarily complicated
histories. I&#x27;ll skip the details, but the idea is that you keep antiquing
a patch -- moving it back and back in the history -- until you can&#x27;t any more.
The fact that perfect merges are associative implies, as it turns out,
that every patch has a unique &amp;quot;most antique&amp;quot; version. The set of patches
leading into the most antique version of &lt;code&gt;q&lt;&#x2F;code&gt; are called &lt;code&gt;q&lt;&#x2F;code&gt;&#x27;s &lt;em&gt;dependencies&lt;&#x2F;em&gt;.
For example, here is a pair of patches where the second one cannot be
antiqued (as an exercise, try to explain why not):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_18.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Since the second patch can&#x27;t be made any more antique, the first patch above is
a dependency of the second one. In my next post, I&#x27;ll come back to antiquing
(and specifically, the question of how to efficiently find the most
antique version of a patch).&lt;&#x2F;p&gt;
&lt;p&gt;I promised to talk about reordering patches, so why did I spend paragraphs
going on about antiques? The point is that (again, because of the associative
property of perfect merges) patches in &amp;quot;parallel&amp;quot; can be applied in any order.
The point of antiquing is to make patches as parallel as possible, and so
then we can be maximally flexible about ordering them.&lt;&#x2F;p&gt;
&lt;p&gt;That last bit is important, so it&#x27;s worth saying again (and with a picture):
patches in sequence&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_19.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;cannot be re-ordered; the same information represented in parallel using an
antique of &lt;code&gt;q&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_20.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;is much more flexible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;case-study-2-parallel-development&quot;&gt;Case study 2: parallel development&lt;&#x2F;h2&gt;
&lt;p&gt;Since I&#x27;ve gone on for so long about reordering patches, let&#x27;s have an example
showing what it&#x27;s good for. Let me start with some good news: you don&#x27;t need
to know about antiquing to use pijul, because pijul does it all for you:
whenever pijul records a patch, it automatically records the most antique
version of that patch. All you&#x27;ll notice is the extra flexibility it brings.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll simulate (a toy example of) a common scenario: you&#x27;re maintaining a
long-running branch of a project that&#x27;s under active development (maybe you&#x27;re
working on a large experimental feature). Occasionally, you need to
exchange some changes with the master branch. Finally (maybe your experimental feature
was a huge success) you want to merge everything back into master.&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, we&#x27;re going to do the following experiment in both pijul and git.
The master branch will evolve in the following sequence:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_21.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On our private branch, we&#x27;ll begin from the same initial file.
We&#x27;ll start by applying the urgent fix from the master branch
(it fixed a critical bug, so we can&#x27;t wait):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_22.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then we&#x27;ll get to implementing our fancy experimental features:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_23.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll leave out the (long) command listings needed to implement the steps
above in pijul and git, but let me mention the one step that we
didn&#x27;t cover before: in order to apply the urgent fix from master, we say&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;$ pijul changes --branch master # Look for the patch you want
$ pijul apply &amp;lt;hash-of-the-patch&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In git, of course, we&#x27;ll use cherry-pick.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the results. In pijul, merging our branch with the master branch
gives no surprises:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_24.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In git, we get a conflict:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;pijul&#x2F;f2v6gfrkf90p26vp57brpvsnq09kvzz6-pijul_tikz_block_25.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s something else a bit funny with git&#x27;s behavior here: if we resolve the
conflict and look at the
history, there are two copies of the urgent fix, with two different hashes.
Since git doesn&#x27;t understand patch reordering like pijul does, &lt;code&gt;git cherry-pick&lt;&#x2F;code&gt; and &lt;code&gt;pijul apply&lt;&#x2F;code&gt; work in slightly different ways: &lt;code&gt;pijul apply&lt;&#x2F;code&gt;
just adds another patch into your set of patches, while &lt;code&gt;git cherry-pick&lt;&#x2F;code&gt;
actually creates a new patch that looks a bit like the original. From then on,
git sees the original patch and its cherry-picked one as two different patches,
which (as we&#x27;ve seen) creates problems from merging down the line.
And it gets worse: reverting one of the copies of the urgent fix (try it!) gives
pretty strange results.&lt;&#x2F;p&gt;
&lt;p&gt;By playing around with this example, you can get git to do some
slightly surprising things. (For example, by inserting an extra merge in
the right place, you can get the conflict to go away. That&#x27;s because git
has a heuristic where if it sees two different patches doing the same
thing, it suppresses the conflict.)&lt;&#x2F;p&gt;
&lt;p&gt;Pijul, on the other hand, understood that the urgent fix could be incorporated
into my private branch with no lossy modifications. That&#x27;s
because pijul silently antiqued the urgent fix, so that
the divergence between the master branch and my own branch became irrelevant.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;So hopefully you have some idea now of what pijul can and can&#x27;t do for you.
It&#x27;s an actively developed implementation of an exciting (for me, at least) new
way of looking at patches and merges, and it has a simple, fast, and totally
lossless merge algorithm with nice properties.&lt;&#x2F;p&gt;
&lt;p&gt;Will it dethrone git? Certainly not yet. For a start, it&#x27;s still
alpha-quality and under heavy development; not only should you be
worried about your data, it has several UI warts as well. Looking toward
the future, I can see reasonable arguments in both directions.&lt;&#x2F;p&gt;
&lt;p&gt;Arguing against pijul&#x27;s future world domination, you could question the
relevance of the examples I&#x27;ve shown. How often do you really end up tripping
on git&#x27;s little corner cases? Would the time saved from pijul&#x27;s improvements
actually justify the cost of switching? Those are totally reasonable
questions, and I don&#x27;t know the answer.&lt;&#x2F;p&gt;
&lt;p&gt;But here&#x27;s a more optimistic point of view: pijul&#x27;s effortless merging
and reordering might really lead to new and productive workflows. Are you
old enough to remember when git was new and most people were still on SVN
(or even CVS)? Lots of people were (quite reasonably) skeptical. &amp;quot;Who
cares about easy branching? It&#x27;s better to merge changes immediately anyway.&amp;quot;
Or, &amp;quot;who cares about distributed repositories? We have a central server,
so we may as well use it.&amp;quot; Those arguments sound silly now that we&#x27;re
all used to DVCSes and the workflow improvements that they bring, but
it took time and experimentation to develop those workflows, and the gains
weren&#x27;t always obvious beforehand. Could the same progression happen with
pijul?&lt;&#x2F;p&gt;
&lt;p&gt;In the next post, I&#x27;ll take a look at pijul&#x27;s innards, focussing particularly on
how it represents your precious data.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;acknowledgement&quot;&gt;Acknowledgement&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;d like to thank Pierre-Étienne Meunier for his comments and corrections on
a draft of this post. Of course, any errors that remain are my own
responsibility.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Patches, Part 1: Merging and patches</title>
        <published>2017-05-08T00:00:00+00:00</published>
        <updated>2017-05-08T00:00:00+00:00</updated>
        <author>
          <name>Unknown</name>
        </author>
        <link rel="alternate" href="https://joe.neeman.me/posts/merging/" type="text/html"/>
        <id>https://joe.neeman.me/posts/merging/</id>
        
        <content type="html">&lt;p&gt;A &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1311.3903&quot;&gt;recent paper&lt;&#x2F;a&gt; suggested a new mathematical
point of view on version control. I first found out about it from &lt;code&gt;pijul&lt;&#x2F;code&gt;,
a new version control system (VCS) that is loosely inspired by that paper. But
if you poke around the &lt;code&gt;pijul&lt;&#x2F;code&gt; &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pijul.com&#x2F;&quot;&gt;home page&lt;&#x2F;a&gt;, you won&#x27;t find
many details about what makes it different from existing VCSes. So I did a bit
of digging, and this series of blog posts is the result.&lt;&#x2F;p&gt;
&lt;p&gt;In the first part (i.e. this one), I&#x27;ll go over some of the theory developed in
the paper. In particular, I&#x27;ll describe a way to think about patches and
merging that is guaranteed to never, ever have a merge conflict. In the second
part, I&#x27;ll show how &lt;code&gt;pijul&lt;&#x2F;code&gt; puts that theory into action, and in the third part
I&#x27;ll dig into &lt;code&gt;pijul&lt;&#x2F;code&gt;&#x27;s implementation.&lt;&#x2F;p&gt;
&lt;p&gt;Before getting into some patch theory, a quick caveat: any real VCS needs to
deal with a lot of tedious details (directories, binary files, file renaming,
etc.). In order to get straight to the interesting new ideas, I&#x27;ll be skipping
all that. For the purposes of these posts, a VCS only needs to keep track of
a single file, which you should think of as a list of lines.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;patches&quot;&gt;Patches&lt;&#x2F;h1&gt;
&lt;p&gt;A patch is the difference between two files. Later in this series we&#x27;ll be
looking at some wild new ideas, so let&#x27;s start with something familiar and
comforting. The kind of patches we&#x27;ll discuss here go back to the early days of
Unix:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a patch works line-by-line (as opposed to, for example, word-by-word); and&lt;&#x2F;li&gt;
&lt;li&gt;a patch can add new lines, but not modify existing lines.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In order to actually have a useful VCS, you need to be able to delete lines
also. But deleting lines turns out to add some complications, so we&#x27;ll deal
with them later.&lt;&#x2F;p&gt;
&lt;p&gt;For an example, let&#x27;s start with a simple file: my to-do list for this morning.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_1.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Looking back at the list, I realize that I forgot something important. Here&#x27;s the new one:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_2.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To go from the original to-do list to the new one, I added the line with the
socks. In the format of the original Unix &amp;quot;diff&amp;quot; utility, the patch would look like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_3.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The &amp;quot;1a2&amp;quot; line is a code saying that we&#x27;re going to add something after line 1 of the
input file, and the next bit is obviously telling us what to insert.&lt;&#x2F;p&gt;
&lt;p&gt;Since this blog isn&#x27;t a command line tool, we&#x27;ll represent patches with pretty diagrams
instead of flat files. Here&#x27;s how we&#x27;ll draw the patch above:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_4.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully it&#x27;s self-explanatory, but just in case: an arrow goes from left to
right to indicate that the line on the right is the same as the one on the
left. Lines on the right with no arrow coming in are the ones that got added.
Since patches aren&#x27;t allowed to re-order the lines, the lines are guaranteed
not to cross.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s something implicit in our notation that really needs to be said out
loud: for us, &lt;b&gt;a patch is tied to a specific input file&lt;&#x2F;b&gt;. This is the first point
where we diverge from the classic Unix ways: the classic Unix patch that we
produced using &amp;quot;diff&amp;quot; could in principle be applied to &lt;em&gt;any&lt;&#x2F;em&gt; input file, and it
would still insert &amp;quot;* put on socks&amp;quot; after the first line. In many cases that
wouldn&#x27;t be what you want, but sometimes it is.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;merging&quot;&gt;Merging&lt;&#x2F;h1&gt;
&lt;p&gt;The best thing about patches is that they can enable multiple people to edit
the same file and then merge their changes afterwards. Let&#x27;s suppose that my
wife also decides to put things on my to-do list: she
takes the original file and adds a line:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_5.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now there are two new versions of my to-do list: mine with the socks, and my
wife&#x27;s with the garbage. Let&#x27;s draw them all together:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_6.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This brings us to merging: since I&#x27;d prefer to have my to-do list as a single
file, I want to merge my wife&#x27;s changes and my own. In this example, it&#x27;s
pretty obvious what the result should be, but let&#x27;s look at the general problem
of merging. We&#x27;ll do this slowly and carefully, and our endpoint might be
different from what you&#x27;re used to.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;patch-composition&quot;&gt;Patch composition&lt;&#x2F;h2&gt;
&lt;p&gt;First, I need to introduce some notation for an obvious concept: the
&lt;em&gt;composition&lt;&#x2F;em&gt; of two patches is the patch that you would get by applying one
patch and then applying the other. Since a &amp;quot;patch&amp;quot; for us also includes the
original file, you can&#x27;t just compose any two old patches. If &lt;code&gt;p&lt;&#x2F;code&gt; is a patch
taking the file &lt;code&gt;O&lt;&#x2F;code&gt; to the file &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;r&lt;&#x2F;code&gt; is a patch taking &lt;code&gt;A&lt;&#x2F;code&gt; to &lt;code&gt;B&lt;&#x2F;code&gt;, then
you can compose the two (but only in one order!) to obtain a patch from &lt;code&gt;O&lt;&#x2F;code&gt; to
&lt;code&gt;B&lt;&#x2F;code&gt;. I&#x27;ll write this composition as &lt;code&gt;pr&lt;&#x2F;code&gt;: first apply &lt;code&gt;p&lt;&#x2F;code&gt;, then &lt;code&gt;r&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s pretty easy to visualize patch composition using our diagrams: to compute
the composition of two paths, just &amp;quot;follow the arrows&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_7.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;to get the (dotted red) patch going from &lt;code&gt;O&lt;&#x2F;code&gt; to &lt;code&gt;B&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;merging-as-composition&quot;&gt;Merging as composition&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m going to define carefully what a merge is in terms of patch composition.
I&#x27;ll do this in a very math-professor kind of way: I&#x27;ll give a precise
definition, followed by some examples, and only afterwards will I explain
why the definition makes sense.
So here&#x27;s the definition: if &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt; are two different patches
taking the file &lt;code&gt;O&lt;&#x2F;code&gt; to the files &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;B&lt;&#x2F;code&gt; respectively, a &lt;em&gt;merge&lt;&#x2F;em&gt; of &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt;
is a pair of patches &lt;code&gt;r&lt;&#x2F;code&gt; and &lt;code&gt;s&lt;&#x2F;code&gt; such that&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;r&lt;&#x2F;code&gt; and &lt;code&gt;s&lt;&#x2F;code&gt; take &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;B&lt;&#x2F;code&gt; respectively to a common output file &lt;code&gt;M&lt;&#x2F;code&gt;, and&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;pr = qs&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We can illustrate this definition with a simple diagram, where the capital
letters denote files, and the lower-case letters are patches going between
them:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_8.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Instead of saying that &lt;code&gt;pr = qs&lt;&#x2F;code&gt;, a mathematician (or anyone who wants
to sound fancy) would say that the diagram above &lt;em&gt;commutes&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is an example of a merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_9.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And here is an example of something that is not a merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_10.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is not a merge because it fails the condition &lt;code&gt;pr = qs&lt;&#x2F;code&gt;: composing the
patches along the top path gives&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_11.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;but composing them along the bottom path gives&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_12.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, the two patches disagree on which of the shoes in the final list
came from the original file. This is the real meaning underlying the condition
&lt;code&gt;pr = qs&lt;&#x2F;code&gt;: it means that there will never be any ambiguity about which lines
came from where. If you&#x27;re used to using &lt;code&gt;blame&lt;&#x2F;code&gt; or &lt;code&gt;annotate&lt;&#x2F;code&gt; commands with
your favorite VCS, you can probably imagine why this sort of ambiguity would be
bad.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-historical-note&quot;&gt;A historical note&lt;&#x2F;h2&gt;
&lt;p&gt;Merging patches is an old idea, of course, and so I just want to briefly
explain how the presentation above differs from &amp;quot;traditional&amp;quot; merging:
traditionally, merging was defined by algorithms (of which there are
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Merge_(version_control)#Merge_algorithms&quot;&gt;many&lt;&#x2F;a&gt;). These algorithms would try to automatically find a good merge; if
they couldn&#x27;t, you would be asked to supply one instead.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll take a different approach: instead of starting with an algorithm, we&#x27;ll
start with a list of properties that we want a good merge to satisfy. At the end,
we&#x27;ll find that there&#x27;s a unique merge that satisfies all these properties
(and fortunately for us, there will also be an efficient algorithm to find it).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;merges-aren-t-unique&quot;&gt;Merges aren&#x27;t unique&lt;&#x2F;h1&gt;
&lt;p&gt;The main problem with merges is that they aren&#x27;t unique. This isn&#x27;t a huge
problem by itself: lots of great things aren&#x27;t unique. The problem is that we
usually want to merge automatically, and an automatic system needs an
unambiguous answer. Eventually, we&#x27;ll deal with this by defining a special
class of merges (called perfect merges) which will be unique. Before that,
we&#x27;ll explore the problem with some examples.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-silly-example&quot;&gt;A silly example&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start with a silly example, in which our merge tool decides to
add some extra nonsense:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_13.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;No sane merge tool would ever do that, of course, but it&#x27;s still a valid
merge according to our rule in the last section. Clearly, we&#x27;ll have
to tighten up the rules to exclude this case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-serious-example&quot;&gt;A serious example&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a more difficult situation with two merges that are actually
reasonable:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_14.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Both of these merges are valid according to our rules above, but you need to
actually know what the lines &lt;em&gt;mean&lt;&#x2F;em&gt; in order to decide that the first merge is
better (especially if it&#x27;s raining outside). Any reasonable automatic merging
tool would refuse to choose, instead requiring its user to do the merge
manually.&lt;&#x2F;p&gt;
&lt;p&gt;The examples above are pretty simple, but how would you decide in general
whether a merge is unambiguous and can be performed automatically? In existing
tools, the details depend on the merging algorithm. Since we started off with
a non-algorithmic approach, let&#x27;s see where that leads: instead of specifying
explicitly which merges we can do, we&#x27;ll describe the properties that an ideal
merge should have.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;perfect-merges&quot;&gt;Perfect merges&lt;&#x2F;h1&gt;
&lt;p&gt;The main idea behind the
definition I&#x27;m about to give is that it will never cause any regrets. That is,
no matter what happens in the future, we can always represent the history just
as well through the merge as we could using the original branches. Obviously,
that&#x27;s a nice property to have; personally, I think it&#x27;s non-obvious why it&#x27;s
a good choice as the &lt;em&gt;defining&lt;&#x2F;em&gt; property of the ideal merge, but we&#x27;ll get to
that later.&lt;&#x2F;p&gt;
&lt;p&gt;Ok, here it comes. Consider a merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_15.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And now suppose that the original creators of patches &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt;
continued working on their own personal branches, which merged sometime in
the future at the file &lt;code&gt;F&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_16.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We say that the merge &lt;code&gt;(r, s)&lt;&#x2F;code&gt; is a &lt;em&gt;perfect merge&lt;&#x2F;em&gt; if for &lt;em&gt;every&lt;&#x2F;em&gt; possible
choice of the merge &lt;code&gt;(u, v)&lt;&#x2F;code&gt;, there is a unique patch &lt;code&gt;w&lt;&#x2F;code&gt; so that &lt;code&gt;u = rw&lt;&#x2F;code&gt;
and &lt;code&gt;v = sw&lt;&#x2F;code&gt;. (In math terms, the diagram commutes.)
We&#x27;re going to call &lt;code&gt;w&lt;&#x2F;code&gt; a &lt;em&gt;continuation&lt;&#x2F;em&gt;, since it tells us how to continue
working from the merged file. To repeat, a merge is perfect if for every
possible future, there is a unique continuation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-perfect-merge&quot;&gt;A perfect merge&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s do a few examples to explore the various corners of our definition.
First, an example of a perfect merge:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_17.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It takes a bit of effort to actually &lt;em&gt;prove&lt;&#x2F;em&gt; that this is a perfect merge;
I&#x27;ll leave that as an exercise. It&#x27;s more interesting to see some examples
that fail to be perfect.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-silly-example-1&quot;&gt;A silly example&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s start with the silly example of a merge that introduced an unnecessary
line:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_18.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This turns out (surprise, surprise) not to be a perfect merge.
To understand how our definition of merge perfection excludes merges like this,
here is an example of a possible future without a continuation:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_19.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Since our patches can&#x27;t delete lines, there&#x27;s no way to get from &lt;code&gt;merged&lt;&#x2F;code&gt;
to &lt;code&gt;future&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-serious-example-1&quot;&gt;A serious example&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s another example, the case where there is an ambiguity in the order
of two lines in the merged file:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_20.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This one fails to be a perfect merge because there is a future with
no valid continuation: imagine that my wife and I manually created the desired merge.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_21.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now what patch (call it &lt;code&gt;w&lt;&#x2F;code&gt;) could be put between &lt;code&gt;merged&lt;&#x2F;code&gt; and &lt;code&gt;future&lt;&#x2F;code&gt; to make
everything commute?
The only possibility is&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_22.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;which isn&#x27;t a legal patch because patches aren&#x27;t allowed to swap lines.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;terminological-remarks&quot;&gt;Terminological remarks&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve been casually reading about pijul, you might have encountered the
word &amp;quot;pushout.&amp;quot; It turns out that the pattern we used for defining a perfect
merge is very common in math. Specifically, in category theory, suppose you
have the following diagram (in which capital letters are objects
and lowercase letters are morphisms):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_23.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If for every &lt;code&gt;u&lt;&#x2F;code&gt; and &lt;code&gt;v&lt;&#x2F;code&gt; there is a unique &lt;code&gt;w&lt;&#x2F;code&gt; such that the diagram commutes,
then &lt;code&gt;(r, s)&lt;&#x2F;code&gt; is said to be the &lt;em&gt;pushout&lt;&#x2F;em&gt; of &lt;code&gt;(p, q)&lt;&#x2F;code&gt;. In other words, what we
called a &amp;quot;perfect merge&amp;quot; above could also be called a &amp;quot;pushout in the category
with files as objects and patches as morphisms.&amp;quot;
For most of this article, we&#x27;ll ignore the general math terminology in favor
of language that&#x27;s more intuitive and specific to files and patches.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conflicts-and-graggles&quot;&gt;Conflicts and graggles&lt;&#x2F;h1&gt;
&lt;p&gt;The main problem with perfect merges is that they don&#x27;t always exist. In fact,
we already saw an example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_24.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The pair of patches above has no perfect merge. We haven&#x27;t actually proved it,
but intuitively it&#x27;s pretty clear, and we also discussed earlier why one
potential merge fails to be perfect. Ok, so not every pair of patches can be
merged perfectly. You probably knew that already, since that&#x27;s where merge
conflicts come from: the VCS doesn&#x27;t know how to merge patches on its own, so
you need to manually resolve some conflicts.&lt;&#x2F;p&gt;
&lt;p&gt;Now we come to the coolest part of the paper: a totally different idea for
dealing with merge conflicts. The critical part is that instead of making do
with an imperfect merge, we enlarge the set of objects that the merge can
produce. That is, not every pair of patches can be perfectly merged to
a &lt;em&gt;file&lt;&#x2F;em&gt;, but maybe they can be merged to something else.
This idea is extremely common in math, and there&#x27;s even some general abstract
nonsense showing that it can always be done: there&#x27;s an abstract way to
generalize files so that every pair of patches of generalized files can be
perfectly merged. The miraculous part here is that in this particular case,
the abstract nonsense condenses into something completely explicit and manageable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;graggles&quot;&gt;Graggles&lt;&#x2F;h2&gt;
&lt;p&gt;A file is an ordered list of lines. A &lt;em&gt;graggle&lt;&#x2F;em&gt;&lt;sup&gt;&lt;a href=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;#footnote1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
(a mixture of &amp;quot;graph&amp;quot; and &amp;quot;file&amp;quot;)
is a directed graph of lines. (Yes, I know it&#x27;s
a terrible name, but it&#x27;s better than &amp;quot;object in the free finite cocompletion
of the category of files and patches,&amp;quot; which is what the paper calls it.)
In other words, whereas a file insists on having its lines in a strict linear order,
a graggle allows them to be any directed graph. It&#x27;s pretty easy to see how relaxing
the strict ordering of lines solves our earlier merging issues.
For example, here&#x27;s a perfect merge of the sort that caused us problems before:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_25.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In retrospect, this is a pretty obvious solution: if we don&#x27;t know what order
shoes and garbage should go in, we should just produce an output that doesn&#x27;t
specify the order. What&#x27;s a bit less obvious (but is proved in the paper)
is that when we work in the world of graggles instead of the world of files,
&lt;em&gt;every&lt;&#x2F;em&gt; pair of patches has a unique perfect merge. What&#x27;s even cooler is
that the perfect merge is easy to compute. I&#x27;ll describe it in a second,
but first I have to say how patches generalize to graggles.&lt;&#x2F;p&gt;
&lt;p&gt;A patch between two graggles (say, &lt;code&gt;A&lt;&#x2F;code&gt; and &lt;code&gt;B&lt;&#x2F;code&gt;) is a function (call it &lt;code&gt;p&lt;&#x2F;code&gt;) from
the lines of &lt;code&gt;A&lt;&#x2F;code&gt; to the lines of &lt;code&gt;B&lt;&#x2F;code&gt; that respects the partial order, in the
sense that if there is a path from &lt;code&gt;x&lt;&#x2F;code&gt; to &lt;code&gt;y&lt;&#x2F;code&gt; in &lt;code&gt;A&lt;&#x2F;code&gt; then there is a path from
&lt;code&gt;p(x)&lt;&#x2F;code&gt; to &lt;code&gt;p(y)&lt;&#x2F;code&gt; in &lt;code&gt;B&lt;&#x2F;code&gt;. (This condition is an extension of the fact that
a patch between two files isn&#x27;t allowed to change the order.)
Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_26.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-perfect-merge&quot;&gt;The perfect merge&lt;&#x2F;h2&gt;
&lt;p&gt;And now for the merge algorithm: let&#x27;s say we have a patch &lt;code&gt;p&lt;&#x2F;code&gt; going from the
graggle &lt;code&gt;A&lt;&#x2F;code&gt; to the graggle &lt;code&gt;B&lt;&#x2F;code&gt; and another patch &lt;code&gt;q&lt;&#x2F;code&gt; going from &lt;code&gt;A&lt;&#x2F;code&gt; to &lt;code&gt;C&lt;&#x2F;code&gt;. To
compute the perfect merge of &lt;code&gt;p&lt;&#x2F;code&gt; and &lt;code&gt;q&lt;&#x2F;code&gt;,&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;write down the graggles &lt;code&gt;B&lt;&#x2F;code&gt; and &lt;code&gt;C&lt;&#x2F;code&gt; next to each other, and then&lt;&#x2F;li&gt;
&lt;li&gt;whenever a line in &lt;code&gt;B&lt;&#x2F;code&gt; and a line in &lt;code&gt;C&lt;&#x2F;code&gt; share a &amp;quot;parent&amp;quot; in &lt;code&gt;A&lt;&#x2F;code&gt;, collapse them into a single line.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That&#x27;s it: two steps. Here&#x27;s the algorithm at work on our previous example: we
want to merge these two patches:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_27.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So first, we write down the two to-be-merged files next to each other:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_28.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For the second step, we see that both of the &amp;quot;to-do&amp;quot; lines came from the same
line in the original file, so we combine those two into one. After doing the
same to the &amp;quot;work&amp;quot; lines, we get the desired output:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_29.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;working-with-graggles&quot;&gt;Working with graggles.&lt;&#x2F;h2&gt;
&lt;p&gt;By generalizing files to graggles, we got a very nice benefit: every pair of
patches has a (unique) perfect merge, and we can compute it easily. But there&#x27;s
an obvious flaw: all the tools that we use (editors, compilers, etc.) work on
files, not graggles. This is where the paper stops providing guidance, but there
is an easy solution: whenever a merge results in something that isn&#x27;t a file,
just make a new patch that turns it into a file. We&#x27;ll call this &lt;em&gt;flattening&lt;&#x2F;em&gt;,
and here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_30.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-looks-like-a-merge-conflict&quot;&gt;That looks like a merge conflict!&lt;&#x2F;h2&gt;
&lt;p&gt;If your eyes haven&#x27;t glazed over by now (sorry, it&#x27;s been a long post), you
might be feeling a bit cheated: I promised you a new framework that avoids the
pitfalls of manual merge resolution, but flattening looks an awful lot like
manual merge resolution. I&#x27;ll answer this criticism in more detail in the next
post, where I demonstrate the &lt;code&gt;pijul&lt;&#x2F;code&gt; tool and how it differs from &lt;code&gt;git&lt;&#x2F;code&gt;. But
here&#x27;s a little teaser: the difference between flattening and manual merge
resolution is that flattening is completely transparent to the VCS: it&#x27;s just
a patch like any other. That means we can do fun things, like re-ordering or
reverting patches, even in the presence of conflicting merges. More on that
in the next post.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;deleting-lines&quot;&gt;Deleting lines&lt;&#x2F;h1&gt;
&lt;p&gt;It&#x27;s time to finally address something I put off way at the beginning of the
post: the system I described was based on patches that can&#x27;t delete lines, and
we obviously need to allow deletions in any practical system. Unfortunately,
the paper doesn&#x27;t help here: it claims that you can incorporate deletion into
the system I described without really changing anything, but there&#x27;s a bug in
the paper. Specifically, if you tweak the definitions to allow deletion then
the category of graggles turns out not to be closed under pushouts any more.
Here&#x27;s an example where the merge algorithm in the paper turns out not to be
perfect:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_31.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(Since this post has dragged on long enough, I&#x27;ll leave it as an exercise to
figure out what the problem is).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ghost-lines&quot;&gt;Ghost lines&lt;&#x2F;h2&gt;
&lt;p&gt;Fortunately, there&#x27;s a trick to emulate line deletion in our original patch
system. I got this idea from pijul, but I&#x27;ll present it in a slightly
different way. The idea is to allow &amp;quot;ghost&amp;quot; lines instead of actually deleting
them. That is, we mark every line in our graggle as either &amp;quot;live&amp;quot; or &amp;quot;ghost.&amp;quot;
Then we add one extra rule to our patches: a live line can turn into a ghost
line, but not the other way around. We&#x27;ll draw ghost lines in gray, and arrows
pointing to ghost lines will be dashed. Here&#x27;s a patch that deletes the &amp;quot;shoes&amp;quot;
line.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;joe.neeman.me&#x2F;posts&#x2F;merging&#x2F;wq7jdh7qid8h2gmdb5687q0a2364v8yj-merge_tikz_block_32.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The last remaining piece is to extend the perfect merge algorithm to cover
our new graggles with ghost lines. This turns out to be easy; here&#x27;s the new
algorithm:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Write down side-by-side the two graggles to be merged.&lt;&#x2F;li&gt;
&lt;li&gt;For every pair of lines with a common parent, &amp;quot;collapse&amp;quot; them into a single line,
&lt;em&gt;and if one of them was a ghost, make the collapsed line a ghost&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The bit in italics is the only new part, and it barely adds any extra complexity.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;I showed you (in great detail) a mathy way of thinking about patches in a VCS,
although I haven&#x27;t shown a whole lot of motivation for it yet. At the very
least, though, next time someone starts droning on about &amp;quot;patch theory,&amp;quot; you&#x27;ll
have some idea what they&#x27;re talking about.&lt;&#x2F;p&gt;
&lt;p&gt;In the next post, I&#x27;ll talk about &lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pijul.com&quot;&gt;pijul&lt;&#x2F;a&gt;, a VCS that is loosely
based around the algorithms I described in this post. There you&#x27;ll get to see
some (toy) examples where pijul&#x27;s solid mathematical underpinnings help it to
avoid corner cases that trip up some more established VCSes.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;acknowledgement&quot;&gt;Acknowledgement&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;d like to thank Pierre-Étienne Meunier for his comments and corrections on
a draft of this post. Of course, any errors that remain are my own
responsibility.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a name=&quot;footnote1&quot;&gt;1&lt;&#x2F;a&gt;:
An earlier version of this post called them &amp;quot;digles&amp;quot; (for &lt;strong&gt;di&lt;&#x2F;strong&gt;rected
&lt;strong&gt;g&lt;&#x2F;strong&gt;raph fi&lt;strong&gt;le&lt;&#x2F;strong&gt;), but a couple years later I decided that &amp;quot;graggles&amp;quot; sounds a
bit better. Plus, if you
&lt;a rel=&quot;noopener&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quiscalus&quot;&gt;mispronounce&lt;&#x2F;a&gt; it a little, it fits in
the pijul&#x27;s whole bird theme.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
