<?xml version="1.0" encoding="utf-8"?>
<rss
  version="2.0" 
  xmlns:content="http://purl.org/rss/1.0/modules/content/"  
  xmlns:dc="http://purl.org/dc/elements/1.1/" 
  xml:base="https://www.drilian.com/" 
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
>
  <channel>
    <title>JoshJers&#39; Ramblings</title>
    <atom:link href="https://www.drilian.com/feed/" rel="self" type="application/rss+xml" />
    <link>https://www.drilian.com/</link>
    <description>Infrequently-updated blog about software development, game development, and music</description>
    <lastBuildDate>Mon, 05 Oct 2026 05:14:54 GMT</lastBuildDate>
    <language>en</language>
    <item>
      <title><![CDATA[FFTs Part 3: Optimizations to the FFT]]></title>
      <link>https://www.drilian.com/posts/2026.09.17-ffts-part-3-optimizations-to-the-fft/</link>
      <pubDate>Thu, 17 Sep 2026 22:26:09 PDT</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2026.09.17-ffts-part-3-optimizations-to-the-fft/</guid>
      <content:encoded>
        <![CDATA[
          <p>This is part 3 of a series of posts about FFTs and the specific algorithms that
<a href="https://github.com/DeadlyRedCube/FFT-20XX" target="_blank" rel="noopener">FFT-20XX</a> uses.</p>
<ol>
<li><a href="https://www.drilian.com/posts/2026.09.08-ffts-part-1-the-discrete-fourier-transform/">The Discrete Fourier Transform</a></li>
<li><a href="https://www.drilian.com/posts/2026.09.10-ffts-part-2-the-basics-of-the-fft/">The Basics of the FFT</a></li>
<li><span class="current-post">Optimizations to the FFT (← you are here)</span></li>
<li>The Inverse FFT</li>
<li>The FFT-20XX Complex-Input Algorithm</li>
<li>The FFT-20XX Real-Input Algorithm(s)</li>
</ol>
<p>In the last post we got to a basic implementation of the <a href="https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm" target="_blank" rel="noopener">radix 2 decimation-in-time Cooley-Tukey algorithm</a>. But there are still other additions and optimizations we can make before
getting into the way FFT-20XX works. We’ll start by diving into the “radix 2” part of the algorithm: we can make
improvements there.</p>
<p><span class="read-more"></span></p>
<h3>Butterflies</h3>
<p>Before we go into the specifics, I’m finally going to bring in a classic FFT diagram: the <strong>butterfly</strong>, an FFT
data flow diagram. The most basic one is for a FFT of length 2, which has only a single step over
two inputs. Here’s a diagram for the radix-2 decimation-in-time 2-length FFT:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-120" viewBox="5 -10 120 50">
    <title>
      Diagram of a 2-length FFT, showing the two inputs going into a single processing node, then back out to the two
      outputs. The second input node has a "0" representing the fact that it is unrotated.
    </title>
    <defs>
      <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
        <path class="arrowhead" d="M 0 0 L 10 5 L 0 10 z"></path>
      </marker>
      <g id="c-r2">
        <circle cx="0" cy="0" r="4"></circle>
      </g>"
      <g id="c-r4">
        <circle cx="0" cy="0" r="5"></circle>
      </g>
    </defs>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="35"></line>
      <line x1="95" x2="95" y1="-5" y2="35"></line>
    </g>
    <g class="traces">
      <path d="M 25 0 L 50 0 80 30 105 30" marker-end="url(#arrow)"></path>
      <path d="M 25 30 L 50 30 80 0 105 0" marker-end="url(#arrow)"></path>
    </g>
    <g class="computation">
      <use href="#c-r2" x="65" y="15"></use>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">1</text>
    </g>
    <g class="output-label" transform="translate(110)">
      <text y="0">0</text>
      <text y="30">1</text>
    </g>
    <g class="angle-label">
      <text class="angle-label" x="50" y="28">0</text>
    </g>
  </svg>
</div>
<p>In this diagram, the inputs are on the left, each computation (just one, in this example) is represented as a small circle,
and the outputs are on the right. The computation (for a decimation-in-time FFT) rotates the bottom input by the given
rotation value; in this diagram it’s just a <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span>, but in general the value there will be some <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> where the actual
rotation angle is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">t/N</annotation></semantics></math></span> clockwise turns (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mn>2</mn><mi>π</mi><mi>t</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">-2\pi t/N</annotation></semantics></math></span> radians).</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The formatting of these butterfly diagrams is a little different than a <em>standard</em> butterfly diagram, which tend to
look more like the following:</p>
  <div class="center-flex">
    <svg class="butterfly-diagram width-120" viewBox="5 -10 120 55">
      <title>
        A classic-style diagram of a 2-element FFT, with a "w" component on the second input showing it rotates 0/2
        times, then gets added to the first input for the first output and subtracted from the the first input for the
        second output.
      </title>
      <g class="traces">
        <line x1="25" x2="85" y1="0" y2="0" marker-end="url(#arrow)"></line>
        <line x1="25" x2="85" y1="30" y2="30" marker-end="url(#arrow)"></line>
        <line x1="90" x2="105" y1="0" y2="0" marker-end="url(#arrow)"></line>
        <line x1="90" x2="105" y1="30" y2="30" marker-end="url(#arrow)"></line>
        <line x1="60" x2="86" y1="0" y2="26" marker-end="url(#arrow)"></line>
        <line x1="60" x2="86" y1="30" y2="4" marker-end="url(#arrow)"></line>
      </g>
      <g class="sum" transform="translate(90)">
        <circle cy="30" r="4"></circle>
        <circle cy="0" r="4"></circle>
      </g>
      <rect class="angle-box" x="32" y="18" width="26" height="24"></rect>
      <g class="angle-label">
        <text class="classic" x="45" y="30">
          w
          <tspan class="small" dy="-2">0</tspan>
          <tspan class="small" dx="-10" dy="10">2</tspan>
        </text>
      </g>
      <text class="negation-label" x="72" y="39">-1</text>
      <g class="input-label" transform="translate(20)">
        <text y="0">0</text>
        <text y="30">1</text>
      </g>
      <g class="output-label" transform="translate(110)">
        <text y="0">0</text>
        <text y="30">1</text>
      </g>
    </svg>
  </div>
<p>These diagrams are more comprehensive (the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi>w</mi><mi>N</mi><mi>k</mi></msubsup></mrow><annotation encoding="application/x-tex">w^k_N</annotation></semantics></math></span> value makes the whole turn fraction of <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">k/N</annotation></semantics></math></span> clockwise turns clear,
where in my diagrams the “<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">/ N</annotation></semantics></math></span>” is implied), it has a <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">-1</annotation></semantics></math></span> multiplier on the lower input going into the sum circle
(to signify that the bottom “sum” is a difference), and the sums are each on their own line as opposed to a central
circle that represents the add/subtract pair, but in practice – especially on lower-resolution displays – my variant
is, imo, less visually noisy.</p>
<p>Plus, once you get into the higher radixes, even the standard diagrams start to be drawn more simply.
</p></blockquote><p></p>
<p>Now here’s one for the decimation-in-time 4-length FFT (where the bit reverse is performed on the input):</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-210" viewBox="0 -10 210 110">
    <title>
      Diagram of a 4-element FFT, showing how the even inputs and odd inputs combine with each other in the same way
      the 2-element FFT's do, then how the results of those interleave to create the final outputs.
    </title>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">2</text>
      <text y="60">1</text>
      <text y="90">3</text>
    </g>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="95"></line>
      <line x1="90" x2="90" y1="-5" y2="95"></line>
      <line x1="175" x2="175" y1="-5" y2="95"></line>
    </g>
    <g class="traces">
      <path d="M 25  0 50  0 80 30 110 30 160 90 185 90" marker-end="url(#arrow)"></path>
      <path d="M 25 30 50 30 80  0 110  0 160 60 185 60" marker-end="url(#arrow)"></path>
      <path d="M 25 60 50 60 80 90 110 90 160 30 185 30" marker-end="url(#arrow)"></path>
      <path d="M 25 90 50 90 80 60 110 60 160  0 185  0" marker-end="url(#arrow)"></path>
    <g></g>
    <g class="output-label" transform="translate(190)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" y="15"></use>
      <use href="#c-r2" y="75"></use>
    </g>
    <g class="computation" transform="translate(135)">
      <use href="#c-r2" y="30"></use>
      <use href="#c-r2" y="60"></use>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">0</text>
      <text y="88">0</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text y="58">0</text>
      <text y="88">1</text>
    </g>
  </g></svg>
</div>
<p>There are two “phases” in this radix-4 FFT (two runs of the outer loop of the code), which are separated via the
vertical dotted lines. Really, though, this is four of the previous (length 2 FFT) butterflies bolted together as two
separate 2-element computations (the first pass) followed by a single 4-element computation loop (the second pass,
itself just two 2-element computations stacked on top of each other wearing a trenchcoat).</p>
<p>Finally, here’s a butterfly diagram for a full 16-element FFT:</p>
<div class="center-flex">
  <svg class="butterfly-diagram" viewBox="0 -10 410 470">
    <title>
      A more complicated diagram of a 16-element FFT, showing the four "phases" of processing and how they interleave.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="455"></line>
      <line x1="90" x2="90" y1="-5" y2="455"></line>
      <line x1="170" x2="170" y1="-5" y2="455"></line>
      <line x1="260" x2="260" y1="-5" y2="455"></line>
      <line x1="380" x2="380" y1="-5" y2="455"></line>
    </g>
    <g class="traces">
      <path d="M 25 0   L 50   0 80  30 110  30 160  90 190  90 250 210 280 210 360 450 390 450" marker-end="url(#arrow)"></path>
      <path d="M 25 30  L 50  30 80   0 110   0 160  60 190  60 250 180 280 180 360 420 390 420" marker-end="url(#arrow)"></path>
      <path d="M 25 60  L 50  60 80  90 110  90 160  30 190  30 250 150 280 150 360 390 390 390" marker-end="url(#arrow)"></path>
      <path d="M 25 90  L 50  90 80  60 110  60 160   0 190   0 250 120 280 120 360 360 390 360" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 80 150 110 150 160 210 190 210 250  90 280  90 360 330 390 330" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 80 120 110 120 160 180 190 180 250  60 280  60 360 300 390 300" marker-end="url(#arrow)"></path>
      <path d="M 25 180 L 50 180 80 210 110 210 160 150 190 150 250  30 280  30 360 270 390 270" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 80 180 110 180 160 120 190 120 250   0 280   0 360 240 390 240" marker-end="url(#arrow)"></path>
      <path d="M 25 240 L 50 240 80 270 110 270 160 330 190 330 250 450 280 450 360 210 390 210" marker-end="url(#arrow)"></path>
      <path d="M 25 270 L 50 270 80 240 110 240 160 300 190 300 250 420 280 420 360 180 390 180" marker-end="url(#arrow)"></path>
      <path d="M 25 300 L 50 300 80 330 110 330 160 270 190 270 250 390 280 390 360 150 390 150" marker-end="url(#arrow)"></path>
      <path d="M 25 330 L 50 330 80 300 110 300 160 240 190 240 250 360 280 360 360 120 390 120" marker-end="url(#arrow)"></path>
      <path d="M 25 360 L 50 360 80 390 110 390 160 450 190 450 250 330 280 330 360  90 390  90" marker-end="url(#arrow)"></path>
      <path d="M 25 390 L 50 390 80 360 110 360 160 420 190 420 250 300 280 300 360  60 390  60" marker-end="url(#arrow)"></path>
      <path d="M 25 420 L 50 420 80 450 110 450 160 390 190 390 250 270 280 270 360  30 390  30" marker-end="url(#arrow)"></path>
      <path d="M 25 450 L 50 450 80 420 110 420 160 360 190 360 250 240 280 240 360   0 390   0" marker-end="url(#arrow)"></path>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">8</text>
      <text y="60">4</text>
      <text y="90">12</text>
      <text y="120">2</text>
      <text y="150">10</text>
      <text y="180">6</text>
      <text y="210">14</text>
      <text y="240">1</text>
      <text y="270">9</text>
      <text y="300">5</text>
      <text y="330">13</text>
      <text y="360">3</text>
      <text y="390">11</text>
      <text y="420">7</text>
      <text y="450">15</text>
    </g>
    <g class="output-label" transform="translate(390)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
      <text y="240">8</text>
      <text y="270">9</text>
      <text y="300">10</text>
      <text y="330">11</text>
      <text y="360">12</text>
      <text y="390">13</text>
      <text y="420">14</text>
      <text y="450">15</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">0</text>
      <text y="88">0</text>
      <text y="148">0</text>
      <text y="208">0</text>
      <text y="268">0</text>
      <text y="328">0</text>
      <text y="388">0</text>
      <text y="448">0</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text y="58">0</text>
      <text y="88">4</text>
      <text y="178">0</text>
      <text y="208">4</text>
      <text y="298">0</text>
      <text y="328">4</text>
      <text y="418">0</text>
      <text y="448">4</text>
    </g>
    <g class="angle-label" transform="translate(190)">
      <text y="118">0</text>
      <text y="148">2</text>
      <text y="178">4</text>
      <text y="208">6</text>
      <text y="358">0</text>
      <text y="388">2</text>
      <text y="418">4</text>
      <text y="448">6</text>
    </g>
    <g class="angle-label" transform="translate(280)">
      <text y="238">0</text>
      <text y="268">1</text>
      <text y="298">2</text>
      <text y="328">3</text>
      <text y="358">4</text>
      <text y="388">5</text>
      <text y="418">6</text>
      <text y="448">7</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" y="15"></use>
      <use href="#c-r2" y="75"></use>
      <use href="#c-r2" y="135"></use>
      <use href="#c-r2" y="195"></use>
      <use href="#c-r2" y="255"></use>
      <use href="#c-r2" y="315"></use>
      <use href="#c-r2" y="375"></use>
      <use href="#c-r2" y="435"></use>
    </g>
    <g class="computation" transform="translate(135)">
      <use href="#c-r2" y="30"></use>
      <use href="#c-r2" y="60"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" y="180"></use>
      <use href="#c-r2" y="270"></use>
      <use href="#c-r2" y="300"></use>
      <use href="#c-r2" y="390"></use>
      <use href="#c-r2" y="420"></use>
    </g>
    <g class="computation" transform="translate(220)">
      <use href="#c-r2" y="60"></use>
      <use href="#c-r2" y="90"></use>
      <use href="#c-r2" y="120"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" y="300"></use>
      <use href="#c-r2" y="330"></use>
      <use href="#c-r2" y="360"></use>
      <use href="#c-r2" y="390"></use>
    </g>
    <g class="computation" transform="translate(320)">
      <use href="#c-r2" y="120"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" y="180"></use>
      <use href="#c-r2" y="210"></use>
      <use href="#c-r2" y="240"></use>
      <use href="#c-r2" y="270"></use>
      <use href="#c-r2" y="300"></use>
      <use href="#c-r2" y="330"></use>
    </g>
  </svg>
</div>
<p>There are four phases in this one, and note that you can still see the structure discussed in the last post of 8
2-element computations in the first phase, then four 4-element computations in the next, then two 8-element
computations, then a single 16-element computation. Also note the rotation angles in each phase: the last phase has
rotations 0-7 (corresponding to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mi mathvariant="normal">/</mi><mn>16</mn></mrow><annotation encoding="application/x-tex">0/16</annotation></semantics></math></span> to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>7</mn><mi mathvariant="normal">/</mi><mn>16</mn></mrow><annotation encoding="application/x-tex">7/16</annotation></semantics></math></span> turns), the previous one has just the even numbers, the one before
that just 0 and 4 (every other even value), down to no rotations in the first phase.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The <strong>first radix-2 phase</strong> of any decimation-in-time FFT will <strong>always have no rotations</strong>, and the <strong>second phase</strong> will only have
<strong>0- or 90-degree rotations</strong> (a <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn></mrow><annotation encoding="application/x-tex">4</annotation></semantics></math></span> in the above diagram, as it’s <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>4</mn><mn>16</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{4}{16}</annotation></semantics></math></span> turns, or <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{4}</annotation></semantics></math></span>). When it comes time to start
optimizing, these first two phases can be done with no multiplications at all (obvious when there’s no rotation, but
a 90 degree (clockwise) rotation is just taking <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>+</mo><mi>i</mi><mi>b</mi></mrow><annotation encoding="application/x-tex">a + ib</annotation></semantics></math></span> and changing it to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>−</mo><mi>i</mi><mi>a</mi></mrow><annotation encoding="application/x-tex">b - ia</annotation></semantics></math></span>), and can typically be special-
cased for a significant speed bost.
</p></blockquote><p></p>
<h3>Doubling the Radix</h3>
<p>We went with a radix 2 breakdown in the last post because it’s the most straightforward, but there are others. The one
we’re going to focus on here is <strong>radix 4</strong>, which, in contrast to the above 16-element butterfly diagram, would instead
look like the following:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-360" viewBox="0 -10 360 470">
    <title>
      A diagram of the same 16-element FFT as before, except this time as a radix-4 transform, with every 4 inputs
      combining together, then those combining in a second pass to generate the final outputs.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="455"></line>
      <line x1="150" x2="150" y1="-5" y2="455"></line>
      <line x1="330" x2="330" y1="-5" y2="455"></line>
    </g>
    <g class="traces">
      <path d="M 25 0 L 50 0 140 90 170 90 310 450 340 450" marker-end="url(#arrow)"></path>
      <path d="M 25 30 L 50 30 140 60 170 60 310 420 340 420" marker-end="url(#arrow)"></path>
      <path d="M 25 60 L 50 60 140 30 170 30 310 390 340 390" marker-end="url(#arrow)"></path>
      <path d="M 25 90 L 50 90 140 0 170 0 310 360 340 360" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 140 210 170 210 310 330 340 330" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 140 180 170 180 310 300 340 300" marker-end="url(#arrow)"></path>
      <path d="M 25 180 L 50 180 140 150 170 150 310 270 340 270" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 140 120 170 120 310 240 340 240" marker-end="url(#arrow)"></path>
      <path d="M 25 240 L 50 240 140 330 170 330 310 210 340 210" marker-end="url(#arrow)"></path>
      <path d="M 25 270 L 50 270 140 300 170 300 310 180 340 180" marker-end="url(#arrow)"></path>
      <path d="M 25 300 L 50 300 140 270 170 270 310 150 340 150" marker-end="url(#arrow)"></path>
      <path d="M 25 330 L 50 330 140 240 170 240 310 120 340 120" marker-end="url(#arrow)"></path>
      <path d="M 25 360 L 50 360 140 450 170 450 310 90 340 90" marker-end="url(#arrow)"></path>
      <path d="M 25 390 L 50 390 140 420 170 420 310 60 340 60" marker-end="url(#arrow)"></path>
      <path d="M 25 420 L 50 420 140 390 170 390 310 30 340 30" marker-end="url(#arrow)"></path>
      <path d="M 25 450 L 50 450 140 360 170 360 310 0 340 0" marker-end="url(#arrow)"></path>
    </g>
    <g class="computation-r4">
      <use href="#c-r4" x="95" y="45"></use>
      <use href="#c-r4" x="95" y="165"></use>
      <use href="#c-r4" x="95" y="285"></use>
      <use href="#c-r4" x="95" y="405"></use>
      <use href="#c-r4" x="240" y="180"></use>
      <use href="#c-r4" x="240" y="210"></use>
      <use href="#c-r4" x="240" y="240"></use>
      <use href="#c-r4" x="240" y="270"></use>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">8</text>
      <text y="60">4</text>
      <text y="90">12</text>
      <text y="120">2</text>
      <text y="150">10</text>
      <text y="180">6</text>
      <text y="210">14</text>
      <text y="240">1</text>
      <text y="270">9</text>
      <text y="300">5</text>
      <text y="330">13</text>
      <text y="360">3</text>
      <text y="390">11</text>
      <text y="420">7</text>
      <text y="450">15</text>
    </g>
    <g class="output-label" transform="translate(340)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
      <text y="240">8</text>
      <text y="270">9</text>
      <text y="300">10</text>
      <text y="330">11</text>
      <text y="360">12</text>
      <text y="390">13</text>
      <text y="420">14</text>
      <text y="450">15</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">0</text>
      <text y="58">0</text>
      <text y="88">0</text>
      <text y="148">0</text>
      <text y="178">0</text>
      <text y="208">0</text>
      <text y="268">0</text>
      <text y="298">0</text>
      <text y="328">0</text>
      <text y="388">0</text>
      <text y="418">0</text>
      <text y="448">0</text>
    </g>>
    <g class="angle-label" transform="translate(167)">
      <text y="118">0</text>
      <text y="148">2</text>
      <text y="178">4</text>
      <text y="208">6</text>
      <text y="238">0</text>
      <text y="268">1</text>
      <text y="298">2</text>
      <text y="328">3</text>
      <text y="358">0</text>
      <text y="388">3</text>
      <text y="418">6</text>
      <text y="448">9</text>
    </g>
  </svg>
</div>
<p>Don’t worry about the specifics of the rotation values or what this four-in/four-out computation node
is doing yet – we’ll get to that shortly. For now, the important thing is that there are now <strong>half as many passes</strong> over
the data, and each computation handles <strong>four values at a time</strong> instead of two.</p>
<p>This will end up being more efficient in a couple different ways. The main efficiency gain is that having half as many
passes means it <strong>halves the number of reads from and writes to memory</strong>, which as the FFT lengths get larger can make
a big difference in how often there are cache misses. Second, it is possible to <strong>reduce the number of rotations</strong>
needed by <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{4}</annotation></semantics></math></span>, which can also make a difference on some CPUs (but not all, which we’ll get to).</p>
<p>To derive a radix 4 solution, you <em>could</em> go back to the original equation, split the inputs into four interleaved
sections (where <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mspace></mspace><mspace width="0.6667em"></mspace><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow><mtext> </mtext><mtext> </mtext><mn>4</mn></mrow><annotation encoding="application/x-tex">k\mod 4</annotation></semantics></math></span> is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow><annotation encoding="application/x-tex">1</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn></mrow><annotation encoding="application/x-tex">2</annotation></semantics></math></span>, and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn></mrow><annotation encoding="application/x-tex">3</annotation></semantics></math></span> respectively), then split the outputs into quarters, and redo all
of the rotation shenanigans that we did when deriving radix-2.</p>
<p>Nah. I’m not doing all that.</p>
<p>Instead, we’re going to start with the radix-2 diagram and build up to what that four-in/four-out computation looks
like. Every pair of radix-2 passes can be turned into a single radix-4 pass, so let’s start by highlighting a couple
such groupings in our radix-2 diagram:</p>
<div class="center-flex">
  <svg class="butterfly-diagram" viewBox="0 -10 410 470">
    <title>
      The diagram of the 16-element radix-2 FFT again, but this time with the first two phases for the first 4 inputs
      highlighted in blue, then last two phases corresponding to outputs 2, 6, 10, and 14 highlighted in red, showing
      them both as being topographically the same, despite the spacing difference.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="455"></line>
      <line x1="90" x2="90" y1="-5" y2="455"></line>
      <line x1="170" x2="170" y1="-5" y2="455"></line>
      <line x1="260" x2="260" y1="-5" y2="455"></line>
      <line x1="380" x2="380" y1="-5" y2="455"></line>
    </g>
    <g class="traces">
      <path d="M 170  90 190  90 250 210 280 210 360 450 390 450" marker-end="url(#arrow)"></path>
      <path d="M 170  30 190  30 250 150 280 150 360 390 390 390" marker-end="url(#arrow)"></path>
      <path d="M 170   0 190   0 250 120 280 120 360 360 390 360" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 80 150 110 150 160 210 190 210 250  90 280  90 360 330 390 330" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 80 120 110 120 160 180 170 180"></path>
      <path d="M 25 180 L 50 180 80 210 110 210 160 150 190 150 250  30 280  30 360 270 390 270" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 80 180 110 180 160 120 190 120 250   0 280   0 360 240 390 240" marker-end="url(#arrow)"></path>
      <path d="M 25 240 L 50 240 80 270 110 270 160 330 190 330 250 450 280 450 360 210 390 210" marker-end="url(#arrow)"></path>
      <path d="M 25 270 L 50 270 80 240 110 240 160 300 190 300 250 420 280 420 360 180 390 180" marker-end="url(#arrow)"></path>
      <path d="M 25 300 L 50 300 80 330 110 330 160 270 190 270 250 390 280 390 360 150 390 150" marker-end="url(#arrow)"></path>
      <path d="M 25 330 L 50 330 80 300 110 300 160 240 190 240 250 360 280 360 360 120 390 120" marker-end="url(#arrow)"></path>
      <path d="M 25 360 L 50 360 80 390 110 390 160 450 190 450 250 330 280 330 360  90 390  90" marker-end="url(#arrow)"></path>
      <path d="M 25 390 L 50 390 80 360 110 360 160 420 190 420 250 300 280 300 360  60 390  60" marker-end="url(#arrow)"></path>
      <path d="M 25 420 L 50 420 80 450 110 450 160 390 190 390 250 270 280 270 360  30 390  30" marker-end="url(#arrow)"></path>
      <path d="M 25 450 L 50 450 80 420 110 420 160 360 190 360 250 240 280 240 360   0 390   0" marker-end="url(#arrow)"></path>
      <line x1="380" x2="390" y1="60" y2="60" marker-end="url(#arrow)"></line>
      <line x1="380" x2="390" y1="180" y2="180" marker-end="url(#arrow)"></line>
      <line x1="380" x2="390" y1="300" y2="300" marker-end="url(#arrow)"></line>
      <line x1="380" x2="390" y1="420" y2="420" marker-end="url(#arrow)"></line>
      <path class="r4-highlight blue" d="M 25 0 L 50 0 80 30 110 30 160 90 170 90"></path>
      <path class="r4-highlight blue" d="M 25 30 L 50 30 80 0 110 0 160 60 170 60"></path>
      <path class="r4-highlight blue" d="M 25 60 L 50 60 80 90 110 90 160 30 170 30"></path>
      <path class="r4-highlight blue" d="M 25 90 L 50 90 80 60 110 60 160 0 170 0"></path>
      <path class="r4-highlight red" d="M 170 60 L 190 60 250 180 280 180 360 420 380 420"></path>
      <path class="r4-highlight red" d="M 170 180 L 190 180 250 60 280 60 360 300 380 300"></path>
      <path class="r4-highlight red" d="M 170 300 L 190 300 250 420 280 420 360 180 380 180"></path>
      <path class="r4-highlight red" d="M 170 420 L 190 420 250 300 280 300 360 60 380 60"></path>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">8</text>
      <text y="60">4</text>
      <text y="90">12</text>
      <text y="120">2</text>
      <text y="150">10</text>
      <text y="180">6</text>
      <text y="210">14</text>
      <text y="240">1</text>
      <text y="270">9</text>
      <text y="300">5</text>
      <text y="330">13</text>
      <text y="360">3</text>
      <text y="390">11</text>
      <text y="420">7</text>
      <text y="450">15</text>
    </g>
    <g class="output-label" transform="translate(390)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
      <text y="240">8</text>
      <text y="270">9</text>
      <text y="300">10</text>
      <text y="330">11</text>
      <text y="360">12</text>
      <text y="390">13</text>
      <text y="420">14</text>
      <text y="450">15</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text class="blue" y="25">0</text>
      <text class="blue" y="85">0</text>
      <text y="148">0</text>
      <text y="208">0</text>
      <text y="268">0</text>
      <text y="328">0</text>
      <text y="388">0</text>
      <text y="448">0</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text class="blue" y="55">0</text>
      <text class="blue" y="85">4</text>
      <text y="178">0</text>
      <text y="208">4</text>
      <text y="298">0</text>
      <text y="328">4</text>
      <text y="418">0</text>
      <text y="448">4</text>
    </g>
    <g class="angle-label" transform="translate(190)">
      <text y="118">0</text>
      <text y="148">2</text>
      <text class="red" y="173">4</text>
      <text y="208">6</text>
      <text y="358">0</text>
      <text y="388">2</text>
      <text class="red" y="413">4</text>
      <text y="448">6</text>
    </g>
    <g class="angle-label" transform="translate(280)">
      <text y="238">0</text>
      <text y="268">1</text>
      <text class="red" y="293">2</text>
      <text y="328">3</text>
      <text y="358">4</text>
      <text y="388">5</text>
      <text class="red" y="413">6</text>
      <text y="448">7</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" class="blue" y="15"></use>
      <use href="#c-r2" class="blue" y="75"></use>
      <use href="#c-r2" y="135"></use>
      <use href="#c-r2" y="195"></use>
      <use href="#c-r2" y="255"></use>
      <use href="#c-r2" y="315"></use>
      <use href="#c-r2" y="375"></use>
      <use href="#c-r2" y="435"></use>
    </g>
    <g class="computation" transform="translate(135)">
      <use href="#c-r2" class="blue" y="30"></use>
      <use href="#c-r2" class="blue" y="60"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" y="180"></use>
      <use href="#c-r2" y="270"></use>
      <use href="#c-r2" y="300"></use>
      <use href="#c-r2" y="390"></use>
      <use href="#c-r2" y="420"></use>
    </g>
    <g class="computation" transform="translate(220)">
      <use href="#c-r2" y="60"></use>
      <use href="#c-r2" y="90"></use>
      <use href="#c-r2" class="red" y="120"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" y="300"></use>
      <use href="#c-r2" y="330"></use>
      <use href="#c-r2" class="red" y="360"></use>
      <use href="#c-r2" y="390"></use>
    </g>
    <g class="computation" transform="translate(320)">
      <use href="#c-r2" y="120"></use>
      <use href="#c-r2" y="150"></use>
      <use href="#c-r2" class="red" y="180"></use>
      <use href="#c-r2" y="210"></use>
      <use href="#c-r2" y="240"></use>
      <use href="#c-r2" y="270"></use>
      <use href="#c-r2" class="red" y="300"></use>
      <use href="#c-r2" y="330"></use>
    </g>
  </svg>
</div>
<p>Each of the groupings of “two pairs radix-2 passes that all use the same 4 values” can be represented generically as:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-210" viewBox="0 -10 210 110">
    <title>
      A diagram similar to the earlier 4-element radix-2 FFT one, showing that inputs 2 and 3 get rotated by 2t, then
      the outputs of the inputs 1 and 3 computation get rotated by t and t + N/4 respectively before going into their
      final passes.
    </title>
    <g class="input-label" transform="translate(23)">
      <text y="0">'0'</text>
      <text y="30">'2'</text>
      <text y="60">'1'</text>
      <text y="90">'3'</text>
    </g>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="95"></line>
      <line x1="90" x2="90" y1="-5" y2="95"></line>
      <line x1="175" x2="175" y1="-5" y2="95"></line>
    </g>
    <g class="traces">
      <path d="M 25  0 50  0 80 30 110 30 160 90 185 90" marker-end="url(#arrow)"></path>
      <path d="M 25 30 50 30 80  0 110  0 160 60 185 60" marker-end="url(#arrow)"></path>
      <path d="M 25 60 50 60 80 90 110 90 160 30 185 30" marker-end="url(#arrow)"></path>
      <path d="M 25 90 50 90 80 60 110 60 160  0 185  0" marker-end="url(#arrow)"></path>
    <g></g>
    <g class="output-label" transform="translate(187)">
      <text y="0">'0'</text>
      <text y="30">'1'</text>
      <text y="60">'2'</text>
      <text y="90">'3'</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" y="15"></use>
      <use href="#c-r2" y="75"></use>
    </g>
    <g class="computation" transform="translate(135)">
      <use href="#c-r2" y="30"></use>
      <use href="#c-r2" y="60"></use>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">2t</text>
      <text y="88">2t</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text y="58">t</text>
      <text y="84">
        t+
        <tspan class="small" dy="-5">N</tspan>
        <tspan class="small" dx="-12" dy="4">&horbar;</tspan>
        <tspan class="small" dx="-11" dy="6">4</tspan>
      </text>
    </g>
  </g></svg>
</div>
<p>The input/output indices are relative to the position in the FFT, but note that the middle two indices are in the
opposite order as the output indices: this will always be true when we’ve done the bit reverse at the outset, and
remains true no matter which intermediate step we’re on (i.e. it just automatically happens that way).</p>
<p>Also note that the angles themselves are relative, too: there will be some base angle <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> (in the first highlighed group
at input indices 0, 8, 4, and 12, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span>. In the second at output indices “2, 6, 10, and 14”, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn></mrow><annotation encoding="application/x-tex">2</annotation></semantics></math></span>) that the
other angles are relative to, and we’ll set it as the first angle in the second radix-2 pass. Both angles in the
first radix-2 pass are double this <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> value (both labeled <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">2t</annotation></semantics></math></span>), and the second angle in the last pass is always a
quarter turn more than <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> (labeled <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>+</mo><mfrac><mi>N</mi><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">t + \frac{N}{4}</annotation></semantics></math></span>).</p>
<p>To get from here to a full radix-4, we can rearrange the angles a bit. If we separate the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> from the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>N</mi><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{N}{4}</annotation></semantics></math></span> in
the last angle (taking advantage of the fact that, since we’re using complex numbers, <code>Rotate(a + b)</code><span class="attrs"></span> is the same as
<code>Rotate(a) * Rotate(b)</code><span class="attrs"></span>), there is a common <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> factor to both of the second radix-2 angles, and we can distribute that
back to the latter two inputs to the first radix-2 step (the first of which has no rotation initially so it just becomes
a rotation of <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span>, and the latter of which becomes effectively <code>Rotate(2t) * Rotate(t)</code><span class="attrs"></span> which, as just explained,
becomes <code>Rotate(2t + t)</code><span class="attrs"></span> or <code>Rotate(3t)</code><span class="attrs"></span>:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-210" viewBox="0 -10 210 110">
    <title>
      Diagram of the same transform as the previous one, but with the angles rearranged. This time inputs 1, 2, and 3 get
      rotated by t, 2t, and 3t respectively, then the second output of the 1 and 3 transform gets rotated a quarter turn.
    </title>
    <g class="input-label" transform="translate(23)">
      <text y="0">'0'</text>
      <text y="30">'2'</text>
      <text y="60">'1'</text>
      <text y="90">'3'</text>
    </g>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="95"></line>
      <line x1="90" x2="90" y1="-5" y2="95"></line>
      <line x1="175" x2="175" y1="-5" y2="95"></line>
    </g>
    <g class="traces">
      <path d="M 25  0 50  0 80 30 110 30 160 90 185 90" marker-end="url(#arrow)"></path>
      <path d="M 25 30 50 30 80  0 110  0 160 60 185 60" marker-end="url(#arrow)"></path>
      <path d="M 25 60 50 60 80 90 110 90 160 30 185 30" marker-end="url(#arrow)"></path>
      <path d="M 25 90 50 90 80 60 110 60 160  0 185  0" marker-end="url(#arrow)"></path>
    <g></g>
    <g class="output-label" transform="translate(187)">
      <text y="0">'0'</text>
      <text y="30">'1'</text>
      <text y="60">'2'</text>
      <text y="90">'3'</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" y="15"></use>
      <use href="#c-r2" y="75"></use>
    </g>
    <g class="computation" transform="translate(135)">
      <use href="#c-r2" y="30"></use>
      <use href="#c-r2" y="60"></use>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">2t</text>
      <text y="58">t</text>
      <text y="88">3t</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text y="84">N/4</text>
    </g>
  </g></svg>
</div>
<p>As mentioned in an above note, a quarter turn (90-degree clockwise rotation) can be done without any sine/cosine
multiplications at all (it transforms <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>+</mo><mi>i</mi><mi>b</mi></mrow><annotation encoding="application/x-tex">a + ib</annotation></semantics></math></span> into <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>−</mo><mi>i</mi><mi>a</mi></mrow><annotation encoding="application/x-tex">b - ia</annotation></semantics></math></span>), so this is mathematically more efficient: it does
<strong>3 rotations</strong> on 3 inputs at the start of the process <strong>instead of 4 rotations</strong> (split between the first and second
inner radix-2 phases). The number of adds/subtractions remains the same, but that’s okay: there’s no way to reduce it
any further.</p>
<p>This, then, is what becomes the <strong>computation node</strong> of the <strong>radix-4 butterfly</strong> diagram we made above! This, then, is
the equivalent radix 4 diagram to the previous diagram (which hides the internal complexities, including the
effectively-free quarter turn):</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-190" viewBox="0 -10 190 110">
    <title>
      A 4-element radix-4 diagram showing the same t, 2t, and 3t rotations as the previous diagram, but instead as a
      single radix-4 process instead of 2 separate radix-2 passes.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="95"></line>
      <line x1="150" x2="150" y1="-5" y2="95"></line>
    </g>
    <g class="traces">
      <path d="M 25 0 L 50 0 140 90 170 90" marker-end="url(#arrow)"></path>
      <path d="M 25 30 L 50 30 140 60 170 60" marker-end="url(#arrow)"></path>
      <path d="M 25 60 L 50 60 140 30 170 30" marker-end="url(#arrow)"></path>
      <path d="M 25 90 L 50 90 140 0 170 0" marker-end="url(#arrow)"></path>
    </g>
    <g class="computation-r4">
      <use href="#c-r4" x="95" y="45"></use>
    </g>
    <g class="input-label" transform="translate(23)">
      <text y="0">'0'</text>
      <text y="30">'2'</text>
      <text y="60">'1'</text>
      <text y="90">'3'</text>
    </g>
    <g class="output-label" transform="translate(170)">
      <text y="0">'0'</text>
      <text y="30">'1'</text>
      <text y="60">'2'</text>
      <text y="90">'3'</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">2t</text>
      <text y="58">t</text>
      <text y="88">3t</text>
    </g>
  </svg>
</div>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>An implementation detail worth mentioning here: on some systems using the first 4-element breakdown from above (the
one with the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>+</mo><mfrac><mi>N</mi><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">t + \frac{N}{4}</annotation></semantics></math></span> term) can be more efficient! There are systems that have <strong>FMA instructions</strong> (the
<a href="http://localhost:8080/posts/2024.12.31-emulating-the-fmadd-instruction-part-1-32-bit-floats/" target="_blank" rel="noopener">fused multiply-add</a>)
which have a <strong>cost close to doing either a multiply or an add</strong> by itself (x64 CPUs with the FMA extension – like
most with AVX2 instructions – have this property). Doing it that way (which is sometimes referred to as radix-<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">2^2</annotation></semantics></math></span>),
even with what seems like an extra complex multiply, can end up being cheaper due to less overall operations and
one less required angle (the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">3t</annotation></semantics></math></span> angle isn’t required, using <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">2t</annotation></semantics></math></span> is sufficient).</p>
<p>There’s a breakdown of this trick on
<a href="https://fgiesen.wordpress.com/2023/03/19/notes-on-ffts-for-implementers/" target="_blank" rel="noopener">The ryg blog: Notes on FFTs: for implementers</a>.
</p></blockquote><p></p>
<p>Here’s the 16-element radix-4 diagram we used earlier, with our same two radix-4 sections highlighted:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-360" viewBox="0 -10 360 470">
    <title>
      Diagram of a 16-element radix-4 FFT with the same corresponding "blue" and "red" highlighted paths as in the
      earlier radix-2 diagram.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="455"></line>
      <line x1="150" x2="150" y1="-5" y2="455"></line>
      <line x1="330" x2="330" y1="-5" y2="455"></line>
    </g>
    <g class="traces">
      <path d="M 150 90 L 170 90 310 450 340 450" marker-end="url(#arrow)"></path>
      <path d="M 330 420 340 420" marker-end="url(#arrow)"></path>
      <path d="M 150 30 L 170 30 310 390 340 390" marker-end="url(#arrow)"></path>
      <path d="M 150  0 L 170  0 310 360 340 360" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 140 210 170 210 310 330 340 330" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 140 180 150 180"></path>
      <path d="M 330 300 340 300" marker-end="url(#arrow)"></path>
      <path d="M 25 180 L 50 180 140 150 170 150 310 270 340 270" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 140 120 170 120 310 240 340 240" marker-end="url(#arrow)"></path>
      <path d="M 25 240 L 50 240 140 330 170 330 310 210 340 210" marker-end="url(#arrow)"></path>
      <path d="M 25 270 L 50 270 140 300 150 300"></path>
      <path d="M 330 180 L 340 180" marker-end="url(#arrow)"></path>
      <path d="M 25 300 L 50 300 140 270 170 270 310 150 340 150" marker-end="url(#arrow)"></path>
      <path d="M 25 330 L 50 330 140 240 170 240 310 120 340 120" marker-end="url(#arrow)"></path>
      <path d="M 25 360 L 50 360 140 450 170 450 310 90 340 90" marker-end="url(#arrow)"></path>
      <path d="M 25 390 L 50 390 140 420 150 420"></path>
      <path d="M 330 60 L 340 60" marker-end="url(#arrow)"></path>
      <path d="M 25 420 L 50 420 140 390 170 390 310 30 340 30" marker-end="url(#arrow)"></path>
      <path d="M 25 450 L 50 450 140 360 170 360 310 0 340 0" marker-end="url(#arrow)"></path>
      <path class="r4-highlight blue" d="M 25  0 L 50  0 140 90 150 90"></path>
      <path class="r4-highlight blue" d="M 25 30 L 50 30 140 60 150 60"></path>
      <path class="r4-highlight blue" d="M 25 60 L 50 60 140 30 150 30"></path>
      <path class="r4-highlight blue" d="M 25 90 L 50 90 140  0 150  0"></path>
      <path class="r4-highlight red" d="M 150 60 L 170 60 310 420 330 420"></path>
      <path class="r4-highlight red" d="M 150 180 L 170 180 310 300 330 300"></path>
      <path class="r4-highlight red" d="M 150 300 L 170 300 310 180 330 180"></path>
      <path class="r4-highlight red" d="M 150 420 L 170 420 310 60 330 60"></path>
    </g>
    <g class="computation-r4">
      <use class="blue" href="#c-r4" x="95" y="45"></use>
      <use href="#c-r4" x="95" y="165"></use>
      <use href="#c-r4" x="95" y="285"></use>
      <use href="#c-r4" x="95" y="405"></use>
      <use href="#c-r4" x="240" y="180"></use>
      <use href="#c-r4" x="240" y="210"></use>
      <use class="red" href="#c-r4" x="240" y="240"></use>
      <use href="#c-r4" x="240" y="270"></use>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">8</text>
      <text y="60">4</text>
      <text y="90">12</text>
      <text y="120">2</text>
      <text y="150">10</text>
      <text y="180">6</text>
      <text y="210">14</text>
      <text y="240">1</text>
      <text y="270">9</text>
      <text y="300">5</text>
      <text y="330">13</text>
      <text y="360">3</text>
      <text y="390">11</text>
      <text y="420">7</text>
      <text y="450">15</text>
    </g>
    <g class="output-label" transform="translate(340)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
      <text y="240">8</text>
      <text y="270">9</text>
      <text y="300">10</text>
      <text y="330">11</text>
      <text y="360">12</text>
      <text y="390">13</text>
      <text y="420">14</text>
      <text y="450">15</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text class="blue" y="25">0</text>
      <text class="blue" y="55">0</text>
      <text class="blue" y="85">0</text>
      <text y="148">0</text>
      <text y="178">0</text>
      <text y="208">0</text>
      <text y="268">0</text>
      <text y="298">0</text>
      <text y="328">0</text>
      <text y="388">0</text>
      <text y="418">0</text>
      <text y="448">0</text>
    </g>>
    <g class="angle-label" transform="translate(167)">
      <text y="118">0</text>
      <text y="148">2</text>
      <text class="red" y="175">4</text>
      <text y="208">6</text>
      <text y="238">0</text>
      <text y="268">1</text>
      <text class="red" y="295">2</text>
      <text y="328">3</text>
      <text y="358">0</text>
      <text y="388">3</text>
      <text class="red" y="415">6</text>
      <text y="448">9</text>
    </g>
  </svg>
</div>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>This formulation of radix-4 is slightly different than the canonical one, which has a different “scrambled” input
or output order from the bit reverse that we’re using. In that case, the reordered indices are  reversed <em>pairs</em> of
bits: for instance, an 8-bit binary value with bits <code>01234567</code><span class="attrs"></span> would becomes <code>67452301</code><span class="attrs"></span> instead of <code>76543210</code><span class="attrs"></span>.</p>
<p>Instead, we’ve kept the radix-2 style bit reverse, the consequence of which is the reversal of the middle 2 inputs
in the above diagrams (with reversed bit <em>pairs</em>, those would be in proper order, and the angles would instead be <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span>,
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">2t</annotation></semantics></math></span>, and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">3t</annotation></semantics></math></span>, respectively).
</p></blockquote><p></p>
<p>Now, hopefully, it’s clear why the angle values are the way they are in this diagram. The first phase has no rotations
(except for the free 90-degree rotation), and the second phase has four computations with increasing rotation values of
the form <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">2t</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">3t</annotation></semantics></math></span>.</p>
<p>The only issue with using the radix-4 algorithm is that it only fully works with FFTs with power-of-four lengths
(because it splits in fours each time, so it works for length 4 but not 8, 16 but not 32, etc). The good news is that
the way we’ve formulated it (by effectively smooshing together two radix-2 passes into a single radix-4 pass), you can
just put a single radix-2 pass in there to make up the difference (and it doesn’t even matter where!)</p>
<p>Here is an 8-length FFT where the first pass is still radix 2 but the second is radix 4:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-275" viewBox="0 -10 275 230">
    <title>
      Diagram of an 8-element FFT where the first pass is done as radix-2 and the last is done as radix-4.
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="215"></line>
      <line x1="90" x2="90" y1="-5" y2="215"></line>
      <line x1="245" x2="245" y1="-5" y2="215"></line>
    </g>
    <g class="traces">
      <path d="M 25 0   L 50   0 80  30 110  30 220 210 255 210" marker-end="url(#arrow)"></path>
      <path d="M 25 30  L 50  30 80   0 110   0 220 180 255 180" marker-end="url(#arrow)"></path>
      <path d="M 25 60  L 50  60 80  90 110  90 220 150 255 150" marker-end="url(#arrow)"></path>
      <path d="M 25 90  L 50  90 80  60 110  60 220 120 255 120" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 80 150 110 150 220  90 255  90" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 80 120 110 120 220  60 255  60" marker-end="url(#arrow)"></path>
      <path d="M 25 180 L 50 180 80 210 110 210 220  30 255  30" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 80 180 110 180 220   0 255   0" marker-end="url(#arrow)"></path>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">4</text>
      <text y="60">2</text>
      <text y="90">6</text>
      <text y="120">1</text>
      <text y="150">5</text>
      <text y="180">3</text>
      <text y="210">7</text>
    </g>
    <g class="output-label" transform="translate(255)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">0</text>
      <text y="88">0</text>
      <text y="148">0</text>
      <text y="208">0</text>
    </g>
    <g class="angle-label" transform="translate(110)">
      <text y="58">0</text>
      <text y="88">2</text>
      <text y="118">0</text>
      <text y="148">1</text>
      <text y="178">0</text>
      <text y="208">3</text>
    </g>
    <g class="computation" transform="translate(65)">
      <use href="#c-r2" y="15"></use>
      <use href="#c-r2" y="75"></use>
      <use href="#c-r2" y="135"></use>
      <use href="#c-r2" y="195"></use>
    </g>
    <g class="computation-r4" transform="translate(165)">
      <use href="#c-r2" y="90"></use>
      <use href="#c-r2" y="120"></use>
    </g>
  </svg>
</div>
<p>And here’s the same 8-length FFT the other way around: radix-4 then radix-2:</p>
<div class="center-flex">
  <svg class="butterfly-diagram width-275" viewBox="0 -10 275 230">
    <title>
      Diagram of an 8-element FFT where the first pass is done as radix-4 and the last is done as radix-2 (the opposite
      ordering as the previous diagram).
    </title>
    <g class="write">
      <line x1="30" x2="30" y1="-5" y2="215"></line>
      <line x1="150" x2="150" y1="-5" y2="215"></line>
      <line x1="245" x2="245" y1="-5" y2="215"></line>
    </g>
    <g class="traces">
      <path d="M 25   0 L 50   0 140  90 170  90 220 210 255 210" marker-end="url(#arrow)"></path>
      <path d="M 25  30 L 50  30 140  60 170  60 220 180 255 180" marker-end="url(#arrow)"></path>
      <path d="M 25  60 L 50  60 140  30 170  30 220 150 255 150" marker-end="url(#arrow)"></path>
      <path d="M 25  90 L 50  90 140   0 170   0 220 120 255 120" marker-end="url(#arrow)"></path>
      <path d="M 25 120 L 50 120 140 210 170 210 220  90 255  90" marker-end="url(#arrow)"></path>
      <path d="M 25 150 L 50 150 140 180 170 180 220  60 255  60" marker-end="url(#arrow)"></path>
      <path d="M 25 180 L 50 180 140 150 170 150 220  30 255  30" marker-end="url(#arrow)"></path>
      <path d="M 25 210 L 50 210 140 120 170 120 220   0 255   0" marker-end="url(#arrow)"></path>
    
      <use href="#c-r4" y="45"></use>
      <use href="#c-r4" y="165"></use>
    </g>
    <g class="computation" transform="translate(195)">
      <use href="#c-r4" y="60"></use>
      <use href="#c-r4" y="90"></use>
      <use href="#c-r4" y="120"></use>
      <use href="#c-r4" y="150"></use>
    </g>
    <g class="input-label" transform="translate(20)">
      <text y="0">0</text>
      <text y="30">4</text>
      <text y="60">2</text>
      <text y="90">6</text>
      <text y="120">1</text>
      <text y="150">5</text>
      <text y="180">3</text>
      <text y="210">7</text>
    </g>
    <g class="output-label" transform="translate(255)">
      <text y="0">0</text>
      <text y="30">1</text>
      <text y="60">2</text>
      <text y="90">3</text>
      <text y="120">4</text>
      <text y="150">5</text>
      <text y="180">6</text>
      <text y="210">7</text>
    </g>
    <g class="angle-label" transform="translate(50)">
      <text y="28">0</text>
      <text y="58">0</text>
      <text y="88">0</text>
      <text y="148">0</text>
      <text y="178">0</text>
      <text y="208">0</text>
    </g>>
    <g class="angle-label" transform="translate(167)">
      <text y="118">0</text>
      <text y="148">1</text>
      <text y="178">2</text>
      <text y="208">3</text>
    </g>
  </svg>
</div>
<p>For longer FFTs, the radix-2 pass can be anywhere in the middle, as well - whatever is most efficient for your
implementation! I found it easiest to place it right after the very first radix-4 pass (i.e. like that last diagram),
mostly for simplicity.</p>
<h3>Other Radix Flavors</h3>
<p>So if radix 4 is more efficient than radix 2, that raises the question: are there other, even more efficient radixes?
radix-8? radix-16? How far is too far?</p>
<p>Radix-4 is the largest power-of-two radix that has “free” rotations in the middle (that quarter turn). Radix-8 has some
45-degree-multiple rotations in the middle, which still require some multiplication. However, because both cosine and
sine of 45 degrees (and related friends) are both <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><msqrt><mn>2</mn></msqrt></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{\sqrt{2}}</annotation></semantics></math></span>, the multiply can be done as a common factor so
it still is <em>somewhat</em> more efficient.</p>
<p>The problems with radix-8 tend to have to do with CPU register limits: you need to load 8 complex values (so 16 scalars),
then (similar to how radix-2 needs 1 and radix-4 needs 3) it tends to need 7 complex twiddles plus the
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><msqrt><mn>2</mn></msqrt></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{\sqrt{2}}</annotation></semantics></math></span> internal value (so another 15 scalars) and you tend to also need some scratch registers as well to
do computations. AVX and AVX2 only have 16 SIMD registers so even before you get to the angles you’ve already run out of
register space. AARCH64 has 32, so you could maybe squeeze in there, but it would be tight. <em>Plus</em> you’re going to be
up against <a href="https://en.wikipedia.org/wiki/CPU_cache#Associativity" target="_blank" rel="noopener">CPU cache associativity</a> for systems with eight-way
associative caches, because you’re loading from 8 locations but <em>also</em> need to be loading the angles from somewhere.</p>
<p>Past radix-8, you’re out of register space, hitting CPU cache associativity limits, and also have more internal
rotations, so it’s going to perform way worse under most circumstances.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>One place where it’s really tempting to use radix-8, however, is on the very first decimation-in-time step, which
has no twiddles (all seven of the twiddle angles are 0), and thus only has those nice-to-compute internal 45-degree
rotations. However, the register limits are still an issue and, depending on how you have to load your values (i.e.
for SIMD purposes you may want to do a deinterleave to separate the real and imaginary components), you may still not
have enough space. But it’s worth considering, if you can.
</p></blockquote><p></p>
<p>There is another radix option worth mentioning: the <a href="https://en.wikipedia.org/wiki/Split-radix_FFT_algorithm" target="_blank" rel="noopener">split-radix FFT algorithm</a>, which I’m not going to detail here. In terms of required math operations, the split-radix
algorithm gets you to some of the lowest-known possible operation counts. Unfortunately, in practice, I found that it
breaks up the nice regularity of using just a standard radix-4 algorithm and ultimately – at least in my attempts at
implementing it – had worse performance overall.</p>
<p>Okay, we’re through all of the radix shenanigans!</p>
<h3>Lookup Tables</h3>
<p>The last thing to touch on in this post is doing <strong>lookup tables for the angles</strong>. These tend to be exactly what they
sound like: you precompute a table for your required FFT length that contains all of the sin/cos values needed for that
length.</p>
<p>The simplest lookup table is one where you have <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>N</mi><mn>2</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{N}{2}</annotation></semantics></math></span> cos/sin values (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow><annotation encoding="application/x-tex">0</annotation></semantics></math></span> turns through <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>N</mi><mn>2</mn></mfrac><mo>−</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">\frac{N}{2} - 1</annotation></semantics></math></span>
turns), in order. However, there are some potential ways to make the table a little more efficient (with size,
cache-friendliness, or both):</p>
<ul>
<li>You don’t need both <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>N</mi><mn>2</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{N}{2}</annotation></semantics></math></span> <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>cos</mi><mo>⁡</mo></mrow><annotation encoding="application/x-tex">\cos</annotation></semantics></math></span> <em>and</em> <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mi>N</mi><mn>2</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{N}{2}</annotation></semantics></math></span> <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>sin</mi><mo>⁡</mo></mrow><annotation encoding="application/x-tex">\sin</annotation></semantics></math></span> values (unless you’re doing SIMD with interleaved
real and imaginary components): because <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mfrac><mi>π</mi><mn>2</mn></mfrac><mo>+</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\cos(t) = \sin(\frac{\pi}{2} + t)</annotation></semantics></math></span>, <strong>you can store <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mn>3</mn><mi>N</mi></mrow><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{3N}{4}</annotation></semantics></math></span> scalar
values</strong> and start the cosine lookup partway into the table, and use <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mn>1</mn><mn>4</mn></mfrac></mrow><annotation encoding="application/x-tex">\frac{1}{4}</annotation></semantics></math></span> less memory.</li>
<li>if you’re doing radix-4, you can <strong>avoid storing the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">3t</annotation></semantics></math></span> angles</strong> and instead compute them using the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow><annotation encoding="application/x-tex">t</annotation></semantics></math></span> and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>t</mi></mrow><annotation encoding="application/x-tex">2t</annotation></semantics></math></span> rotations,
which saves a bunch of table space. This may sound like you’re adding a complex multiply <em>back in</em> after eliminating
it, but you can move that computation into an outer loop so it isn’t an issue.</li>
<li>It can also be convenient – for SIMD loads and CPU cache-friendliness – to store not only the angles in order for
size <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span>, but to <strong>store all of the table sizes from some minimum length</strong> (like 4 or 8) up to your max length.
<ul>
<li>Doing this is nice because it means you can build a single table for your <em>maximum</em> required FFT length and it will
perform just as well for any smaller FFT lengths as well).</li>
<li>Also, the earlier passes of the algorithm can take advantage of these smaller tables (the second radix-2 pass only
needs the angles for a length-4 FFT, and the next only needs the angles for a length-8 FFT, etc). Loading from them
can help with CPU cache coherence (as all of the required angles at that level are contiguous), plus SIMD loading
if you need multiple angles within a single pass.</li>
</ul>
</li>
</ul>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>It’s also worth noting, for some algorithmic variants (i.e. if you do the bit reverse at the end, or if you are doing a decimation-in-frequency implementation), it can be better to store the table in bit-reversed order (for example, for a
16-length FFT you’d store the rotations in order 0 4 2 6 1 5 3 7)! This has a neat property: if you make a table for a
length-<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span> FFT, the first <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span> entries are also exactly what would be in a table for a length-<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span> FFT! This means
the table can be built for the maximum required FFT length, but unlike the last bullet point above, with no extra
memory footprint.
</p></blockquote><p></p>
<p>For FFT-20XX I found all of the above bullet points to be useful.</p>
<h3>What Now?</h3>
<p>No code this time, but using the above tricks (namely: using radix-4 instead of radix-2 where possible and switching
to lookup tables for the rotations) gets you to a somewhat decent baseline FFT implementation! There are many more
places to optimize: figuring out SIMD support and optimizing the bit reversal pass (or – spoiler alert – eliminating
it altogether). We’ll get to those once we finally get into FFT-20XX’s algorithms.</p>
<p>Before that, next time, we’ll touch on the decimation-in-frequency version of the algorithm on our way to finally
talking about the <strong>inverse FFT</strong> and how to compute it!</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[FFTs Part 2: The Basics of the FFT]]></title>
      <link>https://www.drilian.com/posts/2026.09.10-ffts-part-2-the-basics-of-the-fft/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:37 PDT</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2026.09.10-ffts-part-2-the-basics-of-the-fft/</guid>
      <content:encoded>
        <![CDATA[
          <p>This is part 2 of a series of posts about FFTs and the specific algorithms that
<a href="https://github.com/DeadlyRedCube/FFT-20XX" target="_blank" rel="noopener">FFT-20XX</a> uses.</p>
<ol>
<li><a href="https://www.drilian.com/posts/2026.09.08-ffts-part-1-the-discrete-fourier-transform/">The Discrete Fourier Transform</a></li>
<li><span class="current-post">The Basics of the FFT (← you are here)</span></li>
<li><a href="https://www.drilian.com/posts/2026.09.17-ffts-part-3-optimizations-to-the-fft/">Optimizations to the FFT</a></li>
<li>The Inverse FFT</li>
<li>The FFT-20XX Complex-Input Algorithm</li>
<li>The FFT-20XX Real-Input Algorithm(s)</li>
</ol>
<p><a href="https://www.drilian.com/posts/2026.09.08-ffts-part-1-the-discrete-fourier-transform/">Part 1</a> went through the core of how the
<a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform" target="_blank" rel="noopener">discrete Fourier transform (DFT)</a> works to turn a series of
samples in time into a set of samples in frequency, and ended up with the following algoritm:</p>
<pre class="language-cs"><code class="language-cs"><span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> length<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>outIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> outIdx <span class="token operator">&lt;</span> length<span class="token punctuation">;</span> outIdx<span class="token operator">++</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Start the sum at 0.</span>
    <span class="token class-name"><span class="token keyword">float</span></span> sum <span class="token operator">=</span> <span class="token number">0</span>

    <span class="token comment">// This is the angular frequency (in radians/sample) for</span>
    <span class="token comment">// the given output sample.</span>
    <span class="token class-name"><span class="token keyword">float</span></span> angularFreq  <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span> <span class="token operator">*</span> pi <span class="token operator">*</span> outIdx <span class="token operator">/</span> length

    <span class="token keyword">for</span> <span class="token punctuation">(</span>inIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> inIdx <span class="token operator">&lt;</span> length<span class="token punctuation">;</span> inIdx<span class="token operator">++</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Get the rotation value for the current input index</span>
      <span class="token comment">// then use that to calculate the resulting rotation.</span>
      <span class="token class-name"><span class="token keyword">float</span></span> radians <span class="token operator">=</span> inIdx <span class="token operator">*</span> angularFreq
      <span class="token class-name">complex</span> rotation <span class="token operator">=</span> <span class="token function">cos</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span> <span class="token operator">+</span> i<span class="token operator">*</span><span class="token function">sin</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span>

      <span class="token comment">// A complex multiply with a unit complex vector is</span>
      <span class="token comment">// a 2D rotation.</span>
      sum <span class="token operator">+=</span> inputs<span class="token punctuation">[</span>inIdx<span class="token punctuation">]</span> <span class="token operator">*</span> rotation
    <span class="token punctuation">}</span>

    outputs<span class="token punctuation">[</span>outIdx<span class="token punctuation">]</span> <span class="token operator">=</span> sum
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>A reminder: we’re going to be talking about these values as <strong>complex numbers</strong> (of the form <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>+</mo><mi>i</mi><mi>b</mi></mrow><annotation encoding="application/x-tex">a + i b</annotation></semantics></math></span>), but if you prefer,
just think of them as a <strong>2D vector</strong> instead (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mi>a</mi><mo separator="true">,</mo><mi>b</mi><mo stretchy="false">]</mo></mrow><annotation encoding="application/x-tex">[a, b]</annotation></semantics></math></span> where <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi></mrow><annotation encoding="application/x-tex">a</annotation></semantics></math></span> is the x coordinate and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow><annotation encoding="application/x-tex">b</annotation></semantics></math></span> is the y coordinate).</p>
<p>We’ll be doing <strong>complex multiplication</strong> of these vectors with others, but in all cases a multiply like <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>⋅</mo><mi>R</mi></mrow><annotation encoding="application/x-tex">P \cdot R</annotation></semantics></math></span> will
be a <strong>rotation</strong> of point <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow><annotation encoding="application/x-tex">P</annotation></semantics></math></span> by the angle that <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow><annotation encoding="application/x-tex">R</annotation></semantics></math></span> represents (where <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo>=</mo><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>+</mo><mi>i</mi><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">R = \cos(\theta) + i\sin(\theta)</annotation></semantics></math></span>).
</p></blockquote><p></p>
<p>This algorithm is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>N</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N^2)</annotation></semantics></math></span>, but there’s a much more efficient, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mtext> </mtext><mi>log</mi><mo>⁡</mo><mtext> </mtext><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N\,\log\,N)</annotation></semantics></math></span> way: the
<strong><a href="https://en.wikipedia.org/wiki/Fast_Fourier_transform" target="_blank" rel="noopener">fast Fourier transform (FFT)</a></strong> – and we’re going to derive it.</p>
<p><span class="read-more"></span></p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The algorithm we end up deriving here is going to work specifically for <strong>FFT lengths that are powers of two</strong>,
because it’s the easiest to implement (plus, to be honest, power-of-two lengths were sufficient for FFT-20XX so that’s all I
implemented). The following techniques can be adapted for other lengths (i.e. if you need a length that is a mutliple of 3
or 5), plus there are ways to do this for arbitrary lengths (for instance:
<a href="https://en.wikipedia.org/wiki/Chirp_Z-transform" target="_blank" rel="noopener">Bluestein’s algorithm</a>). This series isn’t going to touch on any of those.
</p></blockquote><p></p>
<h3>Halving the Work</h3>
<p>The first step of deriving the FFT algoritm is figuring out where we can halve the amount of work. Since the DFT boils down
to “every output takes every input multiplied by a rotation”, we can visualize all of the rotations as a 2D grid, where
each column is an input and each row is an output.</p>
<p>Here’s a visualization of the rotations for an
FFT of length 8 (adding a dividing line between the top and bottom halves, which should make sense in a moment):</p>
<div class="center-flex">
  <div class="angle-grid-8" role="img" aria-label="2D diagram of the rotation angles for an 8-element where every column is an input to the FFT and every row is an output of the FFT. The elements in each row represent the rotations for that row, which are all summed together for that output. For each row, from left (input 0) to right (input 7) the angle starts at 0, then changes by a consistent amount: each output 'k' the amount is k/8 turns, so output 0 is all angles of 0 (no rotation), output 1 has each angle rotated 1/8 turn more than the last, etc. This relationship is also true if you were to swap the rows/columns.">
    <div></div>
    <div></div>
    <div class="col-label even">in[0]</div>
    <div class="col-label-small even">[0]</div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div class="col-label-small even">[1]</div>
    <div></div>
    <div class="col-label even">in[2]</div>
    <div class="col-label-small even">[2]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div class="col-label-small even">[3]</div>
    <div></div>
    <div class="col-label even">in[4]</div>
    <div class="col-label-small even">[4]</div>
    <div></div>
    <div class="col-label odd">in[5]</div>
    <div class="col-label-small even">[5]</div>
    <div></div>
    <div class="col-label even">in[6]</div>
    <div class="col-label-small even">[6]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div class="col-label-small even">[7]</div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[0] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[1] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[2] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[3] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="divider"></div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[4] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[5] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.9999999999998" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345316" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[6] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.9999999999998" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999983" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[7] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345316" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999983" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865493" y2="170.71067811865456"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<p>Each rotation (as per the above pseudocode) for a given input index <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span> and output index <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span> is <strong><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mi>k</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">n k/N</annotation></semantics></math></span> turns</strong> (where
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span> is 8, the FFT length). But <strong>rotations are cyclical</strong>, and, for instance, turning a full turn (360 degrees) is
equivalent to not turning at all.</p>
<p>We can use this cyclicity to our advantage! If you look at the <strong>even inputs</strong> (columns indexed 0, 2, 4, and 6), you’ll note that the
<strong>rotations in the bottom half are identical to the top half</strong>. For instance, with input 2, outputs 0 and 4 are both
unrotated, outputs 1 and 5 both rotate a quarter turn, and so on.</p>
<p>For the <strong>odd inputs</strong> (columns 1, 3, 5, 7), the angles <em>aren’t</em> the same. However, they’re still related: the
<strong>bottom half</strong> rotations are always <strong>180 degrees</strong> from the corresponding <strong>top half</strong> rotations.
As an example, looking at the column for input 1, we see that output 4’s rotation is a half turn, where output 0 is
unrotated – they’re facing away from each other. The other odd outputs are similar, with the bottom half rotations
<strong>always pointing the opposite direction</strong> as their top-half counterparts.</p>
<p>In other words, the bottom half rotations are <strong>negated versions of the top half</strong> (because
with 2D vector <code>A</code><span class="attrs"></span>, <code>Rotate(A, 180 degrees)</code><span class="attrs"></span> is the same as the 2D vector <code>-A</code><span class="attrs"></span>).</p>
<p>Let’s rearrange the columns of the diagram to group the even and odd elements together (evens to the left, odds to the right):</p>
<div class="center-flex">
  <div class="angle-grid-8" role="img" aria-label="2D diagram of the rotations of an 8-element FFT. This diagram has the same information in it as the above, except the inputs (columns) have been rearranged such that the even ones (0, 2, 4, and 6) are all first and the odd ones are after.">
    <div></div>
    <div></div>
    <div class="col-label even">in[0]</div>
    <div class="col-label-small even">[0]</div>
    <div></div>
    <div class="col-label even">in[2]</div>
    <div class="col-label-small even">[2]</div>
    <div></div>
    <div class="col-label even">in[4]</div>
    <div class="col-label-small even">[4]</div>
    <div></div>
    <div class="col-label even">in[6]</div>
    <div class="col-label-small even">[6]</div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div class="col-label-small even">[1]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div class="col-label-small even">[3]</div>
    <div></div>
    <div class="col-label odd">in[5]</div>
    <div class="col-label-small even">[5]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div class="col-label-small even">[7]</div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[0] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[1] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[2] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[3] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="divider"></div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[4] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[5] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.9999999999998" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345316" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[6] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.9999999999998" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999983" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[7] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999983" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345316" y2="170.7106781186548"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865493" y2="170.71067811865456"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<p>We can then rewrite the bottom-half odd inputs as negations of the top-half ones (note the minus signs in place of the
usual plus signs):</p>
<div class="center-flex">
  <div class="angle-grid-8" role="img" aria-label="2D diagram of the rotations of an 8-element FFT. This diagram is the same as the above diagram, except the bottom-half odd angles (the bottom-right quadrant) now match the corresponding rows in the upper-right (the top-half odd angles), but instead of being written as adding those inputs to the output, they're written as subtractions (i.e. the angles are the same, but negated).">
    <div></div>
    <div></div>
    <div class="col-label even">in[0]</div>
    <div class="col-label-small even">[0]</div>
    <div></div>
    <div class="col-label even">in[2]</div>
    <div class="col-label-small even">[2]</div>
    <div></div>
    <div class="col-label even">in[4]</div>
    <div class="col-label-small even">[4]</div>
    <div></div>
    <div class="col-label even">in[6]</div>
    <div class="col-label-small even">[6]</div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div class="col-label-small even">[1]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div class="col-label-small even">[3]</div>
    <div></div>
    <div class="col-label odd">in[5]</div>
    <div class="col-label-small even">[5]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div class="col-label-small even">[7]</div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[0] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[1] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[2] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[3] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="divider"></div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[4] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[5] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.9999999999998" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[6] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[7] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999983" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-neg angle-plus">-</div>
    <svg class="odd-neg" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<p>Now all of the <strong>corresponding</strong> top and bottom half outputs use the <strong>same corresponding rotations per input</strong>; the
only difference is that in the <strong>bottom half</strong> we <strong>subtract</strong> the rotated <strong>odd inputs</strong> instead of adding them.
Our code, then, can switch to summing up all of the even and odd elements separately, then computing two outputs at once
by doing <code>evenSum + oddSum</code><span class="attrs"></span> for the first (top) half of the outputs and <code>evenSum - oddSum</code><span class="attrs"></span> for the second (bottom) half,
which means we’re now doing half the work as before!</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>If you want this (and the next section) done algebraically instead of visually, the wiki page for the
<a href="https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm" target="_blank" rel="noopener">Cooley-Tukey algorithm</a> has a fairly clear breakdown
of the derivation of the whole thing.
</p></blockquote><p></p>
<p>Here’s what the code looks like if we do that:</p>
<pre class="language-cs"><code class="language-cs"><span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> length<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// NOTE: Now looping over half the length</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>outIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> outIdx <span class="token operator">&lt;</span> length <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> outIdx<span class="token operator">++</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Start the sums at 0.</span>
    <span class="token class-name"><span class="token keyword">float</span></span> evenSum <span class="token operator">=</span> <span class="token number">0</span>
    <span class="token class-name"><span class="token keyword">float</span></span> oddSum <span class="token operator">=</span> <span class="token number">0</span>

    <span class="token comment">// angular frequency for the given output</span>
    <span class="token comment">// (in radians/sample)</span>
    <span class="token class-name"><span class="token keyword">float</span></span> angularFreq  <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span> <span class="token operator">*</span> pi <span class="token operator">*</span> outIdx <span class="token operator">/</span> length

    <span class="token comment">// NOTE: now incrementing by 2 to do odds and evens</span>
    <span class="token comment">// separately.</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span>inIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> inIdx <span class="token operator">&lt;</span> length<span class="token punctuation">;</span> inIdx <span class="token operator">+=</span> <span class="token number">2</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Get the rotation value for the current input index</span>
      <span class="token comment">// then use that to calculate the resulting rotation.</span>
      <span class="token class-name"><span class="token keyword">float</span></span> evenRads <span class="token operator">=</span> inIdx <span class="token operator">*</span> angularFreq
      <span class="token class-name">complex</span> evenRot <span class="token operator">=</span> <span class="token function">cos</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span> <span class="token operator">+</span> i<span class="token operator">*</span><span class="token function">sin</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span>
      evenSum <span class="token operator">+=</span> inputs<span class="token punctuation">[</span>inIdx <span class="token operator">+</span> <span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">*</span> evenRot

      <span class="token comment">// Do the same with the next index (inIdx + 1), for</span>
      <span class="token comment">// the odd sum.</span>
      <span class="token class-name"><span class="token keyword">float</span></span> oddRads <span class="token operator">=</span> <span class="token punctuation">(</span>inIdx <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> angularFreq
      <span class="token class-name">complex</span> oddRot <span class="token operator">=</span> <span class="token function">cos</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span> <span class="token operator">+</span> i<span class="token operator">*</span><span class="token function">sin</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span>
      oddSum <span class="token operator">+=</span> inputs<span class="token punctuation">[</span>inIdx <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">*</span> oddRot
    <span class="token punctuation">}</span>

    <span class="token comment">// Computing 2 outputs in the same loop!</span>
    outputs<span class="token punctuation">[</span>outIdx<span class="token punctuation">]</span>            <span class="token operator">=</span> evenSum <span class="token operator">+</span> oddSum
    outputs<span class="token punctuation">[</span>outIdx <span class="token operator">+</span> length<span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> evenSum <span class="token operator">-</span> oddSum
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>If you compare this code to what we started with,  it does half the rotations and half the adds (counting
subtractions) as the original: the inner loop does effectively the same amount of work in both, but the outer loop runs
half as much. Unfortunately, it’s not quite to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mtext> </mtext><mi>log</mi><mo>⁡</mo><mtext> </mtext><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N\,\log\,N)</annotation></semantics></math></span> yet, so we need a way to effectively do this halving of
work recursively.</p>
<h3>Halving Work Again (and Again and…)</h3>
<p>Let’s look again at just the first half of the outputs from our 8-length example:</p>
<div class="center-flex">
  <div class="angle-grid-8" role="img" aria-label="This is the same diagram as the above 2D angles diagram, except without the bottom half.">
    <div></div>
    <div></div>
    <div class="col-label even">in[0]</div>
    <div class="col-label-small even">[0]</div>
    <div></div>
    <div class="col-label even">in[2]</div>
    <div class="col-label-small even">[2]</div>
    <div></div>
    <div class="col-label even">in[4]</div>
    <div class="col-label-small even">[4]</div>
    <div></div>
    <div class="col-label even">in[6]</div>
    <div class="col-label-small even">[6]</div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div class="col-label-small even">[1]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div class="col-label-small even">[3]</div>
    <div></div>
    <div class="col-label odd">in[5]</div>
    <div class="col-label-small even">[5]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div class="col-label-small even">[7]</div>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[0] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[1] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[2] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label"><span class="out-name">out</span><span class="out-name-small">O</span>[3] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<p>If we look at just the even inputs for a moment, the pattern of rotations in that 4×4 block of the diagram ends up
being the exact same pattern of rotations as for a standard 4-element FFT (half the size of our 8-element example):</p>
<div class="center-flex">
  <div class="angle-grid-4" role="img" aria-label="2D diagram of the rotations of a 4-element FFT. Similar to the above 8-element one, except, of course, only 4 inputs and outputs. The rotation angles are the same as if you were to remove all of the odd inputs from the 8-element FFT's diagram, and take only the top half of the outputs.">
    <div></div>
    <div></div>
    <div class="col-label even">in[0]</div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div></div>
    <div class="col-label even">in[2]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div class="row-label">out[0] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label">out[1] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label">out[2] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label">out[3] =</div>
    <div class="even angle-plus"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>If you care about the algebra: this is because for each <em>even</em> rotation (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mi>n</mi><mi>k</mi></mrow><mi>N</mi></mfrac></mrow><annotation encoding="application/x-tex">\frac{n k}{N}</annotation></semantics></math></span>), <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span> is a multiple of 2. So if
we say <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>m</mi><mo>=</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">2m = k</annotation></semantics></math></span>, then the rotation becomes <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mn>2</mn><mi>n</mi><mi>m</mi></mrow><mi>N</mi></mfrac></mrow><annotation encoding="application/x-tex">\frac{2 n m}{N}</annotation></semantics></math></span> which is equivalent to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mfrac><mrow><mi>n</mi><mi>m</mi></mrow><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow></mfrac></mrow><annotation encoding="application/x-tex">\frac{n m}{N/2}</annotation></semantics></math></span>, which is exactly how
the angles for an FFT of length <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span> would be specified.
</p></blockquote><p></p>
<p>This means that we can <strong>calculate the even sums recursively</strong>, computing the <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span> even sums as if they were an
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span>-length DFT, which means we can do the same even/odd dance to halve its work as well.</p>
<p>That’s great for the even inputs, but what about the odd inputs? They have a similar pattern, but it’s not <em>quite</em> the
same:</p>
<div class="center-flex">
  <div class="angle-grid-4" role="img" aria-label="2D diagram of the odd-input rotations from the 8-element FFT diagrams, isolated.">
    <div></div>
    <div></div>
    <div class="col-label odd">in[1]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div></div>
    <div class="col-label odd">in[5]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div class="row-label odd">odd[0] =</div>
    <div class="odd angle-plus"></div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label odd">odd[1] =</div>
    <div class="odd angle-plus"></div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134523" y2="29.28932188134526"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="29.28932188134523"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label odd">odd[2] =</div>
    <div class="odd angle-plus"></div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="row-label odd">odd[3] =</div>
    <div class="odd angle-plus"></div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.7106781186547" y2="29.28932188134516"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.289321881345302" y2="29.289321881345202"></line><circle cx="100" cy="100" r="100"></circle></svg>
  </div>
</div>
<p>The first row is the same as in the 4-element DFT: no rotation. The second row, however, is different: each angle in the
row is 1/8th of a turn rotated relative to the corresponding row of the 4-element DFT. The third and fourth rows are
also different, with an extra 2/8th and 3/8th turn added to the angles in each row, respectively.</p>
<p>In other words, any given <strong>output <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span>’s odd inputs</strong> will have their rotations be <strong><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">k/N</annotation></semantics></math></span> turns more</strong> than doing a
standard <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow><annotation encoding="application/x-tex">N/2</annotation></semantics></math></span>-length FFT with those inputs.</p>
<p>Since “rotating every vector by <code>t</code><span class="attrs"></span> then summing” is the same as “summing then rotating by <code>t</code><span class="attrs"></span>” (that is, <code>Rotate(A, t) + Rotate(B, t)</code><span class="attrs"></span> equals
<code>Rotate(A + B, t)</code><span class="attrs"></span>), we can calculate each odd sum by doing the work as a half-length DFT (like we can with
the evens), then adjust each sum by rotating by its corresponding <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mi>N</mi></mrow><annotation encoding="application/x-tex">k/N</annotation></semantics></math></span>-turn angle (called a <strong>twiddle factor</strong>, which is a fun
name to say), like so:</p>
<div class="center-flex">
  <div class="angle-grid-4-mul" role="img" aria-label="2D diagram of the odd-input rotations from the 8-element FFT (like the previous diagram). This time, however, the rotatiosn for the inputs match the ones from the 4-element FFT, but each row is being multiplied by a single constant rotation, starting with a rotation of 0 turns (no rotation), then increasing 1/8th of a turn for every output.">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div class="col-label even">in[1]</div>
    <div></div>
    <div class="col-label odd">in[3]</div>
    <div></div>
    <div class="col-label even">in[5]</div>
    <div></div>
    <div class="col-label odd">in[7]</div>
    <div></div>
    <div class="row-label odd">odd[0] =</div>
    <svg class="odd-rot" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-rot">&times;</div>
    <div class="tall-lbracket"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="tall-rbracket"></div>
    <div class="row-label odd">odd[1] =</div>
    <svg class="odd-rot" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="170.71067811865476" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-rot">&times;</div>
    <div class="tall-lbracket"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="tall-rbracket"></div>
    <div class="row-label odd">odd[2] =</div>
    <svg class="odd-rot" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-rot">&times;</div>
    <div class="tall-lbracket"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="tall-rbracket"></div>
    <div class="row-label odd">odd[3] =</div>
    <svg class="odd-rot" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="29.28932188134526" y2="170.71067811865476"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd-rot">&times;</div>
    <div class="tall-lbracket"></div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="200" y2="100"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="99.99999999999999" y2="0"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="even angle-plus">+</div>
    <svg class="even" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="0" y2="100.00000000000001"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="odd angle-plus">+</div>
    <svg class="odd" viewBox="-5 -5 210 210"><line class="x-axis" x1="0" x2="200" y1="100" y2="100"></line><line class="y-axis" x1="100" x2="100" y1="0" y2="200"></line><line class="angle-line" x1="100" y1="100" x2="100" y2="200"></line><circle cx="100" cy="100" r="100"></circle></svg>
    <div class="tall-rbracket"></div>
  </div>
</div>
<p>Using that, we can now recursively calculate the even and odd sums using half-length FFTs, apply the twiddles, then do
the add and subtract to get the final outputs.</p>
<p>Let’s write this as code again, this time recursively! To do this, for now we’ll use some temporary storage for simplicity:</p>
<pre class="language-cs"><code class="language-cs"><span class="token comment">// Helper function to build a complex</span>
<span class="token comment">// rotation value</span>
<span class="token return-type class-name">complex</span> <span class="token function">Twiddle</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> k<span class="token punctuation">,</span> <span class="token class-name"><span class="token keyword">int</span></span> len<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token class-name"><span class="token keyword">float</span></span> angularFreq <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span> <span class="token operator">*</span> pi <span class="token operator">*</span> k <span class="token operator">/</span> len
  <span class="token keyword">return</span> <span class="token function">cos</span><span class="token punctuation">(</span>angularFreq<span class="token punctuation">)</span> <span class="token operator">+</span> i<span class="token operator">*</span><span class="token function">sin</span><span class="token punctuation">(</span>angularFreq<span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> len<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>len <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// To end the recursion, special-case</span>
    <span class="token comment">// the 1-length FFT, which sums one input,</span>
    <span class="token comment">// unrotated (i.e. it does nothing).</span>
    outputs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> inputs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
    <span class="token keyword">return</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Temp storage for the recursion</span>
  complex<span class="token punctuation">[</span>length <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">]</span> evens
  complex<span class="token punctuation">[</span>length <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">]</span> odds

  <span class="token comment">// Separate the even and odd inputs</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>k <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> k <span class="token operator">&lt;</span> len <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> k<span class="token operator">++</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    evens<span class="token punctuation">[</span>k<span class="token punctuation">]</span> <span class="token operator">=</span> inputs<span class="token punctuation">[</span><span class="token number">2</span> <span class="token operator">*</span> k<span class="token punctuation">]</span>
    odds<span class="token punctuation">[</span>k<span class="token punctuation">]</span>  <span class="token operator">=</span> inputs<span class="token punctuation">[</span><span class="token number">2</span> <span class="token operator">*</span> k <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Recurse into the even and odd FFTs</span>
  <span class="token comment">// (each using the same array as input and</span>
  <span class="token comment">// output, which is fine here)</span>
  <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>evens<span class="token punctuation">,</span> evens<span class="token punctuation">,</span> len<span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">)</span>
  <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>odds<span class="token punctuation">,</span>  odds<span class="token punctuation">,</span>  len<span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">)</span>

  <span class="token keyword">for</span> <span class="token punctuation">(</span>k <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> k <span class="token operator">&lt;</span> len <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> k<span class="token operator">++</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Get the corresponding recursively-calcualted</span>
    <span class="token comment">// sum from the relevant arrays.</span>
    <span class="token class-name">complex</span> evenSum <span class="token operator">=</span> evens<span class="token punctuation">[</span>k<span class="token punctuation">]</span>
    <span class="token class-name">complex</span> oddSum <span class="token operator">=</span> odds<span class="token punctuation">[</span>k<span class="token punctuation">]</span>

    <span class="token comment">// The odd sum needs to be rotated by this</span>
    <span class="token comment">// output's twiddle factor.</span>
    oddSum <span class="token operator">*=</span> <span class="token function">Twiddle</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> len<span class="token punctuation">)</span>

    <span class="token comment">// Now, as before, calculate two outputs from</span>
    <span class="token comment">// these sums.</span>
    outputs<span class="token punctuation">[</span>k<span class="token punctuation">]</span>         <span class="token operator">=</span> evenSum <span class="token operator">+</span> oddSum
    outputs<span class="token punctuation">[</span>k <span class="token operator">+</span> len<span class="token operator">/</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> evenSum <span class="token operator">-</span> oddSum
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is now truly, algorithmically <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mtext> </mtext><mi>log</mi><mo>⁡</mo><mtext> </mtext><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N\,\log\,N)</annotation></semantics></math></span>! We halve the work at <em>each level</em> of the FFT, which does <em>way</em> fewer
rotations and adds than the initial DFT algorithm (for instance, the original algorithm for a 1024-length DFT would do
around a million rotations, but this one does around five thousand).</p>
<p>But it’s not ideal: it’s recursive, and uses temporary storage per recursion. We can do better! But now we get
into the fun that is…</p>
<h3>Deinterleaving and Bit Reversal</h3>
<p>To do all of this without recursion or scratch space, we need a way to do each level of FFT in-place. So let’s look at
how data is flowing through the system.</p>
<p>Here’s a diagram of the routine (at a single recursion level) where, from left to right:</p>
<ul>
<li>The inputs are split into evens and odds.</li>
<li>Each of those two groups has a half-length sub-DFT performed on it.</li>
<li>Those results are then used in pairs by each step of the computation loop to write corresponding pairs of outputs.</li>
</ul>
<div class="center-flex">
  <svg class="interleave-diagram wide" viewBox="-40 0 350 320">
    <title>Diagram of the routine, which is laid out as described above.</title>
    <rect x="35" y="5" width="100" height="150"></rect>
    <rect x="35" y="165" width="100" height="150"></rect>
    <rect x="175" y="5" width="100" height="310"></rect>
    <text class="input-label even" x="-20" y="10">0</text><line x1="-20" x2="35" y1="10" y2="10"></line>
    <text class="input-label odd" x="-20" y="30">1</text><line x1="-20" x2="35" y1="50" y2="30"></line>
    <text class="input-label even" x="-20" y="50">2</text><line x1="-20" x2="35" y1="90" y2="50"></line>
    <text class="input-label odd" x="-20" y="70">3</text><line x1="-20" x2="35" y1="130" y2="70"></line>
    <text class="input-label even" x="-20" y="90">4</text><line x1="-20" x2="35" y1="170" y2="90"></line>
    <text class="input-label odd" x="-20" y="110">5</text><line x1="-20" x2="35" y1="210" y2="110"></line>
    <text class="input-label even" x="-20" y="130">6</text><line x1="-20" x2="35" y1="250" y2="130"></line>
    <text class="input-label odd" x="-20" y="150">7</text><line x1="-20" x2="35" y1="290" y2="150"></line>
    <text class="input-label even" x="-20" y="170">8</text><line x1="-20" x2="35" y1="30" y2="170"></line>
    <text class="input-label odd" x="-20" y="190">9</text><line x1="-20" x2="35" y1="70" y2="190"></line>
    <text class="input-label even" x="-20" y="210">10</text><line x1="-20" x2="35" y1="110" y2="210"></line>
    <text class="input-label odd" x="-20" y="230">11</text><line x1="-20" x2="35" y1="150" y2="230"></line>
    <text class="input-label even" x="-20" y="250">12</text><line x1="-20" x2="35" y1="190" y2="250"></line>
    <text class="input-label odd" x="-20" y="270">13</text><line x1="-20" x2="35" y1="230" y2="270"></line>
    <text class="input-label even" x="-20" y="290">14</text><line x1="-20" x2="35" y1="270" y2="290"></line>
    <text class="input-label odd" x="-20" y="310">15</text><line x1="-20" x2="35" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="155" y="10">0</text><line x1="135" x2="145" y1="10" y2="10"></line><line x1="165" x2="175" y1="10" y2="10"></line>
    <text class="inner-label even" x="155" y="30">2</text><line x1="135" x2="145" y1="30" y2="30"></line><line x1="165" x2="175" y1="30" y2="30"></line>
    <text class="inner-label even" x="155" y="50">4</text><line x1="135" x2="145" y1="50" y2="50"></line><line x1="165" x2="175" y1="50" y2="50"></line>
    <text class="inner-label even" x="155" y="70">6</text><line x1="135" x2="145" y1="70" y2="70"></line><line x1="165" x2="175" y1="70" y2="70"></line>
    <text class="inner-label even" x="155" y="90">8</text><line x1="135" x2="145" y1="90" y2="90"></line><line x1="165" x2="175" y1="90" y2="90"></line>
    <text class="inner-label even" x="155" y="110">10</text><line x1="135" x2="145" y1="110" y2="110"></line><line x1="165" x2="175" y1="110" y2="110"></line>
    <text class="inner-label even" x="155" y="130">12</text><line x1="135" x2="145" y1="130" y2="130"></line><line x1="165" x2="175" y1="130" y2="130"></line>
    <text class="inner-label even" x="155" y="150">14</text><line x1="135" x2="145" y1="150" y2="150"></line><line x1="165" x2="175" y1="150" y2="150"></line>
    <text class="inner-label odd" x="155" y="170">1</text><line x1="135" x2="145" y1="170" y2="170"></line><line x1="165" x2="175" y1="170" y2="170"></line>
    <text class="inner-label odd" x="155" y="190">3</text><line x1="135" x2="145" y1="190" y2="190"></line><line x1="165" x2="175" y1="190" y2="190"></line>
    <text class="inner-label odd" x="155" y="210">5</text><line x1="135" x2="145" y1="210" y2="210"></line><line x1="165" x2="175" y1="210" y2="210"></line>
    <text class="inner-label odd" x="155" y="230">7</text><line x1="135" x2="145" y1="230" y2="230"></line><line x1="165" x2="175" y1="230" y2="230"></line>
    <text class="inner-label odd" x="155" y="250">9</text><line x1="135" x2="145" y1="250" y2="250"></line><line x1="165" x2="175" y1="250" y2="250"></line>
    <text class="inner-label odd" x="155" y="270">11</text><line x1="135" x2="145" y1="270" y2="270"></line><line x1="165" x2="175" y1="270" y2="270"></line>
    <text class="inner-label odd" x="155" y="290">13</text><line x1="135" x2="145" y1="290" y2="290"></line><line x1="165" x2="175" y1="290" y2="290"></line>
    <text class="inner-label odd" x="155" y="310">15</text><line x1="135" x2="145" y1="310" y2="310"></line><line x1="165" x2="175" y1="310" y2="310"></line>
    <!-- -->
    <text class="output-label even" x="290" y="10">0</text><line x1="275" x2="285" y1="10" y2="10"></line>
    <text class="output-label odd" x="290" y="30">1</text><line x1="275" x2="285" y1="30" y2="30"></line>
    <text class="output-label even" x="290" y="50">2</text><line x1="275" x2="285" y1="50" y2="50"></line>
    <text class="output-label odd" x="290" y="70">3</text><line x1="275" x2="285" y1="70" y2="70"></line>
    <text class="output-label even" x="290" y="90">4</text><line x1="275" x2="285" y1="90" y2="90"></line>
    <text class="output-label odd" x="290" y="110">5</text><line x1="275" x2="285" y1="110" y2="110"></line>
    <text class="output-label even" x="290" y="130">6</text><line x1="275" x2="285" y1="130" y2="130"></line>
    <text class="output-label odd" x="290" y="150">7</text><line x1="275" x2="285" y1="150" y2="150"></line>
    <text class="output-label even" x="290" y="170">8</text><line x1="275" x2="285" y1="170" y2="170"></line>
    <text class="output-label odd" x="290" y="190">9</text><line x1="275" x2="285" y1="190" y2="190"></line>
    <text class="output-label even" x="290" y="210">10</text><line x1="275" x2="285" y1="210" y2="210"></line>
    <text class="output-label odd" x="290" y="230">11</text><line x1="275" x2="285" y1="230" y2="230"></line>
    <text class="output-label even" x="290" y="250">12</text><line x1="275" x2="285" y1="250" y2="250"></line>
    <text class="output-label odd" x="290" y="270">13</text><line x1="275" x2="285" y1="270" y2="270"></line>
    <text class="output-label even" x="290" y="290">14</text><line x1="275" x2="285" y1="290" y2="290"></line>
    <text class="output-label odd" x="290" y="310">15</text><line x1="275" x2="285" y1="310" y2="310"></line>
    <!-- -->
    <text class="box-label" x="85" y="80">Even DFT</text>
    <text class="box-label" x="85" y="240">Odd DFT</text>
    <text class="box-label" x="225" y="160"></text>
    <!-- -->
    <line class="even" x1="175" y1="10" x2="225" y2="20"></line>
    <line class="odd" x1="175" y1="170" x2="225" y2="20"></line>
    <line class="even" x1="225" y1="20" x2="275" y2="10"></line>
    <line class="odd" x1="225" y1="20" x2="275" y2="170"></line>
    <circle cx="225" cy="20" r="3"></circle>
    <line class="even" x1="175" y1="30" x2="225" y2="60"></line>
    <line class="odd" x1="175" y1="190" x2="225" y2="60"></line>
    <line class="even" x1="225" y1="60" x2="275" y2="30"></line>
    <line class="odd" x1="225" y1="60" x2="275" y2="190"></line>
    <circle cx="225" cy="60" r="3"></circle>
    <line class="even" x1="175" y1="50" x2="225" y2="100"></line>
    <line class="odd" x1="175" y1="210" x2="225" y2="100"></line>
    <line class="even" x1="225" y1="100" x2="275" y2="50"></line>
    <line class="odd" x1="225" y1="100" x2="275" y2="210"></line>
    <circle cx="225" cy="100" r="3"></circle>
    <line class="even" x1="175" y1="70" x2="225" y2="140"></line>
    <line class="odd" x1="175" y1="230" x2="225" y2="140"></line>
    <line class="even" x1="225" y1="140" x2="275" y2="70"></line>
    <line class="odd" x1="225" y1="140" x2="275" y2="230"></line>
    <circle cx="225" cy="140" r="3"></circle>
    <line class="even" x1="175" y1="90" x2="225" y2="180"></line>
    <line class="odd" x1="175" y1="250" x2="225" y2="180"></line>
    <line class="even" x1="225" y1="180" x2="275" y2="90"></line>
    <line class="odd" x1="225" y1="180" x2="275" y2="250"></line>
    <circle cx="225" cy="180" r="3"></circle>
    <line class="even" x1="175" y1="110" x2="225" y2="220"></line>
    <line class="odd" x1="175" y1="270" x2="225" y2="220"></line>
    <line class="even" x1="225" y1="220" x2="275" y2="110"></line>
    <line class="odd" x1="225" y1="220" x2="275" y2="270"></line>
    <circle cx="225" cy="220" r="3"></circle>
    <line class="even" x1="175" y1="130" x2="225" y2="260"></line>
    <line class="odd" x1="175" y1="290" x2="225" y2="260"></line>
    <line class="even" x1="225" y1="260" x2="275" y2="130"></line>
    <line class="odd" x1="225" y1="260" x2="275" y2="290"></line>
    <circle cx="225" cy="260" r="3"></circle>
    <line class="even" x1="175" y1="150" x2="225" y2="300"></line>
    <line class="odd" x1="175" y1="310" x2="225" y2="300"></line>
    <line class="even" x1="225" y1="300" x2="275" y2="150"></line>
    <line class="odd" x1="225" y1="300" x2="275" y2="310"></line>
    <circle cx="225" cy="300" r="3"></circle>
  </svg>
</div>
<p>If you look at the flow through this diagram, what’s interesting is that in the box on the right (representing the loop)
each computation pulls in from the same row that it writes out to (for instance, DFT result indices ‘0’ and ‘1’ are lined
up perfectly with final outputs ‘0’ and ‘8’).</p>
<p>This means that, <em>theoretically</em>, we could <strong>compute the entire FFT in-place</strong> (where the inputs and outputs are the same
array), by doing the following:</p>
<ol>
<li><strong>Deinterleave</strong> the data in-place (separating the evens and odds).</li>
<li><strong>Calculate the sub-DFTs</strong> recursively (again, in-place), using the first and last halves of the data as the even and odd
sub-DFTs, respectively.</li>
<li>Then, <strong>run the computation loop</strong> over the results, each iteration of which reads from and writes to the same elements,
so no rearranging needs to take place.</li>
</ol>
<p>There’s one problem with this: it turns out, efficiently <strong>deinterleaving an array in-place</strong> is one of those problems
that sounds like it would be super easy, but is instead <strong>kind of a nightmare</strong>!</p>
<p>To try to avoid that, let’s keep investigating the pattern of data moves. We know that, in terms of data flow, the data into the last
step comes in from two half-length sub-FFTs:</p>
<div class="center-flex">
  <svg class="interleave-diagram narrow" viewBox="20 0 290 320">
    <title>Diagram showing the even and odd 8-element sub-FFTs of a 16-element FFT, writing out to even and odd intermediate values (respectively), which then feed into the final step labeled "combine" which represents the final combination step.</title>
    <rect x="35" y="5" width="100" height="150"></rect>
    <rect x="35" y="165" width="100" height="150"></rect>
    <rect x="175" y="5" width="100" height="310"></rect>
    <line x1="25" x2="35" y1="10" y2="10"></line>
    <line x1="25" x2="35" y1="30" y2="30"></line>
    <line x1="25" x2="35" y1="50" y2="50"></line>
    <line x1="25" x2="35" y1="70" y2="70"></line>
    <line x1="25" x2="35" y1="90" y2="90"></line>
    <line x1="25" x2="35" y1="110" y2="110"></line>
    <line x1="25" x2="35" y1="130" y2="130"></line>
    <line x1="25" x2="35" y1="150" y2="150"></line>
    <line x1="25" x2="35" y1="170" y2="170"></line>
    <line x1="25" x2="35" y1="190" y2="190"></line>
    <line x1="25" x2="35" y1="210" y2="210"></line>
    <line x1="25" x2="35" y1="230" y2="230"></line>
    <line x1="25" x2="35" y1="250" y2="250"></line>
    <line x1="25" x2="35" y1="270" y2="270"></line>
    <line x1="25" x2="35" y1="290" y2="290"></line>
    <line x1="25" x2="35" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="155" y="10">0</text><line x1="135" x2="145" y1="10" y2="10"></line><line x1="165" x2="175" y1="10" y2="10"></line>
    <text class="inner-label even" x="155" y="30">2</text><line x1="135" x2="145" y1="30" y2="30"></line><line x1="165" x2="175" y1="30" y2="30"></line>
    <text class="inner-label even" x="155" y="50">4</text><line x1="135" x2="145" y1="50" y2="50"></line><line x1="165" x2="175" y1="50" y2="50"></line>
    <text class="inner-label even" x="155" y="70">6</text><line x1="135" x2="145" y1="70" y2="70"></line><line x1="165" x2="175" y1="70" y2="70"></line>
    <text class="inner-label even" x="155" y="90">8</text><line x1="135" x2="145" y1="90" y2="90"></line><line x1="165" x2="175" y1="90" y2="90"></line>
    <text class="inner-label even" x="155" y="110">10</text><line x1="135" x2="145" y1="110" y2="110"></line><line x1="165" x2="175" y1="110" y2="110"></line>
    <text class="inner-label even" x="155" y="130">12</text><line x1="135" x2="145" y1="130" y2="130"></line><line x1="165" x2="175" y1="130" y2="130"></line>
    <text class="inner-label even" x="155" y="150">14</text><line x1="135" x2="145" y1="150" y2="150"></line><line x1="165" x2="175" y1="150" y2="150"></line>
    <text class="inner-label odd" x="155" y="170">1</text><line x1="135" x2="145" y1="170" y2="170"></line><line x1="165" x2="175" y1="170" y2="170"></line>
    <text class="inner-label odd" x="155" y="190">3</text><line x1="135" x2="145" y1="190" y2="190"></line><line x1="165" x2="175" y1="190" y2="190"></line>
    <text class="inner-label odd" x="155" y="210">5</text><line x1="135" x2="145" y1="210" y2="210"></line><line x1="165" x2="175" y1="210" y2="210"></line>
    <text class="inner-label odd" x="155" y="230">7</text><line x1="135" x2="145" y1="230" y2="230"></line><line x1="165" x2="175" y1="230" y2="230"></line>
    <text class="inner-label odd" x="155" y="250">9</text><line x1="135" x2="145" y1="250" y2="250"></line><line x1="165" x2="175" y1="250" y2="250"></line>
    <text class="inner-label odd" x="155" y="270">11</text><line x1="135" x2="145" y1="270" y2="270"></line><line x1="165" x2="175" y1="270" y2="270"></line>
    <text class="inner-label odd" x="155" y="290">13</text><line x1="135" x2="145" y1="290" y2="290"></line><line x1="165" x2="175" y1="290" y2="290"></line>
    <text class="inner-label odd" x="155" y="310">15</text><line x1="135" x2="145" y1="310" y2="310"></line><line x1="165" x2="175" y1="310" y2="310"></line>
    <!-- -->
    <text class="output-label even" x="290" y="10">0</text><line x1="275" x2="285" y1="10" y2="10"></line>
    <text class="output-label odd" x="290" y="30">1</text><line x1="275" x2="285" y1="30" y2="30"></line>
    <text class="output-label even" x="290" y="50">2</text><line x1="275" x2="285" y1="50" y2="50"></line>
    <text class="output-label odd" x="290" y="70">3</text><line x1="275" x2="285" y1="70" y2="70"></line>
    <text class="output-label even" x="290" y="90">4</text><line x1="275" x2="285" y1="90" y2="90"></line>
    <text class="output-label odd" x="290" y="110">5</text><line x1="275" x2="285" y1="110" y2="110"></line>
    <text class="output-label even" x="290" y="130">6</text><line x1="275" x2="285" y1="130" y2="130"></line>
    <text class="output-label odd" x="290" y="150">7</text><line x1="275" x2="285" y1="150" y2="150"></line>
    <text class="output-label even" x="290" y="170">8</text><line x1="275" x2="285" y1="170" y2="170"></line>
    <text class="output-label odd" x="290" y="190">9</text><line x1="275" x2="285" y1="190" y2="190"></line>
    <text class="output-label even" x="290" y="210">10</text><line x1="275" x2="285" y1="210" y2="210"></line>
    <text class="output-label odd" x="290" y="230">11</text><line x1="275" x2="285" y1="230" y2="230"></line>
    <text class="output-label even" x="290" y="250">12</text><line x1="275" x2="285" y1="250" y2="250"></line>
    <text class="output-label odd" x="290" y="270">13</text><line x1="275" x2="285" y1="270" y2="270"></line>
    <text class="output-label even" x="290" y="290">14</text><line x1="275" x2="285" y1="290" y2="290"></line>
    <text class="output-label odd" x="290" y="310">15</text><line x1="275" x2="285" y1="310" y2="310"></line>
    <!-- -->
    <text class="box-label" x="85" y="80">Even DFT</text>
    <text class="box-label" x="85" y="240">Odd DFT</text>
    <text class="box-label" x="225" y="160">Combine</text>
  </svg>
</div>
<p>What if we track this pattern of deinterleaves all the way back to the lowest level (the two-element FFTs), where the
indices from right to left (from output to input) get deinterleaved at each step? Here’s the ordering we end up with:</p>
<div class="center-flex">
  <svg class="interleave-diagram" viewBox="0 0 310 320">
    <title>Compact diagram showing the inputs (ordered 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15) feeding into eight 2-element sub-FFTs, then those being fed in pairs into four 4-element sub-FFTs, each of which outputs the indices in a new order where the even/odd indices are interleaved. Each pair of those feeds into one of the two 8-element sub-FFTs which, again, outputs its input indices in interleaved order, then those two finally feed into the final 16-element FFT pass, which outputs the indices in the expected 0 to 15 sequential order.</title>
    <rect x="35" y="5" width="30" height="30"></rect>
    <rect x="35" y="45" width="30" height="30"></rect>
    <rect x="35" y="85" width="30" height="30"></rect>
    <rect x="35" y="125" width="30" height="30"></rect>
    <rect x="35" y="165" width="30" height="30"></rect>
    <rect x="35" y="205" width="30" height="30"></rect>
    <rect x="35" y="245" width="30" height="30"></rect>
    <rect x="35" y="285" width="30" height="30"></rect>
    <rect x="105" y="5" width="30" height="70"></rect>
    <rect x="105" y="85" width="30" height="70"></rect>
    <rect x="105" y="165" width="30" height="70"></rect>
    <rect x="105" y="245" width="30" height="70"></rect>
    <rect x="175" y="5" width="30" height="150"></rect>
    <rect x="175" y="165" width="30" height="150"></rect>
    <rect x="245" y="5" width="30" height="310"></rect>
    <text class="input-label" x="20" y="10">0</text><line x1="25" x2="35" y1="10" y2="10"></line>
    <text class="input-label" x="20" y="30">8</text><line x1="25" x2="35" y1="30" y2="30"></line>
    <text class="input-label" x="20" y="50">4</text><line x1="25" x2="35" y1="50" y2="50"></line>
    <text class="input-label" x="20" y="70">12</text><line x1="25" x2="35" y1="70" y2="70"></line>
    <text class="input-label" x="20" y="90">2</text><line x1="25" x2="35" y1="90" y2="90"></line>
    <text class="input-label" x="20" y="110">10</text><line x1="25" x2="35" y1="110" y2="110"></line>
    <text class="input-label" x="20" y="130">6</text><line x1="25" x2="35" y1="130" y2="130"></line>
    <text class="input-label" x="20" y="150">14</text><line x1="25" x2="35" y1="150" y2="150"></line>
    <text class="input-label" x="20" y="170">1</text><line x1="25" x2="35" y1="170" y2="170"></line>
    <text class="input-label" x="20" y="190">9</text><line x1="25" x2="35" y1="190" y2="190"></line>
    <text class="input-label" x="20" y="210">5</text><line x1="25" x2="35" y1="210" y2="210"></line>
    <text class="input-label" x="20" y="230">13</text><line x1="25" x2="35" y1="230" y2="230"></line>
    <text class="input-label" x="20" y="250">3</text><line x1="25" x2="35" y1="250" y2="250"></line>
    <text class="input-label" x="20" y="270">11</text><line x1="25" x2="35" y1="270" y2="270"></line>
    <text class="input-label" x="20" y="290">7</text><line x1="25" x2="35" y1="290" y2="290"></line>
    <text class="input-label" x="20" y="310">15</text><line x1="25" x2="35" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="85" y="10">0</text><line x1="65" x2="75" y1="10" y2="10"></line><line x1="95" x2="105" y1="10" y2="10"></line>
    <text class="inner-label even" x="85" y="30">8</text><line x1="65" x2="75" y1="30" y2="30"></line><line x1="95" x2="105" y1="30" y2="30"></line>
    <text class="inner-label odd" x="85" y="50">4</text><line x1="65" x2="75" y1="50" y2="50"></line><line x1="95" x2="105" y1="50" y2="50"></line>
    <text class="inner-label odd" x="85" y="70">12</text><line x1="65" x2="75" y1="70" y2="70"></line><line x1="95" x2="105" y1="70" y2="70"></line>
    <text class="inner-label even" x="85" y="90">2</text><line x1="65" x2="75" y1="90" y2="90"></line><line x1="95" x2="105" y1="90" y2="90"></line>
    <text class="inner-label even" x="85" y="110">10</text><line x1="65" x2="75" y1="110" y2="110"></line><line x1="95" x2="105" y1="110" y2="110"></line>
    <text class="inner-label odd" x="85" y="130">6</text><line x1="65" x2="75" y1="130" y2="130"></line><line x1="95" x2="105" y1="130" y2="130"></line>
    <text class="inner-label odd" x="85" y="150">14</text><line x1="65" x2="75" y1="150" y2="150"></line><line x1="95" x2="105" y1="150" y2="150"></line>
    <text class="inner-label even" x="85" y="170">1</text><line x1="65" x2="75" y1="170" y2="170"></line><line x1="95" x2="105" y1="170" y2="170"></line>
    <text class="inner-label even" x="85" y="190">9</text><line x1="65" x2="75" y1="190" y2="190"></line><line x1="95" x2="105" y1="190" y2="190"></line>
    <text class="inner-label odd" x="85" y="210">5</text><line x1="65" x2="75" y1="210" y2="210"></line><line x1="95" x2="105" y1="210" y2="210"></line>
    <text class="inner-label odd" x="85" y="230">13</text><line x1="65" x2="75" y1="230" y2="230"></line><line x1="95" x2="105" y1="230" y2="230"></line>
    <text class="inner-label even" x="85" y="250">3</text><line x1="65" x2="75" y1="250" y2="250"></line><line x1="95" x2="105" y1="250" y2="250"></line>
    <text class="inner-label even" x="85" y="270">11</text><line x1="65" x2="75" y1="270" y2="270"></line><line x1="95" x2="105" y1="270" y2="270"></line>
    <text class="inner-label odd" x="85" y="290">7</text><line x1="65" x2="75" y1="290" y2="290"></line><line x1="95" x2="105" y1="290" y2="290"></line>
    <text class="inner-label odd" x="85" y="310">15</text><line x1="65" x2="75" y1="310" y2="310"></line><line x1="95" x2="105" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="155" y="10">0</text><line x1="135" x2="145" y1="10" y2="10"></line><line x1="165" x2="175" y1="10" y2="10"></line>
    <text class="inner-label even" x="155" y="30">4</text><line x1="135" x2="145" y1="30" y2="30"></line><line x1="165" x2="175" y1="30" y2="30"></line>
    <text class="inner-label even" x="155" y="50">8</text><line x1="135" x2="145" y1="50" y2="50"></line><line x1="165" x2="175" y1="50" y2="50"></line>
    <text class="inner-label even" x="155" y="70">12</text><line x1="135" x2="145" y1="70" y2="70"></line><line x1="165" x2="175" y1="70" y2="70"></line>
    <text class="inner-label odd" x="155" y="90">2</text><line x1="135" x2="145" y1="90" y2="90"></line><line x1="165" x2="175" y1="90" y2="90"></line>
    <text class="inner-label odd" x="155" y="110">6</text><line x1="135" x2="145" y1="110" y2="110"></line><line x1="165" x2="175" y1="110" y2="110"></line>
    <text class="inner-label odd" x="155" y="130">10</text><line x1="135" x2="145" y1="130" y2="130"></line><line x1="165" x2="175" y1="130" y2="130"></line>
    <text class="inner-label odd" x="155" y="150">14</text><line x1="135" x2="145" y1="150" y2="150"></line><line x1="165" x2="175" y1="150" y2="150"></line>
    <text class="inner-label even" x="155" y="170">1</text><line x1="135" x2="145" y1="170" y2="170"></line><line x1="165" x2="175" y1="170" y2="170"></line>
    <text class="inner-label even" x="155" y="190">5</text><line x1="135" x2="145" y1="190" y2="190"></line><line x1="165" x2="175" y1="190" y2="190"></line>
    <text class="inner-label even" x="155" y="210">9</text><line x1="135" x2="145" y1="210" y2="210"></line><line x1="165" x2="175" y1="210" y2="210"></line>
    <text class="inner-label even" x="155" y="230">13</text><line x1="135" x2="145" y1="230" y2="230"></line><line x1="165" x2="175" y1="230" y2="230"></line>
    <text class="inner-label odd" x="155" y="250">3</text><line x1="135" x2="145" y1="250" y2="250"></line><line x1="165" x2="175" y1="250" y2="250"></line>
    <text class="inner-label odd" x="155" y="270">7</text><line x1="135" x2="145" y1="270" y2="270"></line><line x1="165" x2="175" y1="270" y2="270"></line>
    <text class="inner-label odd" x="155" y="290">11</text><line x1="135" x2="145" y1="290" y2="290"></line><line x1="165" x2="175" y1="290" y2="290"></line>
    <text class="inner-label odd" x="155" y="310">15</text><line x1="135" x2="145" y1="310" y2="310"></line><line x1="165" x2="175" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="225" y="10">0</text><line x1="205" x2="215" y1="10" y2="10"></line><line x1="235" x2="245" y1="10" y2="10"></line>
    <text class="inner-label even" x="225" y="30">2</text><line x1="205" x2="215" y1="30" y2="30"></line><line x1="235" x2="245" y1="30" y2="30"></line>
    <text class="inner-label even" x="225" y="50">4</text><line x1="205" x2="215" y1="50" y2="50"></line><line x1="235" x2="245" y1="50" y2="50"></line>
    <text class="inner-label even" x="225" y="70">6</text><line x1="205" x2="215" y1="70" y2="70"></line><line x1="235" x2="245" y1="70" y2="70"></line>
    <text class="inner-label even" x="225" y="90">8</text><line x1="205" x2="215" y1="90" y2="90"></line><line x1="235" x2="245" y1="90" y2="90"></line>
    <text class="inner-label even" x="225" y="110">10</text><line x1="205" x2="215" y1="110" y2="110"></line><line x1="235" x2="245" y1="110" y2="110"></line>
    <text class="inner-label even" x="225" y="130">12</text><line x1="205" x2="215" y1="130" y2="130"></line><line x1="235" x2="245" y1="130" y2="130"></line>
    <text class="inner-label even" x="225" y="150">14</text><line x1="205" x2="215" y1="150" y2="150"></line><line x1="235" x2="245" y1="150" y2="150"></line>
    <text class="inner-label odd" x="225" y="170">1</text><line x1="205" x2="215" y1="170" y2="170"></line><line x1="235" x2="245" y1="170" y2="170"></line>
    <text class="inner-label odd" x="225" y="190">3</text><line x1="205" x2="215" y1="190" y2="190"></line><line x1="235" x2="245" y1="190" y2="190"></line>
    <text class="inner-label odd" x="225" y="210">5</text><line x1="205" x2="215" y1="210" y2="210"></line><line x1="235" x2="245" y1="210" y2="210"></line>
    <text class="inner-label odd" x="225" y="230">7</text><line x1="205" x2="215" y1="230" y2="230"></line><line x1="235" x2="245" y1="230" y2="230"></line>
    <text class="inner-label odd" x="225" y="250">9</text><line x1="205" x2="215" y1="250" y2="250"></line><line x1="235" x2="245" y1="250" y2="250"></line>
    <text class="inner-label odd" x="225" y="270">11</text><line x1="205" x2="215" y1="270" y2="270"></line><line x1="235" x2="245" y1="270" y2="270"></line>
    <text class="inner-label odd" x="225" y="290">13</text><line x1="205" x2="215" y1="290" y2="290"></line><line x1="235" x2="245" y1="290" y2="290"></line>
    <text class="inner-label odd" x="225" y="310">15</text><line x1="205" x2="215" y1="310" y2="310"></line><line x1="235" x2="245" y1="310" y2="310"></line>
    <!-- -->
    <text class="output-label even" x="290" y="10">0</text><line x1="275" x2="285" y1="10" y2="10"></line>
    <text class="output-label odd" x="290" y="30">1</text><line x1="275" x2="285" y1="30" y2="30"></line>
    <text class="output-label even" x="290" y="50">2</text><line x1="275" x2="285" y1="50" y2="50"></line>
    <text class="output-label odd" x="290" y="70">3</text><line x1="275" x2="285" y1="70" y2="70"></line>
    <text class="output-label even" x="290" y="90">4</text><line x1="275" x2="285" y1="90" y2="90"></line>
    <text class="output-label odd" x="290" y="110">5</text><line x1="275" x2="285" y1="110" y2="110"></line>
    <text class="output-label even" x="290" y="130">6</text><line x1="275" x2="285" y1="130" y2="130"></line>
    <text class="output-label odd" x="290" y="150">7</text><line x1="275" x2="285" y1="150" y2="150"></line>
    <text class="output-label even" x="290" y="170">8</text><line x1="275" x2="285" y1="170" y2="170"></line>
    <text class="output-label odd" x="290" y="190">9</text><line x1="275" x2="285" y1="190" y2="190"></line>
    <text class="output-label even" x="290" y="210">10</text><line x1="275" x2="285" y1="210" y2="210"></line>
    <text class="output-label odd" x="290" y="230">11</text><line x1="275" x2="285" y1="230" y2="230"></line>
    <text class="output-label even" x="290" y="250">12</text><line x1="275" x2="285" y1="250" y2="250"></line>
    <text class="output-label odd" x="290" y="270">13</text><line x1="275" x2="285" y1="270" y2="270"></line>
    <text class="output-label even" x="290" y="290">14</text><line x1="275" x2="285" y1="290" y2="290"></line>
    <text class="output-label odd" x="290" y="310">15</text><line x1="275" x2="285" y1="310" y2="310"></line>
  </svg>
</div>
<p>At a glance, the pattern of inputs on the left isn’t necessarily obvious, but it turns out it’s straightforward: the
values are the <strong>original indices with their bits reversed</strong>!</p>
<p>For a 16-element FFT (like the diagrams above), we have 4 bits worth of index (since <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mn>16</mn><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><mn>4</mn></mrow><annotation encoding="application/x-tex">\log_2 16 \implies 4</annotation></semantics></math></span>), so here’s
the table of reversals:</p>
<div class="center-flex">
  <div class="bit-reverse-table">
    <div>0</div>
    <div>0000</div>
    <div>&harr;</div>
    <div>0000</div>
    <div>0</div>
    <div>1</div>
    <div>000<span class="set-bit">1</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">1</span>000</div>
    <div>8</div>
    <div>2</div>
    <div>00<span class="set-bit">1</span>0</div>
    <div>&harr;</div>
    <div>0<span class="set-bit">1</span>00</div>
    <div>4</div>
    <div>3</div>
    <div>00<span class="set-bit">11</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">11</span>00</div>
    <div>12</div>
    <div>4</div>
    <div>0<span class="set-bit">1</span>00</div>
    <div>&harr;</div>
    <div>00<span class="set-bit">1</span>0</div>
    <div>2</div>
    <div>5</div>
    <div>0<span class="set-bit">1</span>0<span class="set-bit">1</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">1</span>0<span class="set-bit">1</span>0</div>
    <div>10</div>
    <div>6</div>
    <div>0<span class="set-bit">11</span>0</div>
    <div>&harr;</div>
    <div>0<span class="set-bit">11</span>0</div>
    <div>6</div>
    <div>7</div>
    <div>0<span class="set-bit">111</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">111</span>0</div>
    <div>14</div>
    <div>8</div>
    <div><span class="set-bit">1</span>000</div>
    <div>&harr;</div>
    <div>000<span class="set-bit">1</span></div>
    <div>1</div>
    <div>9</div>
    <div><span class="set-bit">1</span>00<span class="set-bit">1</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">1</span>00<span class="set-bit">1</span></div>
    <div>9</div>
    <div>10</div>
    <div><span class="set-bit">1</span>0<span class="set-bit">1</span>0</div>
    <div>&harr;</div>
    <div>0<span class="set-bit">1</span>0<span class="set-bit">1</span></div>
    <div>5</div>
    <div>11</div>
    <div><span class="set-bit">1</span>0<span class="set-bit">11</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">11</span>0<span class="set-bit">1</span></div>
    <div>13</div>
    <div>12</div>
    <div><span class="set-bit">11</span>00</div>
    <div>&harr;</div>
    <div>00<span class="set-bit">11</span></div>
    <div>3</div>
    <div>13</div>
    <div><span class="set-bit">11</span>0<span class="set-bit">1</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">1</span>0<span class="set-bit">11</span></div>
    <div>11</div>
    <div>14</div>
    <div><span class="set-bit">111</span>0</div>
    <div>&harr;</div>
    <div>0<span class="set-bit">111</span></div>
    <div>7</div>
    <div>15</div>
    <div><span class="set-bit">1111</span></div>
    <div>&harr;</div>
    <div><span class="set-bit">1111</span></div>
    <div>15</div>
  </div>
</div>
<p>As you can see, the ordering on the right of this table (0, 8, 4, 12, etc) is the same as the input ordering from the
previous diagram and, in their bit representations, you can see that each index is just the 4 bits of the other reversed.</p>
<p>Here is a simple routine that does a bit-reversed copy from one array to another:</p>
<pre class="language-cs"><code class="language-cs"><span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">BitReverseCopy</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> len<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> k <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> k <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> k<span class="token operator">++</span><span class="token punctuation">)</span>
    outputs<span class="token punctuation">[</span>k<span class="token punctuation">]</span> <span class="token operator">=</span> inputs<span class="token punctuation">[</span><span class="token function">BitRev</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> len<span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The above code doesn’t have an implementation of <code>BitRev</code><span class="attrs"></span>, because an efficient implementation on many systems is sorta
unreadable; it turns into a sequence of swapping increasingly-large groups of bits until you reach the size of the <code>int</code><span class="attrs"></span>.
For a C++20 implementaton, here’s a <a href="https://godbolt.org/z/d91sh7x4e" target="_blank" rel="noopener">compiler explorer link</a>.</p>
<p>Additionally, the above routine doesn’t work in-place. It is <em>totally possible</em> to do this bit reversal in place: it
can be written efficiently as a square matrix transpose (or two) if you get creative with the indexing of
the “matrix” rows. However, the final FFT-20XX algorithms do not have an explicit bit reversal step, so I’m not going to
go into those details. Sorry!
</p></blockquote><p></p>
<h3>Going Iterative</h3>
<p>We now have all of the pieces we need to assemble the final algorithm. Let’s go back to this earlier diagram of the data
flow for a 16-element FFT:</p>
<div class="center-flex">
  <svg class="interleave-diagram" viewBox="0 0 310 320">
    <title>The same diagram of the flow of indices through a 16-element FFT that we saw earlier.</title>
    <rect x="35" y="5" width="30" height="30"></rect>
    <rect x="35" y="45" width="30" height="30"></rect>
    <rect x="35" y="85" width="30" height="30"></rect>
    <rect x="35" y="125" width="30" height="30"></rect>
    <rect x="35" y="165" width="30" height="30"></rect>
    <rect x="35" y="205" width="30" height="30"></rect>
    <rect x="35" y="245" width="30" height="30"></rect>
    <rect x="35" y="285" width="30" height="30"></rect>
    <rect x="105" y="5" width="30" height="70"></rect>
    <rect x="105" y="85" width="30" height="70"></rect>
    <rect x="105" y="165" width="30" height="70"></rect>
    <rect x="105" y="245" width="30" height="70"></rect>
    <rect x="175" y="5" width="30" height="150"></rect>
    <rect x="175" y="165" width="30" height="150"></rect>
    <rect x="245" y="5" width="30" height="310"></rect>
    <text class="input-label" x="20" y="10">0</text><line x1="25" x2="35" y1="10" y2="10"></line>
    <text class="input-label" x="20" y="30">8</text><line x1="25" x2="35" y1="30" y2="30"></line>
    <text class="input-label" x="20" y="50">4</text><line x1="25" x2="35" y1="50" y2="50"></line>
    <text class="input-label" x="20" y="70">12</text><line x1="25" x2="35" y1="70" y2="70"></line>
    <text class="input-label" x="20" y="90">2</text><line x1="25" x2="35" y1="90" y2="90"></line>
    <text class="input-label" x="20" y="110">10</text><line x1="25" x2="35" y1="110" y2="110"></line>
    <text class="input-label" x="20" y="130">6</text><line x1="25" x2="35" y1="130" y2="130"></line>
    <text class="input-label" x="20" y="150">14</text><line x1="25" x2="35" y1="150" y2="150"></line>
    <text class="input-label" x="20" y="170">1</text><line x1="25" x2="35" y1="170" y2="170"></line>
    <text class="input-label" x="20" y="190">9</text><line x1="25" x2="35" y1="190" y2="190"></line>
    <text class="input-label" x="20" y="210">5</text><line x1="25" x2="35" y1="210" y2="210"></line>
    <text class="input-label" x="20" y="230">13</text><line x1="25" x2="35" y1="230" y2="230"></line>
    <text class="input-label" x="20" y="250">3</text><line x1="25" x2="35" y1="250" y2="250"></line>
    <text class="input-label" x="20" y="270">11</text><line x1="25" x2="35" y1="270" y2="270"></line>
    <text class="input-label" x="20" y="290">7</text><line x1="25" x2="35" y1="290" y2="290"></line>
    <text class="input-label" x="20" y="310">15</text><line x1="25" x2="35" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="85" y="10">0</text><line x1="65" x2="75" y1="10" y2="10"></line><line x1="95" x2="105" y1="10" y2="10"></line>
    <text class="inner-label even" x="85" y="30">8</text><line x1="65" x2="75" y1="30" y2="30"></line><line x1="95" x2="105" y1="30" y2="30"></line>
    <text class="inner-label odd" x="85" y="50">4</text><line x1="65" x2="75" y1="50" y2="50"></line><line x1="95" x2="105" y1="50" y2="50"></line>
    <text class="inner-label odd" x="85" y="70">12</text><line x1="65" x2="75" y1="70" y2="70"></line><line x1="95" x2="105" y1="70" y2="70"></line>
    <text class="inner-label even" x="85" y="90">2</text><line x1="65" x2="75" y1="90" y2="90"></line><line x1="95" x2="105" y1="90" y2="90"></line>
    <text class="inner-label even" x="85" y="110">10</text><line x1="65" x2="75" y1="110" y2="110"></line><line x1="95" x2="105" y1="110" y2="110"></line>
    <text class="inner-label odd" x="85" y="130">6</text><line x1="65" x2="75" y1="130" y2="130"></line><line x1="95" x2="105" y1="130" y2="130"></line>
    <text class="inner-label odd" x="85" y="150">14</text><line x1="65" x2="75" y1="150" y2="150"></line><line x1="95" x2="105" y1="150" y2="150"></line>
    <text class="inner-label even" x="85" y="170">1</text><line x1="65" x2="75" y1="170" y2="170"></line><line x1="95" x2="105" y1="170" y2="170"></line>
    <text class="inner-label even" x="85" y="190">9</text><line x1="65" x2="75" y1="190" y2="190"></line><line x1="95" x2="105" y1="190" y2="190"></line>
    <text class="inner-label odd" x="85" y="210">5</text><line x1="65" x2="75" y1="210" y2="210"></line><line x1="95" x2="105" y1="210" y2="210"></line>
    <text class="inner-label odd" x="85" y="230">13</text><line x1="65" x2="75" y1="230" y2="230"></line><line x1="95" x2="105" y1="230" y2="230"></line>
    <text class="inner-label even" x="85" y="250">3</text><line x1="65" x2="75" y1="250" y2="250"></line><line x1="95" x2="105" y1="250" y2="250"></line>
    <text class="inner-label even" x="85" y="270">11</text><line x1="65" x2="75" y1="270" y2="270"></line><line x1="95" x2="105" y1="270" y2="270"></line>
    <text class="inner-label odd" x="85" y="290">7</text><line x1="65" x2="75" y1="290" y2="290"></line><line x1="95" x2="105" y1="290" y2="290"></line>
    <text class="inner-label odd" x="85" y="310">15</text><line x1="65" x2="75" y1="310" y2="310"></line><line x1="95" x2="105" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="155" y="10">0</text><line x1="135" x2="145" y1="10" y2="10"></line><line x1="165" x2="175" y1="10" y2="10"></line>
    <text class="inner-label even" x="155" y="30">4</text><line x1="135" x2="145" y1="30" y2="30"></line><line x1="165" x2="175" y1="30" y2="30"></line>
    <text class="inner-label even" x="155" y="50">8</text><line x1="135" x2="145" y1="50" y2="50"></line><line x1="165" x2="175" y1="50" y2="50"></line>
    <text class="inner-label even" x="155" y="70">12</text><line x1="135" x2="145" y1="70" y2="70"></line><line x1="165" x2="175" y1="70" y2="70"></line>
    <text class="inner-label odd" x="155" y="90">2</text><line x1="135" x2="145" y1="90" y2="90"></line><line x1="165" x2="175" y1="90" y2="90"></line>
    <text class="inner-label odd" x="155" y="110">6</text><line x1="135" x2="145" y1="110" y2="110"></line><line x1="165" x2="175" y1="110" y2="110"></line>
    <text class="inner-label odd" x="155" y="130">10</text><line x1="135" x2="145" y1="130" y2="130"></line><line x1="165" x2="175" y1="130" y2="130"></line>
    <text class="inner-label odd" x="155" y="150">14</text><line x1="135" x2="145" y1="150" y2="150"></line><line x1="165" x2="175" y1="150" y2="150"></line>
    <text class="inner-label even" x="155" y="170">1</text><line x1="135" x2="145" y1="170" y2="170"></line><line x1="165" x2="175" y1="170" y2="170"></line>
    <text class="inner-label even" x="155" y="190">5</text><line x1="135" x2="145" y1="190" y2="190"></line><line x1="165" x2="175" y1="190" y2="190"></line>
    <text class="inner-label even" x="155" y="210">9</text><line x1="135" x2="145" y1="210" y2="210"></line><line x1="165" x2="175" y1="210" y2="210"></line>
    <text class="inner-label even" x="155" y="230">13</text><line x1="135" x2="145" y1="230" y2="230"></line><line x1="165" x2="175" y1="230" y2="230"></line>
    <text class="inner-label odd" x="155" y="250">3</text><line x1="135" x2="145" y1="250" y2="250"></line><line x1="165" x2="175" y1="250" y2="250"></line>
    <text class="inner-label odd" x="155" y="270">7</text><line x1="135" x2="145" y1="270" y2="270"></line><line x1="165" x2="175" y1="270" y2="270"></line>
    <text class="inner-label odd" x="155" y="290">11</text><line x1="135" x2="145" y1="290" y2="290"></line><line x1="165" x2="175" y1="290" y2="290"></line>
    <text class="inner-label odd" x="155" y="310">15</text><line x1="135" x2="145" y1="310" y2="310"></line><line x1="165" x2="175" y1="310" y2="310"></line>
    <!-- -->
    <text class="inner-label even" x="225" y="10">0</text><line x1="205" x2="215" y1="10" y2="10"></line><line x1="235" x2="245" y1="10" y2="10"></line>
    <text class="inner-label even" x="225" y="30">2</text><line x1="205" x2="215" y1="30" y2="30"></line><line x1="235" x2="245" y1="30" y2="30"></line>
    <text class="inner-label even" x="225" y="50">4</text><line x1="205" x2="215" y1="50" y2="50"></line><line x1="235" x2="245" y1="50" y2="50"></line>
    <text class="inner-label even" x="225" y="70">6</text><line x1="205" x2="215" y1="70" y2="70"></line><line x1="235" x2="245" y1="70" y2="70"></line>
    <text class="inner-label even" x="225" y="90">8</text><line x1="205" x2="215" y1="90" y2="90"></line><line x1="235" x2="245" y1="90" y2="90"></line>
    <text class="inner-label even" x="225" y="110">10</text><line x1="205" x2="215" y1="110" y2="110"></line><line x1="235" x2="245" y1="110" y2="110"></line>
    <text class="inner-label even" x="225" y="130">12</text><line x1="205" x2="215" y1="130" y2="130"></line><line x1="235" x2="245" y1="130" y2="130"></line>
    <text class="inner-label even" x="225" y="150">14</text><line x1="205" x2="215" y1="150" y2="150"></line><line x1="235" x2="245" y1="150" y2="150"></line>
    <text class="inner-label odd" x="225" y="170">1</text><line x1="205" x2="215" y1="170" y2="170"></line><line x1="235" x2="245" y1="170" y2="170"></line>
    <text class="inner-label odd" x="225" y="190">3</text><line x1="205" x2="215" y1="190" y2="190"></line><line x1="235" x2="245" y1="190" y2="190"></line>
    <text class="inner-label odd" x="225" y="210">5</text><line x1="205" x2="215" y1="210" y2="210"></line><line x1="235" x2="245" y1="210" y2="210"></line>
    <text class="inner-label odd" x="225" y="230">7</text><line x1="205" x2="215" y1="230" y2="230"></line><line x1="235" x2="245" y1="230" y2="230"></line>
    <text class="inner-label odd" x="225" y="250">9</text><line x1="205" x2="215" y1="250" y2="250"></line><line x1="235" x2="245" y1="250" y2="250"></line>
    <text class="inner-label odd" x="225" y="270">11</text><line x1="205" x2="215" y1="270" y2="270"></line><line x1="235" x2="245" y1="270" y2="270"></line>
    <text class="inner-label odd" x="225" y="290">13</text><line x1="205" x2="215" y1="290" y2="290"></line><line x1="235" x2="245" y1="290" y2="290"></line>
    <text class="inner-label odd" x="225" y="310">15</text><line x1="205" x2="215" y1="310" y2="310"></line><line x1="235" x2="245" y1="310" y2="310"></line>
    <!-- -->
    <text class="output-label even" x="290" y="10">0</text><line x1="275" x2="285" y1="10" y2="10"></line>
    <text class="output-label odd" x="290" y="30">1</text><line x1="275" x2="285" y1="30" y2="30"></line>
    <text class="output-label even" x="290" y="50">2</text><line x1="275" x2="285" y1="50" y2="50"></line>
    <text class="output-label odd" x="290" y="70">3</text><line x1="275" x2="285" y1="70" y2="70"></line>
    <text class="output-label even" x="290" y="90">4</text><line x1="275" x2="285" y1="90" y2="90"></line>
    <text class="output-label odd" x="290" y="110">5</text><line x1="275" x2="285" y1="110" y2="110"></line>
    <text class="output-label even" x="290" y="130">6</text><line x1="275" x2="285" y1="130" y2="130"></line>
    <text class="output-label odd" x="290" y="150">7</text><line x1="275" x2="285" y1="150" y2="150"></line>
    <text class="output-label even" x="290" y="170">8</text><line x1="275" x2="285" y1="170" y2="170"></line>
    <text class="output-label odd" x="290" y="190">9</text><line x1="275" x2="285" y1="190" y2="190"></line>
    <text class="output-label even" x="290" y="210">10</text><line x1="275" x2="285" y1="210" y2="210"></line>
    <text class="output-label odd" x="290" y="230">11</text><line x1="275" x2="285" y1="230" y2="230"></line>
    <text class="output-label even" x="290" y="250">12</text><line x1="275" x2="285" y1="250" y2="250"></line>
    <text class="output-label odd" x="290" y="270">13</text><line x1="275" x2="285" y1="270" y2="270"></line>
    <text class="output-label even" x="290" y="290">14</text><line x1="275" x2="285" y1="290" y2="290"></line>
    <text class="output-label odd" x="290" y="310">15</text><line x1="275" x2="285" y1="310" y2="310"></line>
  </svg>
</div>
<p>This diagram takes the inputs in what we now know to be bit-reversed order, and does four (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mn>16</mn></mrow><annotation encoding="application/x-tex">\log_2 16</annotation></semantics></math></span>) passes on the inputs:</p>
<ul>
<li>The first pass does eight 2-element computation loops</li>
<li>The next pass does four 4-element  loops</li>
<li>Next is two 8-element loops</li>
<li>Finally, a single 16-element loop to get the final outputs</li>
</ul>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>In each of the above passes, the number of computation loops multiplied by the number of elements in the loop
is 16 (<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>8</mn><mo>×</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">8\times2</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mo>×</mo><mn>4</mn></mrow><annotation encoding="application/x-tex">4\times4</annotation></semantics></math></span>, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mo>×</mo><mn>8</mn></mrow><annotation encoding="application/x-tex">2\times8</annotation></semantics></math></span>, and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>×</mo><mn>16</mn></mrow><annotation encoding="application/x-tex">1\times16</annotation></semantics></math></span>), which means each pass processes <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span> elements. Since there
are <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>N</mi></mrow><annotation encoding="application/x-tex">\log_2 N</annotation></semantics></math></span> passes of <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow><annotation encoding="application/x-tex">N</annotation></semantics></math></span> elements each, this algorithm – like the recursive one – is our target <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mtext> </mtext><mi>log</mi><mo>⁡</mo><mtext> </mtext><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N\,\log\,N)</annotation></semantics></math></span>.
</p></blockquote><p></p>
<p>This process can (finally!) be done iteratively instead of recursively, by getting the elements into the expected
bit-reversed order, then modifying each element in place, in passes:</p>
<pre class="language-cs"><code class="language-cs"><span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> len<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>len <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Special case length 1.</span>
    outputs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> inputs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
    <span class="token keyword">return</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Do the bit reversal so we can do the rest</span>
  <span class="token comment">// iteratively in the outputs, in-place.</span>
  <span class="token function">BitReverseCopy</span><span class="token punctuation">(</span>inputs<span class="token punctuation">,</span> outputs<span class="token punctuation">,</span> len<span class="token punctuation">)</span>

  <span class="token comment">// Outer loop iterates all of the subFFT lengths:</span>
  <span class="token comment">// starting at 2, then doubling until we reach the</span>
  <span class="token comment">// full length ... this is log2(len) iterations.</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> subLen <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> subLen <span class="token operator">&lt;=</span> len<span class="token punctuation">;</span> subLen <span class="token operator">*=</span> <span class="token number">2</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// This loop iterates over each subsection, as the starting</span>
    <span class="token comment">// index into the data.</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> subStart <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> subStart <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> subStart <span class="token operator">+=</span> subLen<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// The data for this subsection starts at subStart.</span>
      <span class="token keyword">var</span> <span class="token operator">*</span>data <span class="token operator">=</span> <span class="token operator">&amp;</span>outputs<span class="token punctuation">[</span>subStart<span class="token punctuation">]</span>

      <span class="token comment">// Finally, do the computation loop for this subsection.</span>
      <span class="token comment">// The logic here is unchanged from the previous code</span>
      <span class="token comment">// examples, but the reads and writes are to the same</span>
      <span class="token comment">// pairs of elements.</span>
      <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">int</span></span> k <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> k <span class="token operator">&lt;</span> subLen <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span> k<span class="token operator">++</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span>
        <span class="token class-name"><span class="token keyword">int</span></span> kOdd <span class="token operator">=</span> k <span class="token operator">+</span> subLen <span class="token operator">/</span> <span class="token number">2</span>
        <span class="token class-name">complex</span> evenSum <span class="token operator">=</span> data<span class="token punctuation">[</span>k<span class="token punctuation">]</span>
        <span class="token class-name">complex</span> oddSum  <span class="token operator">=</span> data<span class="token punctuation">[</span>kOdd<span class="token punctuation">]</span>
        oddSum <span class="token operator">*=</span> <span class="token function">Twiddle</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> subLen<span class="token punctuation">)</span>
        data<span class="token punctuation">[</span>k<span class="token punctuation">]</span>    <span class="token operator">=</span> evenSum <span class="token operator">+</span> oddSum
        data<span class="token punctuation">[</span>kOdd<span class="token punctuation">]</span> <span class="token operator">=</span> evenSum <span class="token operator">-</span> oddSum
      <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The bit reversal does not <em>necessarily</em> have to come at the beginning: the algorithm could instead be written to take the
inputs in natural order and end up with the <em>outputs</em> in bit-reversed order, and do the bit reversal in-place as the
last step. The indexing within the passes would be different, but the algorithm would otherwise work the same.</p>
<p>In fact, some applications can just deal with the frequency-space outputs being out of order, and so can skip the
bit-reversal step entirely! This is really nice when it works out.
</p></blockquote><p></p>
<p>What we’ve ended up with is the <strong>radix-2 decimation in time
<a href="https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm" target="_blank" rel="noopener">Cooley-Tukey algorithm</a></strong>. It’s <strong>radix-2</strong> because
each “recursion” splits into <em>two</em> sub-FFTs, and it’s <strong>decimation in time</strong> because each pass deinterleaves the input,
<strong>time domain</strong> samples.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>If you go back up to the rotation diagrams again, you might note that the rotations are diagonally symmetrical, which
means you could instead split on even/odd <em>output</em> for the first and last half of the <em>inputs</em> (instead of the other
way around, like we did). This will end up as a similar but different algorithm, and is the <strong>decimation in frequency</strong>
version of an FFT.
</p></blockquote><p></p>
<h3>Room for Improvement</h3>
<p>While we’ve now gotten to the core FFT algorithm, this still isn’t anywhere near an optimal implementation, and
there are a number of things we’ll want to do to get this even faster. For instance:</p>
<ul>
<li>As mentioned above, this is a <strong>radix 2</strong> algorithm, but there are other radixes
(such as <strong>radix-4</strong>, where each FFT splits into <strong>4 sub-FFTs</strong>) that are more efficient and worth investigating.</li>
<li>The sines and cosines could be done via a lookup table instead of calculating them at runtime, since for a given FFT
length there are only so many unique angles that are required.</li>
<li>Doing the bit reverse pass to get things into the correct order (either at the start or end) can be a sizeable chunk
of time, and depending on implementation can wreak havoc on your CPU’s caches.</li>
<li>And then there’s doing SIMD optimizations and otherwise trying to make the memory accesses CPU-cache-friendly.</li>
</ul>
<p>We’ll touch on the first couple of those next time!</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[FFTs Part 1: The Discrete Fourier Transform]]></title>
      <link>https://www.drilian.com/posts/2026.09.08-ffts-part-1-the-discrete-fourier-transform/</link>
      <pubDate>Tue, 08 Sep 2026 17:58:16 PDT</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2026.09.08-ffts-part-1-the-discrete-fourier-transform/</guid>
      <content:encoded>
        <![CDATA[
          <p>Because the world clearly doesn’t already have enough of them, I recently released a new FFT library:
<a href="https://github.com/DeadlyRedCube/FFT-20XX" target="_blank" rel="noopener">FFT-20XX</a>. To the best of my ability to tell, it uses an algorithm that is
at least slightly novel, and I thought it might be interesting to write about it.</p>
<p>This is the first of a series of posts about FFTs and the specific algorithms that FFT-20XX uses:</p>
<ol>
<li><span class="current-post">The Discrete Fourier Transform (← you are here)</span></li>
<li><a href="https://www.drilian.com/posts/2026.09.10-ffts-part-2-the-basics-of-the-fft/">The Basics of the FFT</a></li>
<li><a href="https://www.drilian.com/posts/2026.09.17-ffts-part-3-optimizations-to-the-fft/">Optimizations to the FFT</a></li>
<li>The Inverse FFT</li>
<li>The FFT-20XX Complex-Input Algorithm</li>
<li>The FFT-20XX Real-Input Algorithm(s)</li>
</ol>
<p>As a first step I thought it might be fun to start with “what even is the
<a href="https://en.wikipedia.org/wiki/Discrete_Fourier_transform" target="_blank" rel="noopener">discrete Fourier transform</a>?”</p>
<p><span class="read-more"></span></p>
<h3>Fourier Transforms</h3>
<p>Any explanation of a Fourier transform would be remiss to not include a link to
<a href="https://www.youtube.com/watch?v=spUNpyF58BY" target="_blank" rel="noopener">this fabulous 3Blue1Brown video</a> that nicely illustrates how the Fourier
transform works. I highly recommend watching it!</p>
<p>This post, however, is specifically about the <strong><em>discrete</em> Fourier transform</strong> (referred to as the <strong>DFT</strong>), which is
a Fourier transform that operates on <em>sampled</em> data (like, say, digital audio or images) instead of continuous data.
The <a href="https://en.wikipedia.org/wiki/Fast_Fourier_transform" target="_blank" rel="noopener">FFT (fast Fourier transform)</a> is the standard algorithm used
to compute the result of a DFT – we’ll get to <em>it</em> in the next post.</p>
<h3>What Does the DFT Even Do?</h3>
<p>Any <strong>periodic signal</strong> (i.e. a signal that repeats, such as looped audio) can be represented as the <strong>sum of a set of
sine waves</strong> at different frequencies. Here is an example of a simple signal that can be broken down into three waves:</p>
<div class="center-flex">
  <div class="frequencies-op-grid">
    <div></div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of a somewhat complex wave, which is composed of the three following waves</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="neutral-wave">
            <path d="M -4 26.27541417207226 L 0 26 L 4 26.27541417207226 L 8 27.08687047960282 L 12 28.39163534651703 L 16 30.123717087412867 L 20 32.20108141721805 L 24 34.53456037344319 L 28 37.03741583879894 L 32 39.634424573944024 L 36 42.269380592272974 L 40 44.910061810326695 L 44 47.54996761896137 L 48 50.20647743031647 L 52 52.91547344082043 L 56 55.72287411460942 L 60 58.673896608108 L 64 61.80116705263379 L 68 65.1129939063652 L 72 68.58318759998738 L 76 72.14373760437005 L 80 75.68144750490086 L 84 79.03929499372978 L 88 82.02285472831247 L 92 84.41163576266976 L 96 85.97468655053538 L 100 86.48935688187389 L 104 85.76172337425785 L 108 83.64692305461331 L 112 80.06752792467483 L 116 75.02814897447776 L 120 68.62468272619022 L 124 61.04699337538327 L 128 52.57433059008888 L 132 43.5633761925625 L 136 34.42944235544899 L 140 25.621954859091538 L 144 17.595892655249294 L 148 10.781269663185476 L 152 5.552996081631804 L 156 2.2035175383848724 L 160 0.9204901301220687 L 164 1.7714141728724258 L 168 4.696642775511691 L 172 9.511542083393863 L 176 15.917859464220413 L 180 23.523613359497315 L 184 31.87011640864081 L 188 40.464141880966416 L 192 48.8127949986968 L 196 56.45839578174618 L 200 63.0106431181261 L 204 68.17351720603564 L 208 71.76477670770707 L 212 73.72648717613508 L 216 74.1257321591635 L 220 73.14544957031458 L 224 71.06613814728155 L 228 68.23992529380155 L 232 65.0591156842361 L 236 61.92179659562011 L 240 59.197322014230636 L 244 57.194511631176205 L 248 56.13518078546002 L 252 56.13518078546002 L 256 57.19451163117621 L 260 59.19732201423062 L 264 61.92179659562011 L 268 65.05911568423612 L 272 68.23992529380159 L 276 71.06613814728155 L 280 73.14544957031458 L 284 74.12573215916352 L 288 73.72648717613507 L 292 71.7647767077071 L 296 68.17351720603571 L 300 63.01064311812612 L 304 56.45839578174622 L 308 48.812794998696866 L 312 40.46414188096646 L 316 31.870116408640833 L 320 23.523613359497325 L 324 15.917859464220427 L 328 9.511542083393898 L 332 4.696642775511648 L 336 1.7714141728724115 L 340 0.92049013012209 L 344 2.2035175383848298 L 348 5.552996081631754 L 352 10.781269663185377 L 356 17.595892655249244 L 360 25.621954859091502 L 364 34.42944235544891 L 368 43.563376192562515 L 372 52.57433059008883 L 376 61.04699337538324 L 380 68.6246827261902 L 384 75.02814897447777 L 388 80.06752792467483 L 392 83.6469230546133 L 396 85.76172337425785 L 400 86.48935688187389 L 404 85.97468655053538 L 408 84.41163576266979 L 412 82.02285472831248 L 416 79.03929499372983 L 420 75.68144750490087 L 424 72.14373760437009 L 428 68.5831875999874 L 432 65.11299390636522 L 436 61.80116705263378 L 440 58.67389660810802 L 444 55.72287411460942 L 448 52.91547344082043 L 452 50.206477430316504 L 456 47.54996761896139 L 460 44.91006181032665 L 464 42.269380592273045 L 468 39.63442457394403 L 472 37.03741583879902 L 476 34.5345603734432 L 480 32.20108141721803 L 484 30.123717087412917 L 488 28.391635346517056 L 492 27.086870479602798 L 496 26.27541417207228 L 500 26"></path>
          </g>
        </svg>
      </div>
    </div>
    <div></div>
    <div></div>
    <div class="frequencies-equal">&equals;</div>
    <div></div>
    <div></div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the lowest frequency and highest-amplitude wave that makes up the above complex wave.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.151469490560995 L 0 20 L 4 20.151469490560995 L 8 20.604348428472594 L 12 21.35406365760071 L 16 22.39304457902389 L 20 23.710799598684094 L 24 25.294022071147175 L 28 27.126724669656564 L 32 29.190400825615853 L 36 31.46421160728997 L 40 33.9251961506301 L 44 36.548503517299025 L 48 39.30764363860248 L 52 42.17475481130309 L 56 45.12088504415349 L 60 48.11628441412059 L 64 51.13070548009804 L 68 54.13370872053914 L 72 57.09496991071173 L 76 59.9845863356896 L 80 62.77337874695218 L 84 65.4331860134452 L 88 67.93714949172556 L 92 70.25998424363073 L 96 72.37823436272546 L 100 74.27050983124842 L 104 75.91770251578507 L 108 77.30317912054987 L 112 78.41294914984232 L 116 79.23580618359732 L 120 79.76344103943433 L 124 79.990525678499 L 128 79.91476700781843 L 132 79.53693003587617 L 136 78.86083014758258 L 140 77.89329457664753 L 144 76.64409346440635 L 148 75.12584120126425 L 152 73.3538690470107 L 156 71.34607031627857 L 160 69.12271969246069 L 164 66.70626849464563 L 168 64.12111796495998 L 172 61.39337286565406 L 176 58.55057787409931 L 180 55.62143943757174 L 184 52.6355358965223 L 188 49.62301880349943 L 192 46.61430845379555 L 196 43.63978670233836 L 200 40.72949016875158 L 204 37.91280692859013 L 208 35.21817975355126 L 212 32.67281889733198 L 216 30.302427327411316 L 220 28.130941177357663 L 224 26.18028804056495 L 228 24.470165546159237 L 232 23.017842453008864 L 236 21.837984270383785 L 240 20.94250516614107 L 244 20.340447657862583 L 248 20.037891301819474 L 252 20.037891301819474 L 256 20.340447657862583 L 260 20.942505166141068 L 264 21.837984270383778 L 268 23.017842453008868 L 272 24.470165546159244 L 276 26.18028804056494 L 280 28.13094117735767 L 284 30.302427327411284 L 288 32.67281889733195 L 292 35.218179753551226 L 296 37.912806928590115 L 300 40.72949016875157 L 304 43.63978670233835 L 308 46.61430845379554 L 312 49.62301880349941 L 316 52.635535896522285 L 320 55.62143943757175 L 324 58.55057787409927 L 328 61.393372865654044 L 332 64.12111796495996 L 336 66.70626849464564 L 340 69.12271969246072 L 344 71.34607031627853 L 348 73.35386904701069 L 352 75.12584120126422 L 356 76.64409346440632 L 360 77.89329457664755 L 364 78.86083014758256 L 368 79.53693003587617 L 372 79.91476700781841 L 376 79.990525678499 L 380 79.76344103943434 L 384 79.23580618359732 L 388 78.41294914984232 L 392 77.30317912054987 L 396 75.91770251578505 L 400 74.27050983124843 L 404 72.37823436272546 L 408 70.25998424363074 L 412 67.9371494917256 L 416 65.43318601344521 L 420 62.77337874695222 L 424 59.98458633568961 L 428 57.09496991071173 L 432 54.13370872053916 L 436 51.13070548009803 L 440 48.11628441412062 L 444 45.12088504415349 L 448 42.17475481130312 L 452 39.30764363860248 L 456 36.54850351729901 L 460 33.9251961506301 L 464 31.46421160728995 L 468 29.190400825615857 L 472 27.126724669656586 L 476 25.294022071147182 L 480 23.71079959868411 L 484 22.393044579023893 L 488 21.354063657600722 L 492 20.604348428472598 L 496 20.151469490561 L 500 20"></path>
          </g>
        </svg>
      </div>
    </div>
    <div></div>
    <div class="frequencies-label">+</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the middle frequency wave that makes up the above complex wave. It has an amplitude lower than the first component wave.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 64.6978257857637 L 0 65 L 4 64.6978257857637 L 8 63.80347771048805 L 12 62.352988964426416 L 16 60.40479958719207 L 20 58.03740192468495 L 24 55.34617818069876 L 28 52.43955747792326 L 32 49.43464725995098 L 36 46.45251504464413 L 40 43.61331062652391 L 44 41.03142525413722 L 48 38.81088281863727 L 52 37.04114874210747 L 56 35.79352542507884 L 60 35.11827948028284 L 64 35.04261649609079 L 68 35.56958492620871 L 72 36.67795326779682 L 76 38.32306547649465 L 80 40.438640153769654 L 84 42.93944101752001 L 88 45.72471106295035 L 92 48.68223205173885 L 96 51.69284577310222 L 100 54.63525491562421 L 104 57.39091012322437 L 108 59.84878633629434 L 112 61.90985597971753 L 116 63.491078773495566 L 120 64.52874741692946 L 124 64.98105434909026 L 128 64.82977617106872 L 132 64.08100786480811 L 136 62.764917226920375 L 140 60.934529411321165 L 144 58.663590551334025 L 148 56.04359653570494 L 152 53.18010664883082 L 156 50.1884905982503 L 160 47.189280281214124 L 164 44.30331356717298 L 168 41.64686575267718 L 172 39.326964841860736 L 176 37.43707939936789 L 180 36.053352711676226 L 184 35.231534982061916 L 188 35.0047371607505 L 192 35.38209690820134 L 196 36.348410439725065 L 200 37.864745084375784 L 204 39.87000787818463 L 208 42.283406993277396 L 212 45.00770683215519 L 216 47.93314563973042 L 220 50.941857792939686 L 224 53.91262259434844 L 228 56.725748241350495 L 232 59.26789419635502 L 236 61.43663766517171 L 240 63.14460020065795 L 244 64.32296817119963 L 248 64.9242652547195 L 252 64.9242652547195 L 256 64.32296817119965 L 260 63.14460020065795 L 264 61.436637665171716 L 268 59.267894196355016 L 272 56.72574824135049 L 276 53.912622594348456 L 280 50.94185779293968 L 284 47.93314563973046 L 288 45.00770683215523 L 292 42.28340699327743 L 296 39.87000787818464 L 300 37.86474508437579 L 304 36.34841043972507 L 308 35.38209690820135 L 312 35.0047371607505 L 316 35.231534982061916 L 320 36.05335271167623 L 324 37.43707939936787 L 328 39.32696484186073 L 332 41.646865752677165 L 336 44.30331356717299 L 340 47.18928028121416 L 344 50.18849059825024 L 348 53.180106648830794 L 352 56.04359653570489 L 356 58.663590551333996 L 360 60.93452941132117 L 364 62.76491722692036 L 368 64.08100786480811 L 372 64.8297761710687 L 376 64.98105434909026 L 380 64.52874741692948 L 384 63.491078773495566 L 388 61.90985597971752 L 392 59.84878633629435 L 396 57.39091012322436 L 400 54.63525491562422 L 404 51.69284577310221 L 408 48.682232051738865 L 412 45.7247110629504 L 416 42.939441017520025 L 420 40.43864015376969 L 424 38.32306547649466 L 428 36.67795326779683 L 432 35.56958492620872 L 436 35.04261649609079 L 440 35.11827948028283 L 444 35.79352542507884 L 448 37.04114874210746 L 452 38.81088281863727 L 456 41.03142525413724 L 460 43.613310626523905 L 464 46.45251504464415 L 468 49.43464725995098 L 472 52.43955747792322 L 476 55.34617818069876 L 480 58.03740192468492 L 484 60.404799587192066 L 488 62.35298896442639 L 492 63.80347771048805 L 496 64.6978257857637 L 500 65"></path>
          </g>
        </svg>
      </div>
    </div>
    <div></div>
    <div class="frequencies-label">+</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the highest-frequency wave that makes up the above complex wave. It has a lower amplitude than the first two.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 41.28275154984232 L 0 41 L 4 41.28275154984232 L 8 42.11323987960523 L 12 43.4392823532073 L 16 45.17755884518903 L 20 47.21884705062547 L 24 49.43488532423618 L 28 51.68643183127152 L 32 53.832013624085654 L 36 55.736815907738205 L 40 57.28115294937452 L 44 58.36798837299426 L 48 58.9290323118303 L 52 58.9290323118303 L 56 58.36798837299426 L 60 57.28115294937453 L 64 55.736815907738205 L 68 53.83201362408565 L 72 51.68643183127153 L 76 49.434885324236184 L 80 47.21884705062548 L 84 45.177558845189026 L 88 43.4392823532073 L 92 42.11323987960523 L 96 41.28275154984232 L 100 41 L 104 41.28275154984232 L 108 42.113239879605224 L 112 43.4392823532073 L 116 45.17755884518903 L 120 47.21884705062547 L 124 49.43488532423618 L 128 51.68643183127153 L 132 53.83201362408566 L 136 55.73681590773821 L 140 57.28115294937452 L 144 58.367988372994255 L 148 58.9290323118303 L 152 58.9290323118303 L 156 58.36798837299426 L 160 57.28115294937453 L 164 55.736815907738205 L 168 53.832013624085654 L 172 51.686431831271534 L 176 49.434885324236184 L 180 47.21884705062548 L 184 45.17755884518903 L 188 43.4392823532073 L 192 42.113239879605224 L 196 41.282751549842324 L 200 41 L 204 41.28275154984232 L 208 42.11323987960522 L 212 43.43928235320729 L 216 45.177558845189026 L 220 47.21884705062547 L 224 49.43488532423618 L 228 51.68643183127152 L 232 53.83201362408566 L 236 55.7368159077382 L 240 57.28115294937452 L 244 58.36798837299426 L 248 58.9290323118303 L 252 58.9290323118303 L 256 58.36798837299426 L 260 57.28115294937453 L 264 55.7368159077382 L 268 53.832013624085654 L 272 51.686431831271506 L 276 49.43488532423617 L 280 47.21884705062548 L 284 45.17755884518905 L 288 43.43928235320732 L 292 42.11323987960523 L 296 41.28275154984233 L 300 41 L 304 41.28275154984232 L 308 42.11323987960523 L 312 43.43928235320729 L 316 45.17755884518903 L 320 47.21884705062546 L 324 49.43488532423619 L 328 51.68643183127152 L 332 53.83201362408567 L 336 55.73681590773821 L 340 57.28115294937452 L 344 58.367988372994255 L 348 58.9290323118303 L 352 58.9290323118303 L 356 58.36798837299427 L 360 57.28115294937453 L 364 55.736815907738226 L 368 53.832013624085654 L 372 51.686431831271534 L 376 49.43488532423618 L 380 47.218847050625484 L 384 45.17755884518902 L 388 43.4392823532073 L 392 42.11323987960523 L 396 41.28275154984232 L 400 41 L 404 41.282751549842324 L 408 42.11323987960521 L 412 43.43928235320729 L 416 45.177558845189004 L 420 47.21884705062546 L 424 49.434885324236156 L 428 51.68643183127152 L 432 53.83201362408564 L 436 55.73681590773821 L 440 57.28115294937452 L 444 58.36798837299426 L 448 58.9290323118303 L 452 58.9290323118303 L 456 58.36798837299426 L 460 57.28115294937453 L 464 55.7368159077382 L 468 53.832013624085654 L 472 51.68643183127154 L 476 49.43488532423621 L 480 47.218847050625484 L 484 45.177558845189054 L 488 43.4392823532073 L 492 42.11323987960523 L 496 41.28275154984232 L 500 41"></path>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>This is true of <em>any</em> periodic signal whether it is analog or sampled – no matter how complex it may seem.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The “periodic signal” bit is important, but there
are many applications that use the DFT on non-periodic signals (like small blocks of a longer piece
of audio) and there are a few tricks that can be done (like <a href="https://en.wikipedia.org/wiki/Window_function" target="_blank" rel="noopener">windowing</a>
the signal) to effectively “pretend” that it is periodic when it’s not.
</p></blockquote><p></p>
<p>The DFT answers a simple question: given a set of samples that represents a periodic signal, which frequencies
are represented in that signal?</p>
<p>To put it another way: the DFT converts a signal that is made up of samples in <strong>time</strong> (where each sample
represents a point on the signal at a given time) into the same signal represented in <strong>frequency</strong> (where each sample
represents the <em>amplitude</em> (how tall the wave is) and <em>phase</em> (the side-to-side position of the wave) of its
corresponding frequency). These two representations are frequently referred to as <strong>time domain</strong> and <strong>frequency
domain</strong>, and both encode the same signal in different ways.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>There is also an <strong>inverse</strong> DFT (the <strong>IDFT</strong>) which converts the other way – from the frequency domain back to the
time domain. Many signal processing applications convert from time to frequency space to do some operation, then use
the inverse DFT to convert back.
</p></blockquote><p></p>
<p>For a given repeating signal that is N samples long, the DFT returns contribution values for  N frequencies, each
with one more cycle than the previous. To put it a different way: for each frequency with index <code>i</code><span class="attrs"></span>
(starting at <code>0</code><span class="attrs"></span> and ending at <code>N - 1</code><span class="attrs"></span>) there are <code>i</code><span class="attrs"></span> cycles of a wave (so index <code>0</code><span class="attrs"></span> has 0 cycles, index <code>1</code><span class="attrs"></span> has 1 cycle, etc).</p>
<p>Here are the 8 waves for a DFT with 8 samples: (the red wave with circles is the cosine and the green wave with squares
is the sine)</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">0</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>
            Diagram of Frequency 0: all of the cosine values are '1' and all of the sine values are '0'.
There are 8 points on each wave representing the samples (this is true for all subsequent waves as well).
          </title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <line x1="0" y1="20" x2="500" y2="20"></line>
            <circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="20" r="5"></circle><circle cx="125" cy="20" r="5"></circle><circle cx="187.5" cy="20" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="312.5" cy="20" r="5"></circle><circle cx="375" cy="20" r="5"></circle><circle cx="437.5" cy="20" r="5"></circle>
          </g>
          <g class="sin-wave">
            <line x1="0" y1="50" x2="500" y2="50"></line>
              <rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="45" width="10" height="10"></rect><rect x="120" y="45" width="10" height="10"></rect><rect x="182.5" y="45" width="10" height="10"></rect><rect x="245" y="45" width="10" height="10"></rect><rect x="307.5" y="45" width="10" height="10"></rect><rect x="370" y="45" width="10" height="10"></rect><rect x="432.5" y="45" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">1</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of Frequency 1: there is a single up to down to up cycle of the cosine and sine waves.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 20.037891301819474 L 0 20 L 4 20.037891301819474 L 8 20.151469490560995 L 12 20.34044765786258 L 16 20.604348428472594 L 20 20.942505166141068 L 24 21.35406365760071 L 28 21.837984270383778 L 32 22.39304457902389 L 36 23.017842453008868 L 40 23.710799598684094 L 44 24.47016554615924 L 48 25.294022071147175 L 52 26.18028804056494 L 56 27.126724669656564 L 60 28.130941177357652 L 64 29.190400825615853 L 68 30.30242732741131 L 72 31.46421160728997 L 76 32.67281889733197 L 80 33.9251961506301 L 84 35.218179753551254 L 88 36.548503517299025 L 92 37.912806928590115 L 96 39.30764363860248 L 100 40.729490168751575 L 104 42.17475481130309 L 108 43.639786702338355 L 112 45.12088504415349 L 116 46.61430845379555 L 120 48.11628441412059 L 124 49.62301880349942 L 128 51.13070548009804 L 132 52.6355358965223 L 136 54.13370872053914 L 140 55.621439437571745 L 144 57.09496991071173 L 148 58.55057787409928 L 152 59.9845863356896 L 156 61.39337286565403 L 160 62.77337874695218 L 164 64.12111796495998 L 168 65.4331860134452 L 172 66.70626849464563 L 176 67.93714949172556 L 180 69.12271969246069 L 184 70.25998424363073 L 188 71.34607031627856 L 192 72.37823436272546 L 196 73.3538690470107 L 200 74.27050983124842 L 204 75.12584120126425 L 208 75.91770251578507 L 212 76.64409346440634 L 216 77.30317912054987 L 220 77.89329457664755 L 224 78.41294914984232 L 228 78.86083014758258 L 232 79.23580618359732 L 236 79.53693003587617 L 240 79.76344103943433 L 244 79.91476700781843 L 248 79.990525678499 L 252 79.990525678499 L 256 79.91476700781843 L 260 79.76344103943434 L 264 79.53693003587617 L 268 79.23580618359732 L 272 78.86083014758258 L 276 78.41294914984233 L 280 77.89329457664753 L 284 77.30317912054988 L 288 76.64409346440635 L 292 75.91770251578507 L 296 75.12584120126425 L 300 74.27050983124843 L 304 73.3538690470107 L 308 72.37823436272546 L 312 71.34607031627857 L 316 70.25998424363074 L 320 69.12271969246069 L 324 67.93714949172556 L 328 66.70626849464563 L 332 65.43318601344521 L 336 64.12111796495998 L 340 62.77337874695216 L 344 61.39337286565406 L 348 59.98458633568961 L 352 58.55057787409931 L 356 57.094969910711754 L 360 55.62143943757174 L 364 54.133708720539154 L 368 52.6355358965223 L 372 51.13070548009805 L 376 49.62301880349943 L 380 48.116284414120614 L 384 46.61430845379555 L 388 45.12088504415348 L 392 43.63978670233836 L 396 42.17475481130308 L 400 40.72949016875158 L 404 39.30764363860247 L 408 37.91280692859013 L 412 36.548503517299054 L 416 35.21817975355126 L 420 33.92519615063012 L 424 32.67281889733198 L 428 31.46421160728997 L 432 30.302427327411316 L 436 29.19040082561585 L 440 28.130941177357663 L 444 27.126724669656564 L 448 26.18028804056495 L 452 25.294022071147175 L 456 24.470165546159237 L 460 23.710799598684094 L 464 23.017842453008864 L 468 22.393044579023893 L 472 21.837984270383785 L 476 21.35406365760071 L 480 20.94250516614107 L 484 20.604348428472594 L 488 20.340447657862583 L 492 20.151469490560995 L 496 20.037891301819474 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="28.786796564403573" r="5"></circle><circle cx="125" cy="50" r="5"></circle><circle cx="187.5" cy="71.21320343559643" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="312.5" cy="71.21320343559643" r="5"></circle><circle cx="375" cy="50.00000000000001" r="5"></circle><circle cx="437.5" cy="28.78679656440358" r="5"></circle>
        </g>
        <g class="sin-wave">
          <path d="M -4 51.507329545393084 L 0 50 L 4 48.492670454606916 L 8 46.989148554463554 L 12 45.49323232637729 L 16 44.00870058456779 L 20 42.53930338505435 L 24 41.08875255268895 L 28 39.66071230476449 L 32 38.25878999488393 L 36 36.886527000472014 L 40 35.54738977694854 L 44 34.24476110116113 L 48 32.9819315261973 L 52 31.762091069161837 L 56 30.588321152916674 L 60 29.46358682213934 L 64 28.390729253362792 L 68 27.372458577916888 L 72 26.411347035901436 L 76 25.50982247848448 L 80 24.670162234939546 L 84 23.89448735991423 L 88 23.18475727546209 L 92 22.542764821372472 L 96 21.970131726301638 L 100 21.468304511145394 L 104 21.038550835001782 L 108 20.681956292954197 L 112 20.39942167376396 L 116 20.19166068439975 L 120 20.059198147151854 L 124 20.002368673885517 L 128 20.021315820782323 L 132 20.115991725704824 L 136 20.286157229100464 L 140 20.531382478139342 L 144 20.85104801255978 L 148 21.24434632947872 L 152 21.710283923214064 L 156 22.24768379496626 L 160 22.855188426019417 L 164 23.5312632069514 L 168 24.274200314190434 L 172 25.082123024125615 L 176 25.952990453873696 L 180 26.884602716726324 L 184 27.874606479254783 L 188 28.920500906034523 L 192 30.01964397697245 L 196 31.169259161278983 L 200 32.366442431225806 L 204 33.60816959797192 L 208 34.891303950927174 L 212 36.21260418135536 L 216 37.56873257020147 L 220 38.95626341945966 L 224 40.37169170578371 L 228 41.81144193448025 L 232 43.27187717151857 L 236 44.74930823074171 L 240 46.24000299307087 L 244 47.74019583416201 L 248 49.246097136699866 L 252 50.75390286330013 L 256 52.259804165837984 L 260 53.759997006929126 L 264 55.25069176925828 L 268 56.72812282848144 L 272 58.18855806551976 L 276 59.62830829421628 L 280 61.04373658054035 L 284 62.43126742979851 L 288 63.78739581864462 L 292 65.1086960490728 L 296 66.39183040202806 L 300 67.6335575687742 L 304 68.83074083872101 L 308 69.98035602302754 L 312 71.07949909396547 L 316 72.1253935207452 L 320 73.11539728327368 L 324 74.04700954612629 L 328 74.91787697587438 L 332 75.72579968580956 L 336 76.4687367930486 L 340 77.14481157398059 L 344 77.75231620503374 L 348 78.28971607678594 L 352 78.75565367052127 L 356 79.14895198744021 L 360 79.46861752186066 L 364 79.71384277089953 L 368 79.88400827429518 L 372 79.97868417921768 L 376 79.99763132611449 L 380 79.94080185284815 L 384 79.80833931560025 L 388 79.60057832623605 L 392 79.3180437070458 L 396 78.96144916499821 L 400 78.53169548885461 L 404 78.02986827369836 L 408 77.45723517862753 L 412 76.81524272453792 L 416 76.10551264008578 L 420 75.32983776506046 L 424 74.49017752151552 L 428 73.58865296409857 L 432 72.62754142208313 L 436 71.60927074663721 L 440 70.53641317786067 L 444 69.41167884708332 L 448 68.23790893083817 L 452 67.0180684738027 L 456 65.75523889883885 L 460 64.45261022305147 L 464 63.11347299952798 L 468 61.74121000511607 L 472 60.33928769523553 L 476 58.911247447311055 L 480 57.46069661494566 L 484 55.991299415432216 L 488 54.50676767362273 L 492 53.01085144553645 L 496 51.507329545393105 L 500 50.00000000000001"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="23.786796564403577" width="10" height="10"></rect><rect x="120" y="15" width="10" height="10"></rect><rect x="182.5" y="23.786796564403573" width="10" height="10"></rect><rect x="245" y="44.99999999999999" width="10" height="10"></rect><rect x="307.5" y="66.21320343559643" width="10" height="10"></rect><rect x="370" y="75" width="10" height="10"></rect><rect x="432.5" y="66.21320343559643" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">2</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of Frequency 2: there are two up to down to up cycles of the cosine and sine waves.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.151469490560995 L 0 20 L 4 20.151469490560995 L 8 20.604348428472594 L 12 21.35406365760071 L 16 22.39304457902389 L 20 23.710799598684094 L 24 25.294022071147175 L 28 27.126724669656564 L 32 29.190400825615853 L 36 31.46421160728997 L 40 33.9251961506301 L 44 36.548503517299025 L 48 39.30764363860248 L 52 42.17475481130309 L 56 45.12088504415349 L 60 48.11628441412059 L 64 51.13070548009804 L 68 54.13370872053914 L 72 57.09496991071173 L 76 59.9845863356896 L 80 62.77337874695218 L 84 65.4331860134452 L 88 67.93714949172556 L 92 70.25998424363073 L 96 72.37823436272546 L 100 74.27050983124842 L 104 75.91770251578507 L 108 77.30317912054987 L 112 78.41294914984232 L 116 79.23580618359732 L 120 79.76344103943433 L 124 79.990525678499 L 128 79.91476700781843 L 132 79.53693003587617 L 136 78.86083014758258 L 140 77.89329457664753 L 144 76.64409346440635 L 148 75.12584120126425 L 152 73.3538690470107 L 156 71.34607031627857 L 160 69.12271969246069 L 164 66.70626849464563 L 168 64.12111796495998 L 172 61.39337286565406 L 176 58.55057787409931 L 180 55.62143943757174 L 184 52.6355358965223 L 188 49.62301880349943 L 192 46.61430845379555 L 196 43.63978670233836 L 200 40.72949016875158 L 204 37.91280692859013 L 208 35.21817975355126 L 212 32.67281889733198 L 216 30.302427327411316 L 220 28.130941177357663 L 224 26.18028804056495 L 228 24.470165546159237 L 232 23.017842453008864 L 236 21.837984270383785 L 240 20.94250516614107 L 244 20.340447657862583 L 248 20.037891301819474 L 252 20.037891301819474 L 256 20.340447657862583 L 260 20.942505166141068 L 264 21.837984270383778 L 268 23.017842453008868 L 272 24.470165546159244 L 276 26.18028804056494 L 280 28.13094117735767 L 284 30.302427327411284 L 288 32.67281889733195 L 292 35.218179753551226 L 296 37.912806928590115 L 300 40.72949016875157 L 304 43.63978670233835 L 308 46.61430845379554 L 312 49.62301880349941 L 316 52.635535896522285 L 320 55.62143943757175 L 324 58.55057787409927 L 328 61.393372865654044 L 332 64.12111796495996 L 336 66.70626849464564 L 340 69.12271969246072 L 344 71.34607031627853 L 348 73.35386904701069 L 352 75.12584120126422 L 356 76.64409346440632 L 360 77.89329457664755 L 364 78.86083014758256 L 368 79.53693003587617 L 372 79.91476700781841 L 376 79.990525678499 L 380 79.76344103943434 L 384 79.23580618359732 L 388 78.41294914984232 L 392 77.30317912054987 L 396 75.91770251578505 L 400 74.27050983124843 L 404 72.37823436272546 L 408 70.25998424363074 L 412 67.9371494917256 L 416 65.43318601344521 L 420 62.77337874695222 L 424 59.98458633568961 L 428 57.09496991071173 L 432 54.13370872053916 L 436 51.13070548009803 L 440 48.11628441412062 L 444 45.12088504415349 L 448 42.17475481130312 L 452 39.30764363860248 L 456 36.54850351729901 L 460 33.9251961506301 L 464 31.46421160728995 L 468 29.190400825615857 L 472 27.126724669656586 L 476 25.294022071147182 L 480 23.71079959868411 L 484 22.393044579023893 L 488 21.354063657600722 L 492 20.604348428472598 L 496 20.151469490561 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="50" r="5"></circle><circle cx="125" cy="80" r="5"></circle><circle cx="187.5" cy="50.00000000000001" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="312.5" cy="49.99999999999999" r="5"></circle><circle cx="375" cy="80" r="5"></circle><circle cx="437.5" cy="50.000000000000014" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 53.010851445536446 L 0 50 L 4 46.989148554463554 L 8 44.00870058456779 L 12 41.08875255268895 L 16 38.25878999488393 L 20 35.54738977694854 L 24 32.9819315261973 L 28 30.588321152916674 L 32 28.390729253362792 L 36 26.411347035901436 L 40 24.670162234939546 L 44 23.18475727546209 L 48 21.970131726301638 L 52 21.038550835001782 L 56 20.39942167376396 L 60 20.059198147151854 L 64 20.021315820782323 L 68 20.286157229100464 L 72 20.85104801255978 L 76 21.710283923214064 L 80 22.855188426019417 L 84 24.274200314190434 L 88 25.952990453873696 L 92 27.874606479254783 L 96 30.01964397697245 L 100 32.366442431225806 L 104 34.891303950927174 L 108 37.56873257020147 L 112 40.37169170578371 L 116 43.27187717151857 L 120 46.24000299307087 L 124 49.246097136699866 L 128 52.259804165837984 L 132 55.25069176925828 L 136 58.18855806551976 L 140 61.04373658054035 L 144 63.78739581864462 L 148 66.39183040202806 L 152 68.83074083872101 L 156 71.07949909396547 L 160 73.11539728327368 L 164 74.91787697587438 L 168 76.4687367930486 L 172 77.75231620503374 L 176 78.75565367052127 L 180 79.46861752186066 L 184 79.88400827429518 L 188 79.99763132611449 L 192 79.80833931560025 L 196 79.3180437070458 L 200 78.53169548885461 L 204 77.45723517862753 L 208 76.10551264008578 L 212 74.49017752151552 L 216 72.62754142208313 L 220 70.53641317786067 L 224 68.23790893083817 L 228 65.75523889883885 L 232 63.11347299952798 L 236 60.33928769523553 L 240 57.46069661494566 L 244 54.50676767362273 L 248 51.507329545393105 L 252 48.49267045460691 L 256 45.49323232637728 L 260 42.53930338505435 L 264 39.66071230476449 L 268 36.886527000472014 L 272 34.24476110116113 L 276 31.762091069161837 L 280 29.463586822139323 L 284 27.37245857791691 L 288 25.509822478484498 L 292 23.894487359914248 L 296 22.542764821372476 L 300 21.468304511145394 L 304 20.681956292954197 L 308 20.19166068439975 L 312 20.002368673885517 L 316 20.115991725704824 L 320 20.531382478139342 L 324 21.244346329478716 L 328 22.247683794966264 L 332 23.53126320695139 L 336 25.08212302412562 L 340 26.88460271672634 L 344 28.92050090603449 L 348 31.16925916127897 L 352 33.60816959797189 L 356 36.212604181355346 L 360 38.95626341945966 L 364 41.811441934480214 L 368 44.74930823074172 L 372 47.74019583416199 L 376 50.75390286330012 L 380 53.7599970069291 L 384 56.72812282848143 L 388 59.6283082942163 L 392 62.43126742979852 L 396 65.10869604907285 L 400 67.63355756877418 L 404 69.98035602302755 L 408 72.1253935207452 L 412 74.04700954612628 L 416 75.72579968580956 L 420 77.14481157398058 L 424 78.28971607678594 L 428 79.14895198744023 L 432 79.71384277089953 L 436 79.97868417921768 L 440 79.94080185284815 L 444 79.60057832623605 L 448 78.96144916499821 L 452 78.02986827369836 L 456 76.81524272453791 L 460 75.32983776506046 L 464 73.58865296409856 L 468 71.60927074663721 L 472 69.41167884708335 L 476 67.0180684738027 L 480 64.4526102230515 L 484 61.74121000511608 L 488 58.911247447311084 L 492 55.99129941543222 L 496 53.01085144553649 L 500 50.000000000000014"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="15" width="10" height="10"></rect><rect x="120" y="44.99999999999999" width="10" height="10"></rect><rect x="182.5" y="75" width="10" height="10"></rect><rect x="245" y="45.00000000000001" width="10" height="10"></rect><rect x="307.5" y="15" width="10" height="10"></rect><rect x="370" y="44.999999999999986" width="10" height="10"></rect><rect x="432.5" y="75" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">3</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of Frequency 3: there are three up to down to up cycles of the cosine and sine waves.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.34044765786258 L 0 20 L 4 20.34044765786258 L 8 21.35406365760071 L 12 23.017842453008868 L 16 25.294022071147175 L 20 28.130941177357652 L 24 31.464211607289972 L 28 35.218179753551254 L 32 39.30764363860248 L 36 43.639786702338355 L 40 48.1162844141206 L 44 52.63553589652229 L 48 57.09496991071174 L 52 61.39337286565403 L 56 65.4331860134452 L 60 69.12271969246069 L 64 72.37823436272546 L 68 75.12584120126425 L 72 77.30317912054987 L 76 78.86083014758258 L 80 79.76344103943434 L 84 79.990525678499 L 88 79.53693003587617 L 92 78.41294914984233 L 96 76.64409346440634 L 100 74.27050983124843 L 104 71.34607031627857 L 108 67.93714949172556 L 112 64.12111796495998 L 116 59.98458633568961 L 120 55.62143943757174 L 124 51.13070548009805 L 128 46.61430845379555 L 132 42.17475481130308 L 136 37.91280692859013 L 140 33.9251961506301 L 144 30.302427327411316 L 148 27.12672466965658 L 152 24.47016554615925 L 156 22.393044579023893 L 160 20.942505166141064 L 164 20.151469490560995 L 168 20.037891301819474 L 172 20.604348428472587 L 176 21.837984270383767 L 180 23.710799598684087 L 184 26.18028804056494 L 188 29.19040082561584 L 192 32.672818897331965 L 196 36.54850351729904 L 200 40.72949016875157 L 204 45.12088504415347 L 208 49.62301880349941 L 212 54.13370872053912 L 216 58.55057787409927 L 220 62.77337874695215 L 224 66.70626849464564 L 228 70.25998424363073 L 232 73.35386904701069 L 236 75.91770251578504 L 240 77.89329457664755 L 244 79.2358061835973 L 248 79.91476700781841 L 252 79.91476700781843 L 256 79.23580618359732 L 260 77.89329457664755 L 264 75.91770251578505 L 268 73.3538690470107 L 272 70.25998424363074 L 276 66.70626849464563 L 280 62.773378746952176 L 284 58.55057787409934 L 288 54.13370872053916 L 292 49.623018803499455 L 296 45.12088504415354 L 300 40.72949016875159 L 304 36.548503517299054 L 308 32.67281889733201 L 312 29.190400825615857 L 316 26.180288040564953 L 320 23.710799598684083 L 324 21.837984270383778 L 328 20.604348428472598 L 332 20.03789130181947 L 336 20.151469490560995 L 340 20.942505166141064 L 344 22.39304457902386 L 348 24.470165546159226 L 352 27.126724669656532 L 356 30.302427327411298 L 360 33.92519615063008 L 364 37.912806928590086 L 368 42.17475481130309 L 372 46.61430845379553 L 376 51.130705480098 L 380 55.621439437571745 L 384 59.984586335689585 L 388 64.12111796495995 L 392 67.93714949172558 L 396 71.34607031627856 L 400 74.27050983124842 L 404 76.64409346440635 L 408 78.41294914984232 L 412 79.53693003587615 L 416 79.990525678499 L 420 79.76344103943434 L 424 78.86083014758259 L 428 77.3031791205499 L 432 75.12584120126427 L 436 72.37823436272546 L 440 69.12271969246073 L 444 65.43318601344521 L 448 61.39337286565402 L 452 57.0949699107118 L 456 52.63553589652231 L 460 48.11628441412058 L 464 43.639786702338405 L 468 39.307643638602485 L 472 35.21817975355132 L 476 31.46421160729 L 480 28.130941177357652 L 484 25.294022071147218 L 488 23.017842453008882 L 492 21.354063657600708 L 496 20.340447657862587 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="71.21320343559643" r="5"></circle><circle cx="125" cy="50.00000000000001" r="5"></circle><circle cx="187.5" cy="28.78679656440357" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="312.5" cy="28.786796564403602" r="5"></circle><circle cx="375" cy="49.999999999999986" r="5"></circle><circle cx="437.5" cy="71.21320343559643" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 54.50676767362271 L 0 50 L 4 45.49323232637729 L 8 41.08875255268895 L 12 36.886527000472014 L 16 32.9819315261973 L 20 29.463586822139337 L 24 26.411347035901432 L 28 23.89448735991423 L 32 21.970131726301638 L 36 20.681956292954197 L 40 20.059198147151854 L 44 20.115991725704824 L 48 20.85104801255978 L 52 22.24768379496626 L 56 24.274200314190434 L 60 26.884602716726324 L 64 30.01964397697245 L 68 33.60816959797192 L 72 37.56873257020147 L 76 41.811441934480236 L 80 46.240002993070874 L 84 50.75390286330013 L 88 55.25069176925827 L 92 59.62830829421628 L 96 63.78739581864463 L 100 67.6335575687742 L 104 71.07949909396547 L 108 74.04700954612629 L 112 76.4687367930486 L 116 78.28971607678594 L 120 79.46861752186066 L 124 79.97868417921768 L 128 79.80833931560025 L 132 78.96144916499821 L 136 77.45723517862753 L 140 75.32983776506045 L 144 72.62754142208313 L 148 69.41167884708335 L 152 65.75523889883888 L 156 61.74121000511607 L 160 57.46069661494563 L 164 53.01085144553645 L 168 48.49267045460691 L 172 44.008700584567826 L 176 39.660712304764516 L 180 35.547389776948556 L 184 31.762091069161837 L 188 28.390729253362807 L 192 25.509822478484484 L 196 23.184757275462083 L 200 21.468304511145394 L 204 20.39942167376396 L 208 20.002368673885517 L 212 20.28615722910046 L 216 21.244346329478716 L 220 22.8551884260194 L 224 25.08212302412562 L 228 27.87460647925478 L 232 31.16925916127897 L 236 34.89130395092714 L 240 38.95626341945966 L 244 43.27187717151855 L 248 47.74019583416199 L 252 52.25980416583799 L 256 56.72812282848143 L 260 61.04373658054032 L 264 65.10869604907285 L 268 68.83074083872101 L 272 72.1253935207452 L 276 74.91787697587439 L 280 77.14481157398059 L 284 78.75565367052126 L 288 79.71384277089953 L 292 79.99763132611449 L 296 79.60057832623605 L 300 78.53169548885461 L 304 76.81524272453792 L 308 74.49017752151555 L 312 71.60927074663721 L 316 68.23790893083817 L 320 64.45261022305144 L 324 60.33928769523551 L 328 55.99129941543222 L 332 51.50732954539306 L 336 46.98914855446354 L 340 42.53930338505436 L 344 38.258789994884 L 348 34.24476110116116 L 352 30.58832115291671 L 356 27.372458577916895 L 360 24.67016223493956 L 364 22.54276482137249 L 368 21.038550835001782 L 372 20.19166068439975 L 376 20.02131582078232 L 380 20.531382478139342 L 384 21.71028392321406 L 388 23.531263206951387 L 392 25.95299045387371 L 396 28.92050090603452 L 400 32.36644243122579 L 404 36.21260418135538 L 408 40.37169170578367 L 412 44.749308230741654 L 416 49.24609713669985 L 420 53.75999700692909 L 424 58.18855806551971 L 428 62.43126742979846 L 432 66.39183040202805 L 436 69.98035602302755 L 440 73.11539728327364 L 444 75.72579968580956 L 448 77.75231620503375 L 452 79.1489519874402 L 456 79.88400827429517 L 460 79.94080185284815 L 464 79.31804370704582 L 468 78.02986827369836 L 472 76.1055126400858 L 476 73.5886529640986 L 480 70.53641317786065 L 484 67.01806847380276 L 488 63.113472999528014 L 492 58.91124744731104 L 496 54.50676767362277 L 500 50.00000000000002"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="23.786796564403573" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="182.5" y="23.78679656440358" width="10" height="10"></rect><rect x="245" y="44.999999999999986" width="10" height="10"></rect><rect x="307.5" y="66.21320343559645" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="432.5" y="66.21320343559643" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">4</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>
            Diagram of Frequency 4: there are four up to down to up cycles of the cosine and sine waves.
Notably, the cosine samples alternate between '1' and '-1', and the sine samples are all '0'.
          </title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.604348428472594 L 0 20 L 4 20.604348428472594 L 8 22.39304457902389 L 12 25.294022071147175 L 16 29.190400825615853 L 20 33.9251961506301 L 24 39.30764363860248 L 28 45.12088504415349 L 32 51.13070548009804 L 36 57.09496991071173 L 40 62.77337874695218 L 44 67.93714949172556 L 48 72.37823436272546 L 52 75.91770251578507 L 56 78.41294914984232 L 60 79.76344103943433 L 64 79.91476700781843 L 68 78.86083014758258 L 72 76.64409346440635 L 76 73.3538690470107 L 80 69.12271969246069 L 84 64.12111796495998 L 88 58.55057787409931 L 92 52.6355358965223 L 96 46.61430845379555 L 100 40.72949016875158 L 104 35.21817975355126 L 108 30.302427327411316 L 112 26.18028804056495 L 116 23.017842453008864 L 120 20.94250516614107 L 124 20.037891301819474 L 128 20.340447657862583 L 132 21.837984270383778 L 136 24.470165546159244 L 140 28.13094117735767 L 144 32.67281889733195 L 148 37.912806928590115 L 152 43.63978670233835 L 156 49.62301880349941 L 160 55.62143943757175 L 164 61.393372865654044 L 168 66.70626849464564 L 172 71.34607031627853 L 176 75.12584120126422 L 180 77.89329457664755 L 184 79.53693003587617 L 188 79.990525678499 L 192 79.23580618359732 L 196 77.30317912054987 L 200 74.27050983124843 L 204 70.25998424363074 L 208 65.43318601344521 L 212 59.98458633568961 L 216 54.13370872053916 L 220 48.11628441412062 L 224 42.17475481130312 L 228 36.54850351729901 L 232 31.46421160728995 L 236 27.126724669656586 L 240 23.71079959868411 L 244 21.354063657600722 L 248 20.151469490561 L 252 20.151469490560995 L 256 21.354063657600715 L 260 23.710799598684098 L 264 27.126724669656568 L 268 31.464211607289972 L 272 36.54850351729903 L 276 42.17475481130309 L 280 48.11628441412065 L 284 54.13370872053908 L 288 59.98458633568954 L 292 65.43318601344514 L 296 70.25998424363073 L 300 74.27050983124842 L 304 77.30317912054986 L 308 79.2358061835973 L 312 79.990525678499 L 316 79.53693003587617 L 320 77.89329457664753 L 324 75.12584120126427 L 328 71.34607031627854 L 332 66.70626849464567 L 336 61.39337286565402 L 340 55.621439437571674 L 344 49.62301880349952 L 348 43.639786702338405 L 352 37.912806928590214 L 356 32.672818897332014 L 360 28.130941177357652 L 364 24.470165546159276 L 368 21.837984270383778 L 372 20.340447657862587 L 376 20.037891301819474 L 380 20.94250516614105 L 384 23.017842453008864 L 388 26.18028804056496 L 392 30.302427327411294 L 396 35.21817975355128 L 400 40.72949016875155 L 404 46.61430845379557 L 408 52.63553589652227 L 412 58.550577874099204 L 416 64.12111796495995 L 420 69.12271969246062 L 424 73.35386904701068 L 428 76.64409346440634 L 432 78.86083014758256 L 436 79.91476700781843 L 440 79.76344103943434 L 444 78.41294914984232 L 448 75.91770251578508 L 452 72.37823436272546 L 456 67.93714949172552 L 460 62.77337874695218 L 464 57.0949699107117 L 468 51.130705480098044 L 472 45.12088504415356 L 476 39.30764363860249 L 480 33.92519615063016 L 484 29.190400825615864 L 488 25.294022071147218 L 492 22.393044579023897 L 496 20.604348428472612 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="80" r="5"></circle><circle cx="125" cy="20" r="5"></circle><circle cx="187.5" cy="80" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="312.5" cy="80" r="5"></circle><circle cx="375" cy="20" r="5"></circle><circle cx="437.5" cy="80" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 55.99129941543221 L 0 50 L 4 44.00870058456779 L 8 38.25878999488393 L 12 32.9819315261973 L 16 28.390729253362792 L 20 24.670162234939546 L 24 21.970131726301638 L 28 20.39942167376396 L 32 20.021315820782323 L 36 20.85104801255978 L 40 22.855188426019417 L 44 25.952990453873696 L 48 30.01964397697245 L 52 34.891303950927174 L 56 40.37169170578371 L 60 46.24000299307087 L 64 52.259804165837984 L 68 58.18855806551976 L 72 63.78739581864462 L 76 68.83074083872101 L 80 73.11539728327368 L 84 76.4687367930486 L 88 78.75565367052127 L 92 79.88400827429518 L 96 79.80833931560025 L 100 78.53169548885461 L 104 76.10551264008578 L 108 72.62754142208313 L 112 68.23790893083817 L 116 63.11347299952798 L 120 57.46069661494566 L 124 51.507329545393105 L 128 45.49323232637728 L 132 39.66071230476449 L 136 34.24476110116113 L 140 29.463586822139323 L 144 25.509822478484498 L 148 22.542764821372476 L 152 20.681956292954197 L 156 20.002368673885517 L 160 20.531382478139342 L 164 22.247683794966264 L 168 25.08212302412562 L 172 28.92050090603449 L 176 33.60816959797189 L 180 38.95626341945966 L 184 44.74930823074172 L 188 50.75390286330012 L 192 56.72812282848143 L 196 62.43126742979852 L 200 67.63355756877418 L 204 72.1253935207452 L 208 75.72579968580956 L 212 78.28971607678594 L 216 79.71384277089953 L 220 79.94080185284815 L 224 78.96144916499821 L 228 76.81524272453791 L 232 73.58865296409856 L 236 69.41167884708335 L 240 64.4526102230515 L 244 58.911247447311084 L 248 53.01085144553649 L 252 46.98914855446354 L 256 41.088752552688945 L 260 35.547389776948535 L 264 30.588321152916667 L 268 26.411347035901432 L 272 23.184757275462086 L 276 21.038550835001782 L 280 20.05919814715185 L 284 20.286157229100453 L 288 21.710283923214043 L 292 24.2742003141904 L 296 27.874606479254773 L 300 32.36644243122579 L 304 37.56873257020146 L 308 43.27187717151854 L 312 49.24609713669985 L 316 55.25069176925825 L 320 61.04373658054036 L 324 66.39183040202805 L 328 71.0794990939655 L 332 74.91787697587436 L 336 77.75231620503375 L 340 79.46861752186066 L 344 79.99763132611449 L 348 79.31804370704582 L 352 77.45723517862757 L 356 74.49017752151555 L 360 70.53641317786065 L 364 65.75523889883891 L 368 60.33928769523551 L 372 54.50676767362277 L 376 48.49267045460692 L 380 42.53930338505442 L 384 36.88652700047203 L 388 31.76209106916181 L 392 27.3724585779169 L 396 23.894487359914212 L 400 21.4683045111454 L 404 20.191660684399746 L 408 20.115991725704824 L 412 21.2443463294787 L 416 23.531263206951387 L 420 26.884602716726267 L 424 31.169259161278955 L 428 36.212604181355374 L 432 41.8114419344802 L 436 47.74019583416202 L 440 53.75999700692908 L 444 59.62830829421629 L 448 65.10869604907278 L 452 69.98035602302755 L 456 74.04700954612633 L 460 77.14481157398058 L 464 79.14895198744023 L 468 79.97868417921768 L 472 79.60057832623605 L 476 78.02986827369837 L 480 75.32983776506049 L 484 71.60927074663722 L 488 67.01806847380276 L 492 61.741210005116095 L 496 55.99129941543229 L 500 50.00000000000003"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="44.99999999999999" width="10" height="10"></rect><rect x="120" y="45.00000000000001" width="10" height="10"></rect><rect x="182.5" y="44.999999999999986" width="10" height="10"></rect><rect x="245" y="45.000000000000014" width="10" height="10"></rect><rect x="307.5" y="44.99999999999998" width="10" height="10"></rect><rect x="370" y="45.00000000000002" width="10" height="10"></rect><rect x="432.5" y="44.99999999999997" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">5</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of Frequency 5: there are five up to down to up cycles of the cosine and sine waves.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.942505166141068 L 0 20 L 4 20.942505166141068 L 8 23.710799598684094 L 12 28.130941177357652 L 16 33.9251961506301 L 20 40.729490168751575 L 24 48.1162844141206 L 28 55.62143943757174 L 32 62.77337874695218 L 36 69.12271969246068 L 40 74.27050983124842 L 44 77.89329457664755 L 48 79.76344103943434 L 52 79.76344103943434 L 56 77.89329457664755 L 60 74.27050983124843 L 64 69.12271969246069 L 68 62.77337874695216 L 72 55.621439437571766 L 76 48.116284414120614 L 80 40.72949016875158 L 84 33.9251961506301 L 88 28.130941177357663 L 92 23.710799598684094 L 96 20.942505166141064 L 100 20 L 104 20.94250516614106 L 108 23.710799598684087 L 112 28.130941177357652 L 116 33.92519615063011 L 120 40.72949016875157 L 124 48.1162844141206 L 128 55.62143943757175 L 132 62.773378746952204 L 136 69.12271969246072 L 140 74.27050983124842 L 144 77.89329457664752 L 148 79.76344103943433 L 152 79.76344103943434 L 156 77.89329457664755 L 160 74.27050983124843 L 164 69.12271969246069 L 168 62.773378746952176 L 172 55.62143943757177 L 176 48.11628441412062 L 180 40.72949016875159 L 184 33.9251961506301 L 188 28.13094117735765 L 192 23.710799598684083 L 196 20.94250516614107 L 200 20 L 204 20.94250516614105 L 208 23.71079959868407 L 212 28.13094117735763 L 216 33.92519615063008 L 220 40.72949016875156 L 224 48.11628441412059 L 228 55.621439437571745 L 232 62.77337874695219 L 236 69.12271969246066 L 240 74.27050983124842 L 244 77.89329457664755 L 248 79.76344103943434 L 252 79.76344103943434 L 256 77.89329457664753 L 260 74.27050983124843 L 264 69.12271969246066 L 268 62.773378746952176 L 272 55.621439437571674 L 276 48.11628441412058 L 280 40.729490168751596 L 284 33.92519615063016 L 288 28.130941177357727 L 292 23.710799598684112 L 296 20.94250516614109 L 300 20 L 304 20.94250516614105 L 308 23.710799598684094 L 312 28.130941177357624 L 316 33.925196150630114 L 320 40.72949016875155 L 324 48.11628441412064 L 328 55.62143943757174 L 332 62.77337874695223 L 336 69.1227196924607 L 340 74.2705098312484 L 344 77.89329457664752 L 348 79.76344103943433 L 352 79.76344103943434 L 356 77.89329457664758 L 360 74.27050983124843 L 364 69.12271969246075 L 368 62.77337874695218 L 372 55.62143943757179 L 376 48.116284414120585 L 380 40.7294901687516 L 384 33.92519615063007 L 388 28.13094117735766 L 392 23.710799598684115 L 396 20.942505166141064 L 400 20 L 404 20.942505166141075 L 408 23.710799598684037 L 412 28.13094117735762 L 416 33.92519615063002 L 420 40.729490168751546 L 424 48.11628441412053 L 428 55.62143943757173 L 432 62.77337874695213 L 436 69.12271969246069 L 440 74.2705098312484 L 444 77.89329457664755 L 448 79.76344103943433 L 452 79.76344103943434 L 456 77.89329457664753 L 460 74.27050983124845 L 464 69.12271969246066 L 468 62.77337874695219 L 472 55.621439437571794 L 476 48.1162844141207 L 480 40.72949016875161 L 484 33.92519615063017 L 488 28.130941177357663 L 492 23.71079959868412 L 496 20.942505166141064 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="71.21320343559643" r="5"></circle><circle cx="125" cy="49.99999999999999" r="5"></circle><circle cx="187.5" cy="28.786796564403602" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="312.5" cy="28.786796564403577" r="5"></circle><circle cx="375" cy="50.00000000000008" r="5"></circle><circle cx="437.5" cy="71.21320343559638" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 57.46069661494565 L 0 50 L 4 42.53930338505435 L 8 35.54738977694854 L 12 29.463586822139337 L 16 24.670162234939546 L 20 21.468304511145394 L 24 20.059198147151854 L 28 20.53138247813934 L 32 22.855188426019417 L 36 26.884602716726317 L 40 32.366442431225806 L 44 38.95626341945966 L 48 46.240002993070874 L 52 53.75999700692911 L 56 61.04373658054034 L 60 67.6335575687742 L 64 73.11539728327368 L 68 77.14481157398059 L 72 79.46861752186065 L 76 79.94080185284815 L 80 78.53169548885461 L 84 75.32983776506045 L 88 70.53641317786067 L 92 64.45261022305147 L 96 57.46069661494563 L 100 50.00000000000001 L 104 42.53930338505438 L 108 35.547389776948556 L 112 29.46358682213934 L 116 24.670162234939543 L 120 21.468304511145394 L 124 20.059198147151854 L 128 20.531382478139342 L 132 22.855188426019424 L 136 26.88460271672634 L 140 32.3664424312258 L 144 38.95626341945961 L 148 46.24000299307083 L 152 53.7599970069291 L 156 61.04373658054032 L 160 67.63355756877418 L 164 73.11539728327368 L 168 77.14481157398059 L 172 79.46861752186065 L 176 79.94080185284815 L 180 78.53169548885461 L 184 75.32983776506046 L 188 70.53641317786065 L 192 64.45261022305144 L 196 57.46069661494567 L 200 50.000000000000014 L 204 42.53930338505441 L 208 35.547389776948584 L 212 29.46358682213937 L 216 24.67016223493956 L 220 21.468304511145398 L 224 20.059198147151854 L 228 20.531382478139342 L 232 22.85518842601942 L 236 26.884602716726306 L 240 32.36644243122579 L 244 38.95626341945965 L 248 46.240002993070874 L 252 53.75999700692909 L 256 61.04373658054036 L 260 67.63355756877418 L 264 73.11539728327371 L 268 77.14481157398059 L 272 79.46861752186066 L 276 79.94080185284815 L 280 78.53169548885461 L 284 75.32983776506049 L 288 70.53641317786074 L 292 64.4526102230515 L 296 57.460696614945725 L 300 50.00000000000002 L 304 42.53930338505442 L 308 35.54738977694854 L 312 29.463586822139373 L 316 24.67016223493954 L 320 21.4683045111454 L 324 20.05919814715185 L 328 20.53138247813934 L 332 22.85518842601944 L 336 26.884602716726334 L 340 32.366442431225785 L 344 38.956263419459596 L 348 46.24000299307076 L 352 53.75999700692908 L 356 61.043736580540255 L 360 67.63355756877417 L 364 73.11539728327364 L 368 77.14481157398058 L 372 79.46861752186065 L 376 79.94080185284815 L 380 78.53169548885461 L 384 75.32983776506043 L 388 70.53641317786067 L 392 64.45261022305151 L 396 57.46069661494563 L 400 50.00000000000003 L 404 42.539303385054325 L 408 35.54738977694864 L 412 29.463586822139376 L 416 24.670162234939596 L 420 21.4683045111454 L 424 20.059198147151857 L 428 20.53138247813934 L 432 22.855188426019392 L 436 26.884602716726327 L 440 32.36644243122578 L 444 38.95626341945969 L 448 46.24000299307086 L 452 53.759997006929076 L 456 61.04373658054035 L 460 67.63355756877417 L 464 73.1153972832737 L 468 77.14481157398058 L 472 79.46861752186065 L 476 79.94080185284815 L 480 78.53169548885462 L 484 75.32983776506049 L 488 70.53641317786068 L 492 64.45261022305151 L 496 57.46069661494564 L 500 50.000000000000036"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="66.21320343559643" width="10" height="10"></rect><rect x="120" y="15" width="10" height="10"></rect><rect x="182.5" y="66.21320343559645" width="10" height="10"></rect><rect x="245" y="44.99999999999998" width="10" height="10"></rect><rect x="307.5" y="23.786796564403573" width="10" height="10"></rect><rect x="370" y="75" width="10" height="10"></rect><rect x="432.5" y="23.786796564403534" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">6</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of Frequency 6: there are six up to down to up cycles of the cosine and sine waves.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 21.35406365760071 L 0 20 L 4 21.35406365760071 L 8 25.294022071147175 L 12 31.464211607289972 L 16 39.30764363860248 L 20 48.1162844141206 L 24 57.09496991071174 L 28 65.4331860134452 L 32 72.37823436272546 L 36 77.30317912054987 L 40 79.76344103943434 L 44 79.53693003587617 L 48 76.64409346440634 L 52 71.34607031627857 L 56 64.12111796495998 L 60 55.62143943757174 L 64 46.61430845379555 L 68 37.91280692859013 L 72 30.302427327411316 L 76 24.47016554615925 L 80 20.942505166141064 L 84 20.037891301819474 L 88 21.837984270383767 L 92 26.18028804056494 L 96 32.672818897331965 L 100 40.72949016875157 L 104 49.62301880349941 L 108 58.55057787409927 L 112 66.70626849464564 L 116 73.35386904701069 L 120 77.89329457664755 L 124 79.91476700781841 L 128 79.23580618359732 L 132 75.91770251578505 L 136 70.25998424363074 L 140 62.773378746952176 L 144 54.13370872053916 L 148 45.12088504415354 L 152 36.548503517299054 L 156 29.190400825615857 L 160 23.710799598684083 L 164 20.604348428472598 L 168 20.151469490560995 L 172 22.39304457902386 L 176 27.126724669656532 L 180 33.92519615063008 L 184 42.17475481130309 L 188 51.130705480098 L 192 59.984586335689585 L 196 67.93714949172558 L 200 74.27050983124842 L 204 78.41294914984232 L 208 79.990525678499 L 212 78.86083014758259 L 216 75.12584120126427 L 220 69.12271969246073 L 224 61.39337286565402 L 228 52.63553589652231 L 232 43.639786702338405 L 236 35.21817975355132 L 240 28.130941177357652 L 244 23.017842453008882 L 248 20.340447657862587 L 252 20.340447657862583 L 256 23.017842453008864 L 260 28.130941177357624 L 264 35.21817975355128 L 268 43.63978670233836 L 272 52.63553589652227 L 276 61.39337286565408 L 280 69.1227196924607 L 284 75.1258412012642 L 288 78.86083014758256 L 292 79.990525678499 L 296 78.41294914984236 L 300 74.27050983124843 L 304 67.93714949172562 L 308 59.984586335689684 L 312 51.130705480098044 L 316 42.17475481130313 L 320 33.92519615063007 L 324 27.12672466965656 L 328 22.393044579023897 L 332 20.15146949056099 L 336 20.604348428472598 L 340 23.71079959868409 L 344 29.190400825615747 L 348 36.54850351729897 L 352 45.1208850441534 L 356 54.13370872053912 L 360 62.77337874695213 L 364 70.25998424363067 L 368 75.91770251578505 L 372 79.2358061835973 L 376 79.91476700781843 L 380 77.89329457664753 L 384 73.35386904701072 L 388 66.70626849464568 L 392 58.55057787409926 L 396 49.62301880349943 L 400 40.72949016875161 L 404 32.672818897331936 L 408 26.180288040565 L 412 21.83798427038382 L 416 20.037891301819474 L 420 20.942505166141046 L 424 24.470165546159187 L 428 30.302427327411202 L 432 37.912806928590065 L 436 46.61430845379556 L 440 55.62143943757162 L 444 64.12111796495994 L 448 71.34607031627858 L 452 76.6440934644063 L 456 79.53693003587615 L 460 79.76344103943433 L 464 77.30317912054991 L 468 72.37823436272546 L 472 65.43318601344532 L 476 57.09496991071181 L 480 48.1162844141206 L 484 39.307643638602606 L 488 31.464211607290018 L 492 25.294022071147168 L 496 21.354063657600747 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="50.00000000000001" r="5"></circle><circle cx="125" cy="80" r="5"></circle><circle cx="187.5" cy="49.999999999999986" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="312.5" cy="50.00000000000008" r="5"></circle><circle cx="375" cy="80" r="5"></circle><circle cx="437.5" cy="50.000000000000014" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 58.91124744731105 L 0 50 L 4 41.08875255268895 L 8 32.9819315261973 L 12 26.411347035901432 L 16 21.970131726301638 L 20 20.059198147151854 L 24 20.85104801255978 L 28 24.274200314190434 L 32 30.01964397697245 L 36 37.56873257020147 L 40 46.240002993070874 L 44 55.25069176925827 L 48 63.78739581864463 L 52 71.07949909396547 L 56 76.4687367930486 L 60 79.46861752186066 L 64 79.80833931560025 L 68 77.45723517862753 L 72 72.62754142208313 L 76 65.75523889883888 L 80 57.46069661494563 L 84 48.49267045460691 L 88 39.660712304764516 L 92 31.762091069161837 L 96 25.509822478484484 L 100 21.468304511145394 L 104 20.002368673885517 L 108 21.244346329478716 L 112 25.08212302412562 L 116 31.16925916127897 L 120 38.95626341945966 L 124 47.74019583416199 L 128 56.72812282848143 L 132 65.10869604907285 L 136 72.1253935207452 L 140 77.14481157398059 L 144 79.71384277089953 L 148 79.60057832623605 L 152 76.81524272453792 L 156 71.60927074663721 L 160 64.45261022305144 L 164 55.99129941543222 L 168 46.98914855446354 L 172 38.258789994884 L 176 30.58832115291671 L 180 24.67016223493956 L 184 21.038550835001782 L 188 20.02131582078232 L 192 21.71028392321406 L 196 25.95299045387371 L 200 32.36644243122579 L 204 40.37169170578367 L 208 49.24609713669985 L 212 58.18855806551971 L 216 66.39183040202805 L 220 73.11539728327364 L 224 77.75231620503375 L 228 79.88400827429517 L 232 79.31804370704582 L 236 76.1055126400858 L 240 70.53641317786065 L 244 63.113472999528014 L 248 54.50676767362277 L 252 45.493232326377274 L 256 36.88652700047203 L 260 29.463586822139373 L 264 23.894487359914212 L 268 20.681956292954197 L 272 20.115991725704824 L 276 22.247683794966274 L 280 26.884602716726334 L 284 33.60816959797182 L 288 41.8114419344802 L 292 50.75390286330005 L 296 59.62830829421619 L 300 67.63355756877417 L 304 74.04700954612626 L 308 78.28971607678591 L 312 79.97868417921768 L 316 78.96144916499823 L 320 75.32983776506043 L 324 69.41167884708332 L 328 61.741210005116095 L 332 53.010851445536396 L 336 44.00870058456777 L 340 35.54738977694855 L 344 28.390729253362895 L 348 23.18475727546212 L 352 20.399421673763975 L 356 20.28615722910046 L 360 22.855188426019392 L 364 27.874606479254727 L 368 34.89130395092717 L 372 43.271877171518526 L 376 52.25980416583791 L 380 61.04373658054035 L 384 68.830740838721 L 388 74.91787697587435 L 392 78.75565367052128 L 396 79.99763132611449 L 400 78.53169548885462 L 404 74.49017752151549 L 408 68.23790893083824 L 412 60.339287695235626 L 416 51.507329545393134 L 420 42.53930338505443 L 424 34.244761101161224 L 428 27.372458577916976 L 432 22.542764821372497 L 436 20.191660684399746 L 440 20.531382478139314 L 444 23.53126320695138 L 448 28.920500906034544 L 452 36.21260418135527 L 456 44.74930823074169 L 460 53.75999700692917 L 464 62.43126742979844 L 468 69.98035602302754 L 472 75.72579968580949 L 476 79.1489519874402 L 480 79.94080185284815 L 484 78.02986827369841 L 488 73.5886529640986 L 492 67.01806847380269 L 496 58.91124744731116 L 500 50.00000000000004"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="75" width="10" height="10"></rect><rect x="120" y="44.999999999999986" width="10" height="10"></rect><rect x="182.5" y="15" width="10" height="10"></rect><rect x="245" y="45.00000000000002" width="10" height="10"></rect><rect x="307.5" y="75" width="10" height="10"></rect><rect x="370" y="44.999999999999964" width="10" height="10"></rect><rect x="432.5" y="15" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">7</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of Frequency 7: there are seven up to down to up cycles of the cosine and sine waves.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 21.837984270383778 L 0 20 L 4 21.837984270383778 L 8 27.126724669656564 L 12 35.218179753551254 L 16 45.12088504415349 L 20 55.62143943757174 L 24 65.4331860134452 L 28 73.3538690470107 L 32 78.41294914984232 L 36 79.990525678499 L 40 77.89329457664755 L 44 72.37823436272546 L 48 64.12111796495998 L 52 54.133708720539154 L 56 43.63978670233836 L 60 33.92519615063012 L 64 26.18028804056495 L 68 21.35406365760071 L 72 20.03789130181947 L 76 22.393044579023886 L 80 28.130941177357652 L 84 36.54850351729904 L 88 46.61430845379554 L 92 57.09496991071171 L 96 66.70626849464564 L 100 74.27050983124842 L 104 78.86083014758256 L 108 79.91476700781843 L 112 77.30317912054987 L 116 71.34607031627854 L 120 62.77337874695222 L 124 52.635535896522306 L 128 42.17475481130312 L 132 32.672818897331965 L 136 25.294022071147182 L 140 20.94250516614106 L 144 20.15146949056099 L 148 23.01784245300884 L 152 29.190400825615832 L 156 37.912806928590086 L 160 48.11628441412059 L 164 58.55057787409926 L 168 67.93714949172558 L 172 75.12584120126422 L 176 79.2358061835973 L 180 79.76344103943434 L 184 76.64409346440635 L 188 70.25998424363071 L 192 61.39337286565402 L 196 51.13070548009804 L 200 40.729490168751596 L 204 31.46421160729 L 208 24.470165546159276 L 212 20.60434842847261 L 216 20.340447657862583 L 220 23.710799598684094 L 224 30.302427327411294 L 228 39.30764363860244 L 232 49.62301880349948 L 236 59.98458633568953 L 240 69.12271969246062 L 244 75.91770251578507 L 248 79.53693003587617 L 252 79.53693003587617 L 256 75.91770251578508 L 260 69.12271969246066 L 264 59.984586335689585 L 268 49.62301880349942 L 272 39.30764363860249 L 276 30.30242732741125 L 280 23.710799598684066 L 284 20.34044765786259 L 288 20.604348428472576 L 292 24.47016554615919 L 296 31.464211607289872 L 300 40.729490168751546 L 304 51.13070548009799 L 308 61.39337286565397 L 312 70.25998424363067 L 316 76.64409346440634 L 320 79.76344103943433 L 324 79.23580618359732 L 328 75.12584120126428 L 332 67.93714949172553 L 336 58.55057787409926 L 340 48.11628441412059 L 344 37.91280692859023 L 348 29.19040082561595 L 352 23.01784245300889 L 356 20.151469490561002 L 360 20.942505166141046 L 364 25.294022071147122 L 368 32.67281889733188 L 372 42.17475481130297 L 376 52.635535896522356 L 380 62.77337874695222 L 384 71.34607031627858 L 388 77.30317912054988 L 392 79.91476700781843 L 396 78.86083014758259 L 400 74.27050983124845 L 404 66.70626849464568 L 408 57.09496991071181 L 412 46.614308453795644 L 416 36.54850351729913 L 420 28.130941177357744 L 424 22.39304457902395 L 428 20.03789130181948 L 432 21.354063657600722 L 436 26.18028804056495 L 440 33.9251961506301 L 444 43.63978670233834 L 448 54.133708720539104 L 452 64.12111796495992 L 456 72.37823436272541 L 460 77.8932945766475 L 464 79.990525678499 L 468 78.4129491498423 L 472 73.35386904701079 L 476 65.43318601344532 L 480 55.621439437571915 L 484 45.120885044153475 L 488 35.218179753551254 L 492 27.126724669656575 L 496 21.83798427038379 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="62.5" cy="28.78679656440358" r="5"></circle><circle cx="125" cy="50.000000000000014" r="5"></circle><circle cx="187.5" cy="71.21320343559643" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="312.5" cy="71.21320343559638" r="5"></circle><circle cx="375" cy="50.000000000000014" r="5"></circle><circle cx="437.5" cy="28.786796564403485" r="5"></circle>
          </g>
          <g class="sin-wave">
            <path d="M -4 60.33928769523551 L 0 50 L 4 39.66071230476449 L 8 30.588321152916674 L 12 23.89448735991423 L 16 20.39942167376396 L 20 20.53138247813934 L 24 24.274200314190434 L 28 31.169259161278983 L 32 40.37169170578371 L 36 50.75390286330011 L 40 61.04373658054034 L 44 69.98035602302754 L 48 76.4687367930486 L 52 79.71384277089953 L 56 79.3180437070458 L 60 75.32983776506046 L 64 68.23790893083817 L 68 58.911247447311055 L 72 48.49267045460694 L 76 38.25878999488394 L 80 29.46358682213934 L 84 23.184757275462083 L 88 20.19166068439975 L 92 20.851048012559772 L 96 25.08212302412562 L 100 32.3664424312258 L 104 41.811441934480214 L 108 52.25980416583799 L 112 62.43126742979852 L 116 71.0794990939655 L 120 77.14481157398058 L 124 79.88400827429517 L 128 78.96144916499821 L 132 74.4901775215155 L 136 67.0180684738027 L 140 57.46069661494562 L 144 46.9891485544636 L 148 36.88652700047207 L 152 28.39072925336281 L 156 22.54276482137249 L 160 20.059198147151854 L 164 21.244346329478716 L 168 25.95299045387371 L 172 33.60816959797188 L 176 43.27187717151854 L 180 53.75999700692909 L 184 63.78739581864458 L 188 72.12539352074523 L 192 77.75231620503375 L 196 79.97868417921768 L 200 78.53169548885461 L 204 73.5886529640986 L 208 65.75523889883891 L 212 55.99129941543228 L 216 45.493232326377274 L 220 35.54738977694854 L 224 27.3724585779169 L 228 21.970131726301652 L 232 20.002368673885517 L 236 21.710283923214043 L 240 26.884602716726267 L 244 34.891303950927174 L 248 44.749308230741704 L 252 55.250691769258246 L 256 65.10869604907278 L 260 73.1153972832737 L 264 78.28971607678594 L 268 79.99763132611449 L 272 78.02986827369837 L 276 72.62754142208307 L 280 64.45261022305141 L 284 54.506767673622775 L 288 44.008700584567876 L 292 34.24476110116122 L 296 26.411347035901507 L 300 21.4683045111454 L 304 20.02131582078232 L 308 22.247683794966232 L 312 27.874606479254727 L 316 36.21260418135537 L 320 46.24000299307086 L 324 56.72812282848141 L 328 66.39183040202803 L 332 74.04700954612632 L 336 78.75565367052128 L 340 79.94080185284815 L 344 77.45723517862757 L 348 71.60927074663729 L 352 63.11347299952803 L 356 53.01085144553651 L 360 42.53930338505443 L 364 32.981931526197386 L 368 25.509822478484548 L 372 21.038550835001818 L 376 20.11599172570483 L 380 22.85518842601943 L 384 28.920500906034544 L 388 37.56873257020148 L 392 47.74019583416201 L 396 58.188558065519736 L 400 67.63355756877417 L 404 74.91787697587435 L 408 79.1489519874402 L 412 79.80833931560026 L 416 76.81524272453797 L 420 70.53641317786075 L 424 61.74121000511621 L 428 51.50732954539325 L 432 41.08875255268892 L 436 31.762091069161826 L 440 24.670162234939546 L 444 20.681956292954197 L 448 20.286157229100457 L 452 23.531263206951376 L 456 30.019643976972397 L 460 38.956263419459574 L 464 49.24609713669998 L 468 59.62830829421637 L 472 68.8307408387209 L 476 75.72579968580949 L 480 79.46861752186064 L 484 79.60057832623605 L 488 76.10551264008578 L 492 69.41167884708335 L 496 60.33928769523554 L 500 50.00000000000005"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="57.5" y="66.21320343559643" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="182.5" y="66.21320343559643" width="10" height="10"></rect><rect x="245" y="44.99999999999997" width="10" height="10"></rect><rect x="307.5" y="23.786796564403534" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="432.5" y="23.786796564403662" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>Ultimately, a DFT will give you a set of values that tell you how much each of these frequencies contributes to the signal.
Specifically, each frequency will have a value that represents both its <strong>amplitude</strong> and its <strong>phase</strong>.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The frequency at index <code>0</code><span class="attrs"></span> always has cosine values of <code>1</code><span class="attrs"></span> and sine values of <code>0</code><span class="attrs"></span>, and this ends up measuring how off-center
the signal is. This is known as the <strong><a href="https://en.wikipedia.org/wiki/DC_bias" target="_blank" rel="noopener">DC bias or DC offset</a></strong>.
</p></blockquote><p></p>
<p>Let’s figure out how to calculate the DFT for a signal!</p>
<h3>Frequency Testing</h3>
<p>The first step of computing the DFT is to figure out how to get a single frequency out of a signal. Ignoring <em>phase</em> for
a moment, this can be done in a surprisingly straightforward (to me, at least) manner: if we <strong>multiply each sample</strong> in the
signal by the <strong>cosine value of the test frequency</strong> and <strong>sum the results</strong>, you end up with a value that represents the
contribution of that frequency to the signal (which is <code>0</code><span class="attrs"></span> if the frequency is not present in the signal).</p>
<p>Here is an interactive example where you can set the signal and test frequencies and see the result of this process.
For purposes of illustration, the final sum in the diagram is instead an average (otherwise it would be too large to fit
in the diagram):</p>
<div class="center-flex">
  <div class="interactive-diagram-container">
    <div class="fft-diagram">
      <svg viewBox="0 0 600 340">
        <title id="diagram1-title">An interactive diagram that demonstrates how the signal and test waves are processed to detect frequencies.</title>
        <g">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g id="diagram1-signal-cos" class="neutral-wave"></g>
        
        <text x="250" y="105">&times;</text>
        <g transform="translate(0, 110)">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g id="diagram1-test-cos" class="cos-wave"></g>
        </g>
        <text x="250" y="220">&equals;</text>
        <g transform="translate(0, 220)">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g id="diagram1-product-cos" class="result-wave"></g>
        </g>
        <text x="525" y="258" class="smaller">avg</text>
        <text x="525" y="268" class="small">&rightarrow;</text>
        <g transform="translate(550, 220)">
          <line class="neutral-axis" x1="0" y1="50" x2="50" y2="50"></line>
          <g id="diagram1-full-sum" class="result-avg"></g>
        </g>
      </g"></svg>
    </div>
    <div class="input-grid">
      <div>Signal Amplitude:</div>
      <input id="diagram1-signal-amplitude-slider" type="range" min="0" max="10" step="1" value="10">
      <div id="diagram1-signal-amplitude-text">1.0</div>
      <div>Signal Cycles:</div>
      <input id="diagram1-signal-freq-slider" type="range" min="0" max="15" step="1" value="2">
      <div id="diagram1-signal-cycle-text">2</div>
      <div class="separator"></div>
      <div>Test Cycles:</div>
      <input id="diagram1-test-freq-slider" type="range" min="0" max="15" step="1" value="2">
      <div id="diagram1-test-cycle-text">2</div>
    </div>
  </div>
</div>
<p>Note that <strong>when the frequencies match</strong>, the resulting <strong>average scales with the amplitude of the signal</strong>, otherwise
(with one exception we’ll touch on in a moment) it is <code>0</code><span class="attrs"></span>. This makes the average effectively a <strong>frequency detector</strong>
(again, <strong>ignoring phase</strong> for now)! This is still true even when there are multiple frequencies represented within the
signal: this will result in a zero value for frequenices that are <em>not</em> in the signal and non-zero values for those that
<em>are</em>.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The mechanism by which this works is that the product of two waves with mismatched frequencies (over a span where they
have no fractional cycles) will have the same amount of area above and beneath the curve, and thus the average area is
zero.</p>
<p>This is hard to illustrate now, but it will be clearer in a later diagram.
</p></blockquote><p></p>
<p>As noted, there’s an exception here, where the multiply-and-sum <em>seems</em> to register a match when it shouldn’t.
For example: if you set the Signal Cycles to <code>1</code><span class="attrs"></span> and the Test Cycles to <code>15</code><span class="attrs"></span> (or vice versa), it will have a non-zero
average even though the frequencies don’t match. In fact, with the exception of frequencies <code>0</code><span class="attrs"></span> and <code>8</code><span class="attrs"></span>, there are
two matches for every other frequency. Why is that?</p>
<p>Well, now it’s time to talk about…</p>
<h3>Aliasing and Negative Frequencies</h3>
<p>Let’s take a closer look at those two frequencies’ <strong>cosine</strong> values:</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">1</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for frequency 1 with sixteen samples. The wave has a single cycle.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 20.037891301819474 L 0 20 L 4 20.037891301819474 L 8 20.151469490560995 L 12 20.34044765786258 L 16 20.604348428472594 L 20 20.942505166141068 L 24 21.35406365760071 L 28 21.837984270383778 L 32 22.39304457902389 L 36 23.017842453008868 L 40 23.710799598684094 L 44 24.47016554615924 L 48 25.294022071147175 L 52 26.18028804056494 L 56 27.126724669656564 L 60 28.130941177357652 L 64 29.190400825615853 L 68 30.30242732741131 L 72 31.46421160728997 L 76 32.67281889733197 L 80 33.9251961506301 L 84 35.218179753551254 L 88 36.548503517299025 L 92 37.912806928590115 L 96 39.30764363860248 L 100 40.729490168751575 L 104 42.17475481130309 L 108 43.639786702338355 L 112 45.12088504415349 L 116 46.61430845379555 L 120 48.11628441412059 L 124 49.62301880349942 L 128 51.13070548009804 L 132 52.6355358965223 L 136 54.13370872053914 L 140 55.621439437571745 L 144 57.09496991071173 L 148 58.55057787409928 L 152 59.9845863356896 L 156 61.39337286565403 L 160 62.77337874695218 L 164 64.12111796495998 L 168 65.4331860134452 L 172 66.70626849464563 L 176 67.93714949172556 L 180 69.12271969246069 L 184 70.25998424363073 L 188 71.34607031627856 L 192 72.37823436272546 L 196 73.3538690470107 L 200 74.27050983124842 L 204 75.12584120126425 L 208 75.91770251578507 L 212 76.64409346440634 L 216 77.30317912054987 L 220 77.89329457664755 L 224 78.41294914984232 L 228 78.86083014758258 L 232 79.23580618359732 L 236 79.53693003587617 L 240 79.76344103943433 L 244 79.91476700781843 L 248 79.990525678499 L 252 79.990525678499 L 256 79.91476700781843 L 260 79.76344103943434 L 264 79.53693003587617 L 268 79.23580618359732 L 272 78.86083014758258 L 276 78.41294914984233 L 280 77.89329457664753 L 284 77.30317912054988 L 288 76.64409346440635 L 292 75.91770251578507 L 296 75.12584120126425 L 300 74.27050983124843 L 304 73.3538690470107 L 308 72.37823436272546 L 312 71.34607031627857 L 316 70.25998424363074 L 320 69.12271969246069 L 324 67.93714949172556 L 328 66.70626849464563 L 332 65.43318601344521 L 336 64.12111796495998 L 340 62.77337874695216 L 344 61.39337286565406 L 348 59.98458633568961 L 352 58.55057787409931 L 356 57.094969910711754 L 360 55.62143943757174 L 364 54.133708720539154 L 368 52.6355358965223 L 372 51.13070548009805 L 376 49.62301880349943 L 380 48.116284414120614 L 384 46.61430845379555 L 388 45.12088504415348 L 392 43.63978670233836 L 396 42.17475481130308 L 400 40.72949016875158 L 404 39.30764363860247 L 408 37.91280692859013 L 412 36.548503517299054 L 416 35.21817975355126 L 420 33.92519615063012 L 424 32.67281889733198 L 428 31.46421160728997 L 432 30.302427327411316 L 436 29.19040082561585 L 440 28.130941177357663 L 444 27.126724669656564 L 448 26.18028804056495 L 452 25.294022071147175 L 456 24.470165546159237 L 460 23.710799598684094 L 464 23.017842453008864 L 468 22.393044579023893 L 472 21.837984270383785 L 476 21.35406365760071 L 480 20.94250516614107 L 484 20.604348428472594 L 488 20.340447657862583 L 492 20.151469490560995 L 496 20.037891301819474 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="22.283614024661397" r="5"></circle><circle cx="62.5" cy="28.786796564403573" r="5"></circle><circle cx="93.75" cy="38.5194970290473" r="5"></circle><circle cx="125" cy="50" r="5"></circle><circle cx="156.25" cy="61.48050297095269" r="5"></circle><circle cx="187.5" cy="71.21320343559643" r="5"></circle><circle cx="218.75" cy="77.7163859753386" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="77.71638597533861" r="5"></circle><circle cx="312.5" cy="71.21320343559643" r="5"></circle><circle cx="343.75" cy="61.48050297095271" r="5"></circle><circle cx="375" cy="50.00000000000001" r="5"></circle><circle cx="406.25" cy="38.5194970290473" r="5"></circle><circle cx="437.5" cy="28.78679656440358" r="5"></circle><circle cx="468.75" cy="22.283614024661404" r="5"></circle>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">15</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the cosine for frequency 15 with sixteen samples. The wave has fifteen cycles.&#xAThe sample positions in this diagram are exactly the same as in the diagram of frequency 1.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 28.130941177357652 L 0 20 L 4 28.130941177357652 L 8 48.11628441412059 L 12 69.12271969246069 L 16 79.76344103943433 L 20 74.27050983124843 L 24 55.62143943757174 L 28 33.92519615063012 L 32 20.94250516614107 L 36 23.710799598684073 L 40 40.72949016875157 L 44 62.77337874695215 L 48 77.89329457664755 L 52 77.89329457664755 L 56 62.77337874695222 L 60 40.72949016875164 L 64 23.71079959868411 L 68 20.942505166141064 L 72 33.92519615063003 L 76 55.621439437571695 L 80 74.27050983124842 L 84 79.76344103943434 L 88 69.12271969246073 L 92 48.116284414120685 L 96 28.130941177357652 L 100 20 L 104 28.130941177357624 L 108 48.116284414120535 L 112 69.12271969246062 L 116 79.76344103943433 L 120 74.2705098312485 L 124 55.62143943757179 L 128 33.92519615063016 L 132 20.942505166141093 L 136 23.71079959868409 L 140 40.729490168751546 L 144 62.773378746952034 L 148 77.89329457664748 L 152 77.89329457664758 L 156 62.77337874695229 L 160 40.72949016875161 L 164 23.71079959868412 L 168 20.942505166141046 L 172 33.92519615062993 L 176 55.62143943757162 L 180 74.27050983124828 L 184 79.76344103943435 L 188 69.12271969246075 L 192 48.1162844141206 L 196 28.130941177357744 L 200 20 L 204 28.13094117735754 L 208 48.116284414120514 L 212 69.12271969246052 L 216 79.76344103943433 L 220 74.27050983124845 L 224 55.621439437571915 L 228 33.925196150630185 L 232 20.942505166141068 L 236 23.710799598684027 L 240 40.729490168751326 L 244 62.77337874695201 L 248 77.8932945766475 L 252 77.89329457664762 L 256 62.773378746952304 L 260 40.72949016875163 L 264 23.710799598684183 L 268 20.94250516614104 L 272 33.925196150630086 L 276 55.621439437571595 L 280 74.27050983124839 L 284 79.76344103943438 L 288 69.12271969246095 L 292 48.116284414120834 L 296 28.130941177357904 L 300 20 L 304 28.13094117735752 L 308 48.11628441412027 L 312 69.1227196924605 L 316 79.76344103943431 L 320 74.27050983124846 L 324 55.62143943757194 L 328 33.9251961506302 L 332 20.942505166141075 L 336 23.710799598684016 L 340 40.729490168751305 L 344 62.7733787469518 L 348 77.89329457664735 L 352 77.89329457664763 L 356 62.77337874695252 L 360 40.72949016875206 L 364 23.710799598684194 L 368 20.942505166140982 L 372 33.9251961506297 L 376 55.621439437571574 L 380 74.27050983124825 L 384 79.76344103943434 L 388 69.12271969246079 L 392 48.116284414120855 L 396 28.130941177357627 L 400 20 L 404 28.130941177357506 L 408 48.11628441412025 L 412 69.12271969246032 L 416 79.76344103943431 L 420 74.2705098312486 L 424 55.621439437572164 L 428 33.925196150630214 L 432 20.94250516614113 L 436 23.710799598683902 L 440 40.72949016875148 L 444 62.77337874695198 L 448 77.8932945766474 L 452 77.89329457664756 L 456 62.773378746952346 L 460 40.72949016875188 L 464 23.710799598684098 L 468 20.942505166141032 L 472 33.92519615062987 L 476 55.62143943757134 L 480 74.2705098312481 L 484 79.76344103943435 L 488 69.12271969246098 L 492 48.11628441412109 L 496 28.130941177357787 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="22.283614024661404" r="5"></circle><circle cx="62.5" cy="28.786796564403602" r="5"></circle><circle cx="93.75" cy="38.519497029047415" r="5"></circle><circle cx="125" cy="50.00000000000008" r="5"></circle><circle cx="156.25" cy="61.480502970952735" r="5"></circle><circle cx="187.5" cy="71.21320343559658" r="5"></circle><circle cx="218.75" cy="77.71638597533868" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="77.71638597533855" r="5"></circle><circle cx="312.5" cy="71.21320343559636" r="5"></circle><circle cx="343.75" cy="61.480502970952436" r="5"></circle><circle cx="375" cy="49.999999999999545" r="5"></circle><circle cx="406.25" cy="38.519497029047116" r="5"></circle><circle cx="437.5" cy="28.7867965644033" r="5"></circle><circle cx="468.75" cy="22.28361402466134" r="5"></circle>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>Note that while the <em>waves</em> are different frequencies, the cosine <em>samples</em> are the same! This is because of
<a href="https://en.wikipedia.org/wiki/Aliasing" target="_blank" rel="noopener">aliasing</a>, which is where a frequency above the maximum representable frequency
ends up sampling as if it were a different, lower frequency.</p>
<p>The frequency above which aliasing occurs is called the <a href="https://en.wikipedia.org/wiki/Nyquist_frequency" target="_blank" rel="noopener">Nyquist frequency</a>,
which is the highest frequency where there can be both a <em>high</em> and <em>low</em> sample for every cycle.
In a signal with <code>N</code><span class="attrs"></span> samples, that frequency has <code>N / 2</code><span class="attrs"></span> cycles. For example, with 16 samples
the Nyquist frequency has 8 cycles (which you can see have a clear <em>up then down</em> pattern):</p>
<div class="center-flex">
  <div class="svg-container">
    <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for frequency 8 with sixteen samples. The wave has eight cycles.&#xA;Every sample on the cosine wave alternates between '1' and '-1'</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 22.39304457902389 L 0 20 L 4 22.39304457902389 L 8 29.190400825615853 L 12 39.30764363860248 L 16 51.13070548009804 L 20 62.77337874695218 L 24 72.37823436272546 L 28 78.41294914984232 L 32 79.91476700781843 L 36 76.64409346440635 L 40 69.12271969246069 L 44 58.55057787409931 L 48 46.61430845379555 L 52 35.21817975355126 L 56 26.18028804056495 L 60 20.94250516614107 L 64 20.340447657862583 L 68 24.470165546159244 L 72 32.67281889733195 L 76 43.63978670233835 L 80 55.62143943757175 L 84 66.70626849464564 L 88 75.12584120126422 L 92 79.53693003587617 L 96 79.23580618359732 L 100 74.27050983124843 L 104 65.43318601344521 L 108 54.13370872053916 L 112 42.17475481130312 L 116 31.46421160728995 L 120 23.71079959868411 L 124 20.151469490561 L 128 21.354063657600715 L 132 27.126724669656568 L 136 36.54850351729903 L 140 48.11628441412065 L 144 59.98458633568954 L 148 70.25998424363073 L 152 77.30317912054986 L 156 79.990525678499 L 160 77.89329457664753 L 164 71.34607031627854 L 168 61.39337286565402 L 172 49.62301880349952 L 176 37.912806928590214 L 180 28.130941177357652 L 184 21.837984270383778 L 188 20.037891301819474 L 192 23.017842453008864 L 196 30.302427327411294 L 200 40.72949016875155 L 204 52.63553589652227 L 208 64.12111796495995 L 212 73.35386904701068 L 216 78.86083014758256 L 220 79.76344103943434 L 224 75.91770251578508 L 228 67.93714949172552 L 232 57.0949699107117 L 236 45.12088504415356 L 240 33.92519615063016 L 244 25.294022071147218 L 248 20.604348428472612 L 252 20.604348428472598 L 256 25.294022071147186 L 260 33.925196150630114 L 264 45.120885044153496 L 268 57.09496991071174 L 272 67.93714949172556 L 276 75.91770251578505 L 280 79.76344103943435 L 284 78.86083014758262 L 288 73.35386904701079 L 292 64.12111796496009 L 296 52.63553589652233 L 300 40.72949016875161 L 304 30.302427327411337 L 308 23.01784245300889 L 312 20.037891301819474 L 316 21.837984270383757 L 320 28.130941177357688 L 324 37.912806928590065 L 328 49.62301880349946 L 332 61.393372865653966 L 336 71.34607031627858 L 340 77.89329457664759 L 344 79.99052567849901 L 348 77.30317912054991 L 352 70.25998424363088 L 356 59.9845863356897 L 360 48.1162844141206 L 364 36.54850351729913 L 368 27.12672466965657 L 372 21.354063657600747 L 376 20.151469490560995 L 380 23.71079959868403 L 384 31.464211607289947 L 388 42.17475481130316 L 392 54.133708720539104 L 396 65.43318601344525 L 400 74.27050983124839 L 404 79.23580618359733 L 408 79.53693003587617 L 412 75.12584120126434 L 416 66.7062684946457 L 420 55.621439437571915 L 424 43.639786702338434 L 428 32.67281889733195 L 432 24.470165546159286 L 436 20.34044765786258 L 440 20.942505166141043 L 444 26.180288040564946 L 448 35.21817975355116 L 452 46.614308453795545 L 456 58.55057787409938 L 460 69.12271969246068 L 464 76.64409346440638 L 468 79.91476700781843 L 472 78.41294914984238 L 476 72.37823436272548 L 480 62.773378746952304 L 484 51.13070548009808 L 488 39.30764363860262 L 492 29.190400825615885 L 496 22.39304457902395 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="80" r="5"></circle><circle cx="62.5" cy="20" r="5"></circle><circle cx="93.75" cy="80" r="5"></circle><circle cx="125" cy="20" r="5"></circle><circle cx="156.25" cy="80" r="5"></circle><circle cx="187.5" cy="20" r="5"></circle><circle cx="218.75" cy="80" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="281.25" cy="80" r="5"></circle><circle cx="312.5" cy="20" r="5"></circle><circle cx="343.75" cy="80" r="5"></circle><circle cx="375" cy="20" r="5"></circle><circle cx="406.25" cy="80" r="5"></circle><circle cx="437.5" cy="20" r="5"></circle><circle cx="468.75" cy="80" r="5"></circle>
        </g>
      </svg>
    </div>
  </div>
</div>
<p>Above this frequency, the samples end up <strong>aliasing</strong> to a lower frequency and cannot be differentiated anymore.
Here are some more example pairs of aliased cosines from our 16-sample example:</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">2</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for frequency 2 with sixteen samples. The wave has two cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 20.151469490560995 L 0 20 L 4 20.151469490560995 L 8 20.604348428472594 L 12 21.35406365760071 L 16 22.39304457902389 L 20 23.710799598684094 L 24 25.294022071147175 L 28 27.126724669656564 L 32 29.190400825615853 L 36 31.46421160728997 L 40 33.9251961506301 L 44 36.548503517299025 L 48 39.30764363860248 L 52 42.17475481130309 L 56 45.12088504415349 L 60 48.11628441412059 L 64 51.13070548009804 L 68 54.13370872053914 L 72 57.09496991071173 L 76 59.9845863356896 L 80 62.77337874695218 L 84 65.4331860134452 L 88 67.93714949172556 L 92 70.25998424363073 L 96 72.37823436272546 L 100 74.27050983124842 L 104 75.91770251578507 L 108 77.30317912054987 L 112 78.41294914984232 L 116 79.23580618359732 L 120 79.76344103943433 L 124 79.990525678499 L 128 79.91476700781843 L 132 79.53693003587617 L 136 78.86083014758258 L 140 77.89329457664753 L 144 76.64409346440635 L 148 75.12584120126425 L 152 73.3538690470107 L 156 71.34607031627857 L 160 69.12271969246069 L 164 66.70626849464563 L 168 64.12111796495998 L 172 61.39337286565406 L 176 58.55057787409931 L 180 55.62143943757174 L 184 52.6355358965223 L 188 49.62301880349943 L 192 46.61430845379555 L 196 43.63978670233836 L 200 40.72949016875158 L 204 37.91280692859013 L 208 35.21817975355126 L 212 32.67281889733198 L 216 30.302427327411316 L 220 28.130941177357663 L 224 26.18028804056495 L 228 24.470165546159237 L 232 23.017842453008864 L 236 21.837984270383785 L 240 20.94250516614107 L 244 20.340447657862583 L 248 20.037891301819474 L 252 20.037891301819474 L 256 20.340447657862583 L 260 20.942505166141068 L 264 21.837984270383778 L 268 23.017842453008868 L 272 24.470165546159244 L 276 26.18028804056494 L 280 28.13094117735767 L 284 30.302427327411284 L 288 32.67281889733195 L 292 35.218179753551226 L 296 37.912806928590115 L 300 40.72949016875157 L 304 43.63978670233835 L 308 46.61430845379554 L 312 49.62301880349941 L 316 52.635535896522285 L 320 55.62143943757175 L 324 58.55057787409927 L 328 61.393372865654044 L 332 64.12111796495996 L 336 66.70626849464564 L 340 69.12271969246072 L 344 71.34607031627853 L 348 73.35386904701069 L 352 75.12584120126422 L 356 76.64409346440632 L 360 77.89329457664755 L 364 78.86083014758256 L 368 79.53693003587617 L 372 79.91476700781841 L 376 79.990525678499 L 380 79.76344103943434 L 384 79.23580618359732 L 388 78.41294914984232 L 392 77.30317912054987 L 396 75.91770251578505 L 400 74.27050983124843 L 404 72.37823436272546 L 408 70.25998424363074 L 412 67.9371494917256 L 416 65.43318601344521 L 420 62.77337874695222 L 424 59.98458633568961 L 428 57.09496991071173 L 432 54.13370872053916 L 436 51.13070548009803 L 440 48.11628441412062 L 444 45.12088504415349 L 448 42.17475481130312 L 452 39.30764363860248 L 456 36.54850351729901 L 460 33.9251961506301 L 464 31.46421160728995 L 468 29.190400825615857 L 472 27.126724669656586 L 476 25.294022071147182 L 480 23.71079959868411 L 484 22.393044579023893 L 488 21.354063657600722 L 492 20.604348428472598 L 496 20.151469490561 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="28.786796564403573" r="5"></circle><circle cx="62.5" cy="50" r="5"></circle><circle cx="93.75" cy="71.21320343559643" r="5"></circle><circle cx="125" cy="80" r="5"></circle><circle cx="156.25" cy="71.21320343559643" r="5"></circle><circle cx="187.5" cy="50.00000000000001" r="5"></circle><circle cx="218.75" cy="28.78679656440358" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="281.25" cy="28.78679656440357" r="5"></circle><circle cx="312.5" cy="49.99999999999999" r="5"></circle><circle cx="343.75" cy="71.2132034355964" r="5"></circle><circle cx="375" cy="80" r="5"></circle><circle cx="406.25" cy="71.21320343559641" r="5"></circle><circle cx="437.5" cy="50.000000000000014" r="5"></circle><circle cx="468.75" cy="28.786796564403602" r="5"></circle>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">14</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the cosine for frequency 14 with sixteen samples. The wave has fourteen cycles.&#xAThe sample positions in this diagram are exactly the same as in the diagram of frequency 2.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 27.126724669656564 L 0 20 L 4 27.126724669656564 L 8 45.12088504415349 L 12 65.4331860134452 L 16 78.41294914984232 L 20 77.89329457664755 L 24 64.12111796495998 L 28 43.63978670233836 L 32 26.18028804056495 L 36 20.03789130181947 L 40 28.130941177357652 L 44 46.61430845379554 L 48 66.70626849464564 L 52 78.86083014758256 L 56 77.30317912054987 L 60 62.77337874695222 L 64 42.17475481130312 L 68 25.294022071147182 L 72 20.15146949056099 L 76 29.190400825615832 L 80 48.11628441412059 L 84 67.93714949172558 L 88 79.2358061835973 L 92 76.64409346440635 L 96 61.39337286565402 L 100 40.729490168751596 L 104 24.470165546159276 L 108 20.340447657862583 L 112 30.302427327411294 L 116 49.62301880349948 L 120 69.12271969246062 L 124 79.53693003587617 L 128 75.91770251578508 L 132 59.984586335689585 L 136 39.30764363860249 L 140 23.710799598684066 L 144 20.604348428472576 L 148 31.464211607289872 L 152 51.13070548009799 L 156 70.25998424363067 L 160 79.76344103943433 L 164 75.12584120126428 L 168 58.55057787409926 L 172 37.91280692859023 L 176 23.01784245300889 L 180 20.942505166141046 L 184 32.67281889733188 L 188 52.635535896522356 L 192 71.34607031627858 L 196 79.91476700781843 L 200 74.27050983124845 L 204 57.09496991071181 L 208 36.54850351729913 L 212 22.39304457902395 L 216 21.354063657600722 L 220 33.9251961506301 L 224 54.133708720539104 L 228 72.37823436272541 L 232 79.990525678499 L 236 73.35386904701079 L 240 55.621439437571915 L 244 35.218179753551254 L 248 21.83798427038379 L 252 21.837984270383753 L 256 35.21817975355116 L 260 55.62143943757181 L 264 73.35386904701073 L 268 79.990525678499 L 272 72.37823436272548 L 276 54.133708720539 L 280 33.92519615063001 L 284 21.35406365760075 L 288 22.393044579023822 L 292 36.54850351729885 L 296 57.094969910711505 L 300 74.27050983124839 L 304 79.91476700781843 L 308 71.34607031627866 L 312 52.63553589652246 L 316 32.672818897331965 L 320 20.94250516614107 L 324 23.017842453008843 L 328 37.91280692859004 L 332 58.55057787409936 L 336 75.12584120126428 L 340 79.76344103943433 L 344 70.2599842436309 L 348 51.13070548009831 L 352 31.46421160729004 L 356 20.60434842847262 L 360 23.710799598684016 L 364 39.3076436386023 L 368 59.984586335689386 L 372 75.91770251578492 L 376 79.53693003587614 L 380 69.12271969246062 L 384 49.62301880349936 L 388 30.30242732741129 L 392 20.340447657862583 L 396 24.47016554615922 L 400 40.7294901687515 L 404 61.39337286565392 L 408 76.64409346440627 L 412 79.23580618359736 L 416 67.93714949172575 L 420 48.116284414120855 L 424 29.190400825616063 L 428 20.151469490561027 L 432 25.294022071147214 L 436 42.17475481130312 L 440 62.773378746952176 L 444 77.30317912054986 L 448 78.86083014758259 L 452 66.70626849464573 L 456 46.614308453795694 L 460 28.130941177357776 L 464 20.037891301819467 L 468 26.18028804056505 L 472 43.639786702338085 L 476 64.1211179649597 L 480 77.8932945766474 L 484 78.41294914984232 L 488 65.4331860134452 L 492 45.120885044153525 L 496 27.126724669656607 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="28.78679656440358" r="5"></circle><circle cx="62.5" cy="50.000000000000014" r="5"></circle><circle cx="93.75" cy="71.21320343559643" r="5"></circle><circle cx="125" cy="80" r="5"></circle><circle cx="156.25" cy="71.21320343559638" r="5"></circle><circle cx="187.5" cy="50.000000000000014" r="5"></circle><circle cx="218.75" cy="28.786796564403485" r="5"></circle><circle cx="250" cy="20" r="5"></circle><circle cx="281.25" cy="28.78679656440356" r="5"></circle><circle cx="312.5" cy="50.00000000000012" r="5"></circle><circle cx="343.75" cy="71.21320343559645" r="5"></circle><circle cx="375" cy="80" r="5"></circle><circle cx="406.25" cy="71.21320343559634" r="5"></circle><circle cx="437.5" cy="49.99999999999975" r="5"></circle><circle cx="468.75" cy="28.7867965644036" r="5"></circle>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">3</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for frequency 3 with sixteen samples. The wave has three cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 20.34044765786258 L 0 20 L 4 20.34044765786258 L 8 21.35406365760071 L 12 23.017842453008868 L 16 25.294022071147175 L 20 28.130941177357652 L 24 31.464211607289972 L 28 35.218179753551254 L 32 39.30764363860248 L 36 43.639786702338355 L 40 48.1162844141206 L 44 52.63553589652229 L 48 57.09496991071174 L 52 61.39337286565403 L 56 65.4331860134452 L 60 69.12271969246069 L 64 72.37823436272546 L 68 75.12584120126425 L 72 77.30317912054987 L 76 78.86083014758258 L 80 79.76344103943434 L 84 79.990525678499 L 88 79.53693003587617 L 92 78.41294914984233 L 96 76.64409346440634 L 100 74.27050983124843 L 104 71.34607031627857 L 108 67.93714949172556 L 112 64.12111796495998 L 116 59.98458633568961 L 120 55.62143943757174 L 124 51.13070548009805 L 128 46.61430845379555 L 132 42.17475481130308 L 136 37.91280692859013 L 140 33.9251961506301 L 144 30.302427327411316 L 148 27.12672466965658 L 152 24.47016554615925 L 156 22.393044579023893 L 160 20.942505166141064 L 164 20.151469490560995 L 168 20.037891301819474 L 172 20.604348428472587 L 176 21.837984270383767 L 180 23.710799598684087 L 184 26.18028804056494 L 188 29.19040082561584 L 192 32.672818897331965 L 196 36.54850351729904 L 200 40.72949016875157 L 204 45.12088504415347 L 208 49.62301880349941 L 212 54.13370872053912 L 216 58.55057787409927 L 220 62.77337874695215 L 224 66.70626849464564 L 228 70.25998424363073 L 232 73.35386904701069 L 236 75.91770251578504 L 240 77.89329457664755 L 244 79.2358061835973 L 248 79.91476700781841 L 252 79.91476700781843 L 256 79.23580618359732 L 260 77.89329457664755 L 264 75.91770251578505 L 268 73.3538690470107 L 272 70.25998424363074 L 276 66.70626849464563 L 280 62.773378746952176 L 284 58.55057787409934 L 288 54.13370872053916 L 292 49.623018803499455 L 296 45.12088504415354 L 300 40.72949016875159 L 304 36.548503517299054 L 308 32.67281889733201 L 312 29.190400825615857 L 316 26.180288040564953 L 320 23.710799598684083 L 324 21.837984270383778 L 328 20.604348428472598 L 332 20.03789130181947 L 336 20.151469490560995 L 340 20.942505166141064 L 344 22.39304457902386 L 348 24.470165546159226 L 352 27.126724669656532 L 356 30.302427327411298 L 360 33.92519615063008 L 364 37.912806928590086 L 368 42.17475481130309 L 372 46.61430845379553 L 376 51.130705480098 L 380 55.621439437571745 L 384 59.984586335689585 L 388 64.12111796495995 L 392 67.93714949172558 L 396 71.34607031627856 L 400 74.27050983124842 L 404 76.64409346440635 L 408 78.41294914984232 L 412 79.53693003587615 L 416 79.990525678499 L 420 79.76344103943434 L 424 78.86083014758259 L 428 77.3031791205499 L 432 75.12584120126427 L 436 72.37823436272546 L 440 69.12271969246073 L 444 65.43318601344521 L 448 61.39337286565402 L 452 57.0949699107118 L 456 52.63553589652231 L 460 48.11628441412058 L 464 43.639786702338405 L 468 39.307643638602485 L 472 35.21817975355132 L 476 31.46421160729 L 480 28.130941177357652 L 484 25.294022071147218 L 488 23.017842453008882 L 492 21.354063657600708 L 496 20.340447657862587 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="38.5194970290473" r="5"></circle><circle cx="62.5" cy="71.21320343559643" r="5"></circle><circle cx="93.75" cy="77.71638597533861" r="5"></circle><circle cx="125" cy="50.00000000000001" r="5"></circle><circle cx="156.25" cy="22.283614024661404" r="5"></circle><circle cx="187.5" cy="28.78679656440357" r="5"></circle><circle cx="218.75" cy="61.4805029709527" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="61.48050297095271" r="5"></circle><circle cx="312.5" cy="28.786796564403602" r="5"></circle><circle cx="343.75" cy="22.283614024661397" r="5"></circle><circle cx="375" cy="49.999999999999986" r="5"></circle><circle cx="406.25" cy="77.7163859753386" r="5"></circle><circle cx="437.5" cy="71.21320343559643" r="5"></circle><circle cx="468.75" cy="38.519497029047315" r="5"></circle>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">13</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the cosine for frequency 13 with sixteen samples. The wave has thirteen cycles.&#xAThe sample positions in this diagram are exactly the same as in the diagram of frequency 3.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 26.180288040564943 L 0 20 L 4 26.180288040564943 L 8 42.174754811303096 L 12 61.39337286565404 L 16 75.91770251578507 L 20 79.76344103943434 L 24 71.34607031627856 L 28 54.13370872053913 L 32 35.21817975355124 L 36 22.393044579023893 L 40 20.942505166141068 L 44 31.464211607289975 L 48 49.623018803499434 L 52 67.93714949172558 L 56 78.86083014758259 L 60 77.89329457664755 L 64 65.43318601344517 L 68 46.61430845379551 L 72 29.190400825615857 L 76 20.34044765786258 L 80 23.710799598684098 L 84 37.91280692859018 L 88 57.094969910711754 L 92 73.35386904701072 L 96 79.990525678499 L 100 74.27050983124838 L 104 58.55057787409925 L 108 39.307643638602485 L 112 24.47016554615922 L 116 20.151469490560995 L 120 28.130941177357624 L 124 45.12088504415351 L 128 64.12111796496004 L 132 77.30317912054988 L 136 79.23580618359729 L 140 69.12271969246058 L 144 51.130705480098044 L 148 32.672818897331936 L 152 21.35406365760071 L 156 21.8379842703838 L 160 33.925196150630114 L 164 52.63553589652236 L 168 70.25998424363083 L 172 79.53693003587615 L 176 76.64409346440632 L 180 62.77337874695219 L 184 43.63978670233831 L 188 27.126724669656568 L 192 20.03789130181947 L 196 25.294022071147243 L 200 40.729490168751745 L 204 59.98458633568962 L 208 75.1258412012643 L 212 79.91476700781843 L 216 72.37823436272546 L 220 55.621439437571695 L 224 36.54850351729894 L 228 23.017842453008797 L 232 20.604348428472594 L 236 30.30242732741136 L 240 48.116284414120514 L 244 66.70626849464561 L 248 78.41294914984233 L 252 78.4129491498423 L 256 66.70626849464551 L 260 48.11628441412039 L 264 30.302427327411266 L 268 20.60434842847257 L 272 23.017842453008946 L 276 36.54850351729924 L 280 55.62143943757202 L 284 72.37823436272541 L 288 79.91476700781843 L 292 75.12584120126422 L 296 59.98458633568951 L 300 40.72949016875163 L 304 25.294022071147175 L 308 20.037891301819478 L 312 27.12672466965664 L 316 43.63978670233853 L 320 62.773378746952204 L 324 76.64409346440638 L 328 79.53693003587614 L 332 70.25998424363058 L 336 52.635535896522036 L 340 33.925196150630015 L 344 21.837984270383796 L 348 21.354063657600715 L 352 32.67281889733203 L 356 51.13070548009795 L 360 69.12271969246068 L 364 79.23580618359732 L 368 77.30317912054983 L 372 64.12111796495984 L 376 45.120885044153496 L 380 28.130941177357617 L 384 20.151469490560984 L 388 24.470165546159336 L 392 39.3076436386027 L 396 58.55057787409956 L 400 74.27050983124863 L 404 79.99052567849898 L 408 73.35386904701068 L 412 57.09496991071164 L 416 37.91280692858997 L 420 23.710799598683987 L 424 20.34044765786256 L 428 29.190400825615786 L 432 46.614308453795516 L 436 65.43318601344521 L 440 77.89329457664758 L 444 78.86083014758253 L 448 67.93714949172539 L 452 49.62301880349916 L 456 31.464211607289716 L 460 20.942505166140972 L 464 22.393044579023893 L 468 35.21817975355132 L 472 54.133708720539275 L 476 71.3460703162787 L 480 79.76344103943431 L 484 75.91770251578512 L 488 61.39337286565409 L 492 42.17475481130309 L 496 26.180288040564903 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="38.5194970290473" r="5"></circle><circle cx="62.5" cy="71.21320343559641" r="5"></circle><circle cx="93.75" cy="77.71638597533861" r="5"></circle><circle cx="125" cy="50.00000000000003" r="5"></circle><circle cx="156.25" cy="22.28361402466141" r="5"></circle><circle cx="187.5" cy="28.78679656440354" r="5"></circle><circle cx="218.75" cy="61.48050297095254" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="61.48050297095266" r="5"></circle><circle cx="312.5" cy="28.786796564403627" r="5"></circle><circle cx="343.75" cy="22.283614024661325" r="5"></circle><circle cx="375" cy="49.999999999999915" r="5"></circle><circle cx="406.25" cy="77.71638597533861" r="5"></circle><circle cx="437.5" cy="71.21320343559665" r="5"></circle><circle cx="468.75" cy="38.51949702904751" r="5"></circle>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">7</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for frequency 7 with sixteen samples. The wave has seven cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 21.837984270383778 L 0 20 L 4 21.837984270383778 L 8 27.126724669656564 L 12 35.218179753551254 L 16 45.12088504415349 L 20 55.62143943757174 L 24 65.4331860134452 L 28 73.3538690470107 L 32 78.41294914984232 L 36 79.990525678499 L 40 77.89329457664755 L 44 72.37823436272546 L 48 64.12111796495998 L 52 54.133708720539154 L 56 43.63978670233836 L 60 33.92519615063012 L 64 26.18028804056495 L 68 21.35406365760071 L 72 20.03789130181947 L 76 22.393044579023886 L 80 28.130941177357652 L 84 36.54850351729904 L 88 46.61430845379554 L 92 57.09496991071171 L 96 66.70626849464564 L 100 74.27050983124842 L 104 78.86083014758256 L 108 79.91476700781843 L 112 77.30317912054987 L 116 71.34607031627854 L 120 62.77337874695222 L 124 52.635535896522306 L 128 42.17475481130312 L 132 32.672818897331965 L 136 25.294022071147182 L 140 20.94250516614106 L 144 20.15146949056099 L 148 23.01784245300884 L 152 29.190400825615832 L 156 37.912806928590086 L 160 48.11628441412059 L 164 58.55057787409926 L 168 67.93714949172558 L 172 75.12584120126422 L 176 79.2358061835973 L 180 79.76344103943434 L 184 76.64409346440635 L 188 70.25998424363071 L 192 61.39337286565402 L 196 51.13070548009804 L 200 40.729490168751596 L 204 31.46421160729 L 208 24.470165546159276 L 212 20.60434842847261 L 216 20.340447657862583 L 220 23.710799598684094 L 224 30.302427327411294 L 228 39.30764363860244 L 232 49.62301880349948 L 236 59.98458633568953 L 240 69.12271969246062 L 244 75.91770251578507 L 248 79.53693003587617 L 252 79.53693003587617 L 256 75.91770251578508 L 260 69.12271969246066 L 264 59.984586335689585 L 268 49.62301880349942 L 272 39.30764363860249 L 276 30.30242732741125 L 280 23.710799598684066 L 284 20.34044765786259 L 288 20.604348428472576 L 292 24.47016554615919 L 296 31.464211607289872 L 300 40.729490168751546 L 304 51.13070548009799 L 308 61.39337286565397 L 312 70.25998424363067 L 316 76.64409346440634 L 320 79.76344103943433 L 324 79.23580618359732 L 328 75.12584120126428 L 332 67.93714949172553 L 336 58.55057787409926 L 340 48.11628441412059 L 344 37.91280692859023 L 348 29.19040082561595 L 352 23.01784245300889 L 356 20.151469490561002 L 360 20.942505166141046 L 364 25.294022071147122 L 368 32.67281889733188 L 372 42.17475481130297 L 376 52.635535896522356 L 380 62.77337874695222 L 384 71.34607031627858 L 388 77.30317912054988 L 392 79.91476700781843 L 396 78.86083014758259 L 400 74.27050983124845 L 404 66.70626849464568 L 408 57.09496991071181 L 412 46.614308453795644 L 416 36.54850351729913 L 420 28.130941177357744 L 424 22.39304457902395 L 428 20.03789130181948 L 432 21.354063657600722 L 436 26.18028804056495 L 440 33.9251961506301 L 444 43.63978670233834 L 448 54.133708720539104 L 452 64.12111796495992 L 456 72.37823436272541 L 460 77.8932945766475 L 464 79.990525678499 L 468 78.4129491498423 L 472 73.35386904701079 L 476 65.43318601344532 L 480 55.621439437571915 L 484 45.120885044153475 L 488 35.218179753551254 L 492 27.126724669656575 L 496 21.83798427038379 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="77.7163859753386" r="5"></circle><circle cx="62.5" cy="28.78679656440358" r="5"></circle><circle cx="93.75" cy="61.4805029709527" r="5"></circle><circle cx="125" cy="50.000000000000014" r="5"></circle><circle cx="156.25" cy="38.51949702904728" r="5"></circle><circle cx="187.5" cy="71.21320343559643" r="5"></circle><circle cx="218.75" cy="22.283614024661375" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="22.283614024661393" r="5"></circle><circle cx="312.5" cy="71.21320343559638" r="5"></circle><circle cx="343.75" cy="38.51949702904733" r="5"></circle><circle cx="375" cy="50.000000000000014" r="5"></circle><circle cx="406.25" cy="61.48050297095274" r="5"></circle><circle cx="437.5" cy="28.786796564403485" r="5"></circle><circle cx="468.75" cy="77.7163859753386" r="5"></circle>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">9</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the cosine for frequency 9 with sixteen samples. The wave has nine cycles.&#xAThe sample positions in this diagram are exactly the same as in the diagram of frequency 7.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 23.017842453008868 L 0 20 L 4 23.017842453008868 L 8 31.464211607289972 L 12 43.639786702338355 L 16 57.09496991071174 L 20 69.12271969246069 L 24 77.30317912054987 L 28 79.990525678499 L 32 76.64409346440634 L 36 67.93714949172556 L 40 55.62143943757174 L 44 42.17475481130311 L 48 30.302427327411316 L 52 22.393044579023893 L 56 20.037891301819474 L 60 23.710799598684087 L 64 32.672818897331965 L 68 45.120885044153496 L 72 58.55057787409927 L 76 70.25998424363073 L 80 77.89329457664755 L 84 79.91476700781843 L 88 75.91770251578508 L 92 66.70626849464567 L 96 54.13370872053916 L 100 40.72949016875159 L 104 29.190400825615857 L 108 21.837984270383778 L 112 20.151469490560995 L 116 24.470165546159254 L 120 33.92519615063008 L 124 46.61430845379553 L 128 59.984586335689585 L 132 71.34607031627856 L 136 78.41294914984233 L 140 79.76344103943433 L 144 75.12584120126427 L 148 65.43318601344521 L 152 52.63553589652231 L 156 39.307643638602485 L 160 28.130941177357652 L 164 21.354063657600708 L 168 20.340447657862583 L 172 25.29402207114713 L 176 35.21817975355119 L 180 48.116284414120535 L 184 61.39337286565398 L 188 72.37823436272544 L 192 78.86083014758256 L 196 79.53693003587617 L 200 74.27050983124843 L 204 64.12111796495999 L 208 51.130705480098044 L 212 37.91280692859012 L 216 27.12672466965656 L 220 20.942505166141064 L 224 20.604348428472598 L 228 26.18028804056496 L 232 36.54850351729907 L 236 49.62301880349936 L 240 62.77337874695213 L 244 73.35386904701068 L 248 79.2358061835973 L 252 79.23580618359732 L 256 73.35386904701072 L 260 62.77337874695219 L 264 49.62301880349943 L 268 36.54850351729903 L 272 26.180288040564932 L 276 20.60434842847259 L 280 20.942505166141075 L 284 27.126724669656518 L 288 37.912806928590065 L 292 51.13070548009798 L 296 64.12111796495994 L 300 74.2705098312484 L 304 79.53693003587615 L 308 78.86083014758259 L 312 72.37823436272546 L 316 61.393372865654044 L 320 48.1162844141206 L 324 35.21817975355125 L 328 25.294022071147168 L 332 20.340447657862576 L 336 21.354063657600722 L 340 28.130941177357684 L 344 39.30764363860232 L 348 52.635535896522136 L 352 65.43318601344507 L 356 75.12584120126417 L 360 79.76344103943433 L 364 78.41294914984236 L 368 71.34607031627864 L 372 59.9845863356897 L 376 46.61430845379565 L 380 33.925196150630185 L 384 24.470165546159286 L 388 20.151469490561002 L 392 21.837984270383753 L 396 29.190400825615807 L 400 40.729490168751525 L 404 54.1337087205391 L 408 66.70626849464561 L 412 75.91770251578504 L 416 79.91476700781843 L 420 77.89329457664755 L 424 70.25998424363074 L 428 58.55057787409928 L 432 45.12088504415348 L 436 32.67281889733196 L 440 23.71079959868408 L 444 20.03789130181947 L 448 22.393044579023904 L 452 30.302427327411344 L 456 42.174754811303146 L 460 55.62143943757181 L 464 67.93714949172562 L 468 76.64409346440638 L 472 79.990525678499 L 476 77.30317912054991 L 480 69.12271969246078 L 484 57.09496991071184 L 488 43.63978670233845 L 492 31.464211607290032 L 496 23.0178424530089 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="77.71638597533861" r="5"></circle><circle cx="62.5" cy="28.78679656440357" r="5"></circle><circle cx="93.75" cy="61.48050297095271" r="5"></circle><circle cx="125" cy="49.999999999999986" r="5"></circle><circle cx="156.25" cy="38.519497029047315" r="5"></circle><circle cx="187.5" cy="71.2132034355964" r="5"></circle><circle cx="218.75" cy="22.283614024661393" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="22.283614024661368" r="5"></circle><circle cx="312.5" cy="71.21320343559644" r="5"></circle><circle cx="343.75" cy="38.51949702904735" r="5"></circle><circle cx="375" cy="50.0000000000001" r="5"></circle><circle cx="406.25" cy="61.48050297095266" r="5"></circle><circle cx="437.5" cy="28.78679656440356" r="5"></circle><circle cx="468.75" cy="77.71638597533855" r="5"></circle>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>Note that each pair has <strong>matching cosine samples</strong>, despite the different wave. Specifically: with a repeating sample count
of <code>N</code><span class="attrs"></span>, any frequency with <strong><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>−</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">N - k</annotation></semantics></math></span> cycles</strong> has the <strong>same cosine values as</strong> the frequency with <strong><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span> cycles</strong> (that is <code>1</code><span class="attrs"></span> matches <code>15</code><span class="attrs"></span>, <code>2</code><span class="attrs"></span> matches <code>14</code><span class="attrs"></span>, etc).</p>
<p>That’s neat! But it’s not the whole picture. We can’t just look at the cosine values, we need to also look at what
happens to the <strong>sine</strong> values as well:</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">1</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the sine for frequency 1 with sixteen samples. The wave has a single cycle.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="sin-wave">
          <path d="M -4 51.507329545393084 L 0 50 L 4 48.492670454606916 L 8 46.989148554463554 L 12 45.49323232637729 L 16 44.00870058456779 L 20 42.53930338505435 L 24 41.08875255268895 L 28 39.66071230476449 L 32 38.25878999488393 L 36 36.886527000472014 L 40 35.54738977694854 L 44 34.24476110116113 L 48 32.9819315261973 L 52 31.762091069161837 L 56 30.588321152916674 L 60 29.46358682213934 L 64 28.390729253362792 L 68 27.372458577916888 L 72 26.411347035901436 L 76 25.50982247848448 L 80 24.670162234939546 L 84 23.89448735991423 L 88 23.18475727546209 L 92 22.542764821372472 L 96 21.970131726301638 L 100 21.468304511145394 L 104 21.038550835001782 L 108 20.681956292954197 L 112 20.39942167376396 L 116 20.19166068439975 L 120 20.059198147151854 L 124 20.002368673885517 L 128 20.021315820782323 L 132 20.115991725704824 L 136 20.286157229100464 L 140 20.531382478139342 L 144 20.85104801255978 L 148 21.24434632947872 L 152 21.710283923214064 L 156 22.24768379496626 L 160 22.855188426019417 L 164 23.5312632069514 L 168 24.274200314190434 L 172 25.082123024125615 L 176 25.952990453873696 L 180 26.884602716726324 L 184 27.874606479254783 L 188 28.920500906034523 L 192 30.01964397697245 L 196 31.169259161278983 L 200 32.366442431225806 L 204 33.60816959797192 L 208 34.891303950927174 L 212 36.21260418135536 L 216 37.56873257020147 L 220 38.95626341945966 L 224 40.37169170578371 L 228 41.81144193448025 L 232 43.27187717151857 L 236 44.74930823074171 L 240 46.24000299307087 L 244 47.74019583416201 L 248 49.246097136699866 L 252 50.75390286330013 L 256 52.259804165837984 L 260 53.759997006929126 L 264 55.25069176925828 L 268 56.72812282848144 L 272 58.18855806551976 L 276 59.62830829421628 L 280 61.04373658054035 L 284 62.43126742979851 L 288 63.78739581864462 L 292 65.1086960490728 L 296 66.39183040202806 L 300 67.6335575687742 L 304 68.83074083872101 L 308 69.98035602302754 L 312 71.07949909396547 L 316 72.1253935207452 L 320 73.11539728327368 L 324 74.04700954612629 L 328 74.91787697587438 L 332 75.72579968580956 L 336 76.4687367930486 L 340 77.14481157398059 L 344 77.75231620503374 L 348 78.28971607678594 L 352 78.75565367052127 L 356 79.14895198744021 L 360 79.46861752186066 L 364 79.71384277089953 L 368 79.88400827429518 L 372 79.97868417921768 L 376 79.99763132611449 L 380 79.94080185284815 L 384 79.80833931560025 L 388 79.60057832623605 L 392 79.3180437070458 L 396 78.96144916499821 L 400 78.53169548885461 L 404 78.02986827369836 L 408 77.45723517862753 L 412 76.81524272453792 L 416 76.10551264008578 L 420 75.32983776506046 L 424 74.49017752151552 L 428 73.58865296409857 L 432 72.62754142208313 L 436 71.60927074663721 L 440 70.53641317786067 L 444 69.41167884708332 L 448 68.23790893083817 L 452 67.0180684738027 L 456 65.75523889883885 L 460 64.45261022305147 L 464 63.11347299952798 L 468 61.74121000511607 L 472 60.33928769523553 L 476 58.911247447311055 L 480 57.46069661494566 L 484 55.991299415432216 L 488 54.50676767362273 L 492 53.01085144553645 L 496 51.507329545393105 L 500 50.00000000000001"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="33.51949702904731" width="10" height="10"></rect><rect x="57.5" y="23.786796564403577" width="10" height="10"></rect><rect x="88.75" y="17.283614024661397" width="10" height="10"></rect><rect x="120" y="15" width="10" height="10"></rect><rect x="151.25" y="17.283614024661397" width="10" height="10"></rect><rect x="182.5" y="23.786796564403573" width="10" height="10"></rect><rect x="213.75" y="33.5194970290473" width="10" height="10"></rect><rect x="245" y="44.99999999999999" width="10" height="10"></rect><rect x="276.25" y="56.48050297095269" width="10" height="10"></rect><rect x="307.5" y="66.21320343559643" width="10" height="10"></rect><rect x="338.75" y="72.7163859753386" width="10" height="10"></rect><rect x="370" y="75" width="10" height="10"></rect><rect x="401.25" y="72.7163859753386" width="10" height="10"></rect><rect x="432.5" y="66.21320343559643" width="10" height="10"></rect><rect x="463.75" y="56.48050297095271" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">15</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the sine for frequency 15 with sixteen samples. The wave has fifteen cycles.&#xAThe sample positions in this diagram are like the ones in the diagram of frequency 1, but negated.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="sin-wave">
            <path d="M -4 70.53641317786065 L 0 50 L 4 29.46358682213934 L 8 20.059198147151854 L 12 26.884602716726324 L 16 46.24000299307087 L 20 67.6335575687742 L 24 79.46861752186066 L 28 75.32983776506046 L 32 57.46069661494566 L 36 35.54738977694858 L 40 21.468304511145394 L 44 22.8551884260194 L 48 38.95626341945966 L 52 61.04373658054032 L 56 77.14481157398058 L 60 78.53169548885464 L 64 64.4526102230515 L 68 42.53930338505436 L 72 24.670162234939593 L 76 20.53138247813933 L 80 32.36644243122579 L 84 53.75999700692909 L 88 73.11539728327364 L 92 79.94080185284815 L 96 70.53641317786065 L 100 50.00000000000002 L 104 29.463586822139373 L 108 20.059198147151857 L 112 26.884602716726267 L 116 46.24000299307087 L 120 67.63355756877408 L 124 79.46861752186065 L 128 75.32983776506049 L 132 57.46069661494573 L 136 35.54738977694855 L 140 21.4683045111454 L 144 22.855188426019346 L 148 38.95626341945949 L 152 61.04373658054025 L 156 77.14481157398053 L 160 78.53169548885462 L 164 64.45261022305151 L 168 42.53930338505443 L 172 24.67016223493966 L 176 20.531382478139314 L 180 32.36644243122561 L 184 53.75999700692896 L 188 73.11539728327362 L 192 79.94080185284815 L 196 70.53641317786075 L 200 50.00000000000004 L 204 29.463586822139465 L 208 20.05919814715186 L 212 26.884602716726185 L 216 46.24000299307074 L 220 67.63355756877415 L 224 79.46861752186064 L 228 75.3298377650605 L 232 57.460696614945654 L 236 35.54738977694866 L 240 21.468304511145472 L 244 22.855188426019335 L 248 38.956263419459574 L 252 61.04373658054013 L 256 77.14481157398052 L 260 78.53169548885462 L 264 64.45261022305162 L 268 42.53930338505445 L 272 24.670162234939557 L 276 20.53138247813931 L 280 32.36644243122576 L 284 53.75999700692873 L 288 73.11539728327347 L 292 79.94080185284815 L 296 70.53641317786092 L 300 50.00000000000028 L 304 29.463586822139483 L 308 20.059198147151875 L 312 26.88460271672617 L 316 46.24000299307072 L 320 67.63355756877414 L 324 79.46861752186062 L 328 75.32983776506052 L 332 57.460696614945675 L 336 35.547389776948684 L 340 21.46830451114548 L 344 22.855188426019236 L 348 38.956263419459155 L 352 61.043736580540106 L 356 77.14481157398042 L 360 78.53169548885477 L 364 64.45261022305164 L 368 42.53930338505468 L 372 24.6701622349398 L 376 20.531382478139307 L 380 32.366442431225565 L 384 53.759997006929126 L 388 73.1153972832736 L 392 79.94080185284817 L 396 70.53641317786062 L 400 50.000000000000085 L 404 29.463586822139497 L 408 20.059198147151875 L 412 26.884602716726018 L 416 46.240002993070696 L 420 67.63355756877395 L 424 79.46861752186058 L 428 75.32983776506052 L 432 57.4606966149459 L 436 35.54738977694889 L 440 21.468304511145423 L 444 22.855188426019318 L 448 38.95626341945933 L 452 61.04373658054028 L 456 77.1448115739805 L 460 78.53169548885471 L 464 64.45261022305147 L 468 42.539303385054495 L 472 24.670162234939696 L 476 20.531382478139264 L 480 32.36644243122538 L 484 53.75999700692889 L 488 73.11539728327344 L 492 79.94080185284818 L 496 70.5364131778608 L 500 50.00000000000033"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="56.48050297095271" width="10" height="10"></rect><rect x="57.5" y="66.21320343559645" width="10" height="10"></rect><rect x="88.75" y="72.71638597533865" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="151.25" y="72.71638597533858" width="10" height="10"></rect><rect x="182.5" y="66.21320343559626" width="10" height="10"></rect><rect x="213.75" y="56.480502970952514" width="10" height="10"></rect><rect x="245" y="44.99999999999984" width="10" height="10"></rect><rect x="276.25" y="33.51949702904719" width="10" height="10"></rect><rect x="307.5" y="23.786796564403506" width="10" height="10"></rect><rect x="338.75" y="17.28361402466129" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="401.25" y="17.28361402466148" width="10" height="10"></rect><rect x="432.5" y="23.78679656440385" width="10" height="10"></rect><rect x="463.75" y="33.51949702904744" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">2</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the sine for frequency 2 with sixteen samples. The wave has two cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="sin-wave">
          <path d="M -4 53.010851445536446 L 0 50 L 4 46.989148554463554 L 8 44.00870058456779 L 12 41.08875255268895 L 16 38.25878999488393 L 20 35.54738977694854 L 24 32.9819315261973 L 28 30.588321152916674 L 32 28.390729253362792 L 36 26.411347035901436 L 40 24.670162234939546 L 44 23.18475727546209 L 48 21.970131726301638 L 52 21.038550835001782 L 56 20.39942167376396 L 60 20.059198147151854 L 64 20.021315820782323 L 68 20.286157229100464 L 72 20.85104801255978 L 76 21.710283923214064 L 80 22.855188426019417 L 84 24.274200314190434 L 88 25.952990453873696 L 92 27.874606479254783 L 96 30.01964397697245 L 100 32.366442431225806 L 104 34.891303950927174 L 108 37.56873257020147 L 112 40.37169170578371 L 116 43.27187717151857 L 120 46.24000299307087 L 124 49.246097136699866 L 128 52.259804165837984 L 132 55.25069176925828 L 136 58.18855806551976 L 140 61.04373658054035 L 144 63.78739581864462 L 148 66.39183040202806 L 152 68.83074083872101 L 156 71.07949909396547 L 160 73.11539728327368 L 164 74.91787697587438 L 168 76.4687367930486 L 172 77.75231620503374 L 176 78.75565367052127 L 180 79.46861752186066 L 184 79.88400827429518 L 188 79.99763132611449 L 192 79.80833931560025 L 196 79.3180437070458 L 200 78.53169548885461 L 204 77.45723517862753 L 208 76.10551264008578 L 212 74.49017752151552 L 216 72.62754142208313 L 220 70.53641317786067 L 224 68.23790893083817 L 228 65.75523889883885 L 232 63.11347299952798 L 236 60.33928769523553 L 240 57.46069661494566 L 244 54.50676767362273 L 248 51.507329545393105 L 252 48.49267045460691 L 256 45.49323232637728 L 260 42.53930338505435 L 264 39.66071230476449 L 268 36.886527000472014 L 272 34.24476110116113 L 276 31.762091069161837 L 280 29.463586822139323 L 284 27.37245857791691 L 288 25.509822478484498 L 292 23.894487359914248 L 296 22.542764821372476 L 300 21.468304511145394 L 304 20.681956292954197 L 308 20.19166068439975 L 312 20.002368673885517 L 316 20.115991725704824 L 320 20.531382478139342 L 324 21.244346329478716 L 328 22.247683794966264 L 332 23.53126320695139 L 336 25.08212302412562 L 340 26.88460271672634 L 344 28.92050090603449 L 348 31.16925916127897 L 352 33.60816959797189 L 356 36.212604181355346 L 360 38.95626341945966 L 364 41.811441934480214 L 368 44.74930823074172 L 372 47.74019583416199 L 376 50.75390286330012 L 380 53.7599970069291 L 384 56.72812282848143 L 388 59.6283082942163 L 392 62.43126742979852 L 396 65.10869604907285 L 400 67.63355756877418 L 404 69.98035602302755 L 408 72.1253935207452 L 412 74.04700954612628 L 416 75.72579968580956 L 420 77.14481157398058 L 424 78.28971607678594 L 428 79.14895198744023 L 432 79.71384277089953 L 436 79.97868417921768 L 440 79.94080185284815 L 444 79.60057832623605 L 448 78.96144916499821 L 452 78.02986827369836 L 456 76.81524272453791 L 460 75.32983776506046 L 464 73.58865296409856 L 468 71.60927074663721 L 472 69.41167884708335 L 476 67.0180684738027 L 480 64.4526102230515 L 484 61.74121000511608 L 488 58.911247447311084 L 492 55.99129941543222 L 496 53.01085144553649 L 500 50.000000000000014"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="23.786796564403577" width="10" height="10"></rect><rect x="57.5" y="15" width="10" height="10"></rect><rect x="88.75" y="23.786796564403573" width="10" height="10"></rect><rect x="120" y="44.99999999999999" width="10" height="10"></rect><rect x="151.25" y="66.21320343559643" width="10" height="10"></rect><rect x="182.5" y="75" width="10" height="10"></rect><rect x="213.75" y="66.21320343559643" width="10" height="10"></rect><rect x="245" y="45.00000000000001" width="10" height="10"></rect><rect x="276.25" y="23.78679656440358" width="10" height="10"></rect><rect x="307.5" y="15" width="10" height="10"></rect><rect x="338.75" y="23.78679656440355" width="10" height="10"></rect><rect x="370" y="44.999999999999986" width="10" height="10"></rect><rect x="401.25" y="66.21320343559644" width="10" height="10"></rect><rect x="432.5" y="75" width="10" height="10"></rect><rect x="463.75" y="66.21320343559645" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">14</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the sine for frequency 14 with sixteen samples. The wave has fourteen cycles.&#xAThe sample positions in this diagram are like the ones in the diagram of frequency 2, but negated.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="sin-wave">
            <path d="M -4 69.41167884708332 L 0 50 L 4 30.588321152916674 L 8 20.39942167376396 L 12 24.274200314190434 L 16 40.37169170578371 L 20 61.04373658054034 L 24 76.4687367930486 L 28 79.3180437070458 L 32 68.23790893083817 L 36 48.49267045460694 L 40 29.46358682213934 L 44 20.19166068439975 L 48 25.08212302412562 L 52 41.811441934480214 L 56 62.43126742979852 L 60 77.14481157398058 L 64 78.96144916499821 L 68 67.0180684738027 L 72 46.9891485544636 L 76 28.39072925336281 L 80 20.059198147151854 L 84 25.95299045387371 L 88 43.27187717151854 L 92 63.78739581864458 L 96 77.75231620503375 L 100 78.53169548885461 L 104 65.75523889883891 L 108 45.493232326377274 L 112 27.3724585779169 L 116 20.002368673885517 L 120 26.884602716726267 L 124 44.749308230741704 L 128 65.10869604907278 L 132 78.28971607678594 L 136 78.02986827369837 L 140 64.45261022305141 L 144 44.008700584567876 L 148 26.411347035901507 L 152 20.02131582078232 L 156 27.874606479254727 L 160 46.24000299307086 L 164 66.39183040202803 L 168 78.75565367052128 L 172 77.45723517862757 L 176 63.11347299952803 L 180 42.53930338505443 L 184 25.509822478484548 L 188 20.11599172570483 L 192 28.920500906034544 L 196 47.74019583416201 L 200 67.63355756877417 L 204 79.1489519874402 L 208 76.81524272453797 L 212 61.74121000511621 L 216 41.08875255268892 L 220 24.670162234939546 L 224 20.286157229100457 L 228 30.019643976972397 L 232 49.24609713669998 L 236 68.8307408387209 L 240 79.46861752186064 L 244 76.10551264008578 L 248 60.33928769523554 L 252 39.66071230476455 L 256 23.894487359914283 L 260 20.531382478139356 L 264 31.169259161279015 L 268 50.75390286330013 L 272 69.98035602302753 L 276 79.71384277089956 L 280 75.32983776506039 L 284 58.911247447311176 L 288 38.25878999488408 L 292 23.18475727546218 L 296 20.851048012559723 L 300 32.36644243122576 L 304 52.259804165837885 L 308 71.07949909396538 L 312 79.88400827429516 L 316 74.4901775215155 L 320 57.46069661494567 L 324 36.886527000472064 L 328 22.54276482137251 L 332 21.24434632947875 L 336 33.608169597971965 L 340 53.75999700692914 L 344 72.12539352074506 L 348 79.97868417921767 L 352 73.58865296409863 L 356 55.99129941543233 L 360 35.547389776948684 L 364 21.97013172630171 L 368 21.710283923213993 L 372 34.89130395092695 L 376 55.25069176925841 L 380 73.11539728327374 L 384 79.99763132611449 L 388 72.6275414220831 L 392 54.506767673622726 L 396 34.24476110116117 L 400 21.46830451114542 L 404 22.247683794966214 L 408 36.21260418135523 L 412 56.728122828481254 L 416 74.04700954612616 L 420 79.94080185284817 L 424 71.6092707466374 L 428 53.01085144553677 L 432 32.98193152619725 L 436 21.03855083500178 L 440 22.855188426019414 L 444 37.56873257020144 L 448 58.188558065519686 L 452 74.91787697587432 L 456 79.80833931560026 L 460 70.5364131778608 L 464 51.50732954539288 L 468 31.7620910691617 L 472 20.681956292954254 L 476 23.53126320695125 L 480 38.95626341945933 L 484 59.62830829421632 L 488 75.72579968580956 L 492 79.60057832623605 L 496 69.41167884708338 L 500 50.0000000000001"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="66.21320343559643" width="10" height="10"></rect><rect x="57.5" y="75" width="10" height="10"></rect><rect x="88.75" y="66.21320343559643" width="10" height="10"></rect><rect x="120" y="44.99999999999997" width="10" height="10"></rect><rect x="151.25" y="23.786796564403534" width="10" height="10"></rect><rect x="182.5" y="15" width="10" height="10"></rect><rect x="213.75" y="23.786796564403662" width="10" height="10"></rect><rect x="245" y="45.00000000000005" width="10" height="10"></rect><rect x="276.25" y="66.21320343559641" width="10" height="10"></rect><rect x="307.5" y="75" width="10" height="10"></rect><rect x="338.75" y="66.2132034355964" width="10" height="10"></rect><rect x="370" y="45.00000000000003" width="10" height="10"></rect><rect x="401.25" y="23.786796564403495" width="10" height="10"></rect><rect x="432.5" y="15" width="10" height="10"></rect><rect x="463.75" y="23.78679656440355" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">3</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the sine for frequency 3 with sixteen samples. The wave has three cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="sin-wave">
          <path d="M -4 54.50676767362271 L 0 50 L 4 45.49323232637729 L 8 41.08875255268895 L 12 36.886527000472014 L 16 32.9819315261973 L 20 29.463586822139337 L 24 26.411347035901432 L 28 23.89448735991423 L 32 21.970131726301638 L 36 20.681956292954197 L 40 20.059198147151854 L 44 20.115991725704824 L 48 20.85104801255978 L 52 22.24768379496626 L 56 24.274200314190434 L 60 26.884602716726324 L 64 30.01964397697245 L 68 33.60816959797192 L 72 37.56873257020147 L 76 41.811441934480236 L 80 46.240002993070874 L 84 50.75390286330013 L 88 55.25069176925827 L 92 59.62830829421628 L 96 63.78739581864463 L 100 67.6335575687742 L 104 71.07949909396547 L 108 74.04700954612629 L 112 76.4687367930486 L 116 78.28971607678594 L 120 79.46861752186066 L 124 79.97868417921768 L 128 79.80833931560025 L 132 78.96144916499821 L 136 77.45723517862753 L 140 75.32983776506045 L 144 72.62754142208313 L 148 69.41167884708335 L 152 65.75523889883888 L 156 61.74121000511607 L 160 57.46069661494563 L 164 53.01085144553645 L 168 48.49267045460691 L 172 44.008700584567826 L 176 39.660712304764516 L 180 35.547389776948556 L 184 31.762091069161837 L 188 28.390729253362807 L 192 25.509822478484484 L 196 23.184757275462083 L 200 21.468304511145394 L 204 20.39942167376396 L 208 20.002368673885517 L 212 20.28615722910046 L 216 21.244346329478716 L 220 22.8551884260194 L 224 25.08212302412562 L 228 27.87460647925478 L 232 31.16925916127897 L 236 34.89130395092714 L 240 38.95626341945966 L 244 43.27187717151855 L 248 47.74019583416199 L 252 52.25980416583799 L 256 56.72812282848143 L 260 61.04373658054032 L 264 65.10869604907285 L 268 68.83074083872101 L 272 72.1253935207452 L 276 74.91787697587439 L 280 77.14481157398059 L 284 78.75565367052126 L 288 79.71384277089953 L 292 79.99763132611449 L 296 79.60057832623605 L 300 78.53169548885461 L 304 76.81524272453792 L 308 74.49017752151555 L 312 71.60927074663721 L 316 68.23790893083817 L 320 64.45261022305144 L 324 60.33928769523551 L 328 55.99129941543222 L 332 51.50732954539306 L 336 46.98914855446354 L 340 42.53930338505436 L 344 38.258789994884 L 348 34.24476110116116 L 352 30.58832115291671 L 356 27.372458577916895 L 360 24.67016223493956 L 364 22.54276482137249 L 368 21.038550835001782 L 372 20.19166068439975 L 376 20.02131582078232 L 380 20.531382478139342 L 384 21.71028392321406 L 388 23.531263206951387 L 392 25.95299045387371 L 396 28.92050090603452 L 400 32.36644243122579 L 404 36.21260418135538 L 408 40.37169170578367 L 412 44.749308230741654 L 416 49.24609713669985 L 420 53.75999700692909 L 424 58.18855806551971 L 428 62.43126742979846 L 432 66.39183040202805 L 436 69.98035602302755 L 440 73.11539728327364 L 444 75.72579968580956 L 448 77.75231620503375 L 452 79.1489519874402 L 456 79.88400827429517 L 460 79.94080185284815 L 464 79.31804370704582 L 468 78.02986827369836 L 472 76.1055126400858 L 476 73.5886529640986 L 480 70.53641317786065 L 484 67.01806847380276 L 488 63.113472999528014 L 492 58.91124744731104 L 496 54.50676767362277 L 500 50.00000000000002"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="17.283614024661397" width="10" height="10"></rect><rect x="57.5" y="23.786796564403573" width="10" height="10"></rect><rect x="88.75" y="56.48050297095269" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="151.25" y="56.48050297095271" width="10" height="10"></rect><rect x="182.5" y="23.78679656440358" width="10" height="10"></rect><rect x="213.75" y="17.283614024661397" width="10" height="10"></rect><rect x="245" y="44.999999999999986" width="10" height="10"></rect><rect x="276.25" y="72.7163859753386" width="10" height="10"></rect><rect x="307.5" y="66.21320343559645" width="10" height="10"></rect><rect x="338.75" y="33.51949702904731" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="401.25" y="33.51949702904728" width="10" height="10"></rect><rect x="432.5" y="66.21320343559643" width="10" height="10"></rect><rect x="463.75" y="72.71638597533861" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">13</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the sine for frequency 13 with sixteen samples. The wave has thirteen cycles.&#xAThe sample positions in this diagram are like the ones in the diagram of frequency 3, but negated.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="sin-wave">
            <path d="M -4 68.23790893083816 L 0 50 L 4 31.762091069161833 L 8 21.038550835001782 L 12 22.24768379496626 L 16 34.89130395092718 L 20 53.759997006929126 L 24 71.07949909396548 L 28 79.71384277089953 L 32 76.10551264008576 L 36 61.74121000511607 L 40 42.53930338505435 L 44 26.41134703590143 L 48 20.002368673885517 L 52 25.952990453873714 L 56 41.81144193448027 L 60 61.04373658054032 L 64 75.72579968580959 L 68 79.80833931560025 L 72 71.60927074663721 L 76 54.50676767362271 L 80 35.547389776948535 L 84 22.542764821372447 L 88 20.851048012559787 L 92 31.169259161279 L 96 49.2460971366999 L 100 67.63355756877426 L 104 78.75565367052128 L 108 78.02986827369836 L 112 65.75523889883883 L 116 46.98914855446355 L 120 29.463586822139373 L 124 20.399421673763957 L 128 23.531263206951436 L 132 37.5687325702015 L 136 56.72812282848152 L 140 73.11539728327377 L 144 79.97868417921768 L 148 74.49017752151549 L 152 58.91124744731105 L 156 39.66071230476443 L 160 24.67016223493954 L 164 20.11599172570483 L 168 27.874606479254872 L 172 44.7493082307417 L 176 63.78739581864467 L 180 77.14481157398058 L 184 79.3180437070458 L 188 69.41167884708332 L 192 51.50732954539303 L 196 32.981931526197215 L 200 21.468304511145337 L 204 21.710283923214075 L 208 33.608169597971994 L 212 52.259804165837906 L 216 69.98035602302754 L 220 79.46861752186066 L 224 76.81524272453787 L 228 63.11347299952784 L 232 44.008700584567784 L 236 27.372458577916845 L 240 20.05919814715186 L 244 25.082123024125604 L 248 40.371691705783746 L 252 59.62830829421637 L 256 74.91787697587446 L 260 79.94080185284813 L 264 72.62754142208308 L 268 55.9912994154321 L 272 36.88652700047186 L 276 23.184757275461983 L 280 20.531382478139395 L 284 30.019643976972393 L 288 47.740195834161995 L 292 66.3918304020281 L 296 78.28971607678596 L 300 78.53169548885462 L 304 67.01806847380269 L 308 48.49267045460685 L 312 30.58832115291658 L 316 20.68195629295416 L 320 22.855188426019424 L 324 36.21260418135544 L 328 55.250691769258424 L 332 72.12539352074535 L 336 79.8840082742952 L 340 75.3298377650604 L 344 60.339287695235555 L 348 41.088752552688945 L 352 25.50982247848444 L 356 20.02131582078232 L 360 26.884602716726306 L 364 43.2718771715186 L 368 62.43126742979861 L 372 76.46873679304866 L 376 79.60057832623605 L 380 70.53641317786062 L 384 53.01085144553633 L 388 34.24476110116098 L 392 21.970131726301556 L 396 21.244346329478805 L 400 32.36644243122609 L 404 50.75390286330053 L 408 68.83074083872106 L 412 79.14895198744024 L 416 77.45723517862746 L 420 64.45261022305127 L 424 45.49323232637744 L 428 28.390729253362856 L 432 20.19166068439975 L 436 24.27420031419045 L 440 38.956263419459745 L 444 58.1885580655199 L 448 74.04700954612642 L 452 79.99763132611449 L 456 73.58865296409837 L 460 57.46069661494528 L 464 38.25878999488391 L 468 23.894487359914194 L 472 20.286157229100482 L 476 28.92050090603466 L 480 46.240002993070696 L 484 65.10869604907272 L 488 77.75231620503372 L 492 78.96144916499821 L 496 68.23790893083812 L 500 49.99999999999988"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="72.7163859753386" width="10" height="10"></rect><rect x="57.5" y="66.21320343559644" width="10" height="10"></rect><rect x="88.75" y="33.51949702904733" width="10" height="10"></rect><rect x="120" y="15" width="10" height="10"></rect><rect x="151.25" y="33.51949702904727" width="10" height="10"></rect><rect x="182.5" y="66.2132034355964" width="10" height="10"></rect><rect x="213.75" y="72.71638597533865" width="10" height="10"></rect><rect x="245" y="45.00000000000006" width="10" height="10"></rect><rect x="276.25" y="17.283614024661382" width="10" height="10"></rect><rect x="307.5" y="23.786796564403524" width="10" height="10"></rect><rect x="338.75" y="56.48050297095252" width="10" height="10"></rect><rect x="370" y="75" width="10" height="10"></rect><rect x="401.25" y="56.480502970952685" width="10" height="10"></rect><rect x="432.5" y="23.7867965644038" width="10" height="10"></rect><rect x="463.75" y="17.283614024661315" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
    <div class="frequencies-label">7</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the sine for frequency 7 with sixteen samples. The wave has seven cycles.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="sin-wave">
          <path d="M -4 60.33928769523551 L 0 50 L 4 39.66071230476449 L 8 30.588321152916674 L 12 23.89448735991423 L 16 20.39942167376396 L 20 20.53138247813934 L 24 24.274200314190434 L 28 31.169259161278983 L 32 40.37169170578371 L 36 50.75390286330011 L 40 61.04373658054034 L 44 69.98035602302754 L 48 76.4687367930486 L 52 79.71384277089953 L 56 79.3180437070458 L 60 75.32983776506046 L 64 68.23790893083817 L 68 58.911247447311055 L 72 48.49267045460694 L 76 38.25878999488394 L 80 29.46358682213934 L 84 23.184757275462083 L 88 20.19166068439975 L 92 20.851048012559772 L 96 25.08212302412562 L 100 32.3664424312258 L 104 41.811441934480214 L 108 52.25980416583799 L 112 62.43126742979852 L 116 71.0794990939655 L 120 77.14481157398058 L 124 79.88400827429517 L 128 78.96144916499821 L 132 74.4901775215155 L 136 67.0180684738027 L 140 57.46069661494562 L 144 46.9891485544636 L 148 36.88652700047207 L 152 28.39072925336281 L 156 22.54276482137249 L 160 20.059198147151854 L 164 21.244346329478716 L 168 25.95299045387371 L 172 33.60816959797188 L 176 43.27187717151854 L 180 53.75999700692909 L 184 63.78739581864458 L 188 72.12539352074523 L 192 77.75231620503375 L 196 79.97868417921768 L 200 78.53169548885461 L 204 73.5886529640986 L 208 65.75523889883891 L 212 55.99129941543228 L 216 45.493232326377274 L 220 35.54738977694854 L 224 27.3724585779169 L 228 21.970131726301652 L 232 20.002368673885517 L 236 21.710283923214043 L 240 26.884602716726267 L 244 34.891303950927174 L 248 44.749308230741704 L 252 55.250691769258246 L 256 65.10869604907278 L 260 73.1153972832737 L 264 78.28971607678594 L 268 79.99763132611449 L 272 78.02986827369837 L 276 72.62754142208307 L 280 64.45261022305141 L 284 54.506767673622775 L 288 44.008700584567876 L 292 34.24476110116122 L 296 26.411347035901507 L 300 21.4683045111454 L 304 20.02131582078232 L 308 22.247683794966232 L 312 27.874606479254727 L 316 36.21260418135537 L 320 46.24000299307086 L 324 56.72812282848141 L 328 66.39183040202803 L 332 74.04700954612632 L 336 78.75565367052128 L 340 79.94080185284815 L 344 77.45723517862757 L 348 71.60927074663729 L 352 63.11347299952803 L 356 53.01085144553651 L 360 42.53930338505443 L 364 32.981931526197386 L 368 25.509822478484548 L 372 21.038550835001818 L 376 20.11599172570483 L 380 22.85518842601943 L 384 28.920500906034544 L 388 37.56873257020148 L 392 47.74019583416201 L 396 58.188558065519736 L 400 67.63355756877417 L 404 74.91787697587435 L 408 79.1489519874402 L 412 79.80833931560026 L 416 76.81524272453797 L 420 70.53641317786075 L 424 61.74121000511621 L 428 51.50732954539325 L 432 41.08875255268892 L 436 31.762091069161826 L 440 24.670162234939546 L 444 20.681956292954197 L 448 20.286157229100457 L 452 23.531263206951376 L 456 30.019643976972397 L 460 38.956263419459574 L 464 49.24609713669998 L 468 59.62830829421637 L 472 68.8307408387209 L 476 75.72579968580949 L 480 79.46861752186064 L 484 79.60057832623605 L 488 76.10551264008578 L 492 69.41167884708335 L 496 60.33928769523554 L 500 50.00000000000005"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="33.5194970290473" width="10" height="10"></rect><rect x="57.5" y="66.21320343559643" width="10" height="10"></rect><rect x="88.75" y="17.283614024661397" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="151.25" y="17.283614024661407" width="10" height="10"></rect><rect x="182.5" y="66.21320343559643" width="10" height="10"></rect><rect x="213.75" y="33.519497029047365" width="10" height="10"></rect><rect x="245" y="44.99999999999997" width="10" height="10"></rect><rect x="276.25" y="56.480502970952685" width="10" height="10"></rect><rect x="307.5" y="23.786796564403534" width="10" height="10"></rect><rect x="338.75" y="72.71638597533861" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="401.25" y="72.71638597533858" width="10" height="10"></rect><rect x="432.5" y="23.786796564403662" width="10" height="10"></rect><rect x="463.75" y="56.48050297095271" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">9</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the sine for frequency 9 with sixteen samples. The wave has nine cycles.&#xAThe sample positions in this diagram are like the ones in the diagram of frequency 9, but negated.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="sin-wave">
            <path d="M -4 63.113472999527986 L 0 50 L 4 36.886527000472014 L 8 26.411347035901432 L 12 20.681956292954197 L 16 20.85104801255978 L 20 26.884602716726324 L 24 37.56873257020147 L 28 50.75390286330013 L 32 63.78739581864463 L 36 74.04700954612629 L 40 79.46861752186066 L 44 78.96144916499821 L 48 72.62754142208313 L 52 61.74121000511607 L 56 48.49267045460691 L 60 35.547389776948556 L 64 25.509822478484484 L 68 20.39942167376396 L 72 21.244346329478716 L 76 27.87460647925478 L 80 38.95626341945966 L 84 52.25980416583799 L 88 65.10869604907279 L 92 74.91787697587436 L 96 79.71384277089953 L 100 78.53169548885461 L 104 71.60927074663721 L 108 60.33928769523551 L 112 46.98914855446354 L 116 34.24476110116111 L 120 24.67016223493956 L 124 20.19166068439975 L 128 21.71028392321406 L 132 28.92050090603452 L 136 40.37169170578372 L 140 53.75999700692914 L 144 66.39183040202805 L 148 75.72579968580956 L 152 79.88400827429517 L 156 78.02986827369836 L 160 70.53641317786065 L 164 58.91124744731104 L 168 45.493232326377274 L 172 32.98193152619737 L 176 23.894487359914265 L 180 20.059198147151857 L 184 22.247683794966235 L 188 30.019643976972414 L 192 41.8114419344802 L 196 55.250691769258246 L 200 67.63355756877417 L 204 76.46873679304859 L 208 79.97868417921768 L 212 77.45723517862753 L 216 69.41167884708332 L 220 57.46069661494563 L 224 44.00870058456777 L 228 31.762091069161816 L 232 23.18475727546207 L 236 20.002368673885517 L 240 22.855188426019392 L 244 31.169259161278948 L 248 43.271877171518526 L 252 56.72812282848141 L 256 68.830740838721 L 260 77.14481157398058 L 264 79.99763132611449 L 268 76.81524272453791 L 272 68.23790893083816 L 276 55.991299415432195 L 280 42.53930338505433 L 284 30.588321152916727 L 288 22.542764821372497 L 292 20.02131582078232 L 296 23.53126320695138 L 300 32.36644243122577 L 304 44.74930823074169 L 308 58.188558065519736 L 312 69.98035602302754 L 316 77.75231620503374 L 320 79.94080185284815 L 324 76.10551264008576 L 328 67.01806847380269 L 332 54.50676767362269 L 336 41.08875255268892 L 340 29.463586822139312 L 344 21.970131726301695 L 348 20.115991725704813 L 352 24.274200314190356 L 356 33.60816959797181 L 360 46.24000299307074 L 364 59.62830829421617 L 368 71.0794990939654 L 372 78.28971607678591 L 376 79.80833931560026 L 380 75.3298377650605 L 384 65.75523889883894 L 388 53.010851445536524 L 392 39.66071230476455 L 396 28.390729253362835 L 400 21.468304511145412 L 404 20.286157229100457 L 408 25.0821230241256 L 412 34.89130395092715 L 416 47.740195834161995 L 420 61.043736580540326 L 424 72.1253935207452 L 428 78.75565367052128 L 432 79.60057832623605 L 436 74.4901775215155 L 440 64.45261022305144 L 444 51.50732954539305 L 448 38.258789994883884 L 452 27.372458577916852 L 456 21.038550835001768 L 460 20.531382478139353 L 464 25.95299045387375 L 468 36.21260418135544 L 472 49.24609713669975 L 476 62.431267429798424 L 480 73.11539728327361 L 484 79.1489519874402 L 488 79.31804370704583 L 492 73.58865296409861 L 496 63.11347299952806 L 500 50.000000000000064"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="56.48050297095269" width="10" height="10"></rect><rect x="57.5" y="23.78679656440358" width="10" height="10"></rect><rect x="88.75" y="72.7163859753386" width="10" height="10"></rect><rect x="120" y="15" width="10" height="10"></rect><rect x="151.25" y="72.71638597533861" width="10" height="10"></rect><rect x="182.5" y="23.786796564403538" width="10" height="10"></rect><rect x="213.75" y="56.480502970952685" width="10" height="10"></rect><rect x="245" y="44.999999999999964" width="10" height="10"></rect><rect x="276.25" y="33.51949702904738" width="10" height="10"></rect><rect x="307.5" y="66.21320343559641" width="10" height="10"></rect><rect x="338.75" y="17.28361402466138" width="10" height="10"></rect><rect x="370" y="75" width="10" height="10"></rect><rect x="401.25" y="17.283614024661382" width="10" height="10"></rect><rect x="432.5" y="66.21320343559641" width="10" height="10"></rect><rect x="463.75" y="33.51949702904719" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>While the <em>cosines</em> are the same in these pairs of frequencies, the <strong><em>sines</em> are negated</strong> and appear vertically
flipped! This means that a given frequency with <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>−</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">N - k</annotation></semantics></math></span> cycles has negated sine values from the frequency with <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span> cycles.</p>
<p>The relationship here is that the aliased frequencies in each pair are <strong>negated</strong> versions of the other, since
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>−</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\cos(-a) = \cos(a)</annotation></semantics></math></span> and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>−</mo><mi>a</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>a</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sin(-a) = -\sin(a)</annotation></semantics></math></span>. Thus, frequency <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>−</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">N - k</annotation></semantics></math></span> has the same angle as the hypothetical frequency at <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mi>k</mi></mrow><annotation encoding="application/x-tex">-k</annotation></semantics></math></span>:</p>
<p>Cosines:</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">-1</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the cosine for a hypothetical frequency -1 with sixteen samples. The wave has a single cycle.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="cos-wave">
          <path d="M -4 20.037891301819474 L 0 20 L 4 20.037891301819474 L 8 20.151469490560995 L 12 20.34044765786258 L 16 20.604348428472594 L 20 20.942505166141068 L 24 21.35406365760071 L 28 21.837984270383778 L 32 22.39304457902389 L 36 23.017842453008868 L 40 23.710799598684094 L 44 24.47016554615924 L 48 25.294022071147175 L 52 26.18028804056494 L 56 27.126724669656564 L 60 28.130941177357652 L 64 29.190400825615853 L 68 30.30242732741131 L 72 31.46421160728997 L 76 32.67281889733197 L 80 33.9251961506301 L 84 35.218179753551254 L 88 36.548503517299025 L 92 37.912806928590115 L 96 39.30764363860248 L 100 40.729490168751575 L 104 42.17475481130309 L 108 43.639786702338355 L 112 45.12088504415349 L 116 46.61430845379555 L 120 48.11628441412059 L 124 49.62301880349942 L 128 51.13070548009804 L 132 52.6355358965223 L 136 54.13370872053914 L 140 55.621439437571745 L 144 57.09496991071173 L 148 58.55057787409928 L 152 59.9845863356896 L 156 61.39337286565403 L 160 62.77337874695218 L 164 64.12111796495998 L 168 65.4331860134452 L 172 66.70626849464563 L 176 67.93714949172556 L 180 69.12271969246069 L 184 70.25998424363073 L 188 71.34607031627856 L 192 72.37823436272546 L 196 73.3538690470107 L 200 74.27050983124842 L 204 75.12584120126425 L 208 75.91770251578507 L 212 76.64409346440634 L 216 77.30317912054987 L 220 77.89329457664755 L 224 78.41294914984232 L 228 78.86083014758258 L 232 79.23580618359732 L 236 79.53693003587617 L 240 79.76344103943433 L 244 79.91476700781843 L 248 79.990525678499 L 252 79.990525678499 L 256 79.91476700781843 L 260 79.76344103943434 L 264 79.53693003587617 L 268 79.23580618359732 L 272 78.86083014758258 L 276 78.41294914984233 L 280 77.89329457664753 L 284 77.30317912054988 L 288 76.64409346440635 L 292 75.91770251578507 L 296 75.12584120126425 L 300 74.27050983124843 L 304 73.3538690470107 L 308 72.37823436272546 L 312 71.34607031627857 L 316 70.25998424363074 L 320 69.12271969246069 L 324 67.93714949172556 L 328 66.70626849464563 L 332 65.43318601344521 L 336 64.12111796495998 L 340 62.77337874695216 L 344 61.39337286565406 L 348 59.98458633568961 L 352 58.55057787409931 L 356 57.094969910711754 L 360 55.62143943757174 L 364 54.133708720539154 L 368 52.6355358965223 L 372 51.13070548009805 L 376 49.62301880349943 L 380 48.116284414120614 L 384 46.61430845379555 L 388 45.12088504415348 L 392 43.63978670233836 L 396 42.17475481130308 L 400 40.72949016875158 L 404 39.30764363860247 L 408 37.91280692859013 L 412 36.548503517299054 L 416 35.21817975355126 L 420 33.92519615063012 L 424 32.67281889733198 L 428 31.46421160728997 L 432 30.302427327411316 L 436 29.19040082561585 L 440 28.130941177357663 L 444 27.126724669656564 L 448 26.18028804056495 L 452 25.294022071147175 L 456 24.470165546159237 L 460 23.710799598684094 L 464 23.017842453008864 L 468 22.393044579023893 L 472 21.837984270383785 L 476 21.35406365760071 L 480 20.94250516614107 L 484 20.604348428472594 L 488 20.340447657862583 L 492 20.151469490560995 L 496 20.037891301819474 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="22.283614024661397" r="5"></circle><circle cx="62.5" cy="28.786796564403573" r="5"></circle><circle cx="93.75" cy="38.5194970290473" r="5"></circle><circle cx="125" cy="50" r="5"></circle><circle cx="156.25" cy="61.48050297095269" r="5"></circle><circle cx="187.5" cy="71.21320343559643" r="5"></circle><circle cx="218.75" cy="77.7163859753386" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="77.71638597533861" r="5"></circle><circle cx="312.5" cy="71.21320343559643" r="5"></circle><circle cx="343.75" cy="61.48050297095271" r="5"></circle><circle cx="375" cy="50.00000000000001" r="5"></circle><circle cx="406.25" cy="38.5194970290473" r="5"></circle><circle cx="437.5" cy="28.78679656440358" r="5"></circle><circle cx="468.75" cy="22.283614024661404" r="5"></circle>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">15</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the cosine of frequency 15 with sixteen samples. The wave has fifteen cycles.&#xA;The sample positions in this diagram match the ones in the cosine diagram for hypothetical frequency -1 exactly.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 28.130941177357652 L 0 20 L 4 28.130941177357652 L 8 48.11628441412059 L 12 69.12271969246069 L 16 79.76344103943433 L 20 74.27050983124843 L 24 55.62143943757174 L 28 33.92519615063012 L 32 20.94250516614107 L 36 23.710799598684073 L 40 40.72949016875157 L 44 62.77337874695215 L 48 77.89329457664755 L 52 77.89329457664755 L 56 62.77337874695222 L 60 40.72949016875164 L 64 23.71079959868411 L 68 20.942505166141064 L 72 33.92519615063003 L 76 55.621439437571695 L 80 74.27050983124842 L 84 79.76344103943434 L 88 69.12271969246073 L 92 48.116284414120685 L 96 28.130941177357652 L 100 20 L 104 28.130941177357624 L 108 48.116284414120535 L 112 69.12271969246062 L 116 79.76344103943433 L 120 74.2705098312485 L 124 55.62143943757179 L 128 33.92519615063016 L 132 20.942505166141093 L 136 23.71079959868409 L 140 40.729490168751546 L 144 62.773378746952034 L 148 77.89329457664748 L 152 77.89329457664758 L 156 62.77337874695229 L 160 40.72949016875161 L 164 23.71079959868412 L 168 20.942505166141046 L 172 33.92519615062993 L 176 55.62143943757162 L 180 74.27050983124828 L 184 79.76344103943435 L 188 69.12271969246075 L 192 48.1162844141206 L 196 28.130941177357744 L 200 20 L 204 28.13094117735754 L 208 48.116284414120514 L 212 69.12271969246052 L 216 79.76344103943433 L 220 74.27050983124845 L 224 55.621439437571915 L 228 33.925196150630185 L 232 20.942505166141068 L 236 23.710799598684027 L 240 40.729490168751326 L 244 62.77337874695201 L 248 77.8932945766475 L 252 77.89329457664762 L 256 62.773378746952304 L 260 40.72949016875163 L 264 23.710799598684183 L 268 20.94250516614104 L 272 33.925196150630086 L 276 55.621439437571595 L 280 74.27050983124839 L 284 79.76344103943438 L 288 69.12271969246095 L 292 48.116284414120834 L 296 28.130941177357904 L 300 20 L 304 28.13094117735752 L 308 48.11628441412027 L 312 69.1227196924605 L 316 79.76344103943431 L 320 74.27050983124846 L 324 55.62143943757194 L 328 33.9251961506302 L 332 20.942505166141075 L 336 23.710799598684016 L 340 40.729490168751305 L 344 62.7733787469518 L 348 77.89329457664735 L 352 77.89329457664763 L 356 62.77337874695252 L 360 40.72949016875206 L 364 23.710799598684194 L 368 20.942505166140982 L 372 33.9251961506297 L 376 55.621439437571574 L 380 74.27050983124825 L 384 79.76344103943434 L 388 69.12271969246079 L 392 48.116284414120855 L 396 28.130941177357627 L 400 20 L 404 28.130941177357506 L 408 48.11628441412025 L 412 69.12271969246032 L 416 79.76344103943431 L 420 74.2705098312486 L 424 55.621439437572164 L 428 33.925196150630214 L 432 20.94250516614113 L 436 23.710799598683902 L 440 40.72949016875148 L 444 62.77337874695198 L 448 77.8932945766474 L 452 77.89329457664756 L 456 62.773378746952346 L 460 40.72949016875188 L 464 23.710799598684098 L 468 20.942505166141032 L 472 33.92519615062987 L 476 55.62143943757134 L 480 74.2705098312481 L 484 79.76344103943435 L 488 69.12271969246098 L 492 48.11628441412109 L 496 28.130941177357787 L 500 20"></path><circle cx="0" cy="20" r="5"></circle><circle cx="31.25" cy="22.283614024661404" r="5"></circle><circle cx="62.5" cy="28.786796564403602" r="5"></circle><circle cx="93.75" cy="38.519497029047415" r="5"></circle><circle cx="125" cy="50.00000000000008" r="5"></circle><circle cx="156.25" cy="61.480502970952735" r="5"></circle><circle cx="187.5" cy="71.21320343559658" r="5"></circle><circle cx="218.75" cy="77.71638597533868" r="5"></circle><circle cx="250" cy="80" r="5"></circle><circle cx="281.25" cy="77.71638597533855" r="5"></circle><circle cx="312.5" cy="71.21320343559636" r="5"></circle><circle cx="343.75" cy="61.480502970952436" r="5"></circle><circle cx="375" cy="49.999999999999545" r="5"></circle><circle cx="406.25" cy="38.519497029047116" r="5"></circle><circle cx="437.5" cy="28.7867965644033" r="5"></circle><circle cx="468.75" cy="22.28361402466134" r="5"></circle>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>Sines:</p>
<div class="center-flex">
  <div class="frequencies-grid">
    <div class="frequencies-label">-1</div>
    <div class="svg-container">
      <div class="fft-diagram">
      <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
        <title>Diagram of the sine for a hypothetical frequency -1 with sixteen samples. The wave has a single cycle, negated vs a standard sine wave.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g class="sin-wave">
          <path d="M -4 48.492670454606916 L 0 50 L 4 51.507329545393084 L 8 53.010851445536446 L 12 54.50676767362271 L 16 55.99129941543221 L 20 57.46069661494565 L 24 58.91124744731105 L 28 60.33928769523551 L 32 61.74121000511607 L 36 63.113472999527986 L 40 64.45261022305147 L 44 65.75523889883887 L 48 67.0180684738027 L 52 68.23790893083816 L 56 69.41167884708332 L 60 70.53641317786065 L 64 71.60927074663721 L 68 72.62754142208311 L 72 73.58865296409857 L 76 74.49017752151552 L 80 75.32983776506046 L 84 76.10551264008578 L 88 76.81524272453791 L 92 77.45723517862753 L 96 78.02986827369836 L 100 78.53169548885461 L 104 78.96144916499821 L 108 79.3180437070458 L 112 79.60057832623605 L 116 79.80833931560025 L 120 79.94080185284815 L 124 79.99763132611449 L 128 79.97868417921768 L 132 79.88400827429518 L 136 79.71384277089953 L 140 79.46861752186066 L 144 79.14895198744023 L 148 78.75565367052128 L 152 78.28971607678594 L 156 77.75231620503374 L 160 77.14481157398058 L 164 76.4687367930486 L 168 75.72579968580956 L 172 74.91787697587438 L 176 74.0470095461263 L 180 73.11539728327368 L 184 72.12539352074522 L 188 71.07949909396548 L 192 69.98035602302755 L 196 68.83074083872101 L 200 67.6335575687742 L 204 66.39183040202808 L 208 65.10869604907283 L 212 63.78739581864464 L 216 62.43126742979853 L 220 61.04373658054034 L 224 59.62830829421629 L 228 58.18855806551975 L 232 56.72812282848143 L 236 55.25069176925829 L 240 53.75999700692913 L 244 52.25980416583799 L 248 50.753902863300134 L 252 49.24609713669987 L 256 47.740195834162016 L 260 46.240002993070874 L 264 44.74930823074172 L 268 43.27187717151856 L 272 41.81144193448024 L 276 40.37169170578372 L 280 38.95626341945965 L 284 37.56873257020149 L 288 36.21260418135538 L 292 34.89130395092719 L 296 33.60816959797193 L 300 32.366442431225806 L 304 31.16925916127899 L 308 30.019643976972457 L 312 28.920500906034526 L 316 27.874606479254787 L 320 26.88460271672632 L 324 25.952990453873706 L 328 25.082123024125618 L 332 24.274200314190438 L 336 23.531263206951397 L 340 22.855188426019406 L 344 22.247683794966267 L 348 21.71028392321407 L 352 21.24434632947873 L 356 20.851048012559783 L 360 20.53138247813934 L 364 20.286157229100464 L 368 20.115991725704824 L 372 20.021315820782323 L 376 20.002368673885517 L 380 20.059198147151854 L 384 20.19166068439975 L 388 20.39942167376396 L 392 20.681956292954197 L 396 21.038550835001786 L 400 21.46830451114539 L 404 21.970131726301638 L 408 22.542764821372472 L 412 23.184757275462076 L 416 23.894487359914226 L 420 24.670162234939536 L 424 25.509822478484477 L 428 26.411347035901436 L 432 27.372458577916877 L 436 28.390729253362796 L 440 29.46358682213933 L 444 30.58832115291667 L 448 31.76209106916183 L 452 32.9819315261973 L 456 34.24476110116114 L 460 35.54738977694854 L 464 36.88652700047202 L 468 38.25878999488393 L 472 39.66071230476447 L 476 41.088752552688945 L 480 42.53930338505434 L 484 44.008700584567784 L 488 45.49323232637727 L 492 46.98914855446355 L 496 48.492670454606895 L 500 49.99999999999999"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="56.48050297095269" width="10" height="10"></rect><rect x="57.5" y="66.21320343559643" width="10" height="10"></rect><rect x="88.75" y="72.7163859753386" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="151.25" y="72.7163859753386" width="10" height="10"></rect><rect x="182.5" y="66.21320343559643" width="10" height="10"></rect><rect x="213.75" y="56.4805029709527" width="10" height="10"></rect><rect x="245" y="45.00000000000001" width="10" height="10"></rect><rect x="276.25" y="33.51949702904731" width="10" height="10"></rect><rect x="307.5" y="23.786796564403577" width="10" height="10"></rect><rect x="338.75" y="17.283614024661404" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="401.25" y="17.2836140246614" width="10" height="10"></rect><rect x="432.5" y="23.78679656440357" width="10" height="10"></rect><rect x="463.75" y="33.51949702904729" width="10" height="10"></rect>
        </g>
      </svg>
      </div>
    </div>
    <div class="frequencies-label">15</div>
    <div class="svg-container">
      <div class="fft-diagram">
        <svg class="width-250" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg">
          <title>Diagram of the sine of frequency 15 with sixteen samples. The wave has fifteen cycles.&#xA;The sample positions in this diagram match the ones in the sine diagram for hypothetical frequency -1 exactly.</title>
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="sin-wave">
            <path d="M -4 70.53641317786065 L 0 50 L 4 29.46358682213934 L 8 20.059198147151854 L 12 26.884602716726324 L 16 46.24000299307087 L 20 67.6335575687742 L 24 79.46861752186066 L 28 75.32983776506046 L 32 57.46069661494566 L 36 35.54738977694858 L 40 21.468304511145394 L 44 22.8551884260194 L 48 38.95626341945966 L 52 61.04373658054032 L 56 77.14481157398058 L 60 78.53169548885464 L 64 64.4526102230515 L 68 42.53930338505436 L 72 24.670162234939593 L 76 20.53138247813933 L 80 32.36644243122579 L 84 53.75999700692909 L 88 73.11539728327364 L 92 79.94080185284815 L 96 70.53641317786065 L 100 50.00000000000002 L 104 29.463586822139373 L 108 20.059198147151857 L 112 26.884602716726267 L 116 46.24000299307087 L 120 67.63355756877408 L 124 79.46861752186065 L 128 75.32983776506049 L 132 57.46069661494573 L 136 35.54738977694855 L 140 21.4683045111454 L 144 22.855188426019346 L 148 38.95626341945949 L 152 61.04373658054025 L 156 77.14481157398053 L 160 78.53169548885462 L 164 64.45261022305151 L 168 42.53930338505443 L 172 24.67016223493966 L 176 20.531382478139314 L 180 32.36644243122561 L 184 53.75999700692896 L 188 73.11539728327362 L 192 79.94080185284815 L 196 70.53641317786075 L 200 50.00000000000004 L 204 29.463586822139465 L 208 20.05919814715186 L 212 26.884602716726185 L 216 46.24000299307074 L 220 67.63355756877415 L 224 79.46861752186064 L 228 75.3298377650605 L 232 57.460696614945654 L 236 35.54738977694866 L 240 21.468304511145472 L 244 22.855188426019335 L 248 38.956263419459574 L 252 61.04373658054013 L 256 77.14481157398052 L 260 78.53169548885462 L 264 64.45261022305162 L 268 42.53930338505445 L 272 24.670162234939557 L 276 20.53138247813931 L 280 32.36644243122576 L 284 53.75999700692873 L 288 73.11539728327347 L 292 79.94080185284815 L 296 70.53641317786092 L 300 50.00000000000028 L 304 29.463586822139483 L 308 20.059198147151875 L 312 26.88460271672617 L 316 46.24000299307072 L 320 67.63355756877414 L 324 79.46861752186062 L 328 75.32983776506052 L 332 57.460696614945675 L 336 35.547389776948684 L 340 21.46830451114548 L 344 22.855188426019236 L 348 38.956263419459155 L 352 61.043736580540106 L 356 77.14481157398042 L 360 78.53169548885477 L 364 64.45261022305164 L 368 42.53930338505468 L 372 24.6701622349398 L 376 20.531382478139307 L 380 32.366442431225565 L 384 53.759997006929126 L 388 73.1153972832736 L 392 79.94080185284817 L 396 70.53641317786062 L 400 50.000000000000085 L 404 29.463586822139497 L 408 20.059198147151875 L 412 26.884602716726018 L 416 46.240002993070696 L 420 67.63355756877395 L 424 79.46861752186058 L 428 75.32983776506052 L 432 57.4606966149459 L 436 35.54738977694889 L 440 21.468304511145423 L 444 22.855188426019318 L 448 38.95626341945933 L 452 61.04373658054028 L 456 77.1448115739805 L 460 78.53169548885471 L 464 64.45261022305147 L 468 42.539303385054495 L 472 24.670162234939696 L 476 20.531382478139264 L 480 32.36644243122538 L 484 53.75999700692889 L 488 73.11539728327344 L 492 79.94080185284818 L 496 70.5364131778608 L 500 50.00000000000033"></path><rect x="-5" y="45" width="10" height="10"></rect><rect x="26.25" y="56.48050297095271" width="10" height="10"></rect><rect x="57.5" y="66.21320343559645" width="10" height="10"></rect><rect x="88.75" y="72.71638597533865" width="10" height="10"></rect><rect x="120" y="75" width="10" height="10"></rect><rect x="151.25" y="72.71638597533858" width="10" height="10"></rect><rect x="182.5" y="66.21320343559626" width="10" height="10"></rect><rect x="213.75" y="56.480502970952514" width="10" height="10"></rect><rect x="245" y="44.99999999999984" width="10" height="10"></rect><rect x="276.25" y="33.51949702904719" width="10" height="10"></rect><rect x="307.5" y="23.786796564403506" width="10" height="10"></rect><rect x="338.75" y="17.28361402466129" width="10" height="10"></rect><rect x="370" y="15" width="10" height="10"></rect><rect x="401.25" y="17.28361402466148" width="10" height="10"></rect><rect x="432.5" y="23.78679656440385" width="10" height="10"></rect><rect x="463.75" y="33.51949702904744" width="10" height="10"></rect>
          </g>
        </svg>
      </div>
    </div>
  </div>
</div>
<p>In practice, when computing the DFT, these frequencies are referred to via their negative indices instead of as the positive
aliased ones.</p>
<p>This explains why all of the frequencies (except DC and Nyquist) have <em>two</em> matches: the cosines match at both the
positive <em>and</em> negative versions of the angle. This may seem redundant, but it isn’t always – we’ll get into why in a
bit.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>It’s also worth noting that the multiply-then-average in the example diagram results in half the signal amplitude for
each match, and when both are summed, the result represents the full amplitude!</p>
<p>However, DC and Nyquist – because they each only have a single match – have an average that matches the amplitude exactly.
</p></blockquote><p></p>
<h3>Measuring Phase</h3>
<p>So far we have been looking at a signal that contains a cosine, comparing it with a test wave that is also a cosine.
But the original description of the DFT mentions both <strong>amplitude</strong> and <strong>phase</strong> as being represented, so what happens
if we let the phase of the signal shift around relative to the test?</p>
<div class="center-flex">
  <div class="interactive-diagram-container">
    <div class="fft-diagram">
      <svg viewBox="0 0 600 340">
        <title id="diagram2-title">An interactive diagram that demonstrates how adjusting the the signal phase affects the average.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g id="diagram2-signal-cos" class="neutral-wave"></g>
        <text x="250" y="105">&times;</text>
        <g transform="translate(0, 110)">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g class="cos-wave">
            <path d="M -4 20.34044765786258 L 0 20 L 4 20.34044765786258 L 8 21.35406365760071 L 12 23.017842453008868 L 16 25.294022071147175 L 20 28.130941177357652 L 24 31.464211607289972 L 28 35.218179753551254 L 32 39.30764363860248 L 36 43.639786702338355 L 40 48.1162844141206 L 44 52.63553589652229 L 48 57.09496991071174 L 52 61.39337286565403 L 56 65.4331860134452 L 60 69.12271969246069 L 64 72.37823436272546 L 68 75.12584120126425 L 72 77.30317912054987 L 76 78.86083014758258 L 80 79.76344103943434 L 84 79.990525678499 L 88 79.53693003587617 L 92 78.41294914984233 L 96 76.64409346440634 L 100 74.27050983124843 L 104 71.34607031627857 L 108 67.93714949172556 L 112 64.12111796495998 L 116 59.98458633568961 L 120 55.62143943757174 L 124 51.13070548009805 L 128 46.61430845379555 L 132 42.17475481130308 L 136 37.91280692859013 L 140 33.9251961506301 L 144 30.302427327411316 L 148 27.12672466965658 L 152 24.47016554615925 L 156 22.393044579023893 L 160 20.942505166141064 L 164 20.151469490560995 L 168 20.037891301819474 L 172 20.604348428472587 L 176 21.837984270383767 L 180 23.710799598684087 L 184 26.18028804056494 L 188 29.19040082561584 L 192 32.672818897331965 L 196 36.54850351729904 L 200 40.72949016875157 L 204 45.12088504415347 L 208 49.62301880349941 L 212 54.13370872053912 L 216 58.55057787409927 L 220 62.77337874695215 L 224 66.70626849464564 L 228 70.25998424363073 L 232 73.35386904701069 L 236 75.91770251578504 L 240 77.89329457664755 L 244 79.2358061835973 L 248 79.91476700781841 L 252 79.91476700781843 L 256 79.23580618359732 L 260 77.89329457664755 L 264 75.91770251578505 L 268 73.3538690470107 L 272 70.25998424363074 L 276 66.70626849464563 L 280 62.773378746952176 L 284 58.55057787409934 L 288 54.13370872053916 L 292 49.623018803499455 L 296 45.12088504415354 L 300 40.72949016875159 L 304 36.548503517299054 L 308 32.67281889733201 L 312 29.190400825615857 L 316 26.180288040564953 L 320 23.710799598684083 L 324 21.837984270383778 L 328 20.604348428472598 L 332 20.03789130181947 L 336 20.151469490560995 L 340 20.942505166141064 L 344 22.39304457902386 L 348 24.470165546159226 L 352 27.126724669656532 L 356 30.302427327411298 L 360 33.92519615063008 L 364 37.912806928590086 L 368 42.17475481130309 L 372 46.61430845379553 L 376 51.130705480098 L 380 55.621439437571745 L 384 59.984586335689585 L 388 64.12111796495995 L 392 67.93714949172558 L 396 71.34607031627856 L 400 74.27050983124842 L 404 76.64409346440635 L 408 78.41294914984232 L 412 79.53693003587615 L 416 79.990525678499 L 420 79.76344103943434 L 424 78.86083014758259 L 428 77.3031791205499 L 432 75.12584120126427 L 436 72.37823436272546 L 440 69.12271969246073 L 444 65.43318601344521 L 448 61.39337286565402 L 452 57.0949699107118 L 456 52.63553589652231 L 460 48.11628441412058 L 464 43.639786702338405 L 468 39.307643638602485 L 472 35.21817975355132 L 476 31.46421160729 L 480 28.130941177357652 L 484 25.294022071147218 L 488 23.017842453008882 L 492 21.354063657600708 L 496 20.340447657862587 L 500 20"></path><circle cx="0" cy="20" r="3"></circle><circle cx="31.25" cy="38.5194970290473" r="3"></circle><circle cx="62.5" cy="71.21320343559643" r="3"></circle><circle cx="93.75" cy="77.71638597533861" r="3"></circle><circle cx="125" cy="50.00000000000001" r="3"></circle><circle cx="156.25" cy="22.283614024661404" r="3"></circle><circle cx="187.5" cy="28.78679656440357" r="3"></circle><circle cx="218.75" cy="61.4805029709527" r="3"></circle><circle cx="250" cy="80" r="3"></circle><circle cx="281.25" cy="61.48050297095271" r="3"></circle><circle cx="312.5" cy="28.786796564403602" r="3"></circle><circle cx="343.75" cy="22.283614024661397" r="3"></circle><circle cx="375" cy="49.999999999999986" r="3"></circle><circle cx="406.25" cy="77.7163859753386" r="3"></circle><circle cx="437.5" cy="71.21320343559643" r="3"></circle><circle cx="468.75" cy="38.519497029047315" r="3"></circle>
          </g>
        </g>
        <text x="250" y="220">&equals;</text>
        <g transform="translate(0, 220)">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g id="diagram2-product-cos" class="result-wave"></g>
        </g>
        <text x="525" y="258" class="smaller">avg</text>
        <text x="525" y="268" class="small">&rightarrow;</text>
        <g transform="translate(550, 220)">
          <line class="neutral-prod" x1="0" y1="50" x2="50" y2="50"></line>
          <g id="diagram2-full-sum" class="result-avg"></g>
        </g>
      </svg>
    </div>
    <div class="input-grid">
      <div>Signal Phase:</div>
      <input id="diagram2-signal-phase" type="range" min="0" max="20" step="1" value="0">
      <div id="diagram2-phase-text">0.0</div>
    </div>
  </div>
</div>
<p>Oh no! <strong>Sliding the phase around</strong> causes resulting average to <strong>oscillate</strong>! While we get the expected <code>0.5</code><span class="attrs"></span> average when
the phase is 0, it is <code>-1</code><span class="attrs"></span> at a phase of <code>0.5</code><span class="attrs"></span> (a half-wavelength offset) and <code>0</code><span class="attrs"></span> at phases of
<code>0.25</code><span class="attrs"></span> and <code>0.75</code><span class="attrs"></span> (quarter- and three-quarter-wavelength offsets)! Clearly there is something else we need to consider
here…and it turns out it’s the <strong>sine</strong> of the test frequency.</p>
<p>Instead of just multiplying by the cosine of the test value, we can instead multiply by both cosine <em>and</em> sine to get two values per sample.
If we then treat those two resulting values as a 2D coordinate (where cosine corresponds to <code>x</code><span class="attrs"></span> and sine to <code>y</code><span class="attrs"></span>), we can average
them all to get our final result, which will be at the origin for non-matching frequencies and at some non-origin position
for matches.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>“Multiply by both sine and cosine” is a simplification for purposes of illustration. It works for now but is
not the general form, which we’ll get to in a bit.
</p></blockquote><p></p>
<div class="center-flex">
  <div class="interactive-diagram-container">
    <div class="fft-diagram">
      <svg class="diagram" viewBox="0 0 600 340">
        <title id="diagram3-title">An interactive diagram that demonstrates how adjusting the the signal and test waves affects a 2D average.</title>
        <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
        <g id="diagram3-signal-wave" class="neutral-wave"></g>
        <text x="250" y="105">&times;</text>
        <g transform="translate(0, 110)">
          <line class="neutral-axis" x1="0" y1="50" x2="500" y2="50"></line>
          <g id="diagram3-test-cos" class="cos-wave"></g>
          <g id="diagram3-test-sin" class="sin-wave"></g>
        </g>
        <text x="250" y="220">&equals;</text>
        <g transform="translate(120, 230)">
          <line class="y-axis" x1="50" y1="0" x2="50" y2="100"></line>
          <line class="x-axis" x1="0" y1="50" x2="100" y2="50"></line>
          <rect class="neutral-axis" x="0" y="0" width="100" height="100"></rect>
          <g id="diagram3-products" class="neutral-wave"></g>
        </g>
        <text x="250" y="268" class="smaller">avg</text>
        <text x="250" y="278" class="small">&rightarrow;</text>
        <g transform="translate(280, 230)">
          <line class="y-axis" x1="50" y1="0" x2="50" y2="100"></line>
          <line class="x-axis" x1="0" y1="50" x2="100" y2="50"></line>
          <rect class="neutral-axis" x="0" y="0" width="100" height="100"></rect>
          <g id="diagram3-average" class="result-avg"></g>
        </g>
        <text id="diagram3-avg-mag-text" x="395" y="268" class="small left">Amp: 0.5</text>
        <text id="diagram3-avg-phase-text" x="395" y="288" class="small left">Phase: 0.00</text>
      </svg>
    </div>
    <div class="input-grid">
      <div>Signal Phase:</div>
      <input id="diagram3-signal-phase-slider" type="range" min="0" max="20" step="1" value="0">
      <div id="diagram3-signal-phase-text"></div>
      <div>Signal Cycles:</div>
      <input id="diagram3-signal-freq-slider" type="range" min="0" max="8" step="1" value="2">
      <div id="diagram3-signal-freq-text"></div>
      <div>Test Cycles:</div>
      <input id="diagram3-test-freq-slider" type="range" min="0" max="15" step="1" value="2">
      <div id="diagram3-test-freq-text"></div>
    </div>
  </div>
</div>
<p>Some observations from this diagram:</p>
<ul>
<li>
<p>If the cycle value magnitudes <strong>don’t match</strong>, the points are all <strong>evenly distributed around the center</strong>
(and thus average to the origin). This makes it much easier to visualize why we end up with no measured amplitude for
mismatched waves.</p>
</li>
<li>
<p>If the cycle value magnitudes <strong>do match</strong> (i.e. a signal of <code>2</code><span class="attrs"></span>
cycles with either <code>2</code><span class="attrs"></span> or <code>-2</code><span class="attrs"></span> test cycles), the points are <strong>off-center</strong>, and thus their average is also off-center.</p>
</li>
<li>
<p><strong>Increasing the phase</strong> causes the resulting average to spin <strong>clockwise</strong> when the test <strong>cycle count is positive</strong>, but
<strong>counter-clockwise</strong> when the test cycle count is <strong>negative</strong>. The magnitude of the average remains the same no matter
the phase.</p>
</li>
<li>
<p>At Nyquist, the resulting points always lie on the x axis, as the test wave’s <em>sine</em> values are all zeros. It turns out:
a phase-shifted wave at Nyquist is indistinguishable from a non-shifted wave with scaled amplitude.</p>
</li>
</ul>
<p>This gives us a new way to think about this computation: rather than thinking of the operation as a multiplication,
instead we can think of each sample of the signal being <strong>rotated by the angle</strong> in the test wave at that sample
position to get a new 2D position. Those 2D positions are then averaged to get a value that corresponds to the
frequency’s contribution.</p>
<p>The computed <strong>amplitude</strong> (the strength of this frequency’s contribution) is the <strong>magnitude of the
resulting average</strong>, and the <strong>phase</strong> is the <strong>angle of the average</strong> (computed, in this case, using <code>atan2</code><span class="attrs"></span>).</p>
<h3>2D Coordinates and Complex Numbers</h3>
<p>Each result of this multiplication with cosine and sine ends up as a <strong>2D coordinate</strong>. Canonically, the Fourier transform
represents these as a <strong>complex number</strong> (of the form <code>x + i*y</code><span class="attrs"></span>). Why complex numbers? Honestly I think it’s a hack to get
a 2D coordinate that is simple to represent with standard math notation (mathemeticians, don’t @ me).</p>
<p>But, to be fair, complex numbers <em>do</em> have a nice property that standard 2D vectors do not: <strong>multiplying</strong> a complex number by another is equivalent to <strong>scaling</strong> the first value
<strong>by the magnitude</strong> of the second then <strong>rotating</strong> it <strong>by the angle</strong> of the second.</p>
<p>Thus, multiplying a sample from the signal (whether it is complex or real-valued) by <code>cos(t) + i*sin(t)</code><span class="attrs"></span> is equivalent to
rotating that sample by the angle <code>t</code><span class="attrs"></span>
(the rotation value has a magnitude of <code>1</code><span class="attrs"></span> so no scaling occurs in this case).</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>This also finally gives us an answer to the earlier question of why we need both the positive and negative versions of a given
frequency: if the input samples are <em>complex</em> instead of purely real-valued, the contributions to these paired frequencies
can be different, and both are needed to fully reconstruct the signal.</p>
<p>For real-valued inputs, however, the paired results will be <a href="https://en.wikipedia.org/wiki/Complex_conjugate" target="_blank" rel="noopener">complex conjugates</a>,
where the result (<code>a + i*b</code><span class="attrs"></span>) for frequency <code>k</code><span class="attrs"></span> is the conjugate of the result for frequency <code>-k</code><span class="attrs"></span>: (<code>a - i*b</code><span class="attrs"></span>). These
results <em>are</em>, in fact, redundant – and we will absolutely take that redundancy into account when implementing the FFT
for real-valued inputs.
</p></blockquote><p></p>
<p>If you’re more familiar with matrices and linear algebra than complex numbers, the reason this works is that this complex multiply:</p>
<div class="center-flex">
  <span class="mathbox">
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>a</mi><mo>+</mo><mi>i</mi><mi>b</mi><mo stretchy="false">)</mo><mo>⋅</mo><mo stretchy="false">(</mo><mi>c</mi><mo>+</mo><mi>i</mi><mi>s</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">(a + ib) \cdot (c + is)</annotation></semantics></math></span></p>
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><mo stretchy="false">(</mo><mi>a</mi><mi>c</mi><mo>−</mo><mi>b</mi><mi>s</mi><mo stretchy="false">)</mo><mo>⋅</mo><mi>i</mi><mo stretchy="false">(</mo><mi>a</mi><mi>s</mi><mo>+</mo><mi>b</mi><mi>c</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\implies (ac - bs) \cdot i(as + bc)</annotation></semantics></math></span>
</p></span><p></p>
</div>
<p>gives exactly the same result as multiplying a 2D vector <code>(a, b)</code><span class="attrs"></span> with a 2x2 rotation matrix representing the same angle:</p>
<div class="center-flex">
  <span class="mathbox">
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>a</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>b</mi></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow><mo>⋅</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>c</mi></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>s</mi></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo>−</mo><mi>s</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mi>c</mi></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\begin{bmatrix}a &amp; b\end{bmatrix} \cdot \begin{bmatrix}
    c &amp; s \\
    -s &amp; c
    \end{bmatrix}</annotation></semantics></math></span></p>
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>a</mi><mi>c</mi><mo>−</mo><mi>b</mi><mi>s</mi></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>a</mi><mi>s</mi><mo>+</mo><mi>b</mi><mi>c</mi></mrow></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow><annotation encoding="application/x-tex">\implies\begin{bmatrix}
      ac - bs &amp;&amp; as + bc
    \end{bmatrix}</annotation></semantics></math></span>
</p></span><p></p>
</div>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>It’s worth pointing out something that shows up in the standard formula for the DFT: mathematical notation has an
<em>absolutely wild</em> shorthand for <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>+</mo><mi>i</mi><mo>∗</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\cos(t) + i*\sin(t)</annotation></semantics></math></span>:</p>
  <div class="center-flex">
    <span class="mathbox">
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mi>i</mi><mi>t</mi></mrow></msup></mrow><annotation encoding="application/x-tex">e^{it}</annotation></semantics></math></span>
</p></span><p></p>
  </div>
<p>That’s right, taking the mathematical constant <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>e</mi></mrow><annotation encoding="application/x-tex">e</annotation></semantics></math></span>
to an <em>imaginary power</em> is the same as a rotation around the origin. Why? Well, <a href="https://www.youtube.com/watch?v=v0YEaeIClKY" target="_blank" rel="noopener">3blue1brown has
an explanation</a> if you’re curious.</p>
<p>Personally I don’t like this shorthand because it obscures the meaning of the equation, <em>especially</em> for
non-mathemeticians (which is, if I’m doing my calculations right, <em>most people</em>).
</p></blockquote><p></p>
<h3>Calculating the DFT</h3>
<p>Okay, we have all of the pieces of how to calculate a DFT, it’s time to put them together.</p>
<p>A DFT takes a set of <strong>complex input samples</strong> in the time domain and produces the same number of <strong>complex output
samples</strong> in the frequency domain, where the first output sample (at index <code>0</code><span class="attrs"></span>) represents the DC offset, the middle one (at index <code>N/2</code><span class="attrs"></span>)
represents the Nyquist frequency, and the rest are either the positive or negative angles in between.</p>
<p>There are a couple minor (but important) differences in the value calculation done in the above interactive diagrams vs
how the DFT does them:</p>
<ul>
<li>Typically the DFT rotates the opposite direction as the above diagrams (i.e. using <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>−</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sin(-t)</annotation></semantics></math></span> instead of <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sin(t)</annotation></semantics></math></span>), so
the rotations actually spin the opposite way (clockwise becomes counter-clockwise and vice versa).</li>
<li>Rather than averaging the results of these rotations, the DFT simply sums them – no division by <code>N</code><span class="attrs"></span> is performed.</li>
</ul>
<p>Otherwise, the process described above can be used to get the contribution of any single frequency within the signal,
so then we just need to do that for every possible frequency in the signal.</p>
<p>Thus, here we are, finally at some pseudocode to calculate the DFT:</p>
<pre class="language-cs"><code class="language-cs"><span class="token return-type class-name"><span class="token keyword">void</span></span> <span class="token function">CalculateDFT</span><span class="token punctuation">(</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> inputs<span class="token punctuation">,</span>
  <span class="token class-name">complex<span class="token punctuation">[</span><span class="token punctuation">]</span></span> outputs<span class="token punctuation">,</span>
  <span class="token class-name"><span class="token keyword">int</span></span> length<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>outIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> outIdx <span class="token operator">&lt;</span> length<span class="token punctuation">;</span> outIdx<span class="token operator">++</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Start the sum at 0.</span>
    <span class="token class-name"><span class="token keyword">float</span></span> sum <span class="token operator">=</span> <span class="token number">0</span>

    <span class="token comment">// This is the angular frequency (in radians/sample) for</span>
    <span class="token comment">// the given output sample (the "test frequency" from the</span>
    <span class="token comment">// earlier examples).</span>
    <span class="token class-name"><span class="token keyword">float</span></span> angularFreq  <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span> <span class="token operator">*</span> pi <span class="token operator">*</span> outIdx <span class="token operator">/</span> length

    <span class="token keyword">for</span> <span class="token punctuation">(</span>inIdx <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> inIdx <span class="token operator">&lt;</span> length<span class="token punctuation">;</span> inIdx<span class="token operator">++</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Get the rotation value for the current input index</span>
      <span class="token comment">// then use that to calculate the resulting rotation.</span>
      <span class="token class-name"><span class="token keyword">float</span></span> radians <span class="token operator">=</span> inIdx <span class="token operator">*</span> angularFreq
      <span class="token class-name">complex</span> rotation <span class="token operator">=</span> <span class="token function">cos</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span> <span class="token operator">+</span> i<span class="token operator">*</span><span class="token function">sin</span><span class="token punctuation">(</span>radians<span class="token punctuation">)</span>

      <span class="token comment">// A complex multiply with a unit complex vector is</span>
      <span class="token comment">// a rotation.</span>
      sum <span class="token operator">+=</span> inputs<span class="token punctuation">[</span>inIdx<span class="token punctuation">]</span> <span class="token operator">*</span> rotation
    <span class="token punctuation">}</span>

    outputs<span class="token punctuation">[</span>outIdx<span class="token punctuation">]</span> <span class="token operator">=</span> sum<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>As mentioned in a previous note, the standard formulation of the DFT uses an exponential shorthand (which I do not
like) to represent the rotation. Thus, the DFT is written in standard mathematical notation as:</p>
  <div class="center-flex">
    <span class="mathbox">
<p><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>X</mi><mi>k</mi></msub><mo>=</mo><mstyle scriptlevel="0" displaystyle="true"><munderover><mo>∑</mo><mrow><mi>n</mi><mo>=</mo><mn>0</mn></mrow><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow></munderover><msub><mi>x</mi><mi>n</mi></msub><mo>⋅</mo><msup><mi>e</mi><mrow><mo>−</mo><mi>i</mi><mn>2</mn><mi>π</mi><mfrac><mi>k</mi><mi>N</mi></mfrac><mi>n</mi></mrow></msup></mstyle></mrow><annotation encoding="application/x-tex">X_k = \displaystyle\sum_{n = 0}^{N - 1} x_n \cdot e^{-i2\pi\frac{k}{N}n}</annotation></semantics></math></span>
</p></span><p></p>
  </div>
<p>…which is equivalent to the above pseudocode.
</p></blockquote><p></p>
<p>We’ve finally done it! We have some code that we can run to calculate the DFT for a given signal.</p>
<p>The above algorithm is <strong><span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>N</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N^2)</annotation></semantics></math></span></strong>, which may seem like the best that can be done. However, there’s a bit of
algorithmic trickery that can be done to get that down to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>N</mi><mi>log</mi><mo>⁡</mo><mi>N</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(N \log N)</annotation></semantics></math></span>, which is considerably more efficient. That
trickery is the <strong>fast Fourier transform (FFT)</strong>, and the next post will go into the details of how that works!</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Protecting Coders From Ourselves: Better Mutex Protection]]></title>
      <link>https://www.drilian.com/posts/2025.01.23-protecting-coders-from-ourselves-better-mutex-protection/</link>
      <pubDate>Thu, 23 Jan 2025 13:55:44 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2025.01.23-protecting-coders-from-ourselves-better-mutex-protection/</guid>
      <content:encoded>
        <![CDATA[
          <p>At some point, if you’ve done multithreaded programming, you’ve probably used a mutex <a href="https://en.wikipedia.org/wiki/Lock_(computer_science)" target="_blank" rel="noopener">(or some other locking mechanism)</a>. Locks are relatively straightforward to understand and use (“lock this thing before accessing your data and unlock it when you’re done”), but they do have their issues. The most commonly-discussed issue with using locks is the dreaded <a href="https://en.wikipedia.org/wiki/Deadlock_(computer_science)" target="_blank" rel="noopener">deadlock</a>, scourge of many a poor soul who needed to hold multiple locks at once for something.</p>
<p>But we’re not here to talk about deadlocks…instead, I’d like to focus on a problem that I have encountered <em>much</em> more frequently: accidentally reading or modifying protected data <strong>without having acquired the lock</strong>.</p>
<p>That’s right, we’re once again going to try to protect ourselves from our worst enemy: ourselves.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>As with the <a href="https://www.drilian.com/posts/2025.01.10-protecting-coders-from-ourselves-min-max-lerp-and-clamp/">previous entry in this semi-series</a>, the examples and implementation are in C++, but you can probably build something similar in your language of choice (unless it doesn’t need it).
</p></blockquote><p></p>
<h3>An Easy Mistake</h3>
<p>It turns out it’s <em>surprisingly easy</em> to not grab a lock before reading or writing things that need to be synchronized. Here’s a toy example:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">IncrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Lock the mutex correctly!</span>
  std<span class="token double-colon punctuation">::</span>lock_guard lock <span class="token punctuation">{</span> m_mutex <span class="token punctuation">}</span><span class="token punctuation">;</span>
  m_thing <span class="token operator">+=</span> c<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">DecrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  m_thing <span class="token operator">-=</span> c<span class="token punctuation">;</span> <span class="token comment">// Whoops, no lock!</span>
<span class="token punctuation">}</span></code></pre>
<p>In the real world, <em>“oops I accessed a thing outside of the lock”</em> bugs tend to be more subtle - it can be especially hard to notice when a thing that <em>should</em> be present <em>isn’t</em>. Since all of the accesses are to <em>normal members</em> of your class/struct/global scope/whatever, it’s easy to slip and put an access to a value where it wasn’t intended, and wasn’t propertly protected.</p>
<p>But what if instead you could do something more like the following:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Note: this is pseudocode, not actual C++</span>
<span class="token keyword">class</span> <span class="token class-name">Foo</span>
<span class="token punctuation">{</span>
  <span class="token comment">// ... class stuff ...</span>

  <span class="token comment">// Mysterious m_state object that protects everything inside of it with a mutex</span>
  MutexProtected m_state
  <span class="token punctuation">{</span>
    <span class="token keyword">int</span> m_thing <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">IncrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token function">lock</span> <span class="token punctuation">(</span>m_state<span class="token punctuation">)</span> <span class="token comment">// Lock here</span>
  <span class="token punctuation">{</span>
    m_thing <span class="token operator">+=</span> c<span class="token punctuation">;</span> <span class="token comment">// Only accessible in the lock</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// m_thing += c; // Won't compile: Can't access outside of lock</span>
<span class="token punctuation">}</span>
</code></pre>
<p>Basically, what if you could have some sort of <strong>protected wrapper</strong> around the state that walls it off and makes it <strong>inaccessible except when the lock is actually held</strong>? With something like that it would become <em>much</em> more difficult to get at values when it shouldn’t be allowed.</p>
<p>Additionally, it would help both with <strong>organization</strong> (forcing the mutex-protected values together in the code) as well as making the <strong>intent</strong> clear: values that are protected sit inside the protected block, clarifying which values are (and are not) intended to be accessed solely through the mutex, even to folks who were not the original author (the list of which stealthily includes the original author, one month in the future).</p>
<p><span class="read-more"></span></p>
<p>With C++ it’s possible to get quite close to the above:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">class</span> <span class="token class-name">Foo</span>
<span class="token punctuation">{</span>
  <span class="token comment">// ... class stuff ...</span>

  <span class="token comment">// State structure for the protected member(s)</span>
  <span class="token keyword">struct</span> <span class="token class-name">State</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> m_thing <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token comment">// The mutex and the state it's protecting</span>
  MutexProtected<span class="token operator">&lt;</span>State<span class="token operator">></span> m_state<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">IncrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// lock the mutex and get the inner state</span>
  <span class="token keyword">auto</span> state <span class="token operator">=</span> m_state<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Use the lock to access the values within.</span>
  state<span class="token operator">-></span>m_thing <span class="token operator">+=</span> c<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The nice thing is, a basic implementation of this is fairly compact. There are just two classes involved: <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span> and its lock object, <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span>.</p>
<blockquote>
<p><strong>EDIT (2025-01-25):</strong> People have pointed out two existing versions of this: boost has <code><a href="https://www.boost.org/doc/libs/1_87_0/doc/html/thread/sds.html" target="_blank" rel="noopener">boost::synchronized_value</a></code>, and there’s <a href="https://github.com/copperspice/cs_libguarded" target="_blank" rel="noopener">cs_libguarded</a> which looks like it has a few variants on this concept, too. So if you don’t feel like rolling your own you can always check one of those out instead!</p>
</blockquote>
<h3>MutexProtected&lt;T&gt;</h3>
<p>The outer class, <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span>, has very few moving parts:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">class</span> <span class="token class-name">MutexProtected</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span><span class="token operator">:</span>
  MutexLocked<span class="token operator">&lt;</span>T<span class="token operator">></span> <span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">&amp;</span>m_t<span class="token punctuation">,</span> m_mutex <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token keyword">private</span><span class="token operator">:</span>
  std<span class="token double-colon punctuation">::</span>mutex m_mutex<span class="token punctuation">;</span>
  T m_t<span class="token punctuation">;</span>  
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>There’s a <strong>lock function</strong> that returns a <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> (which we’ll get to in a moment), and it contains both a <strong>mutex</strong> as well as a <code>T</code><span class="attrs"></span> (the templated type), which contains <strong>the data to be protected by the mutex</strong>. In the earlier example, this was the <code>State</code><span class="attrs"></span> struct that contained the protected <code>m_thing</code><span class="attrs"></span> value, but it could contain any number of values/objects that should only be accessed from within the same lock.</p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>You may be wondering “there’s a <code>Lock</code><span class="attrs"></span>, so why is there no corresponding <code>Unlock</code><span class="attrs"></span> function?” - this is because we’re taking advantage of the classic C++ idiom of <a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization" target="_blank" rel="noopener">RAII</a>, so the returned <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> object holds the lifetime of the lock, and unlocks the mutex when it goes out of scope. As such, there’s no need to manually unlock the mutex; it will happen automatically.
</p></blockquote><p></p>
<h3>MutexLocked&lt;T&gt;</h3>
<p>But what is the thing that the <code>Lock</code><span class="attrs"></span> function returns? Well, that’s the <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> class and it looks like this:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">class</span> <span class="token class-name">MutexLocked</span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span><span class="token operator">:</span>
  <span class="token comment">// Add the standard pointer-like accessors.</span>
  T <span class="token operator">*</span><span class="token keyword">operator</span><span class="token operator">-></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> m_p<span class="token punctuation">;</span> <span class="token punctuation">}</span>
  T <span class="token operator">&amp;</span><span class="token keyword">operator</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator">*</span>m_p<span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token keyword">private</span><span class="token operator">:</span>
  <span class="token comment">// Construct this with a pointer to the state and the mutex to lock.</span>
  <span class="token function">MutexLocked</span><span class="token punctuation">(</span>T <span class="token operator">*</span>p<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>mutex <span class="token operator">&amp;</span>mutex<span class="token punctuation">)</span>
  <span class="token operator">:</span> m_lock<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">lock_guard</span><span class="token punctuation">(</span>mutex<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span> m_p<span class="token punctuation">{</span>p<span class="token punctuation">}</span>
    <span class="token punctuation">{</span> <span class="token punctuation">}</span>

  std<span class="token double-colon punctuation">::</span>lock_guard<span class="token operator">&lt;</span>std<span class="token double-colon punctuation">::</span>mutex<span class="token operator">></span> m_lock<span class="token punctuation">;</span>
  T <span class="token operator">*</span>m_p <span class="token operator">=</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span>

  <span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
  <span class="token keyword">friend</span> <span class="token keyword">class</span> <span class="token class-name">MutexProtected</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>As you can see, it also doesn’t have much to it! It constructs with a pointer to the <strong>state object</strong> (the <code>m_t</code><span class="attrs"></span> in the <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span>) and the <strong>mutex</strong> (which it <strong>immediately locks</strong> in the constructor), and so all it does is hold onto the lock until destruction time, while giving the user a way to access the state object (via the two public operators).</p>
<p>So, using this setup in full:</p>
<ol>
<li>Call <code>Lock</code><span class="attrs"></span> on the <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span> to get a <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span></li>
<li>Access the protected state through that acquired object (using the <code>-&gt;</code><span class="attrs"></span> operator) to do whatever needs to be done within the lock</li>
<li>Let the <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> leave scope, at which point the lock is released</li>
</ol>
<p>A nice little feature of this is if you do have a single thing that you’re doing in the lock (Say, inserting a value into a protected queue), the above steps can even fit nicely on a single line (while still being perfectly readable), thanks to the rules of C++ <a href="https://en.cppreference.com/w/cpp/language/lifetime" target="_blank" rel="noopener">temporary object lifetimes</a>:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">EnqueueItem</span><span class="token punctuation">(</span>Item <span class="token operator">*</span>i<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  state<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span>queue<span class="token punctuation">.</span><span class="token function">Enqueue</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Lock/Update/Unlock</span>

  <span class="token comment">// Do more stuff here outside of the lock, it's guaranteed to be released</span>
<span class="token punctuation">}</span></code></pre>
<h3>Sub-Functions That Require A Lock</h3>
<p>This type of object also helps with a secondary part of this problem, which is when your class has functions (that are likely <code>private</code><span class="attrs"></span> or <code>protected</code><span class="attrs"></span>) that require the lock to <em>already be acquired</em> before you call it:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// This function is intended to only be called while the lock is held</span>
<span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">AdjustStateWithLockHeld</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span>
  <span class="token punctuation">{</span> m_thing <span class="token operator">+=</span> delta<span class="token punctuation">;</span> <span class="token punctuation">}</span>

Void <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">IncrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  std<span class="token double-colon punctuation">::</span>lock_guard lock <span class="token punctuation">{</span> m_mutex <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token comment">// Call this function while the lock is held only</span>
  <span class="token function">AdjustStateWithLockHeld</span><span class="token punctuation">(</span>delta<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

Void <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">DecrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Oh no I have once again forgotten to lock the mutex before doing the thing</span>
  <span class="token function">AdjustStateWithLockHeld</span><span class="token punctuation">(</span><span class="token operator">-</span>delta<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In the above example, <code>AdjustStateWithLockHeld</code><span class="attrs"></span> is assuming that the lock is being held by its caller and that it’s free to manipulate the state within it. It doesn’t make much sense in this example, but if you have mutex-protected state that has a complex update process (such as multiple things needing to be kept in sync), it can be nice to move such logic to a subroutine.</p>
<p>Thankfully, using the <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span> object, the state is only accessible through a <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> object. In order for the sub-function to be able to do anything with the state, it would need to additionally take a reference to the <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> for the state in question, thus effectively ensuring that the mutex is locked by the caller:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Now the function takes the locked State object as a parameter:</span>
<span class="token keyword">void</span> <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">AdjustStateWithLockHeld</span><span class="token punctuation">(</span>
    MutexLocked<span class="token operator">&lt;</span>State<span class="token operator">></span> <span class="token operator">&amp;</span>state<span class="token punctuation">,</span> 
    <span class="token keyword">int</span> delta<span class="token punctuation">)</span>
  <span class="token punctuation">{</span> state<span class="token operator">-></span>m_thing <span class="token operator">+=</span> delta<span class="token punctuation">;</span> <span class="token punctuation">}</span>

Void <span class="token class-name">Foo</span><span class="token double-colon punctuation">::</span><span class="token function">DecrementThing</span><span class="token punctuation">(</span><span class="token keyword">int</span> delta<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Now state has to be locked to get the object to pass to the sub-function!</span>
  <span class="token keyword">auto</span> state <span class="token operator">=</span> m_state<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">AdjustStateWithLockHeld</span><span class="token punctuation">(</span>state<span class="token punctuation">,</span> <span class="token operator">-</span>delta<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>A Variation</h3>
<p>For completeness, I also wanted to mention an alternate form of this that I thought of while designing it: where, rather than <code>Lock</code><span class="attrs"></span> returning an object that represents the scoped lock, you instead pass a lambda (or function) that gets all of the state values in it:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">State</span>
<span class="token punctuation">{</span>
  <span class="token keyword">int</span> a<span class="token punctuation">,</span> b<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

MutexProtected<span class="token operator">&lt;</span>State<span class="token operator">></span> state<span class="token punctuation">;</span>

state<span class="token punctuation">.</span><span class="token function">Lock</span><span class="token punctuation">(</span>
  <span class="token punctuation">[</span><span class="token operator">&amp;</span><span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token keyword">int</span> <span class="token operator">&amp;</span>a<span class="token punctuation">,</span> <span class="token keyword">int</span> <span class="token operator">&amp;</span>b<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// "a" and "b" correspond to the two values of the same name in the state structure.</span>
    <span class="token comment">//  All the things that need to modify state values must, then, happen in this lambda,</span>
    <span class="token comment">//  as there is no way to access the struct members directly.</span>
    a <span class="token operator">+=</span> someVariable<span class="token punctuation">;</span>
    b <span class="token operator">-=</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The main advantage this has over the other form is that it makes it considerably more difficult to accidentally (or intentionally?) grab a reference to the internal state structure that could then persist outside of the lock. However, in practice I felt like that kind of mistake is not going to be super common relative to the problem being solved, but also should be easy to catch during code review. Plus, there are additional downsides to this version that I didn’t like:</p>
<ul>
<li>It’s easy to get the order or names of the lambda paramters wrong and end up doing the wrong things with the wrong values since they’d effectively have to match the order in the state.</li>
<li>It gets harder to call sub-functions that need the lock (you’d have to pass <em>all</em> of the state objects that need updating which can be a pain in practice).</li>
<li>It’s worse to debug, since you have to step <em>into</em> the Lock call instead of just over it, and then into the lambda from there.
<ul>
<li>This is also the reason I didn’t consider another variant where instead of a parameter per state object it’s a single reference to <code>State</code><span class="attrs"></span> and you access it that way.</li>
</ul>
</li>
</ul>
<p>All said, I felt like having a lock object that provides access to the inner state as-is (via the <code>-&gt;</code><span class="attrs"></span> operator) was cleaner in practice, and easier to step through in the debugger.</p>
<h3>Limitations</h3>
<p>This, of course, isn’t a perfect solution:</p>
<ul>
<li>There are absolutely cases where some things need to be accessible outside of the mutex lock (i.e. an atomic which can be safely read at any time but only gets updated from within the mutex due to sequencing issues), and as such those values couldn’t live inside of the inner state object.</li>
<li>Also, this is C++ so there’s nothing preventing someone from grabbing a reference or pointer to the state object from the lock and holding onto it until after the lock ends, then partying on the data. However, that kind of code is more likely to get caught at review time.</li>
<li>There are likely other cases (multiple locks, perhaps, which I haven’t fully thought through with this because I haven’t needed to) where this would present problems. This is definitely primarily intended for the “I have a set of data that should only ever be accessed during a lock” case.</li>
</ul>
<h3>Potential Improvements</h3>
<p>Also, there are some improvements that could be made:</p>
<ul>
<li>Perhaps instead of using a <code>lock_guard</code><span class="attrs"></span> you could use a <code>unique_lock</code><span class="attrs"></span> which would let you wait on a critical section using the lock (if you need to wait for the state to be in some specific configuration), which could even be built into the <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span> class (or a similar one) if the critical section is a core part of the usage.</li>
<li>It’s a good idea to declare a custom move constructor and move assignment operator for <code>MutexLocked&lt;T&gt;</code><span class="attrs"></span> that nulls the <code>m_t</code><span class="attrs"></span> pointer of the object being moved from so that you can’t do a move of it to some other location (which subsequently releases the lock) and then still party on the internal pointer. (I also have asserts in my production version in the pointer-access operators that assert the pointer is non-null)</li>
<li>Similarly, it may be nice to have a constructor for <code>MutexProtected&lt;T&gt;</code><span class="attrs"></span> that takes constructor arguments for the contained state structure (similar to, say <code><a href="https://en.cppreference.com/w/cpp/container/vector/emplace_back" target="_blank" rel="noopener">std::vector::emplace_back</a></code>), especially if you are going to have state structures that cannot default construct.</li>
<li>There are also other flavors of locking that might occur: for instance, a <code>TryLock</code><span class="attrs"></span> function that returns a <code>std::optional&lt;MutexLocked&lt;T&gt;&gt;</code><span class="attrs"></span>, and only locks the lock (and returns the locked state) if there is no contention on the mutex.</li>
</ul>
<h3>Closing Time</h3>
<p>All in all, having an abstraction like this makes it way more difficult to party all over internal state without properly locking the mutex first. Switching some old code to use this actually found a couple places where I’d done things incorrectly (reading values that should have been mutex protected on read, in those cases).</p>
<p>So, yeah, by protecting our data from being accessed when it shouldn’t be, we’re also protecting ourselves from ourselves.</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Protecting Coders From Ourselves: Min, Max, Lerp, and Clamp]]></title>
      <link>https://www.drilian.com/posts/2025.01.10-protecting-coders-from-ourselves-min-max-lerp-and-clamp/</link>
      <pubDate>Fri, 10 Jan 2025 23:13:29 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2025.01.10-protecting-coders-from-ourselves-min-max-lerp-and-clamp/</guid>
      <content:encoded>
        <![CDATA[
          <p><em>Imagine this:</em> you’ve got some value, <code>x</code><span class="attrs"></span>, that you want to ensure is at least <code>1</code><span class="attrs"></span>. That is to say, you want to ensure its <strong>minimum value</strong> is <code>1</code><span class="attrs"></span>. So, being the smart, experienced programmer that you are, you write the following:</p>
<pre class="language-cpp"><code class="language-cpp">x <span class="token operator">=</span> <span class="token function">Min</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>You give yourself the small, satisfied nod of a job well done and run the program and then it all goes <em>immediately sideways</em> because that should have been <code>Max</code><span class="attrs"></span> and not <code>Min</code><span class="attrs"></span>.</p>
<p>If you’ve been writing code for basically any length of time, the above was probably less an <em>“imagine this”</em> and more a <em>“remember this”</em> because if you’re anything like me, you’ve done this over. and over. and <em>over</em>.</p>
<p>Inspired by <em>once again</em> mistakenly using <code>Min</code><span class="attrs"></span> instead of <code>Max</code><span class="attrs"></span> to limit the minimum allowed value of something, I’ve decided to start a little series (will it have more than one entry? who knows!) called <strong>Protecting Coders From Ourselves</strong>, in which we rework some bit of API surface to make it less error-prone. We’re going to deal with the “I chose the wrong <code>Min</code><span class="attrs"></span>/<code>Max</code><span class="attrs"></span> again” problem, but first I want to talk about <code>Clamp and Lerp</code><span class="attrs"></span>.</p>
<p><span class="read-more"></span></p>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>The examples here are in C++, but the concepts should be relevant to basically any language.
</p></blockquote><p></p>
<h3>Clamp and Lerp</h3>
<h4>Clamp</h4>
<p>Clamp is a simple enough function: Take some value <code>v</code><span class="attrs"></span> and make sure it is no less than <code>min</code><span class="attrs"></span> and no greater than <code>max</code><span class="attrs"></span>. Almost every <code>clamp</code><span class="attrs"></span> function in every library I’ve seen has three parameters, one of which represents the value to be clamped, and two of which represent the range that it should be clamped within:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c<span class="token punctuation">)</span></code></pre>
<p>The question, of course, is <strong>“which parameter is which?”</strong> Many languages (ex: <a href="https://en.cppreference.com/w/cpp/algorithm/clamp" target="_blank" rel="noopener">C++</a>, <a href="https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-9.0" target="_blank" rel="noopener">C#</a>, <a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-clamp" target="_blank" rel="noopener">HLSL</a>, and <a href="https://docs.rs/num/latest/num/fn.clamp.html" target="_blank" rel="noopener">Rust</a>) have the following arrangement in their standard libraries:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>valueToClamp<span class="token punctuation">,</span> min<span class="token punctuation">,</span> max<span class="token punctuation">)</span></code></pre>
<p>where the first parameter is the value being clamped and the last two are the min and max ends of the range.</p>
<p>But I’ve also seen this one (looking at <em>you</em>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp" target="_blank" rel="noopener">CSS</a>):</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>min<span class="token punctuation">,</span> valueToClamp<span class="token punctuation">,</span> max<span class="token punctuation">)</span></code></pre>
<p>This one puts the value to clamp in the middle of the range (which, honestly, is conceptually where it belongs).</p>
<h4>Lerp</h4>
<p>Another common function that takes a value and a range is <code>Lerp</code><span class="attrs"></span>, which uses a value in the range <code>[0, 1]</code><span class="attrs"></span> to <a href="https://en.wikipedia.org/wiki/Linear_interpolation" target="_blank" rel="noopener">linearly interpolate</a> between two endpoint values. Most lerp functions that I’ve seen (ex: <a href="https://en.cppreference.com/w/cpp/numeric/lerp" target="_blank" rel="noopener">C++</a>, <a href="https://learn.microsoft.com/en-us/dotnet/api/system.double.lerp?view=net-9.0" target="_blank" rel="noopener">C#</a>, and <a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-lerp" target="_blank" rel="noopener">HLSL</a>) have their parameters ordered as follows:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Lerp</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> t<span class="token punctuation">)</span></code></pre>
<p>where <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span> are the <strong>range endpoints</strong> and <code>t</code><span class="attrs"></span> is the <strong>interpolating value</strong>.</p>
<p>Depending on the projects you work on, you maybe don’t write code that uses <code>Lerp</code><span class="attrs"></span> very often (or ever), but I do, and for me the combination of <code>Lerp</code><span class="attrs"></span> and <code>Clamp</code><span class="attrs"></span> are a source of constant, mild confusion.</p>
<h3>Parameter Confusion</h3>
<p>Both of these functions take three parameters, two of which represent a <strong>range</strong> and one which is a <strong>value</strong> that is either limited by or used to interpolate within the range. In isolation it’s easy to rationalize the order of the parameters for each:</p>
<ul>
<li><code class="inline-code language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> min<span class="token punctuation">,</span> max<span class="token punctuation">)</span></code>: Clamp <code>v</code><span class="attrs"></span> to be within the range <code>[min, max]</code><span class="attrs"></span>.</li>
<li><code class="inline-code language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>min<span class="token punctuation">,</span> v<span class="token punctuation">,</span> max<span class="token punctuation">)</span></code>: Clamp such that <code>min &lt;= v &lt;= max</code><span class="attrs"></span>.</li>
<li><code class="inline-code language-cpp"><span class="token function">Lerp</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> t<span class="token punctuation">)</span></code>: Get a linearly-interpolated value between <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span> using <code>t</code><span class="attrs"></span>.</li>
</ul>
<p>…but in combination I am <em>constantly</em> second-guessing which order the parameters need to go in. Sometimes, for instance, I’ll write the equivalent of <code class="inline-code language-cpp"><span class="token function">Lerp</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> a<span class="token punctuation">,</span> b<span class="token punctuation">)</span></code> and wonder why nothing is working the way I expect.</p>
<p>That brings us (finally) to the question of the article: <strong>how can we make it clear</strong> which parameters are which in these functions?</p>
<p>An obvious way to do this, given language support, is to make use of <strong>named parameters</strong> when calling the function:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>v<span class="token operator">=</span>value<span class="token punctuation">,</span> min<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span> max<span class="token operator">=</span><span class="token number">5</span><span class="token punctuation">)</span></code></pre>
<p>but not all languages (looking at <em>you</em>, C++) support named parameters, so what then?</p>
<h3>Grouping the Range Values</h3>
<p>What if instead of the above, calls looked more like this:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token function">Clamp</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token punctuation">{</span>b<span class="token punctuation">,</span> c<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">Lerp</span><span class="token punctuation">(</span><span class="token punctuation">{</span>a<span class="token punctuation">,</span> b<span class="token punctuation">}</span><span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>With this added structure, it’s clear <em>even without reasonable variable names</em> which part is the <strong>range</strong> and which is the <strong>value</strong>, and it’s <em>much</em> more difficult to accidentally call them with the parameters in the wrong order, since, for instance, <code class="inline-code language-cpp"><span class="token function">Lerp</span><span class="token punctuation">(</span>t<span class="token punctuation">,</span> <span class="token punctuation">{</span>a<span class="token punctuation">,</span> b<span class="token punctuation">}</span><span class="token punctuation">)</span></code> wouldn’t even compile.</p>
<p>To do this, we need a simple range structure. in C++ it could look something like this:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">struct</span> <span class="token class-name">ValueRange</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Use a constructor to ensure both endpoints are required.</span>
  <span class="token function">ValueRange</span><span class="token punctuation">(</span>T a_<span class="token punctuation">,</span> T b_<span class="token punctuation">)</span>
    <span class="token operator">:</span> a<span class="token punctuation">{</span>a_<span class="token punctuation">}</span><span class="token punctuation">,</span> b<span class="token punctuation">{</span>b_<span class="token punctuation">}</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  T a<span class="token punctuation">;</span>
  T b<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Using this range, then, you could define <code>Clamp</code><span class="attrs"></span> and <code>Lerp</code><span class="attrs"></span> as follows:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
T <span class="token function">Clamp</span><span class="token punctuation">(</span>T v<span class="token punctuation">,</span> ValueRange<span class="token operator">&lt;</span>T<span class="token operator">></span> range<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">Max</span><span class="token punctuation">(</span>range<span class="token punctuation">.</span>a<span class="token punctuation">,</span> <span class="token function">Min</span><span class="token punctuation">(</span>range<span class="token punctuation">.</span>b<span class="token punctuation">,</span> v<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span>

<span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
T <span class="token function">Lerp</span><span class="token punctuation">(</span>ValueRange<span class="token operator">&lt;</span>T<span class="token operator">></span> range<span class="token punctuation">,</span> T t<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">return</span> range<span class="token punctuation">.</span>b <span class="token operator">*</span> t <span class="token operator">+</span> range<span class="token punctuation">.</span>a <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">-</span> t<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now instead of <em>three</em> parameters, they take <em>two</em>, which matches how they work conceptually: with a <strong>range</strong> and a <strong>value</strong> in some order.</p>
<p>Once you start grouping your input parameters , you may start seeing other places to do it, like <code class="inline-code language-cpp"><span class="token generic-function"><span class="token function">IsInRange</span><span class="token generic class-name"><span class="token operator">&lt;</span>Inclusive<span class="token operator">></span></span></span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">}</span><span class="token punctuation">)</span></code>.</p>
<h3>Min and Max</h3>
<p>Back to the Min/Max problem. <a href="https://peoplemaking.games/@TomF@mastodon.gamedev.place/113802749875252216" target="_blank" rel="noopener">As stated perfectly by Tom Forsyth</a>:</p>
<blockquote>
<p>“Almost every time I use [min or max], I think very carefully and then pick the wrong one.”</p>
</blockquote>
<p>This tends to happen because you think <em>“I need to make sure the max value of <code>x</code><span class="attrs"></span> is 10”</em> and it just feels <em>right</em> to turn that into <code class="inline-code language-cpp"><span class="token function">Max</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span></code>…<strong>that’s how it gets you</strong>. Or, well, me. That’s how it gets <em>me</em>. Basically every time I have to do this, I will choose the wrong one on the first try, have my code explode on me, and then go back and headdesk at it until it turns into the correct one.</p>
<p>Basically. Every. Time.</p>
<h3>Reframing The Problem</h3>
<p>But what if you looked at it a different way? What if you instead thought of it as <em>“I want to clamp <code>x</code><span class="attrs"></span> so that it’s no larger than 10”</em>? You want something that just clamps one end of it, in a clear way. What if you could declare a <strong>one-sided clamp</strong>:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Using "Open" to declare a side of the range is open. Same as:</span>
<span class="token comment">//  x = Min(x, 10);</span>
x <span class="token operator">=</span> <span class="token function">Clamp</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token punctuation">{</span>Open<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Another alternative, ensure that y is no smaller than 2, same as: </span>
<span class="token comment">//  y = Max(y, 2);</span>
y <span class="token operator">=</span> <span class="token function">Clamp</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token number">2</span><span class="token punctuation">,</span> Open<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>To do this efficiently, we’ll have multiple overloads of <code>Clamp</code><span class="attrs"></span>, and define two additional “range” structures, each of which takes an <code>OpenEnded_t</code><span class="attrs"></span>:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Declare this as a nice, type-safe enum class</span>
<span class="token keyword">enum</span> <span class="token keyword">class</span> <span class="token class-name">OpenEnded_t</span>
<span class="token punctuation">{</span>
  Open<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// But make "Open" easy to reach using C++20's "using enum" feature.</span>
<span class="token keyword">using</span> <span class="token keyword">enum</span> <span class="token class-name">OpenEnded_t</span><span class="token punctuation">;</span>

<span class="token comment">// This is a "range" where only the "a" value is specified, the "b" end is open</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">struct</span> <span class="token class-name">ValueRangeOpenB</span>
<span class="token punctuation">{</span>
  <span class="token function">ValueRangeOpenB</span><span class="token punctuation">(</span>T a_<span class="token punctuation">,</span> OpenEnded_t<span class="token punctuation">)</span>
    <span class="token operator">:</span> <span class="token function">a</span><span class="token punctuation">(</span>a_<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>
  T a<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// Like the above, but it's the "b" end that's specified while "a" is open</span>
<span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
<span class="token keyword">struct</span> <span class="token class-name">ValueRangeOpenA</span>
<span class="token punctuation">{</span>
  <span class="token function">ValueRangeOpenA</span><span class="token punctuation">(</span>OpenEnded_t<span class="token punctuation">,</span> T b_<span class="token punctuation">)</span>
    <span class="token operator">:</span> <span class="token function">b</span><span class="token punctuation">(</span>b_<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span>
  T b<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<blockquote class="note">
          <div class="header"><span>Note</span></div>
<p>You can, of course, call <code>Open</code><span class="attrs"></span> whatever you’d prefer: I considered many options (including <code>Unbounded</code><span class="attrs"></span>, <code>Infinite</code><span class="attrs"></span>, <code>OpenEnded</code><span class="attrs"></span>, and <code>None</code><span class="attrs"></span>), but <code>Open</code><span class="attrs"></span> was short and, to my mind, clear.</p>
<p>If you have a global <code>Open</code><span class="attrs"></span> function (or you’re in a class that has a function named <code>Open</code><span class="attrs"></span>), this likely won’t work. You could add a second enum value called <code>OpenEnded</code><span class="attrs"></span> that could be used interchangeably with <code>Open</code><span class="attrs"></span> for that case, or just specify it fully qualified, or just pick a less-inconvenient name.
</p></blockquote><p></p>
<p>Once you have these structures, you can define two additional overloads of <code>Clamp</code><span class="attrs"></span>:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
T <span class="token function">Clamp</span><span class="token punctuation">(</span>T v<span class="token punctuation">,</span> ValueRangeOpenB<span class="token operator">&lt;</span>T<span class="token operator">></span> range<span class="token punctuation">)</span>
  <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Max</span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> range<span class="token punctuation">.</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>


<span class="token keyword">template</span> <span class="token operator">&lt;</span><span class="token keyword">typename</span> <span class="token class-name">T</span><span class="token operator">></span>
T <span class="token function">Clamp</span><span class="token punctuation">(</span>T v<span class="token punctuation">,</span> ValueRangeOpenA<span class="token operator">&lt;</span>T<span class="token operator">></span> range<span class="token punctuation">)</span>
  <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Min</span><span class="token punctuation">(</span>v<span class="token punctuation">,</span> range<span class="token punctuation">.</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre>
<p>These just turn into the correct call to <code>Min</code><span class="attrs"></span> or <code>Max</code><span class="attrs"></span>, but now you can think of it in terms of <strong>limiting one side of its range or the other</strong>, rather than trying to A Beautiful Mind your way into picking the correct function right off the bat.</p>
<p>Now, finally, you can limit a value in multiple ways using the same concept, which can make it easier to reason about when you’re writing the code, and also easier to understand when you’re reading it a month later.</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Keep within a range:</span>
x <span class="token operator">=</span> <span class="token function">Clamp</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Limit the lower bound:</span>
y <span class="token operator">=</span> <span class="token function">Clamp</span><span class="token punctuation">(</span>y<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token number">1</span><span class="token punctuation">,</span> Open<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Limit the upper bound:</span>
z <span class="token operator">=</span> <span class="token function">Clamp</span><span class="token punctuation">(</span>z<span class="token punctuation">,</span> <span class="token punctuation">{</span>Open<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3>Final Thoughts</h3>
<p>These are ideas I first proposed at my job, and got <em>immediate</em> buy-in from the dev team, because we <em>all</em> kept making the same kinds of mistakes with these functions. There are, of course, times when <code>Min</code><span class="attrs"></span> and <code>Max</code><span class="attrs"></span> are still the appropriate function to use (like when you’re thinking “I need the minimum of these values”) - but when you’re trying to limit the range of something, <code>Clamp</code><span class="attrs"></span> is a clearer declaration of intent.</p>
<p>There are ways to improve these functions:</p>
<ul>
<li>In C++ I <em>highly recommend</em> making all of this <code>constexpr</code><span class="attrs"></span> (including the constructors) so that you can use these functions at compile time as well.
<ul>
<li>Depending on your codebase it may be desirable to additionally mark them <code>[[nodiscard]]</code><span class="attrs"></span> and <code>noexcept</code><span class="attrs"></span>.</li>
<li>Also, restricting the template types using <a href="https://en.cppreference.com/w/cpp/language/constraints" target="_blank" rel="noopener">C++20 concepts</a> can help give you better error messages if you try to compile it with something that it can’t work with.</li>
</ul>
</li>
<li>The full-range <code>Clamp</code><span class="attrs"></span> function may want to have some validation that <code>b &gt;= a</code><span class="attrs"></span> (perhaps an <code>assert</code><span class="attrs"></span>).</li>
</ul>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Parser Stuff: Strings]]></title>
      <link>https://www.drilian.com/posts/2025.01.03-parser-stuff-strings/</link>
      <pubDate>Fri, 03 Jan 2025 13:38:29 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2025.01.03-parser-stuff-strings/</guid>
      <content:encoded>
        <![CDATA[
          <p>A while back I was working on a programming language idea and while I haven’t made any progress on it in ages, I really liked the string design that I came up with. I don’t know that any of the ideas are original, but I haven’t seen anything exactly like it so I figure I’d throw the idea out into the ether in case anyone else happens to do something similar in their own personal language that they definitely shouldn’t be making 😆</p>
<p>(<strong>I’ll note upfront</strong>: this design uses dollar signs (<code>$</code><span class="attrs"></span>) and backticks(<code>`</code><span class="attrs"></span>). There are many languages that do so (like Javascript!) but these keys are not universally on all keyboards internationally so for those locales it may be more difficult to type these out…my language design was pretty much just for me with my standard US keyboard so I didn’t take this into account)</p>
<p><span class="read-more"></span></p>
<h3>Basic Strings</h3>
<p>There’s nothing fancy about these, they’re just like most other languages’ basic strings:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token string">"This is a string"</span>
<span class="token string">"This is a string with a newline at the end: \n"</span>
<span class="token string">"Quotes? \"Escape them\""</span>
"Oops forgot to end <span class="token keyword">this</span> one<span class="token punctuation">,</span> it<span class="token number">'</span>s a compiler error</code></pre>
<p>Just a pair of double quotes with everything between being non-quotes (or escaped quotes), contained within a single line.</p>
<h3>Raw Strings</h3>
<p>Raw strings in my language design are kind of a blend between <a href="https://en.cppreference.com/w/cpp/language/string_literal" target="_blank" rel="noopener">C++11-style raw strings</a> and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/raw-string" target="_blank" rel="noopener">C#11 raw strings</a> (the elevens!), using a different delimiting character variation than I’ve seen elsewhere. In this case, the simplest one starts and ends with a pair of backticks:</p>
<pre><code><span class="token string">``This is a single raw string
it is multiple lines long``</span></code></pre>
<p>It can contain any character sequence except the delimiting sequence (again, at simplest a pair of backticks: ``)</p>
<p>But what if your string needs to contain a consecutive pair of backticks? This is where the C++11 raw string inspiration comes in: you can put any string of characters between the ticks (excluding ticks, obviously, or newlines), and then the start and end have to match.</p>
<pre><code><span class="token string">`uniqueString`This is a single string
it contains backticks without terminating: `` ... see?
This is the last line and ends here:`uniqueString`</span></code></pre>
<p>This one starts with <code>`uniqueString`</code><span class="attrs"></span>, and so the only thing that will terminate it is that same sequence: <code>`uniqueString`</code><span class="attrs"></span> (with the tick marks around it).</p>
<p>To add to this, cribbing from C#11 it will:</p>
<ol>
<li>Trim the very first newline if there is one</li>
<li>Also trim the last newline if there is one</li>
<li>Unindent every line of it based on the indentation of the final quote sequence:</li>
</ol>
<pre><code>myString <span class="token operator">=</span> <span class="token string">``
  This is actually the first line of the string, the newline was ignored
  {
    indented further
  }
  ``</span><span class="token operator">;</span> <span class="token comment">// Note that this is indended 2 spaces</span></code></pre>
<p>which turns into the string (note the lack of being completely indented:</p>
<pre><code>This is actually the first line, the newline was ignored
{
  indented further
}
</code></pre>
<p>This makes it easier to generate code (or text files or whatever) that are properly indented, without having to make the indenting of the string in your code all weird.</p>
<h3>Interpolated Strings</h3>
<p>I’m additionally adding string interpolated string support (which is a weird term), using a mix of C# and Javascript’s setup:</p>
<pre><code><span class="token string">$"This string has a ${</span>value<span class="token string">} in it"</span></code></pre>
<p>If a string starts with <code>$</code><span class="attrs"></span>, it’s treated as an interpolated string. A string-convertible expression can be inserted in-place in the string within <code>${}</code><span class="attrs"></span>.</p>
<p>But what if you need to have the character sequence <code>${</code><span class="attrs"></span> in your string? Add more dollar signs to the start, and you need that many dollar signs before a <code>{</code><span class="attrs"></span> to enter the Interpolation Zone:</p>
<pre><code><span class="token string">$$"This string has a $${</span>value<span class="token string">} in it, ${but this isn't one}"</span></code></pre>
<p>Raw strings can also be used as interpolated strings (making for some nice codegen), same rules apply:</p>
<pre><code><span class="token string">    $$``
      Interpolated string with
        multiple lines and a $${</span>value<span class="token string">} in it.
        ${this is not a value because only one $}
      ``</span></code></pre>
<p>If <code>value == 5</code><span class="attrs"></span> this would turn into the following string (upon formatting):</p>
<pre><code>Interpolated string with
  multiple lines and a 5 in it.
  ${this is not a value because only one $}
</code></pre>
<h3>I Just Think They’re Neat</h3>
<p>Anyway, I think this is a really nice combination of properties that make it easy to format strings nicely without being overly-complicated to actually use (unlike C++'s raw strings, which I have to look up literally every time I need to use one). Need a hardcoded regex or path with backslashes? In most cases, just use a raw string with <code>``</code><span class="attrs"></span> on either end:</p>
<pre><code><span class="token string">``C:\Path\With\Single\Backslashes``</span></code></pre>
<p>or</p>
<pre><code><span class="token string">``^[\r\n \t]*Hi[\r\n \t]*$``</span></code></pre>
<p>Hope this was at least mildly interesting to someone!</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Emulating the FMAdd Instruction, Part 2: 64-bit Floats]]></title>
      <link>https://www.drilian.com/posts/2025.01.02-emulating-the-fmadd-instruction-part-2-64-bit-floats/</link>
      <pubDate>Thu, 02 Jan 2025 02:22:15 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2025.01.02-emulating-the-fmadd-instruction-part-2-64-bit-floats/</guid>
      <content:encoded>
        <![CDATA[
          <p>(This post follows <a href="https://www.drilian.com/posts/2024.12.31-emulating-the-fmadd-instruction-part-1-32-bit-floats/">Part 1: 32-bit floats</a> and will make very little sense without having read that one first. Honestly, it might make little sense <em>having</em> read that one first, I dunno!)</p>
<p>Last time we went over how to calculate the results of the FMAdd instruction (a fused-multiply-add calculated as if it had infinite internal precision) for 32-bit <strong>single</strong>-precision float values:</p>
<ul>
<li>Calculate the double-precision product of <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span></li>
<li>Add this product to <code>c</code><span class="attrs"></span> to get a double-precision sum</li>
<li>Calculate the error of the sum</li>
<li>Use the error to odd-round the sum</li>
<li>Round the double-precision sum back down to single precision</li>
</ul>
<p>This requires casting up to a 64-bit <strong>double</strong>-precision float to get extra bits of precision. But what if you can’t do that? What if you’re using doubles? You can’t just (in most cases) cast up to a <em>quad</em>-precision float. So what do you do?</p>
<h3>Double-Precision FMAdd</h3>
<p>(Like the 32-bit version, this is based on <a href="https://www.lri.fr/~melquion/doc/08-tc.pdf" target="_blank" rel="noopener">Emulation of FMA and correctly-rounded sums: proved algorithms using rounding to odd</a> by Sylvie Boldo and Guillaume Melquiond, there are additional details in there)</p>
<p>To do this natively as doubles, we need to invent a new operation: <code>MulWithError</code><span class="attrs"></span>. This is the multiplication equivalent of the <code>AddWithError</code><span class="attrs"></span> function from the 32-bit solution:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token punctuation">(</span><span class="token keyword">double</span> prod<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span><span class="token keyword">double</span> x<span class="token punctuation">,</span> <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> prod <span class="token operator">=</span> x <span class="token operator">*</span> y<span class="token punctuation">;</span>
  <span class="token keyword">double</span> err <span class="token operator">=</span> <span class="token comment">// ??? how do we do this</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>prod<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We’ll get to how to implement that in a moment, but first we’ll walk through how to use that function to calculate a proper FMAdd.</p>
<p><span class="read-more"></span></p>
<p>We need to do the following:</p>
<ul>
<li>Calculate the product of <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span> and the error of that product</li>
<li>Calculate the sum of that product and <code>c</code><span class="attrs"></span> (giving us <code>a * b + c</code><span class="attrs"></span>) and the error of this sum
<ul>
<li>We’re not using the error of the product … yet</li>
</ul>
</li>
<li>Add the two error terms (product error and sum error) together, rounding the result to odd</li>
<li>Add this summed error term to our actual result, which will round normally.</li>
</ul>
<p>In code, that looks like this:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Start with an "OddRoundedToAdd" helper since we do</span>
<span class="token comment">//  this operation frequently</span>
<span class="token keyword">double</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span><span class="token keyword">double</span> x<span class="token punctuation">,</span> <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> sum<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddwithError</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">double</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">double</span> a<span class="token punctuation">,</span> <span class="token keyword">double</span> b<span class="token punctuation">,</span> <span class="token keyword">double</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> ab<span class="token punctuation">,</span> <span class="token keyword">double</span> abErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> abc<span class="token punctuation">,</span> <span class="token keyword">double</span> abcErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>ab<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Odd-round the sum of the two errors before </span>
  <span class="token comment">//  adding it in to the final result.</span>
  <span class="token keyword">double</span> err <span class="token operator">=</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span>abErr<span class="token punctuation">,</span> abcErr<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> abc <span class="token operator">+</span> err<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>By keeping the error terms from both the product and the sum, we have <strong>kept all of the exact result</strong>. That is, we can assemble the mathematically-exact result given enough precision by doing <code>abc + abErr + abcErr</code><span class="attrs"></span>.</p>
<p>But we can’t do infinite-precision addition of three values. However, we <em>can</em> odd-round an intermediate result, the same way we did with the single-precision case.</p>
<p>In this case, we know that <code>abErr</code><span class="attrs"></span> and <code>abcErr</code><span class="attrs"></span> both (necessarily) have much lower magnitudes than the final result, as each error value’s highest bit is lower than the lowest bit of the mantissa of their respective operations. So, if we odd-round the sum of these two values, it actually effectively fulfills the condition of <strong>having more bits of precision than the final result</strong>. Thus, if we add the error terms together with odd rounding, the odd-rounded fake-sticky final digit will be taken into account by the <em>actual</em> sticky bit used when doing the final sum of the result and error terms.</p>
<p>I hope that makes sense?</p>
<h3>Breaking Down Multiplication With Error</h3>
<p>(Like <code>AddWithError</code><span class="attrs"></span>, this is based on <a href="https://ir.cwi.nl/pub/9159/9159D.pdf" target="_blank" rel="noopener">A Floating-Point Technique For Extending the Available Precision</a> by T.J. Dekker)</p>
<p>So how do we calculate the error term of a 64-bit multiply? We can’t use 128-bit values, but what we <em>can</em> do is break each 64-bit value up into two values, each with less bits of precision.</p>
<p>We’ll break <code>x</code><span class="attrs"></span> and <code>y</code><span class="attrs"></span> (our two multiplicands) up into high and low values, where:</p>
<pre class="language-cpp"><code class="language-cpp">x <span class="token operator">=</span> xh <span class="token operator">+</span> xl<span class="token punctuation">;</span>
y <span class="token operator">=</span> yh <span class="token operator">+</span> yl<span class="token punctuation">;</span></code></pre>
<p>We do this by breaking the double’s mantissa up:</p>
<ul>
<li><code>xh</code><span class="attrs"></span> contains the top 25 bits of <code>x</code><span class="attrs"></span>’s mantissa (plus its implied 1, giving it 26 bits of precision).</li>
<li><code>xl</code><span class="attrs"></span> contains the bottom 27 bits of <code>x</code><span class="attrs"></span>’s mantissa. The highest-set ‘1’ in this mantissa will become the implied <code>1</code><span class="attrs"></span> bit that’s part of the standard floating-point format, so this value will have 27 bits of precision, max.</li>
<li><code>yh</code><span class="attrs"></span> and <code>yl</code><span class="attrs"></span> are the same, but for <code>y</code><span class="attrs"></span>.</li>
</ul>
<pre class="language-cpp"><code class="language-cpp"><span class="token punctuation">(</span><span class="token keyword">double</span> h<span class="token punctuation">,</span> <span class="token keyword">double</span> l<span class="token punctuation">)</span> <span class="token function">Split</span><span class="token punctuation">(</span><span class="token keyword">double</span> v<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// In C++ this Zero function can be implemented by  masking off</span>
  <span class="token comment">//  the bottom 27 bits by casting to a 64-bit int:</span>
  <span class="token comment">// constexpr uint64_t Mask = ~0x07ff'ffff;</span>
  <span class="token comment">// double h = std::bit_cast&lt;double>(</span>
  <span class="token comment">//              std::bit_cast&lt;uint64_t>(v) &amp; Mask);</span>
  <span class="token keyword">double</span> h <span class="token operator">=</span> <span class="token function">ZeroBottom27BitsOfMantissa</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// We can get the lower bits of the mantissa (correctly normalized and</span>
  <span class="token comment">//  with correct signs) by subtracting the extracted upper bits from </span>
  <span class="token comment">//  the original value.</span>
  <span class="token keyword">double</span> l <span class="token operator">=</span> v <span class="token operator">-</span> h<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>h<span class="token punctuation">,</span> l<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>What does this split give us? Well, we can now break the multiplication up into a sum of multiplies that now each have enough bits of precision to be exactly representable (<code>27BitValueA * 27BitValueB == 54BitValue</code><span class="attrs"></span>, which fits perfectly in a double (with the implied <code>1</code><span class="attrs"></span> bit), using our old friend from Algebra, <a href="https://en.wikipedia.org/wiki/FOIL_method" target="_blank" rel="noopener">FOIL</a>:</p>
<pre class="language-cpp"><code class="language-cpp">    x <span class="token operator">*</span> y 
<span class="token operator">=</span> <span class="token punctuation">(</span>xh <span class="token operator">+</span> xl<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>yh <span class="token operator">+</span> yl<span class="token punctuation">)</span> 
<span class="token operator">=</span> xh<span class="token operator">*</span>yh <span class="token operator">+</span> xh<span class="token operator">*</span>yl <span class="token operator">+</span> xl<span class="token operator">*</span>yh <span class="token operator">+</span> xl<span class="token operator">*</span>yl<span class="token punctuation">;</span></code></pre>
<p>We can’t actually do those adds directly, but what we can do is similar to how we did <code>AddWithError</code><span class="attrs"></span>: use a sequence of precision-preserving operations to calculate the difference between that idealized result and our rounded product:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token punctuation">(</span><span class="token keyword">double</span> prod<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span><span class="token keyword">double</span> x<span class="token punctuation">,</span> <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> prod <span class="token operator">=</span> x <span class="token operator">*</span> y<span class="token punctuation">;</span>

  <span class="token punctuation">(</span>xh<span class="token punctuation">,</span> xl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span>yh<span class="token punctuation">,</span> yl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Parentheses to demonstrate the precise order these </span>
  <span class="token comment">//  operations must occur in</span>
  <span class="token keyword">double</span> err <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>xh<span class="token operator">*</span>yh <span class="token operator">-</span> prod<span class="token punctuation">)</span> <span class="token operator">+</span> xh<span class="token operator">*</span>yl<span class="token punctuation">)</span> <span class="token operator">+</span> xl<span class="token operator">*</span>yh<span class="token punctuation">)</span> <span class="token operator">+</span> xl<span class="token operator">*</span>yl<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>prod<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It works like this:</p>
<ul>
<li>Calculate the (rounded) product of <code>x</code><span class="attrs"></span> and <code>y</code><span class="attrs"></span></li>
<li>Subtract that rounded product from the product of <code>xh</code><span class="attrs"></span> and <code>yh</code><span class="attrs"></span>
<ul>
<li>These should have roughly the same magnitude (and definitely the same sign) so this is a precision-preserving subtraction.</li>
<li>Since <code>|xh * yh| &lt;= rounded(|x * y|)</code><span class="attrs"></span> (because <code>xh</code><span class="attrs"></span> and <code>yh</code><span class="attrs"></span> are truncated versions of <code>x</code><span class="attrs"></span> and <code>y</code><span class="attrs"></span> and thus have lower magnitudes) this is a <code>smaller - larger</code><span class="attrs"></span> operation and we’ll get a result with a sign opposite that of the final product.</li>
</ul>
</li>
<li>Keep adding in next-lower-magnitudes of values, which will continue to preserve precision
<ul>
<li>(because we have a value that is opposite-sign these are effectively subtractions, in the same way that <code>a + -b</code><span class="attrs"></span> is)</li>
<li>It’s also worth noting here that <code>xh*yl</code><span class="attrs"></span> and <code>xl*yh</code><span class="attrs"></span> will have equivalent magnitudes so the order that you add them in doesn’t matter, as long as they’re both after <code>xh*yh</code><span class="attrs"></span> and before <code>xl*yl</code><span class="attrs"></span></li>
</ul>
</li>
</ul>
<p>Once you’ve done that, you have the computed product as well as the error term, and we can then follow our FMAdd algorithm above to calculate the FMAdd.</p>
<p>So, that’s it, we’re done, right?</p>
<h3>Edge Cases</h3>
<p>Nope! Well, yes if you just wanted the gist, but now it’s time to get into all those annoying implementation details that the papers this is based on completely glossed over. Here’s where it gets ugly (unless you thought it was already ugly, in which case, sorry, it’s about to get <em>worse</em> somehow).</p>
<p>In our single-precision case, <strong>we didn’t have to worry about exponent overflow or underflow</strong> because we were using double-precision intermediates, which not only have additional mantissa range, but also additional <em>exponent</em> range.</p>
<p>It’s possible that the product of <code>a * b</code><span class="attrs"></span> (an intermediate value in our calculation) goes <strong>out of range</strong> of what a double can represent, but that the addition of <code>c</code><span class="attrs"></span> might bring the final result back into range (which can happen when the sign of <code>c</code><span class="attrs"></span> is opposite the sign of <code>a * b</code><span class="attrs"></span>). This causes a different set of errors on either end:</p>
<ul>
<li>If <code>a*b</code><span class="attrs"></span> is too large to be represented, it turns into <em>infinity</em> which means that adding <code>c</code><span class="attrs"></span> in will just leave it as infinity even though the final result should have been a representable value (albeit one with a very large magnitude)</li>
<li>If <code>a*b</code><span class="attrs"></span> is too small to be represented, it will go <a href="https://en.wikipedia.org/wiki/Subnormal_number" target="_blank" rel="noopener">subnormal</a> which means bits of the intermediate result will slide off the bottom of the mantissa and we lose bits of information, which can causes us to round incorrectly at our final result.</li>
</ul>
<p>To solve this, we’ll introduced a bias into the calculation, for when the value goes very small or very large:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">double</span> <span class="token function">CalculateFMAddBias</span><span class="token punctuation">(</span><span class="token keyword">double</span> a<span class="token punctuation">,</span> <span class="token keyword">double</span> b<span class="token punctuation">,</span> <span class="token keyword">double</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Calculate what our final result would be if we just did it normally</span>
  <span class="token keyword">double</span> testResult <span class="token operator">=</span> <span class="token function">Abs</span><span class="token punctuation">(</span>a <span class="token operator">*</span> b <span class="token operator">+</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>testResult <span class="token operator">&lt;</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">500</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">Max</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token number">800</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Our result is very small and our maximum value is not so large </span>
    <span class="token comment">//  that we'll blow up with a bias, so bias our values up to</span>
    <span class="token comment">//  ensure we don't go subnormal in our intermediate result</span>
    <span class="token keyword">return</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token number">110</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfinite</span><span class="token punctuation">(</span>testResult<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// We hit infinity, but that might be due to exponent overflow,</span>
    <span class="token comment">//  so bias everything down (this may cause c to go subnormal, </span>
    <span class="token comment">//  but if that's the case then a*b on its own is infinity and</span>
    <span class="token comment">//  so it won't affect the final result in any way)</span>
    <span class="token keyword">return</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">55</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">else</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// No bias needed</span>
    <span class="token keyword">return</span> <span class="token number">1.0</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>For any results that aren’t extreme, the bias will remain <code>1.0</code><span class="attrs"></span> but, for values at the extremes, we’ll scale our intermediates down (using powers of 2 which only affect the exponent and not the mantissa) into a range such that we can’t temporarily poke outside of range. Also note that my choices of powers of 2 are not perfectly chosen, I didn’t bother trying to figure out the exact right biases/thresholds so I just picked ones that I knew were good enough.</p>
<p>So then we do our FMAdd calculation as before, but with the bias introduced (and then backed out at the end):</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Do our multiplication with the bias applied to 'a'</span>
<span class="token comment">//  (the choice of applying it to 'a' vs 'b' is completely</span>
<span class="token comment">//  arbitrary)</span>
<span class="token punctuation">(</span><span class="token keyword">double</span> ab<span class="token punctuation">,</span> <span class="token keyword">double</span> abErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span>a <span class="token operator">*</span> bias<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Then the sum with the bias applied to 'c'</span>
<span class="token punctuation">(</span><span class="token keyword">double</span> abc<span class="token punctuation">,</span> <span class="token keyword">double</span> abcErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>ab<span class="token punctuation">,</span> c <span class="token operator">*</span> bias<span class="token punctuation">)</span><span class="token punctuation">;</span>

err <span class="token operator">=</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span>abErr<span class="token punctuation">,</span> abcErr<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Calculate our final result then un-bias the result.</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>abc <span class="token operator">+</span> err<span class="token punctuation">)</span> <span class="token operator">/</span> bias<span class="token punctuation">;</span></code></pre>
<p>Alright, we’ve avoided both overflow and underflow and everything is great, right?</p>
<h3>Two (Point Five) Last Annoying Implementation Details</h3>
<p>Nope, sorry again! It turns out there are still two cases we need to deal with.</p>
<h4>Case 1: Infinity or NaN even with the bias</h4>
<p>If our result (without error applied) hits infinity even with the avoid-infinity bound, then we should just go ahead and return now to avoid Causing Problems Later (that is, turning what should be infinity <em>into</em> a NaN). And if it’s already NaN we can just return now because it’s going to be NaN forever.</p>
<p>Except, there’s one additional necessary check here, for a case caught by <a href="https://bsky.app/profile/dzaima.bsky.social" target="_blank" rel="noopener">dzaima over on Bluesky</a>): in the event <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span> are finite numbers but <code>a * b</code><span class="attrs"></span> blows up to infinity, and then <code>c</code><span class="attrs"></span> is the <em>opposite</em> infinity, the correct return value is whichever infinity (positive or negative) <code>c</code><span class="attrs"></span> is, so in our early-out check has to catch that case as well:</p>
<pre class="language-cpp"><code class="language-cpp">  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>abc<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span> 
    <span class="token comment">// If we got NaN (or Inf, which won't affect the output) and </span>
    <span class="token comment">//  a and b are both finite but c is infinite, return c (without</span>
    <span class="token comment">//  this check, we will incorrectly return NaN instead of -Inf</span>
    <span class="token comment">//  for FMAdd(1e200, 1e200, -Infinity))</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfinite</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> <span class="token keyword">return</span> c<span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token comment">// Otherwise, return whichever Inf or NaN we got directly;</span>
    <span class="token keyword">return</span> abc<span class="token punctuation">;</span>
  <span class="token punctuation">}</span></code></pre>
<h4>Case 2: Subnormal Results</h4>
<p>If our result is subnormal (after the bias is backed out), then it’s going to lose bits of precision as it shifts down (because the exponent can’t go any lower so instead the value itself shifts down the mantissa), which means whoops here’s another rounding step, and the dreaded <strong>double-rounding</strong> returns.</p>
<p>In this case we need to actually odd-round the addition of the error term as well, so that when the bias is backed out and it rounds, it does the correct thing:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token comment">// Multiply the smallest-representable normalized value by our avoid-</span>
<span class="token comment">//  subnormal bias. Any (biased) value below this will go subnormal.</span>
<span class="token comment">//  (In production code it'd be nicer to use something like</span>
<span class="token comment">//  std::numeric_limits instead of hard-coding -1022)</span>
<span class="token keyword">const</span> <span class="token keyword">double</span> SubnormThreshold <span class="token operator">=</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1022</span><span class="token punctuation">)</span> <span class="token operator">*</span> AvoidDenormalBias<span class="token punctuation">;</span>

<span class="token keyword">if</span> <span class="token punctuation">(</span>bias <span class="token operator">==</span> AvoidSubnormalBias <span class="token operator">&amp;&amp;</span> <span class="token function">Abs</span><span class="token punctuation">(</span>abc<span class="token punctuation">)</span> <span class="token operator">&lt;</span> SubnormThreshold<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Odd-round the addition of the error so that the rounding that </span>
  <span class="token comment">//  happens on the divide by the bias is correct.</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> finalSum<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>abc<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  finalSum <span class="token operator">=</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>finalSum<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> finalSum <span class="token operator">/</span> bias<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>And this almost works, except there’s one <em>more</em> annoying case, and that’s where <strong>our result is going subnormal, but only by exactly one bit</strong>. Remember that the odd-rounding trick only works if we have <strong>two or more</strong> bits so that the final rounding works properly, but in this case we’re truncating the mantissa by exactly one bit, so we have to do even more work:</p>
<ul>
<li>Split the value that will be shifting down into a high and low part (same as we did for the multiply)</li>
<li>Add our error term to the low part of it
<ul>
<li>This preserves additional bits of the error term since we gave ourselves more headroom by removing the upper half of its mantissa</li>
</ul>
</li>
<li>Remove the bias from both the high and low parts separately
<ul>
<li>Removing the bias from the high part doesn’t round since we know the lowest bit is 0</li>
<li>Removing the bias from the low part applies the actual final rounding (correctly) since we gave ourselves more bits to work with</li>
</ul>
</li>
<li>Sum the halves back together and return that as our final result
<ul>
<li>This sum is (thankfully) perfectly representable by the final precision and doesn’t introduce any additional error.</li>
</ul>
</li>
</ul>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">const</span> <span class="token keyword">double</span> OneBitSubnormalThreshold <span class="token operator">=</span> 
  OneBitSubnormalThreshold <span class="token operator">*</span> <span class="token number">0.5</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">Abs</span><span class="token punctuation">(</span>finalResult<span class="token punctuation">.</span>result<span class="token punctuation">)</span> <span class="token operator">>=</span> k_oneBitDenormThreshold<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Split into halves</span>
  <span class="token punctuation">(</span>rh<span class="token punctuation">,</span> rl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>finalSum<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Add the error term into the low part of the split</span>
  rl <span class="token operator">=</span> <span class="token function">OddRoundedAddition</span><span class="token punctuation">(</span>rl<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Scale them both down by the bias. Note that </span>
  <span class="token comment">//  the rh division cannot round since the lowest bit</span>
  <span class="token comment">//  is 0</span>
  rh <span class="token operator">/=</span> bias<span class="token punctuation">;</span>

  <span class="token comment">// This division is what actually introduces the final</span>
  <span class="token comment">//  rounding (correctly, since we gave ourselves more</span>
  <span class="token comment">//  bits to work with)</span>
  rl <span class="token operator">/=</span> bias<span class="token punctuation">;</span>

  <span class="token comment">// This sum is perfectly representable by the final</span>
  <span class="token comment">//  precision and will not introduce additional error.</span>
  <span class="token keyword">return</span> rh <span class="token operator">+</span> rl<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>OMG Are We Done Now?</h3>
<p>As far as I’m aware, those are all the implementation details to doing a 64-bit double-precision FMAdd implementation. It’s <em>conceptually</em> not that much more complicated than the 32-bit one, but mechanically it’s worse, plus there are those fun extra edge cases to consider.</p>
<p>Here’s the final code:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token punctuation">(</span><span class="token keyword">double</span> h<span class="token punctuation">,</span> <span class="token keyword">double</span> l<span class="token punctuation">)</span> <span class="token function">Split</span><span class="token punctuation">(</span><span class="token keyword">double</span> v<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> h <span class="token operator">=</span> <span class="token function">ZeroBottom27BitsOfMantissa</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">double</span> l <span class="token operator">=</span> v <span class="token operator">-</span> h<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>h<span class="token punctuation">,</span> l<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token punctuation">(</span><span class="token keyword">double</span> prod<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span>
  <span class="token keyword">double</span> x<span class="token punctuation">,</span> 
  <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> prod <span class="token operator">=</span> x <span class="token operator">*</span> y<span class="token punctuation">;</span>

  <span class="token punctuation">(</span>xh<span class="token punctuation">,</span> xl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span>yh<span class="token punctuation">,</span> yl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">double</span> err <span class="token operator">=</span> 
    <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span>xh<span class="token operator">*</span>yh <span class="token operator">-</span> prod<span class="token punctuation">)</span> <span class="token operator">+</span> xh<span class="token operator">*</span>yl<span class="token punctuation">)</span> <span class="token operator">+</span> xl<span class="token operator">*</span>yh<span class="token punctuation">)</span> <span class="token operator">+</span> xl<span class="token operator">*</span>yl<span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>prod<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">double</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span><span class="token keyword">double</span> x<span class="token punctuation">,</span> <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> sum<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddwithError</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">double</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">double</span> a<span class="token punctuation">,</span> <span class="token keyword">double</span> b<span class="token punctuation">,</span> <span class="token keyword">double</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token keyword">double</span> AvoidSubnormalBias <span class="token operator">=</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token number">110</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">double</span> bias <span class="token operator">=</span> <span class="token number">1.0</span><span class="token punctuation">;</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Calculate our final result as if done normally</span>
    <span class="token keyword">double</span> testResult <span class="token operator">=</span> <span class="token function">Abs</span><span class="token punctuation">(</span>a <span class="token operator">*</span> b <span class="token operator">+</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Bias if the result goes too low or too high</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>testResult <span class="token operator">&lt;</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">500</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token function">Max</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c<span class="token punctuation">)</span> <span class="token operator">&lt;</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token number">800</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> bias <span class="token operator">=</span> AvoidSubnormalBias<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// too low</span>
    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfinite</span><span class="token punctuation">(</span>testResult<span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> bias <span class="token operator">=</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">55</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// too high</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Calculate using our bias</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> ab<span class="token punctuation">,</span> <span class="token keyword">double</span> abErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">MulWithError</span><span class="token punctuation">(</span>a <span class="token operator">*</span> bias<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> abc<span class="token punctuation">,</span> <span class="token keyword">double</span> abcErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>ab<span class="token punctuation">,</span> c <span class="token operator">*</span> bias<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Check for infinity or NaN and return early</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>abc<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span> 
    <span class="token comment">// Handle the case of "a multiply of two finite values hit infinity</span>
    <span class="token comment">//  even *with* the bias, but c is the opposite infinity" case and</span>
    <span class="token comment">//  return the correct result  of "c"</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IsInfinite</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span><span class="token function">IsInfiniteOrNaN</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> <span class="token keyword">return</span> c<span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token comment">// Otherwise just return the inf or nan directly</span>
    <span class="token keyword">return</span> abc<span class="token punctuation">;</span> 
  <span class="token punctuation">}</span>

  <span class="token comment">// Odd-round the intermediate error resultt</span>
  <span class="token keyword">double</span> err <span class="token operator">=</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span>abErr<span class="token punctuation">,</span> abcErr<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Multiply the smallest-representable normalized value by our avoid-</span>
  <span class="token comment">//  subnormal bias. Any (biased) value below this will go subnormal</span>
  <span class="token keyword">const</span> <span class="token keyword">double</span> SubnormThreshold <span class="token operator">=</span> <span class="token function">Pow2</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1022</span><span class="token punctuation">)</span> <span class="token operator">*</span> AvoidSubnormalBias<span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>bias <span class="token operator">==</span> AvoidSubnormalBias <span class="token operator">&amp;&amp;</span> <span class="token function">Abs</span><span class="token punctuation">(</span>abc<span class="token punctuation">)</span> <span class="token operator">&lt;</span> SubnormThreshold<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token punctuation">(</span><span class="token keyword">double</span> finalSum<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>abc<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// This is half of SubnormThreshold. Any value between SubnormThresold</span>
    <span class="token comment">//  and this value will only lose a single bit of precision when</span>
    <span class="token comment">//  the bias is removed, which requires some extra care</span>
    <span class="token keyword">const</span> <span class="token keyword">double</span> OneBitSubnormalThreshold <span class="token operator">=</span> 
      OneBitSubnormalThreshold <span class="token operator">*</span> <span class="token number">0.5</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">Abs</span><span class="token punctuation">(</span>finalSum<span class="token punctuation">)</span> <span class="token operator">>=</span> OneBitSubnormalThreshold<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Split into halves</span>
      <span class="token punctuation">(</span>rh<span class="token punctuation">,</span> rl<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Split</span><span class="token punctuation">(</span>finalSum<span class="token punctuation">)</span><span class="token punctuation">;</span>

      <span class="token comment">// Add the error term into the LOW part of our split value</span>
      rl <span class="token operator">=</span> <span class="token function">OddRoundedAdd</span><span class="token punctuation">(</span>rl<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span><span class="token punctuation">;</span>

      <span class="token comment">// Divide out the bias from both halves (which will cause rl to</span>
      <span class="token comment">//  round to its final, correctly-rounded value) then sum them </span>
      <span class="token comment">//  together (which is perfectly representable).</span>
      rh <span class="token operator">/=</span> bias<span class="token punctuation">;</span>
      rl <span class="token operator">/=</span> bias<span class="token punctuation">;</span>
      <span class="token keyword">return</span> rh <span class="token operator">+</span> rl<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">else</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// For more-than-one-bit subnormals, we do an odd-rounded addition of</span>
      <span class="token comment">//  the error term and then divide out the bias, doing full rounding</span>
      <span class="token comment">//  just once.</span>
      finalSum <span class="token operator">=</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>finalSum<span class="token punctuation">,</span> finalSumErr<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> finalSum <span class="token operator">/</span> bias<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">else</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Not subnormal, so we can calculate our final result normally and un-</span>
    <span class="token comment">//  bias the result.</span>
    <span class="token keyword">return</span> <span class="token punctuation">(</span>abc <span class="token operator">+</span> err<span class="token punctuation">)</span> <span class="token operator">/</span> bias<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Compare that to the 32-bit version and you can see why this one got its own post:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">float</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">float</span> a<span class="token punctuation">,</span> <span class="token keyword">float</span> b<span class="token punctuation">,</span> <span class="token keyword">float</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> product <span class="token operator">=</span> <span class="token keyword">double</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token keyword">double</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> sum<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>product<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>
  sum <span class="token operator">=</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token keyword">float</span><span class="token punctuation">(</span>sum<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Hopefully you never have to <em>actually</em> implement this yourself, but if you do? I hope this helps.</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Emulating the FMAdd Instruction, Part 1: 32-bit Floats]]></title>
      <link>https://www.drilian.com/posts/2024.12.31-emulating-the-fmadd-instruction-part-1-32-bit-floats/</link>
      <pubDate>Tue, 31 Dec 2024 21:51:24 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2024.12.31-emulating-the-fmadd-instruction-part-1-32-bit-floats/</guid>
      <content:encoded>
        <![CDATA[
          <p>A thing that I had to do at work is write an emulation of the FMAdd (fused multiply-add) instruction for hardware where it wasn’t natively supported (specifically I was writing a SIMD implementation, but the idea is the same), and so I thought I’d share a little bit about how FMAdd works, since I’ve already been posting about <a href="https://www.drilian.com/posts/2023.01.10-floating-point-numbers-and-rounding/">how float rounding works</a>.</p>
<p>So, screw it, here we go with another unnecessarily technical, mathy post!</p>
<h3>What is the FMAdd Instruction?</h3>
<p>A <strong>fused multiply-add</strong> is basically doing a multiply and an add as a single operation, and it gives you the result as if it were computed with infinite precision and then rounded down at the final result. FMAdd computes <code>(a * b) + c</code><span class="attrs"></span> without intermediate floating-point error being introduced:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">float</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">float</span> a<span class="token punctuation">,</span> <span class="token keyword">float</span> b<span class="token punctuation">,</span> <span class="token keyword">float</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// ??? Somehow do this with no intermediate rounding</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>a <span class="token operator">*</span> b<span class="token punctuation">)</span> <span class="token operator">+</span> c<span class="token punctuation">;</span> 
<span class="token punctuation">}</span></code></pre>
<p>Computing it normally (using the code above) for some values will get you <strong>double rounding</strong> (explained in a moment) which means you might be an extra bit off (or, more formally, <a href="https://en.wikipedia.org/wiki/Unit_in_the_last_place" target="_blank" rel="noopener">one ULP</a>) from where your actual result should be. An extra bit doesn’t <em>sound</em> like a lot, but it can add up over many operations.</p>
<p>Fused multiply-add avoids this extra rounding, making it more accurate than a multiply followed by a separate add, which is great! (It can also be faster if it’s supported by hardware but, as you’ll see, computing it without a dedicated instruction on the CPU is actually surprisingly spendy, especially once you get into doing it for 64-bit floats, but sometimes you need precision instead of performance).</p>
<p><span class="read-more"></span></p>
<h3>Double Rounding</h3>
<p>Double rounding happens when the intermediate value rounds down (or up), then the final result <em>also</em> rounds in the same direction - but because of the first rounding, actually overshoots the correctly-rounded final value by a bit.</p>
<p>Here’s an example using two successive sums of some 4-bit float values. We’ll do the following sum (in top-down order):</p>
<pre class="language-cpp"><code class="language-cpp">  <span class="token number">1.000</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">+</span> <span class="token number">1.001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">0</span>
<span class="token operator">+</span> <span class="token number">1.100</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">0</span></code></pre>
<p>The first sum, done with “infinite” internal precision, looks like this:</p>
<pre class="language-cpp"><code class="language-cpp">  <span class="token number">1.000</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">+</span> <span class="token number">1.001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">0</span>
  <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span>
  <span class="token number">1.000</span> <span class="token number">0000</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">+</span> <span class="token number">0.000</span> <span class="token number">1001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
  <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
  <span class="token number">1.000</span> <span class="token number">1001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span></code></pre>
<p>If we were to then use that result directly (with no intermediate rounding) and do the second sum, only rounding the final result:</p>
<pre class="language-cpp"><code class="language-cpp">   <span class="token number">1.000</span> <span class="token number">1001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
 <span class="token operator">+</span> <span class="token number">0.000</span> <span class="token number">1100</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
   <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
 <span class="token operator">=</span> <span class="token number">1.001</span> <span class="token number">0101</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">-></span> <span class="token number">1.001</span>      <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span> <span class="token comment">// Rounds down</span></code></pre>
<p>The final result rounds (to nearest) to <code>1.001</code><span class="attrs"></span>.</p>
<p>However, if we were to round that intermediate value to 4 bits <em>first</em>, we’d get this:</p>
<pre class="language-cpp"><code class="language-cpp">   <span class="token number">1.000</span> <span class="token number">1001</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">-></span> <span class="token number">1.001</span>      <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span> <span class="token comment">// Rounded up</span>
 <span class="token operator">+</span> <span class="token number">0.000</span> <span class="token number">1100</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
   <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
   <span class="token number">1.001</span> <span class="token number">1100</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span>
<span class="token operator">-></span> <span class="token number">1.010</span>      <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">4</span> <span class="token comment">// Up again</span></code></pre>
<p>In this one, we end up with <code>1.010</code><span class="attrs"></span> instead of <code>1.001</code><span class="attrs"></span> because of the intermediate rounding, which pushed us past the correctly-rounded final result.</p>
<h3>How to Pretend That You Have Infinite Precision</h3>
<p>Okay, for FMAdd we want to calculate a multiply, and then <em>somehow</em> throw an add in there and have it act as if we didn’t lose any precision on the multiply.</p>
<p>First we’re going to handle the case of 32-bit floats (<strong>singles</strong>) because it’s a <em>wildly</em> simpler case on CPUs that have 64-bit floats (<strong>doubles</strong>).</p>
<p>(also, sorry in advance, the term “double” for a “double-precision float” and the “double” in “double rounding” are two different instances of “double” but I’ve written so much of this post and like hell am I changing it now so hopefully it’s not too confusing)</p>
<p>The immediately obvious thing to try to get an accurate single-precision FMA is “hey, what if we do the multiply and add as doubles and then round the result back down to a single”:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">float</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">float</span> a<span class="token punctuation">,</span> <span class="token keyword">float</span> b<span class="token punctuation">,</span> <span class="token keyword">float</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token comment">// Do the math as 64-bit floats and truncate at the end. </span>
  <span class="token comment">//  Surely that's good enough, right?</span>
  <span class="token keyword">return</span> <span class="token keyword">float</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">double</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token keyword">double</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">double</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span></code></pre>
<p>While that gives a much better result than doing it as pure 32-bits, it actually <em>can</em> still have double rounding. But where does the extra rounding come from, in this case?</p>
<p>The multiply itself isn’t the source of the first rounding: Surprisingly (to me, at least): <strong>casting two singles to doubles and multiplying those together <em>always</em> results in an exact answer</strong> - this is because each of the single-precision values has 24 bits of precision, but a double can store 53 bits of precision, which is more than enough to store the result of multipling two singles (<code>2 * 24</code><span class="attrs"></span> bits of precision max). Since floats are stored as:</p>
<pre class="language-cpp"><code class="language-cpp">sign <span class="token operator">*</span> <span class="token number">1.</span>mantissa <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token punctuation">(</span>exponent<span class="token punctuation">)</span></code></pre>
<p>…it means we’re multiplying two numbers of the form <code>1.xxxxxxxxxx</code><span class="attrs"></span> and <code>1.yyyyyyyyy</code><span class="attrs"></span> together then adding the exponents together to get the new number, so unlike addition and subtraction (where, say, <code>1 + 1*10^60</code><span class="attrs"></span> requires a <em>ton</em> of extra precision), if two float numbers have wildly different exponents it doesn’t actually matter because the exponents and significand values are handled separately.</p>
<p>To illustrate this, let’s pretend we have two 4-digit (base 10) numbers and we multiply them and store the result using 8 digits (double precision):</p>
<pre class="language-cpp"><code class="language-cpp">  <span class="token punctuation">(</span><span class="token number">1.234</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1.457</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">100</span><span class="token punctuation">)</span>
<span class="token operator">-></span> <span class="token punctuation">(</span><span class="token number">1.2340000</span> <span class="token operator">*</span> <span class="token number">1.4570000</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token operator">^</span><span class="token number">1</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">100</span><span class="token punctuation">)</span>
 <span class="token operator">=</span>  <span class="token number">1.7979380</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">101</span>  <span class="token comment">// no rounding here!</span></code></pre>
<p>Great, so the double-precision multiply is fine and introduces no rounding at all. So then how do we get double rounding?</p>
<p>As mentioned above, an add (or subtract) can introduce rounding:</p>
<pre class="language-cpp"><code class="language-cpp">  <span class="token punctuation">(</span><span class="token number">1.234</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1.457</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">9</span><span class="token punctuation">)</span>
<span class="token operator">-></span> <span class="token punctuation">(</span><span class="token number">1.2340000</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">1.4570000</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">9</span><span class="token punctuation">)</span>
 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">1.45700001234</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">9</span><span class="token punctuation">)</span> <span class="token comment">// Too many digits!</span>
<span class="token operator">-></span> <span class="token number">1.45700000</span> <span class="token comment">// Rounded to nearest here</span></code></pre>
<p>This rounding happens at double precision (so well below the threshold of our target 32-bit result), but there’s still rounding, and then the value is rounded <em>again</em> when converted back down to single precision. <em>That’s</em> the double rounding and the source of a potential error.</p>
<p>Okay, so, double rounding is bad? Kinda! But it turns out there is a way to introduce a <strong>new rounding mode</strong> to use for the first rounding that, in the right situations, does not introduce any additional error and ensures that your final result is correct.</p>
<h3>A New Rounding Mode?</h3>
<p>(This technique is based off of the paper <a href="https://www.lri.fr/~melquion/doc/08-tc.pdf" target="_blank" rel="noopener">Emulation of FMA and correctly-rounded sums: proved algorithms using rounding to odd</a> by Sylvie Boldo and Guillaume Melquiond; if you want the full technical details of this whole process, that’s where you’ll find them. Believe it or not, I’m actually trying to go into less detail!)</p>
<p>The key to eliminating the extra precision loss is by using a non-standard rounding mode: <strong>rounding to odd</strong>. <a href="https://www.drilian.com/posts/2023.01.10-floating-point-numbers-and-rounding/">Standard floating point rounding</a> calculates results <strong>with some additional bits of precision</strong> (three bits, to be precise), and then rounds based on the result (usually using “round to nearest with round to even on ties”, although that detail doesn’t end up mattering here - this technique works with any standard rounding mode).</p>
<p>So, assume that we have some way of calculating a double precision addition and also having access to the error between the calculated result and the mathematically exact result. Given those two values we can perform a Round To Odd step:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">double</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span><span class="token keyword">double</span> value<span class="token punctuation">,</span> <span class="token keyword">double</span> errorTerm<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>errorTerm <span class="token operator">!=</span> <span class="token number">0.0</span> <span class="token comment">// if the result is not exact</span>
    <span class="token operator">&amp;&amp;</span> <span class="token function">LowestBitOfMantissa</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">//and mantissa is even</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// We need to round, so round either up or down to odd</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>errorTerm <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Round up to an odd value</span>
      value <span class="token operator">=</span> <span class="token function">AddOneBitToMantissa</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">else</span> <span class="token comment">// (errorTerm &lt; 0)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Round down to an odd value</span>
      value <span class="token operator">=</span> <span class="token function">SubtractOneBitFromMantissa</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Basically: if we have any error at all, and the mantissa is currently even, either add or subtract a single bit’s worth of mantissa, based on the sign of the error.</p>
<p>(In practice, I found that I also had to ensure the result was not <em>Infinity</em> before doing this operation, since I implemented this using some bitwise shenanigans that would end up “rounding” Infinity to NaN, so, you know, <strong>watch out for that</strong>).</p>
<h3>Why Does Odd-Rounding the Intermediate Value Work?</h3>
<p>Round to odd works <strong>as long as we have more bits of value than the final result</strong> - specifically we need at least two extra bits. Standard float rounding makes use of something called a “sticky” bit - basically the lowest bit of the extra precision is a 1 <strong>if any of the bits below it would have been 1</strong>.</p>
<p>And, hey, that is basically what “round to odd” does!</p>
<ul>
<li>If the mantissa is odd, regardless of whether there’s error or not the lowest bit is already odd.</li>
<li>If the error was positive and the mantissa was even, we set the lower bit to 1 anyway, effectively <em>stickying</em> (yeah that’s a word now) all the error bits below it.</li>
<li>If the error was negative and the mantissa was even, we subtract 1 from the mantissa, making the lower bit odd, and effectively sticky since some of the digits below it are also 1s.</li>
</ul>
<p>Effectively, round to odd is just “emulate having a sticky bit at the bottom of your intermediate result” - that way, you have a guaranteed tiebreaker for the final rounding step.</p>
<p>But note that I said it requires you to have at least two extra bits. In the case of our using-doubles-instead-of-singles intermediate addition, good news: we have <em>way more</em> than two extra bits - our intermediate value is a whole-ass double-precision float, so we have <em>29 extra bits</em> vs. our single-precision final value and (mathematically speaking) 29 is greater than 2.</p>
<p>So, for the true single-precision FMAdd instruction we need to do the following:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token keyword">float</span> <span class="token function">FMAdd</span><span class="token punctuation">(</span><span class="token keyword">float</span> a<span class="token punctuation">,</span> <span class="token keyword">float</span> b<span class="token punctuation">,</span> <span class="token keyword">float</span> c<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> product <span class="token operator">=</span> <span class="token keyword">double</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token keyword">double</span><span class="token punctuation">(</span>b<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// No rounding here</span>
  
  <span class="token comment">// Calculate our sum, but somehow get the error along with it</span>
  <span class="token punctuation">(</span><span class="token keyword">double</span> sum<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>product<span class="token punctuation">,</span> c<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Round our intermediate value to odd</span>
  sum <span class="token operator">=</span> <span class="token function">RoundToOdd</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token comment">// Final rounding here, which now does the correct thing and gives us</span>
  <span class="token comment">//  a properly-rounded final result (as if we'd used infinite bits)</span>
  <span class="token keyword">return</span> <span class="token keyword">float</span><span class="token punctuation">(</span>sum<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That’s it! …wait, what’s that <code>AddWithError</code><span class="attrs"></span> function, we haven’t even–</p>
<h3>Calculating An Exact Addition Result</h3>
<p>Right, we need to calculate that intermediate addition along with some accurate error term. It turns out it’s possible to calculate a set of numbers, <strong>sum</strong> and <strong>error</strong> where <code>mathematicallyExactSum = sum + error</code><span class="attrs"></span>.</p>
<p>For this, we have to dive back to July of 1971 and check out the <em>actually typewritten</em> paper <a href="https://ir.cwi.nl/pub/9159/9159D.pdf" target="_blank" rel="noopener">A Floating-Point Technique For Extending the Available Precision</a> by T.J. Dekker. Give that a read if you want way more details on this whole thing.</p>
<p>Calculating the error term of adding two numbers (I’ll use <code>x</code><span class="attrs"></span> and <code>y</code><span class="attrs"></span>) is <em>relatively</em> straightforward if <code>|x| &gt; |y|</code><span class="attrs"></span>:</p>
<pre class="language-cpp"><code class="language-cpp">sum <span class="token operator">=</span> x <span class="token operator">+</span> y<span class="token punctuation">;</span>
err <span class="token operator">=</span> y <span class="token operator">-</span> <span class="token punctuation">(</span>sum <span class="token operator">-</span> x<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>(this is equation 4.14 in the linked paper)</p>
<p>This is just a different ordering of <code>(x + y) - sum</code><span class="attrs"></span> that preserves accuracy: due to the nature of the values involved in these subtractions (<code>sum</code><span class="attrs"></span>’s value is directly related to those of <code>x</code><span class="attrs"></span> and <code>y</code><span class="attrs"></span>, and <code>y</code><span class="attrs"></span> is smaller than <code>x</code><span class="attrs"></span>), it turns out that each of those subtractions is an exact result (the paper has a proof of this, and it’s a <em>lot</em> so I’m not going to expand on that here), so we get the precise difference between the calculated sum and the real sum.</p>
<p>But this only works if you know that <code>x</code><span class="attrs"></span>’s magnitude is larger than (or equal to) <code>y</code><span class="attrs"></span>’s. If you <em>don’t</em> know which of the two values has a larger magnitude, you can do a bit more work and end up with:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token punctuation">(</span><span class="token keyword">double</span> sum<span class="token punctuation">,</span> <span class="token keyword">double</span> err<span class="token punctuation">)</span> <span class="token function">AddWithError</span><span class="token punctuation">(</span>
  <span class="token keyword">double</span> x<span class="token punctuation">,</span> 
  <span class="token keyword">double</span> y<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token keyword">double</span> sum <span class="token operator">=</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span>
  <span class="token keyword">double</span> intermediate <span class="token operator">=</span> sum <span class="token operator">-</span> x<span class="token punctuation">;</span>
  <span class="token keyword">double</span> err1 <span class="token operator">=</span> y <span class="token operator">-</span> intermediate<span class="token punctuation">;</span>
  <span class="token keyword">double</span> err2 <span class="token operator">=</span> x <span class="token operator">-</span> <span class="token punctuation">(</span>sum <span class="token operator">-</span> intermediate<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>sum<span class="token punctuation">,</span> err1 <span class="token operator">+</span> err2<span class="token punctuation">)</span><span class="token punctuation">;</span>      
<span class="token punctuation">}</span></code></pre>
<p>(This is effectively the expanded version of listing 4.16 from the linked paper)</p>
<ul>
<li><code>err1</code><span class="attrs"></span> here is the same as the value in the first version we calculated (a precision-preserving rewrite of <code>(x + y) - sum</code><span class="attrs"></span>)</li>
<li><code>err2</code><span class="attrs"></span> is, mathematically, <code>x - (sum - (sum - x))</code><span class="attrs"></span> or <code>0</code><span class="attrs"></span>; its goal is to calculate the error involved in calculating err1, since without the <code>|x| &gt; |y|</code><span class="attrs"></span> guarantee those subtractions might NOT be exact … but these ones will be.</li>
<li>Thus, summing these two error terms together gives us a final, precise error term.</li>
</ul>
<p>(More details in the paper, hopefully this isn’t too glossed over that it loses any meaning)</p>
<h3>Finally, the End (For Single-Precision Floats)</h3>
<p>So, yeah, that’s how you implement the FMAdd instruction for single-precision floats on a machine that has double-precision support:</p>
<ul>
<li>Calculate the double-precision product of <code>a</code><span class="attrs"></span> and <code>b</code><span class="attrs"></span></li>
<li>Add this product to <code>c</code><span class="attrs"></span> to get a double-precision sum</li>
<li>Calculate the error of the sum</li>
<li>Use the error to odd-round the sum</li>
<li>Round the double-precision sum back down to single precision</li>
</ul>
<p>But what if you have to calculate FMAdd for double-precision floats? You can’t easily just cast up to, like, <em>quad-precision</em> floats and do the work there, so what now? Can you still do this?</p>
<p>The answer is yes, but it’s a <em><strong>lot</strong></em> more work, and that’s what <a href="https://www.drilian.com/posts/2025.01.02-emulating-the-fmadd-instruction-part-2-64-bit-floats/">the next post</a> is about.</p>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[C++17-Style Hex Floats (And How To Parse Them)]]></title>
      <link>https://www.drilian.com/posts/2024.12.30-c-17-style-hex-floats-and-how-to-parse-them/</link>
      <pubDate>Mon, 30 Dec 2024 12:00:00 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2024.12.30-c-17-style-hex-floats-and-how-to-parse-them/</guid>
      <content:encoded>
        <![CDATA[
          <p>C++17 added support for hex float literals, so you can put more bit-accurate floating point values into your code. They’re handy to have, and I wanted to be able to parse them from a text file in a C# app I was writing.</p>
<p>Some examples:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token number">0x1.0p0</span>         <span class="token comment">// == 1.0</span>
<span class="token number">0x1.8p1</span>         <span class="token comment">// == 3</span>
<span class="token number">0x8.0p-3</span>        <span class="token comment">// == 1.0</span>
<span class="token number">0x0.8p1</span>         <span class="token comment">// == 1.0</span>
<span class="token number">0xAB.CDEFp-10</span>   <span class="token comment">// == 0.16777776181697846</span>
<span class="token number">0x0.0000000ABp0</span> <span class="token comment">// == 2.4883775040507317E-09</span></code></pre>
<h3>What Am I Even Looking At Here?</h3>
<p>(Before reading this, if you don’t have a good feel for how floats work, maybe check <a href="https://www.drilian.com/posts/2023.01.10-floating-point-numbers-and-rounding/">my previous post about floats (and float rounding)</a>)</p>
<p>I had a bit of a mental block on this number format for a bit - like, what does it even mean to have fractional hex digits? But it turns out it’s a concept that we already use all the time and my brain just needed some prodding to make the connection.</p>
<p>With our standard base 10 numbers, moving the decimal point left one digit means dividing the number by 10:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token number">12.3</span> <span class="token operator">==</span> <span class="token number">1.23</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">1</span> <span class="token operator">==</span> <span class="token number">0.123</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">2</span></code></pre>
<p>Hex floats? Same deal, just in 16s instead:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token number">0x1B.C8</span> <span class="token operator">==</span> <span class="token number">0x1.BC8</span> <span class="token operator">*</span> <span class="token number">16</span><span class="token operator">^</span><span class="token number">1</span> <span class="token operator">==</span> <span class="token number">0x0.1BC8</span> <span class="token operator">*</span> <span class="token number">16</span><span class="token operator">^</span><span class="token number">2</span></code></pre>
<p>Okay, so now what’s the “<code>p</code><span class="attrs"></span>” part in the number? Well, that’s the start of the exponent. A standard float has an exponent starting with ‘<code>e</code><span class="attrs"></span>’:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token number">1.3e2</span> <span class="token operator">==</span> <span class="token number">1.3</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token operator">^</span><span class="token number">2</span></code></pre>
<p>But ‘<code>e</code><span class="attrs"></span>’ is a hex digit, so you can’t use ‘<code>e</code><span class="attrs"></span>’ anymore as the exponent starter, so they chose ‘<code>p</code><span class="attrs"></span>’ instead (why not ‘<code>x</code><span class="attrs"></span>’, the second letter? Probably because a hex number starts with ‘<code>0x</code><span class="attrs"></span>’, so ‘<code>x</code><span class="attrs"></span>’ also already has a use - but ‘<code>p</code><span class="attrs"></span>’ is free so it wins)</p>
<p>The exponent for a hex float is in powers of 2 (so it corresponds perfectly to the exponent as it is stored in the value), so:</p>
<pre class="language-cpp"><code class="language-cpp"><span class="token number">0x1.ABp3</span> <span class="token operator">==</span> <span class="token number">0x1.AB</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token operator">^</span><span class="token number">3</span></code></pre>
<p>So that’s how a hex float literal works! Here’s a quick breakdown:</p>
<div class="code-box">
<span class="keyword">&lt;hex-digits&gt;</span><span class="string">[ '.'</span> <span class="operator">[fractional-hex-digits]</span><span class="string">]</span> <span class="keyword">'p' &lt;exponent&gt;</span>
</div>
<span class="read-more">(Parsing code below the fold)</span>
<h3>Okay Now What’s This About Parsing Them?</h3>
<p>C++ conveniently has functions to parse these (std::strtod/strtof handle this nicely). However, if you’re (hypothetically) making a parser, and you happen to be writing it in C# which does <em>not</em> have an inbuilt way to parse these, then you’ll have to parse your own.</p>
<p>It ended up being a little more complicated than I thought for a couple reasons.</p>
<ul>
<li>Arbitrarily long hex strings seem to be supported, which means you need to track both where the top-most non-zero hex value starts (i.e. skip any leading zeros) but also properly handle bits that are extra tiny which may affect rounding.</li>
<li>In order to properly handle said rounding, running through the hex digits in reverse and pushing them in from the top ends up being a nice strategy, because float rounding works via a sticky bit that stays set as things right-shift down through it.</li>
</ul>
<p>Ultimately I ended up with the following algorithm (in C#) to parse a hex float, which I’m <em>definitely</em> sure is <em>~perfect~</em> and has absolutely no bugs whatsoever. It is also <em>absolutely</em> the most efficient version of this possible, with no room for improvement. Yep.</p>
<p>I’m throwing it in here in case anyone ever finds it useful.</p>
<pre class="language-cs"><code class="language-cs"><span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">IsDigit</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">char</span></span> c<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>c <span class="token operator">>=</span> <span class="token char">'0'</span> <span class="token operator">&amp;&amp;</span> c <span class="token operator">&lt;=</span> <span class="token char">'9'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">static</span> <span class="token return-type class-name"><span class="token keyword">bool</span></span> <span class="token function">IsHexDigit</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">char</span></span> c<span class="token punctuation">)</span> 
  <span class="token operator">=></span> <span class="token function">IsDigit</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> 
    <span class="token operator">||</span> <span class="token punctuation">(</span>c <span class="token operator">>=</span> <span class="token char">'A'</span> <span class="token operator">&amp;&amp;</span> c <span class="token operator">&lt;=</span> <span class="token char">'F'</span><span class="token punctuation">)</span> 
    <span class="token operator">||</span> <span class="token punctuation">(</span>c <span class="token operator">>=</span> <span class="token char">'a'</span> <span class="token operator">&amp;&amp;</span> c <span class="token operator">&lt;=</span> <span class="token char">'f'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token return-type class-name"><span class="token keyword">double</span></span> <span class="token function">ParseHexFloat</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> s<span class="token punctuation">)</span>
<span class="token punctuation">{</span> 
  <span class="token comment">// This doesn't handle a negative sign, if only because the parser I have only</span>
  <span class="token comment">// needed to support positive values, but it'd be easy to add</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">.</span>Length <span class="token operator">&lt;</span> <span class="token number">2</span> <span class="token operator">||</span> s<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token char">'0'</span> <span class="token operator">||</span> <span class="token keyword">char</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token char">'x'</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Missing 0x prefix"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">.</span>Length <span class="token operator">&lt;</span> <span class="token number">3</span> <span class="token operator">||</span> <span class="token operator">!</span><span class="token function">IsHexDigit</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span>
        <span class="token string">"Hex float literal must contain at least one whole part digit"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    
  <span class="token class-name"><span class="token keyword">int</span></span> i <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token class-name"><span class="token keyword">int</span></span> decimalPointIndex <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token class-name"><span class="token keyword">int</span></span> firstNonZeroHexDigitIndex <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>

  <span class="token comment">// Scan through our digits, looking for the index of the first set (non-zero)</span>
  <span class="token comment">//  hex value and the decimal point (if we have one).</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span>i <span class="token operator">&lt;</span> s<span class="token punctuation">.</span>Length <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span><span class="token function">IsHexDigit</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">||</span> s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'.'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'.'</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Found the decimal point! Hopefully there wasn't already one!</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>decimalPointIndex <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Too many decimal points"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

      decimalPointIndex <span class="token operator">=</span> i<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token char">'0'</span> <span class="token operator">&amp;&amp;</span> firstNonZeroHexDigitIndex <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> firstNonZeroHexDigitIndex <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Here's our top-most set hex value.</span>

    i<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Also make a note of where our last hex digit was (usually the digit before</span>
  <span class="token comment">//  the 'p' that we should be at right now)</span>
  <span class="token class-name"><span class="token keyword">int</span></span> lastHexDigitIndex <span class="token operator">=</span> i <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>

  <span class="token comment">// ... but if the previous character was the decimal point, the last hex</span>
  <span class="token comment">//  digit is before that.</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>lastHexDigitIndex <span class="token operator">==</span> decimalPointIndex<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> lastHexDigitIndex<span class="token operator">--</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// If we didn't find a decimal point, it's EFFECTIVELY here, at the 'p'</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>decimalPointIndex <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> decimalPointIndex <span class="token operator">=</span> i<span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// Validate and skip the 'p' character</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">>=</span> s<span class="token punctuation">.</span>Length <span class="token operator">||</span> <span class="token keyword">char</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token char">'p'</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Missing exponent 'p'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
  i<span class="token operator">++</span><span class="token punctuation">;</span>

  <span class="token comment">// Grab the sign if we have one</span>
  <span class="token class-name"><span class="token keyword">bool</span></span> negativeExponent <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">&lt;</span> s<span class="token punctuation">.</span>Length <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'+'</span> <span class="token operator">||</span> s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    negativeExponent <span class="token operator">=</span> <span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    i<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">>=</span> s<span class="token punctuation">.</span>Length<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Missing exponent digits"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// Parse the exponent!</span>
  <span class="token class-name"><span class="token keyword">int</span></span> exponent <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span>i <span class="token operator">&lt;</span> s<span class="token punctuation">.</span>Length <span class="token operator">&amp;&amp;</span> <span class="token function">IsDigit</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">.</span>MaxValue <span class="token operator">/</span> <span class="token number">10</span> <span class="token operator">&lt;</span> exponent<span class="token punctuation">)</span>
      <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Exponent overflow"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    
    exponent <span class="token operator">*=</span> <span class="token number">10</span><span class="token punctuation">;</span>
    exponent <span class="token operator">+=</span> <span class="token punctuation">(</span><span class="token keyword">int</span><span class="token punctuation">)</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">-</span> <span class="token char">'0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    i<span class="token operator">++</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>negativeExponent<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> exponent <span class="token operator">=</span> <span class="token operator">-</span>exponent<span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// If we had no non-zero hex digits, there's no point in continuing, it's</span>
  <span class="token comment">//  zero. </span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>firstNonZeroHexDigitIndex <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0.0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    
  <span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator">!=</span> s<span class="token punctuation">.</span>Length<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">FormatException</span><span class="token punctuation">(</span><span class="token string">"Unexpected characters at end of string"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    
  <span class="token comment">// We have the supplied exponent, but we want to massage it a bit. In a</span>
  <span class="token comment">//  (IEEE) floating-point value, the mantissa is entirely fractional - that</span>
  <span class="token comment">//  is, the value is 1.mantissa * 2^(exponent) - there's an implied 1</span>
  <span class="token comment">//  (excluding subnormal floats, which we'll handle properly, but we can</span>
  <span class="token comment">//  ignore for the moment). Two things need to happen here:</span>
  <span class="token comment">//   1. We need to adjust the exponent based on the position of the first</span>
  <span class="token comment">//      non-zero hex digit, to match the fact that we're parsing hex digits</span>
  <span class="token comment">//      such that the top hex digit is sitting in the top 4 bits of our 64-</span>
  <span class="token comment">//      bit int.</span>
  <span class="token comment">//   2. But we EXPECT a single bit to be above the mantissa (the implied</span>
  <span class="token comment">//      1) so subtract 1 from our adjustment to take into account that</span>
  <span class="token comment">//      there will be 4 bits in that hex, so if we had parsed a single "1"</span>
  <span class="token comment">//      (from, say, 0x1p0, which just equals 1.0) our effective exponent</span>
  <span class="token comment">//      should be 3 (which we will later shift back down to 0 to position</span>
  <span class="token comment">//      the 1s bit at the very top)</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>decimalPointIndex <span class="token operator">>=</span> firstNonZeroHexDigitIndex<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> exponent <span class="token operator">+=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>decimalPointIndex <span class="token operator">-</span> firstNonZeroHexDigitIndex<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
  <span class="token keyword">else</span>
    <span class="token punctuation">{</span> exponent <span class="token operator">+=</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>decimalPointIndex <span class="token operator">-</span> firstNonZeroHexDigitIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// Now that we have the exponent and know the bounds of our hex digits,</span>
  <span class="token comment">//  we can parse backwards through the hex digits, shifting them in from</span>
  <span class="token comment">//  the top. We do this so that we can easily handle the rounding to the</span>
  <span class="token comment">//  final 53 bits of significand (by ensuring that we don't ever shift</span>
  <span class="token comment">//  any 1s off the bottom)</span>
  <span class="token class-name"><span class="token keyword">ulong</span></span> mantissa <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> lastHexDigitIndex<span class="token punctuation">;</span> i <span class="token operator">>=</span> firstNonZeroHexDigitIndex<span class="token punctuation">;</span> i<span class="token operator">--</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// Skip the '.' if there was one.</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token char">'.'</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token class-name"><span class="token keyword">char</span></span> c <span class="token operator">=</span> <span class="token keyword">char</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span>s<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name"><span class="token keyword">ulong</span></span> v <span class="token operator">=</span> <span class="token punctuation">(</span>c <span class="token operator">>=</span> <span class="token char">'a'</span> <span class="token operator">&amp;&amp;</span> c <span class="token operator">&lt;=</span> <span class="token char">'f'</span><span class="token punctuation">)</span> 
      <span class="token punctuation">?</span> <span class="token punctuation">(</span><span class="token keyword">ulong</span><span class="token punctuation">)</span><span class="token punctuation">(</span>c <span class="token operator">-</span> <span class="token char">'a'</span> <span class="token operator">+</span> <span class="token number">10</span><span class="token punctuation">)</span> 
      <span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token keyword">ulong</span><span class="token punctuation">)</span><span class="token punctuation">(</span>c <span class="token operator">-</span> <span class="token char">'0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Shift the mantissa down, but keep any 1s that happen to be in the bottom</span>
    <span class="token comment">//  4 bits (this is a reasonably-efficient emulation of the "sticky bit"</span>
    <span class="token comment">//  that is used to round a floating point number properly.</span>
    mantissa <span class="token operator">=</span> <span class="token punctuation">(</span>mantissa <span class="token operator">>></span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">0xf</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Add in our parsed hex value, putting its 4 bits at the very top of the</span>
    <span class="token comment">//  mantissa ulong.</span>
    mantissa <span class="token operator">|=</span> <span class="token punctuation">(</span>v <span class="token operator">&lt;&lt;</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// We know the mantissa is non-zero (checked earlier), and we want to position</span>
  <span class="token comment">//  the highest set bit at the top of our ulong so shift up until the top bit</span>
  <span class="token comment">//  is set (and adjust our exponent down 1 to compensate).</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">0x8000_0000_0000_0000ul</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    mantissa <span class="token operator">&lt;&lt;=</span> <span class="token number">1</span><span class="token punctuation">;</span>
    exponent<span class="token operator">--</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">int</span></span> DoubleExponentBias <span class="token operator">=</span> <span class="token number">1023</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">int</span></span> MaxBiasedDoubleExponent <span class="token operator">=</span> <span class="token number">1023</span> <span class="token operator">+</span> DoubleExponentBias<span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">ulong</span></span> MantissaMask <span class="token operator">=</span> <span class="token number">0x000f_ffff_ffff_fffful</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">ulong</span></span> ImpliedOneBit <span class="token operator">=</span> <span class="token number">0x0010_0000_0000_0000ul</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">int</span></span> ExponentShift <span class="token operator">=</span> <span class="token number">52</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token class-name"><span class="token keyword">int</span></span> MantissaShiftRight <span class="token operator">=</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token type-expression class-name"><span class="token keyword">double</span></span><span class="token punctuation">)</span><span class="token operator">*</span><span class="token number">8</span> <span class="token operator">-</span> ExponentShift <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>

  <span class="token comment">// Exponents are stored in a biased form (they can't go negative) so add our</span>
  <span class="token comment">//  bias now.</span>
  exponent <span class="token operator">+=</span> DoubleExponentBias<span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>exponent <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// We have a subnormal value, which means there is no implied 1, so first</span>
    <span class="token comment">//  we need to shift our mantissa down by one to get rid of the implied 1.</span>
    <span class="token comment">//  (note that we're not letting any 1s shift off the bottom, keeping them</span>
    <span class="token comment">//  sticky)</span>
    mantissa <span class="token operator">=</span> <span class="token punctuation">(</span>mantissa <span class="token operator">>></span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Continue to denormalize the mantissa until our exponent reaches zero</span>
    <span class="token keyword">while</span> <span class="token punctuation">(</span>exponent <span class="token operator">&lt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      mantissa <span class="token operator">=</span> <span class="token punctuation">(</span>mantissa <span class="token operator">>></span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">|</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      exponent<span class="token operator">++</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// Now to do the actual rounding of the mantissa. Sometimes floating point</span>
  <span class="token comment">//  rounding needs 3 bits (guard, round, sticky) to do rounding, but in our</span>
  <span class="token comment">//  case, two will suffice: one bit that represents the uppermost bit that</span>
  <span class="token comment">//  shifts right off of the edge of the mantissa (i.e. the "0.5" bit) and</span>
  <span class="token comment">//  then "literally any 1 bit underneath that" (which is why we've been</span>
  <span class="token comment">//  holding on to extra 1s when shifting right) that is the tiebreaker</span>
  <span class="token class-name"><span class="token keyword">bool</span></span> roundBit      <span class="token operator">=</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">0b10000000000</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token class-name"><span class="token keyword">bool</span></span> tiebreakerBit <span class="token operator">=</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">0b01111111111</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">;</span>

  <span class="token comment">// Now that we have those bits, we can shift our mantissa down into its</span>
  <span class="token comment">//  proper place (as the lower 53 bits of our 64-bit ulong).</span>
  mantissa <span class="token operator">>>=</span> MantissaShiftRight<span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>roundBit<span class="token punctuation">)</span>
  <span class="token punctuation">{</span>
    <span class="token comment">// If there's a tiebreaker, we'll increment the mantissa. Otherwise,</span>
    <span class="token comment">//  if there's a tie (could round either way), we round so that the</span>
    <span class="token comment">//  mantissa value is even (lowest bit in the double is 0)</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>tiebreakerBit <span class="token operator">||</span> <span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span>
      <span class="token punctuation">{</span> mantissa<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token comment">// If we have a subnormal float we may have overflowed into the implied 1</span>
    <span class="token comment">//  bit, otherwise we might have overflowed into the ... I guess the</span>
    <span class="token comment">//  implied *2* bit?</span>
    <span class="token class-name"><span class="token keyword">ulong</span></span> overflowedMask <span class="token operator">=</span> ImpliedOneBit <span class="token operator">&lt;&lt;</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>exponent <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">?</span> <span class="token number">0</span> <span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>mantissa <span class="token operator">&amp;</span> overflowedMask<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
      <span class="token comment">// Shift back down one. This is not going to drop a 1 off the bottom</span>
      <span class="token comment">//  because if we overflowed it means we were odd, and added one to</span>
      <span class="token comment">//  become even.</span>
      exponent<span class="token operator">++</span><span class="token punctuation">;</span>
      mantissa <span class="token operator">>>=</span> <span class="token number">1</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// It's possible that the truncation we ended up with a 0 mantissa after all,</span>
  <span class="token comment">//  so our final value has rounded allll the way down to 0.</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>mantissa <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0.0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// If our exponent is too large to be represented, this value is infinity.</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>exponent <span class="token operator">></span> MaxBiasedDoubleExponent<span class="token punctuation">)</span>
    <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">double</span><span class="token punctuation">.</span>PositiveInfinity<span class="token punctuation">;</span> <span class="token punctuation">}</span>

  <span class="token comment">// Mask off the implied one bit (if we have one)</span>
  mantissa <span class="token operator">&amp;=</span> <span class="token operator">~</span>ImpliedOneBit<span class="token punctuation">;</span>

  <span class="token comment">// Alright assemble the final double's bits, which means shifting and</span>
  <span class="token comment">//  adding the exponent into its proper place.</span>
  <span class="token comment">//  (if we had a sign to apply we'd apply it to the top bit). </span>
  <span class="token class-name"><span class="token keyword">ulong</span></span> assembled <span class="token operator">=</span> mantissa <span class="token operator">|</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">ulong</span><span class="token punctuation">)</span>exponent<span class="token punctuation">)</span> <span class="token operator">&lt;&lt;</span> ExponentShift<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token class-name"><span class="token keyword">double</span></span> result <span class="token operator">=</span> BitConverter<span class="token punctuation">.</span><span class="token function">UInt64BitsToDouble</span><span class="token punctuation">(</span>assembled<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>

        ]]>
      </content:encoded>
    </item>
    <item>
      <title><![CDATA[Wordpress No More]]></title>
      <link>https://www.drilian.com/posts/2024.12.29-wordpress-no-more/</link>
      <pubDate>Sun, 29 Dec 2024 12:00:00 PST</pubDate>
      <dc:creator><![CDATA[Josh Jersild]]></dc:creator>
      <guid>https://www.drilian.com/posts/2024.12.29-wordpress-no-more/</guid>
      <content:encoded>
        <![CDATA[
          <p>I’ve ported the blog off of Wordpress onto a static site generator (Specifically, <a href="https://www.11ty.dev/" target="_blank" rel="noopener">11ty/Eleventy</a>). I have a bit more control over the format here, and it’s easier for me to write pages (Wordpress was fighting me on all sorts of formatting which I can just <strong>do</strong> now).</p>
<p>What this means is I can finally start copying over the rest of my posts that were on (the now-defunct, sadly) cohost.org (rest easy, little eggbug).</p>
<p>Likely there are things on the new site that aren’t set up correctly yet, so if you happen to notice anything, find me on <a href="https://bsky.app/profile/joshjers.drilian.com" target="_blank" rel="noopener">Bluesky</a> or <a href="https://mastodon.gamedev.place/@JoshJers" target="_blank" rel="noopener">Mastodon</a> and let me know!</p>
<p>And now, for fun, here’s a pic I took in November at <a href="https://www.nps.gov/arch/index.htm" target="_blank" rel="noopener">Arches National Park</a>:</p>
<div class="image-container">
        <a href="https://www.drilian.com/assets/2024/delicate-arch.png" target="_blank"><img src="https://www.drilian.com/assets/2024/delicate-arch-small.jpg" alt="Photo of Delicate Arch"></a>
        </div>
        ]]>
      </content:encoded>
    </item>
  </channel>
</rss>