mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-03 19:53:45 +00:00
The operator caught what no check had ever tested: nobody had LOOKED at
the rendered pages. A ten-inspector visual audit of all 129 pages (every
page opened as an image) found 40 defects, including didactic
correctness bugs invisible to the text layer:
BAD, fixed:
- ch03 printed WRONG Lean operators: \lean{P /\ Q} lost its backslash
('P / Q') and \lean{P \/ Q} lost the operator entirely ('P Q') —
TeX ate them inside the non-verbatim macro. Now the unicode ∧/∨ the
book uses everywhere else.
- ch12: the doubling display overflowed its box border, slicing the
math; stacked on two lines.
- toolkit Card 6: the headroom-audit display was clipped by the page
edge; now an align* stack.
UGLY, fixed:
- title page: the 'pyramid motif' at 5% white opacity on near-black
rendered as smudge artifacts, plus a clipped ∀ glyph in the corner
— redrawn with solid mixed colors (no transparency), glyph removed;
the footer's mid-word paragraph gap was a \vspace landing inside
horizontal mode; fixed with \par
- ch06: both clock diagrams' wrap-around arrows ran counterclockwise,
retracing over earlier arcs — target position expressed as 12 (one
revolution) so the arc continues clockwise, landing on 0 (mod 12)
and 1 (mod 11) correctly
- ch04: two_mul'' printed as two_mul" (quote ligature)
- one-line orphaned box fragments and stranded solution headings
throughout: bigidea/tryit/pitfall/aha/checkpoint are now unbreakable
(none exceeds half a page), worked boxes announce '(continued)' after
a break, \solhead keeps four lines with \Needspace
- --all/--receipt flags printed as one merged dash: \ddash macro
- inline code no longer hyphen-breaks at underscores (codeguards)
- ch09's 2^{...} smudge, glossary margin overflow, ch08 orphaned
listing line, ch13 command-line layout, three >10pt overfulls
THE STRUCTURAL LESSON, encoded: the two worst clipping bugs had been
announced as 80pt/73pt overfull warnings in every build log and ignored.
check-book.sh now FAILS on any overfull box past 10pt — the machine was
telling us; now it is allowed to stop us.
132 pages; publication-history and README counts synced; every fixed
page re-rendered and verified by eye. Button: ALL GREEN (96 checks).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
372 lines
18 KiB
TeX
372 lines
18 KiB
TeX
\chapter{The Denotation Bridge: What Do Five Numbers \emph{Mean}?}
|
|
\label{ch:denotation}
|
|
|
|
\section{Two worlds, one bridge}
|
|
|
|
We now hold two very different objects. On one side, mathematics:
|
|
the field $\Fp$, where elements are abstract clock positions and $+$ means
|
|
ideal modular addition. On the other side, the extracted model: arrays of
|
|
five \lean{U64} words, shuffled by loads, adds, and stores. The entire
|
|
question of verified cryptography is how to say --- precisely --- that the
|
|
second \emph{implements} the first.
|
|
|
|
The answer is a single function, small enough to write on one line and
|
|
important enough to carry this whole book. Given limbs
|
|
$a = (a_0, a_1, a_2, a_3, a_4)$, define the \textbf{denotation}:
|
|
\[
|
|
\denote{a} \;=\; a_0 + 2^{51} a_1 + 2^{102} a_2 + 2^{153} a_3 + 2^{204} a_4
|
|
\;\in\; \Fp .
|
|
\]
|
|
Read $\denote{a}$ as ``the field element these limbs \emph{mean}.'' It is
|
|
positional notation, nothing more --- base $2^{51}$ instead of base 10, with
|
|
the result interpreted on the clock face of $\Fp$. In Lean:
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
def denote (a : Array U64 5) : ZMod p :=
|
|
a[0].val + 2^51 * a[1].val + 2^102 * a[2].val
|
|
+ 2^153 * a[3].val + 2^204 * a[4].val
|
|
\end{lstlisting}
|
|
|
|
With the bridge in hand, correctness of an operation becomes a
|
|
\emph{commuting square} --- one picture you should internalize until you see
|
|
it in your sleep:
|
|
|
|
\begin{center}
|
|
\begin{tikzpicture}[
|
|
world/.style={font=\small,align=center},
|
|
arr/.style={-{Stealth},thick,ink2},
|
|
lbl/.style={font=\small\color{ink2}}
|
|
]
|
|
\node[world] (tl) at (0,2.6) {$(a, b)$\\ \footnotesize limb arrays};
|
|
\node[world] (tr) at (7.2,2.6) {$\mathtt{add}(a,b)$\\ \footnotesize limb array};
|
|
\node[world] (bl) at (0,0) {$(\denote{a}, \denote{b})$\\ \footnotesize field elements};
|
|
\node[world] (br) at (7.2,0) {$\denote{a} + \denote{b}$\\ \footnotesize field element};
|
|
\draw[arr] (tl) -- node[lbl,above] {machine code} (tr);
|
|
\draw[arr] (bl) -- node[lbl,below] {ideal math} (br);
|
|
\draw[arr] (tl) -- node[lbl,left] {$\denote{\cdot}$} (bl);
|
|
\draw[arr] (tr) -- node[lbl,right] {$\denote{\cdot}$} (br);
|
|
\node[font=\small\color{accent},align=center] at (3.6,1.3)
|
|
{\textbf{the theorem:}\\ both routes agree};
|
|
\end{tikzpicture}
|
|
\end{center}
|
|
|
|
\begin{bigidea}
|
|
\textbf{The correctness of an implementation is the statement that
|
|
denotation commutes with every operation:}
|
|
\[
|
|
\denote{\mathtt{add}(a,b)} = \denote{a} + \denote{b},
|
|
\qquad
|
|
\denote{\mathtt{mul}(a,b)} = \denote{a} \cdot \denote{b},
|
|
\qquad\dots
|
|
\]
|
|
The left-hand side lives in the machine world (with its bounds hypotheses
|
|
and \lean{Result}s); the right-hand side is pure mathematics. One equation
|
|
per operation, and the ugly optimized code is pinned, forever, to the
|
|
textbook meaning. Every verified-crypto project you will ever read is this
|
|
diagram, instantiated.
|
|
\end{bigidea}
|
|
|
|
\section{Why redundancy is freedom (and where bugs hide)}
|
|
|
|
A subtlety with consequences: denotation is \textbf{many-to-one}. The limb
|
|
arrays $(19, 0, 0, 0, 0)$ and $(19 + (2^{51}-19),\; 2^{51}-1,\; \dots)$ --- i.e.\ $p$ itself added limb-wise --- --- or more
|
|
mundanely, unreduced sums whose limbs exceed $2^{51}$ --- can denote the
|
|
\emph{same} field element. The representation has slack, and the
|
|
implementation \emph{exploits} it: the fast \code{add} from
|
|
Chapter~\ref{ch:rust} just adds limbs pairwise, letting values drift above
|
|
$2^{51}$, and nobody reduces until a cheaper moment. The commuting square
|
|
still closes because $\denote{\cdot}$ doesn't care how bloated the limbs are
|
|
--- positional value is positional value.
|
|
|
|
\begin{worked}{denoting the prime itself --- a telescope in radix 51}
|
|
The canonical example of a collision uses the most important limb array in
|
|
the codebase: the representation of $p$ itself,
|
|
\[
|
|
P \;=\; (2^{51}-19,\; 2^{51}-1,\; 2^{51}-1,\; 2^{51}-1,\; 2^{51}-1).
|
|
\]
|
|
Claim: $\denote{P} = p \equiv 0$, so $P$ collides with
|
|
$(0,0,0,0,0)$. Verify by hand --- the sum telescopes beautifully. Write
|
|
out the positional value:
|
|
\[
|
|
\denote{P}_{\Z} = (2^{51}-19) + (2^{51}-1)2^{51} + (2^{51}-1)2^{102}
|
|
+ (2^{51}-1)2^{153} + (2^{51}-1)2^{204}.
|
|
\]
|
|
Expand each product: $(2^{51}-1)2^{51k} = 2^{51(k+1)} - 2^{51k}$. So the
|
|
sum is
|
|
\[
|
|
(2^{51} - 19) + (2^{102} - 2^{51}) + (2^{153} - 2^{102})
|
|
+ (2^{204} - 2^{153}) + (2^{255} - 2^{204}).
|
|
\]
|
|
Every power except the last cancels against its neighbor --- slide a pen
|
|
along the chain and watch $2^{51}, 2^{102}, 2^{153}, 2^{204}$ annihilate
|
|
--- leaving
|
|
\[
|
|
\denote{P}_{\Z} = 2^{255} - 19 = p \;\equiv\; 0 \pmod p. \qquad ✓
|
|
\]
|
|
This computation is why the constant array $P$ (and its multiple $16P$,
|
|
Chapter~\ref{ch:lean}'s worked example) appears throughout the dalek
|
|
source: adding $P$ limb-wise changes nothing denotationally, and that
|
|
``nothing'' is exactly the freedom the subtraction trick spends. When the
|
|
verified \code{sub} spec says \lean{denote (sub a b) = denote a - denote b},
|
|
the proof's central move is this telescope, done once, as a lemma.
|
|
\end{worked}
|
|
|
|
This is also exactly where the carry bugs of Chapter~\ref{ch:why} live: code
|
|
that is correct only while the drift stays within headroom, and wrong on the
|
|
rare inputs where it spills. In the verified development that danger becomes
|
|
a visible, machine-checked pair of clauses attached to every operation:
|
|
|
|
\begin{lstlisting}[language=Lean]
|
|
theorem add_spec (ha : Bnd54 a) (hb : Bnd54 b) :
|
|
∃ c, add a b = .ok c
|
|
∧ Bnd55 c -- (1) bounds: the envelope holds
|
|
∧ denote c = denote a + denote b -- (2) value: the meaning is right
|
|
\end{lstlisting}
|
|
|
|
Clause (2) is the commuting square. Clause (1) feeds the \emph{next}
|
|
operation's hypothesis --- correctness composes only because every theorem
|
|
hands the following one the envelope it requires. A chain of such specs is
|
|
the formal skeleton of ``this sequence of optimized operations computes the
|
|
formula we claim.''
|
|
|
|
\begin{aha}
|
|
The denotation idea is vastly older and bigger than cryptography. Compilers
|
|
prove ``optimized code means the same as naive code''; databases prove
|
|
``this query plan means the same query''; hardware verifies ``this pipelined
|
|
circuit means this instruction set.'' The pattern --- map both sides into a
|
|
mathematical meaning-space and prove the square commutes --- is called
|
|
\emph{denotational semantics}, and you have now used it for real. It is the
|
|
single most transferable idea in this book.
|
|
\end{aha}
|
|
|
|
\section{Multiplication: where the bridge earns its keep}
|
|
|
|
Addition's square closes in an afternoon. Multiplication is the boss fight,
|
|
and seeing \emph{why} teaches you what verified arithmetic is really like.
|
|
Schoolbook multiplication of two 5-limb numbers produces nine columns of
|
|
partial products $\sum_{i+j=k} a_i b_j$; each column then owes a
|
|
\emph{carry} to the next; and columns $k \ge 5$ --- weights $2^{255}$ and up
|
|
--- must be folded back using the Chapter~\ref{ch:modular} identity
|
|
$2^{255} \equiv 19$. The implementation interleaves all three concerns for
|
|
speed. The proof must un-interleave them:
|
|
|
|
\[
|
|
\denote{\mathtt{mul}(a,b)}
|
|
\;\overset{?}{=}\;
|
|
\Big(\textstyle\sum_{k=0}^{8} 2^{51k} \sum_{i+j=k} a_i b_j \Big) \bmod p
|
|
\;\overset{?}{=}\; \denote{a} \cdot \denote{b} .
|
|
\]
|
|
|
|
The right equality is algebra --- \lean{ring} territory. The left is a walk
|
|
through the extracted code: every intermediate \lean{U128} product bounded
|
|
(no overflow --- the $2^{54}$ headroom at work), every carry accounted,
|
|
every $\times 19$ fold placed. In the companion projects this is a long
|
|
\lean{calc}-and-\lean{have} museum: dozens of small steps, each dispatched
|
|
by \lean{omega} or a bound lemma, composed into one commuting square.
|
|
|
|
\begin{worked}{the $\times 19$ fold, derived at the real weights}
|
|
The fold is where students usually first believe the whole enterprise is
|
|
black magic; five lines of weight arithmetic dispel it, at full scale.
|
|
Schoolbook multiplication of $a$ and $b$ (limbs $a_0..a_4$, $b_0..b_4$)
|
|
produces columns $c_k = \sum_{i+j=k} a_i b_j$ at weights $2^{51k}$ for
|
|
$k = 0, \dots, 8$. Columns $5$--$8$ carry weights $2^{255}, 2^{306},
|
|
2^{357}, 2^{408}$ --- all off the top of the representation. Reduce each
|
|
weight with the Chapter~\ref{ch:modular} identity
|
|
$2^{255} \equiv 19$:
|
|
\[
|
|
2^{51(k+5)} \;=\; 2^{255} \cdot 2^{51k} \;\equiv\; 19 \cdot 2^{51k}
|
|
\pmod{p},
|
|
\qquad k = 0, 1, 2, 3 .
|
|
\]
|
|
So column $5$ re-enters at weight $2^{0}$ scaled by $19$, column $6$ at
|
|
weight $2^{51}$ scaled by $19$, and so on --- each overflow column lands
|
|
exactly one radix position below where it left, times $19$. The folded
|
|
five-column result is therefore
|
|
\[
|
|
\begin{array}{lcl}
|
|
r_0 &=& c_0 + 19\, c_5\\
|
|
r_1 &=& c_1 + 19\, c_6\\
|
|
r_2 &=& c_2 + 19\, c_7\\
|
|
r_3 &=& c_3 + 19\, c_8\\
|
|
r_4 &=& c_4
|
|
\end{array}
|
|
\]
|
|
--- which, written with the products expanded ($r_0 = a_0 b_0 +
|
|
19(a_1 b_4 + a_2 b_3 + a_3 b_2 + a_4 b_1)$, etc.), is
|
|
\emph{character-for-character} the mysterious formula block in
|
|
\code{curve25519-dalek}'s \code{mul}, the one decorated with the comment
|
|
``see the comment above'' that every reader of that file has squinted at.
|
|
You have now derived it. Two bound checks make it safe, both already
|
|
yours: each $19 c_k < 2^{5} \cdot 2^{111} = 2^{116}$ (the
|
|
Chapter~\ref{ch:automation} column bound plus five bits for the $19$),
|
|
still comfortably inside a \lean{U128}. And the formal proof of
|
|
\lean{mul}'s commuting square is precisely this box: the four weight
|
|
identities as \lean{have}s, the bound checks as \lean{omega} goals, and
|
|
\lean{ring} to shuffle the expanded polynomial.
|
|
\end{worked}
|
|
|
|
One more representational dialect, because you will meet it in the Pasta
|
|
repos: \textbf{Montgomery form} stores $x$ as $x \cdot R \bmod p$ (with
|
|
$R = 2^{256}$) because it makes reduction after multiplication cheap. The
|
|
bridge absorbs the twist without complaint --- define
|
|
$\denote{a}_{\mathrm{M}} = (\text{positional value of } a) \cdot R^{-1}$
|
|
and the same commuting squares govern everything. Denotation is a
|
|
\emph{policy about meaning}, and it bends to fit the representation, not
|
|
the other way around.
|
|
|
|
\begin{worked}{the cast --- moving an equation from $\N$ to the clock face}
|
|
Every denotation proof ends with the same quiet move --- ``now reduce
|
|
mod $p$'' --- and in a proof assistant that move must be performed, not
|
|
gestured at. Here is the by-hand version of what \lean{push_cast} and
|
|
friends do, on the Interlude's own equation. You hold an \emph{exact}
|
|
identity of natural numbers:
|
|
\[
|
|
\underbrace{v}_{\text{output value}} + 15\,c_2
|
|
= \underbrace{(a_0 + 4a_1)}_{\denote{a}\text{'s integer}} +
|
|
\underbrace{(b_0 + 4b_1)}_{\denote{b}\text{'s integer}}
|
|
\qquad\text{in } \N .
|
|
\]
|
|
You want its shadow in $\Zmod{15}$. The bridge is the \emph{cast
|
|
homomorphism} $\iota : \N \to \Zmod{15}$ (send each number to its clock
|
|
position), and the three facts that make it usable:
|
|
(i)~$\iota(x + y) = \iota(x) + \iota(y)$ and
|
|
$\iota(x \cdot y) = \iota(x)\,\iota(y)$ --- casting commutes with
|
|
arithmetic, so the equation's \emph{shape} survives;
|
|
(ii)~equal naturals cast to equal clock positions --- so the equation's
|
|
\emph{truth} survives; (iii)~$\iota(15) = 0$ --- the modulus dies.
|
|
Apply (ii), then distribute $\iota$ through both sides by (i), then
|
|
kill the $15$ by (iii):
|
|
\[
|
|
\iota(v) + 0 \cdot \iota(c_2) = \iota(a_0) + 4\,\iota(a_1) +
|
|
\iota(b_0) + 4\,\iota(b_1)
|
|
\quad\Longrightarrow\quad
|
|
\denote{\text{out}} = \denote{a} + \denote{b}. \;\blacksquare
|
|
\]
|
|
That is all \lean{push_cast} does: drive $\iota$ inward through $+$ and
|
|
$\times$ by fact (i), normalizing the statement so facts (ii)/(iii) can
|
|
fire. Knowing the hand version buys you two things. You can
|
|
\emph{predict} when the tactic will fail --- fact (i) has no clause for
|
|
subtraction or division in $\N$ (truncation! Chapter~\ref{ch:lean}), so
|
|
casts do not push through them, which is precisely why the proof
|
|
pathway insists on an exact identity with the correction term
|
|
\emph{added on the left} rather than subtracted on the right. And you
|
|
can \emph{read} the real proofs' cast steps as what they are: the
|
|
one-line ceremony where a theorem about machine words becomes a theorem
|
|
about field elements --- the bridge being crossed, visibly, in both
|
|
media.
|
|
\end{worked}
|
|
|
|
\begin{pitfall}
|
|
When a denotation proof refuses to close, the failure is information ---
|
|
read it like a detective, in order: (1) Is the \emph{bound} hypothesis
|
|
strong enough for the intermediate products? (Count bits, on paper.)
|
|
(2) Is the \emph{denotation} right for this representation --- radix, limb
|
|
count, Montgomery factor? (3) Only then suspect the code. In the companion
|
|
projects this checklist ran hundreds of times; its order reflects the actual
|
|
base rates of what was wrong.
|
|
\end{pitfall}
|
|
|
|
\begin{tryit}
|
|
Open \code{exercises/Ch09.lean}. It builds a miniature of the whole story
|
|
you can hold in your head: a \emph{2-limb, radix-4} representation of
|
|
$\Zmod{15}$ (limbs are values $0$--$3$, denotation $a_0 + 4a_1$, and
|
|
$16 \equiv 1$ makes the fold trivial). You will write \lean{denote}, prove
|
|
the commuting square for the provided \lean{add} with carry, then for
|
|
\lean{mul} with its fold --- every conceptual ingredient of the dalek proof,
|
|
at a scale where \lean{decide} can double-check your work.
|
|
\end{tryit}
|
|
|
|
\section*{Exercises}
|
|
|
|
\exercise{Compute by hand the denotation of the limb arrays $(19,0,0,0,0)$
|
|
and $(0,0,0,0,2^{51})$ in the radix-51 system, reducing mod $p = 2^{255}-19$.
|
|
Conclude that $\denote{\cdot}$ is not injective by exhibiting the collision.}
|
|
|
|
\exercise{In the mini-system of the Try It box, find two distinct limb pairs
|
|
denoting the same element of $\Zmod{15}$, and check that the provided
|
|
\lean{add} treats them interchangeably \emph{as far as denotation goes} ---
|
|
compute both sides.}
|
|
|
|
\exercise{Sketch the multiplication column sums $\sum_{i+j=k} a_i b_j$ for
|
|
the 2-limb system and carry out the fold $16 \equiv 1$ by hand for
|
|
$a = (3,2)$, $b = (1,3)$. Check against direct computation in $\Zmod{15}$.}
|
|
|
|
\exercise{(Paper, challenge) For the radix-51 system with limbs bounded by
|
|
$2^{54}$: bound one column $\sum_{i+j=4} a_i b_j$ of partial products and
|
|
confirm it fits a \lean{U128}. How much headroom remains? This number ---
|
|
not elegance --- is why the invariant chose $2^{54}$.}
|
|
|
|
\section*{Solutions and pathways}
|
|
\solutionsintro
|
|
|
|
\solhead{9.1}
|
|
\pathway Compute both denotations as integers, then reduce mod $p$. One of
|
|
them should ring a bell from the reduction identity.
|
|
\answer $\denote{(19,0,0,0,0)}_{\Z} = 19$. For $(0,0,0,0,2^{51})$: the
|
|
top limb sits at weight $2^{204}$, so the value is
|
|
$2^{51} \cdot 2^{204} = 2^{255} \equiv 19 \pmod p$ --- the crown identity
|
|
of Chapter~\ref{ch:modular} again. Two visibly different arrays, one
|
|
field element: $\denote{\cdot}$ is not injective, witnessed by the pair
|
|
$\big((19,0,0,0,0),\, (0,0,0,0,2^{51})\big)$. Note that the second array
|
|
even violates the ``reduced'' bound ($2^{51} \not< 2^{51}$) --- it is
|
|
exactly the kind of drifted-but-meaningful value lazy carries produce,
|
|
and the denotation handles it without complaint.
|
|
|
|
\solhead{9.2}
|
|
\pathway In the toy system, hunt for a nonzero array whose positional
|
|
value is a multiple of $15$. The largest representable value is
|
|
$3 + 4\cdot 3 = 15$ itself --- convenient.
|
|
\answer $\denote{(3,3)} = 3 + 4 \cdot 3 = 15 \equiv 0 = \denote{(0,0)}$.
|
|
Interchangeability under \lean{add} with, say, $(1,2)$: the machine runs
|
|
differ --- $\mathtt{add}\ (3,3)\ (1,2)$ produces carries, $\mathtt{add}\
|
|
(0,0)\ (1,2)$ does not --- but both outputs denote
|
|
$0 + (1 + 4\cdot 2) = 9$. (With the definitions in
|
|
\code{exercises/Ch09.lean}: $\mathtt{add}\ (3,3)\ (1,2)$: $s_0 = 4$,
|
|
$s_1 = 3 + 2 + 1 = 6$, output $(0 + 1, 2) = (1,2)$, denotation $9$ ✓;
|
|
$\mathtt{add}\ (0,0)\ (1,2) = (1,2)$, denotation $9$ ✓.) The spec never
|
|
promised equal \emph{arrays} --- only equal \emph{meanings}; representation
|
|
freedom is preserved by every operation, which is the entire content of
|
|
``the square commutes.''
|
|
|
|
\solhead{9.3}
|
|
\pathway Write the three columns of the $2\times 2$ schoolbook product,
|
|
fold the top one with $16 \equiv 1$, then cross-check against a direct
|
|
computation in $\Zmod{15}$ --- at toy scale you can afford both routes,
|
|
which is the point of having a toy.
|
|
\answer For $a = (3,2)$, $b = (1,3)$: columns
|
|
$c_0 = a_0 b_0 = 3$, $c_1 = a_0 b_1 + a_1 b_0 = 9 + 2 = 11$,
|
|
$c_2 = a_1 b_1 = 6$ at weights $1, 4, 16$. Fold: $16 \equiv 1$, so $c_2$
|
|
re-enters at weight $1$:
|
|
\[
|
|
\text{value} \equiv (c_0 + c_2) + 4 c_1 = 9 + 44 = 53 \equiv 53 - 45 = 8
|
|
\pmod{15}.
|
|
\]
|
|
Direct route: $\denote{a} = 3 + 8 = 11$, $\denote{b} = 1 + 12 = 13$;
|
|
$11 \cdot 13 = 143 = 9 \cdot 15 + 8 \equiv 8$ ✓. Both routes, one
|
|
answer --- you have closed a commuting square numerically. The Lean
|
|
theorem \lean{mulVal_spec} is this computation with the numbers replaced
|
|
by universally quantified variables: same fold, all inputs at once.
|
|
|
|
\solhead{9.4}
|
|
\pathway This is the Chapter~\ref{ch:automation} worked example ---
|
|
reconstruct it from memory before checking back.
|
|
\answer Each product $a_i b_j < 2^{54} \cdot 2^{54} = 2^{108}$; column
|
|
$c_4$ has five of them, so $c_4 < 5 \cdot 2^{108} < 2^{111} < 2^{128}$,
|
|
leaving $17$ bits of headroom --- consumed, in the real code, by the
|
|
carry-in from the previous column and the $\times 19$ fold factor
|
|
($19 < 2^5$: the fold costs five of the seventeen bits, which is why the
|
|
margin looks generous and is not). Run the same audit at bound $2^{55}$:
|
|
$5 \cdot 2^{110} < 2^{113}$, fold to $2^{118}$ --- still fits, but the
|
|
\emph{addition} chain budget halves; at $2^{63}$: $5 \cdot 2^{126} >
|
|
2^{128}$ --- broken outright. The invariant $2^{54}$ is where the
|
|
operations' competing demands settle, and now you have audited the
|
|
settlement yourself.
|
|
|
|
\begin{checkpoint}
|
|
You should now be able to: write the radix-51 denotation from memory; draw
|
|
the commuting square and label which side owns bounds and \lean{Result}s;
|
|
explain why many-to-one representation is both the performance trick and
|
|
the bug habitat; and recognize the two-clause shape (bounds propagation +
|
|
value equation) as the universal skeleton of implementation-correctness
|
|
theorems.
|
|
\end{checkpoint}
|