Module 16 — The Spectral Test in Higher Dimensions
Source: The Art of Computer Programming, Vol. 2, 3rd ed., §3.3.4, Algorithm S (the general spectral test). Lab:
labs/module-16-spectral-hd· Grade it:./grade 16This module extends module 12, which built the spectral test for and . The lesson below recaps what it needs, but read module 12's lesson first — its lattice theorem, dual vectors, and certified search are assumed vocabulary here. If you own Vol. 2, this module is Algorithm S itself, simplified but honest: the same reduce-then-enumerate architecture, the same exact arithmetic, the same certification logic.
Module 12 ended with a verdict machine that stops at . That is not where simulations stop: a particle-transport code draws direction and energy and time-of-flight; a numerical integrator in six dimensions consumes 6-tuples. RANDU was condemned by its triples — but a generator can be fine at and collapse at , and Knuth's Table 1 grades every multiplier through or beyond for exactly that reason. By the end of this module you will compute and exactly for , reproduce the Table-1-style verdicts for four real generators, and — the deeper prize — implement the reduce-then-enumerate architecture that underlies every modern lattice computation, from generator vetting to cryptanalysis.
The quantity is unchanged from module 12:
the squared length of the shortest nonzero vector of the dual lattice; the widest gap between adjacent hyperplanes covering the -tuple point set is .
1. Recap — and why higher dimensions are a different animal
Module 12 solved and with two ad-hoc weapons:
- : Gauss–Lagrange reduction. A greedy "subtract the best multiple of the shorter vector" loop that provably ends at a shortest vector. The optimality proof leaned on the quadratic-form inequality — a rank-2 miracle.
- : a certified plane scan. Choose freely, pin to its centered residue, scan a square of side , and grow until . Cost , with — a few thousand for . Cheap.
Both weapons break at higher t, for different and instructive reasons.
The scan explodes. The same centered-residue scan at dimension costs with . At , that is norm evaluations for a single multiplier — and squaring the modulus doubles the exponent's pain. The polynomial got the wrong variable: exponential in , and is what we are increasing.
Greedy stops being optimal. At rank 2, a locally reduced basis is globally shortest. At rank 3 and above it is not: you can size-reduce every pair of basis vectors and still not hold a shortest lattice vector in your hands (the shortest vector may need three or more rows added together). No cheap local rule certifies global optimality anymore. This is not a gap in our cleverness but the shape of the problem: the shortest-vector problem (SVP) is NP-hard as the dimension grows (under randomized reductions, Ajtai 1998; for the max-norm variant it has been NP-hard since van Emde Boas 1981). Nobody knows a polynomial algorithm, and lattice cryptography is a bet that none exists.
Knuth's answer — and this module's architecture. Algorithm S handles up to 8 by refusing to choose between the weapons and instead composing them:
- Reduce the basis by cheap, exact, unimodular transformations. This does not prove anything about the minimum, but it makes every basis vector short.
- Enumerate integer combinations of the reduced basis inside a box whose edges are certified to contain every candidate shorter than the best vector seen. Because the basis is reduced, the box is tiny — for our four benchmark generators at , between 9 and 729 candidates. Total.
Fixed means fixed, small exponent: the exponential lives in , and for the constant is microscopic. The reduction buys speed; the enumeration buys the proof; exact integer arithmetic keeps both honest.
2. The dual pair: two bases, one identity
Everything in this module happens to a pair of integer matrices, transformed in lockstep. (Rows are basis vectors throughout.)
The V-basis (dual side). The dual lattice is generated by the rows
Lemma D. These rows generate exactly , and .
Proof. Each row satisfies the congruence: . Conversely take any . Subtract from : all coordinates after the first become zero, and the first becomes , which the congruence forces to be a multiple — that is . So . The matrix is lower triangular with diagonal , so — which is right: one congruence mod cuts down to a sublattice of index . ∎
This is module 12's basis , grown up: one row to absorb the modulus, one row per free coordinate.
The U-basis (primal side). Pair with
is the generating tuple from module 12's lattice theorem (the consecutive outputs starting at ) and the absorb the mod- wraparounds; the rows generate (t-tuple lattice), determinant .
Theorem (the anchor identity). .
Proof. Entry is . Three cases: ; for , ; and for , , because beyond its first coordinate has a single nonzero entry, the 1 in position . ∎
Why carry U at all, when V alone defines the problem? Two reasons, and they structure the whole module:
- is a checkable invariant. Reduction will perform dozens of row operations; one sign slip silently corrupts the lattice and every verdict after it. The identity is an integer check that certifies, at any moment, that and still describe the same exact dual pair. Every stage test hammers it.
- 's rows are the search bounds. The enumeration box of stage 3 is computed from the inverse of — and is , kept in exact integers for free. That is precisely how Knuth's step S8 gets its bounds. (Stage 3 recomputes them from the adjugate so the function is self-contained; exercise H1 shows they are the same numbers.)
3. Algorithm R: size reduction, with the invariant proved
The transformation phase (Knuth's steps S5–S7, restructured as a sweep to a fixpoint):
Algorithm R (pairwise size reduction of the dual pair). Given square integer bases , with , shorten 's rows.
R1. [Sweep.] For each ordered pair (i, j), i ≠ j, do steps R2–R3.
R2. [Quotient.] d ← v_i·v_j, n ← v_j·v_j. If 2|d| ≤ n, the pair is
already reduced; continue with the next pair.
R3. [Transform.] q ← round(d/n) (nonzero, since |d/n| > 1/2);
v_i ← v_i − q·v_j and u_j ← u_j + q·u_i.
R4. [Done?] If a full sweep applied no transformation, terminate.
Otherwise return to R1.
Step R3 is Gauss–Lagrange's "subtract the best multiple", applied to every pair instead of the one pair rank 2 has. Three proofs carry the algorithm, and all three are stage-2 test assertions in disguise.
The invariant survives (the two-line matrix argument). The -update is with ; the -update is with . Then
because the two linear terms cancel and (). Hence
Note the shape of the pairing: shearing row of by row is undone, on the other side of the identity, by shearing row j of by row i — indices crossed, sign flipped. Get either wrong and the product picks up a term; the tests will catch it, but the matrix identity tells you why it must be exactly this update. Both and have determinant 1 (unimodular), so the two lattices — as point sets — never change; only their descriptions do.
Termination (invariant + strictly decreasing integer). R3 fires only when . Writing the new norm out:
With and we have and , so lies within of , whence has the opposite sign of (e.g. gives ). The correction term is therefore strictly negative: every applied transformation strictly decreases , a positive integer. A strictly decreasing sequence of positive integers is finite — the sweep loop halts. This is module 01's oldest proof pattern, running here on matrix norms.
The strict inequality in R2 is load-bearing. If you transform whenever , the tie lets change the basis without changing the norm, and two rows can trade the same shear back and forth forever. (The stage-2 idempotence test exists to catch exactly this.)
What the fixpoint buys — and what it doesn't. On exit every pair satisfies . For this is precisely the Gauss–Lagrange reduced condition, so module 12's optimality proof applies verbatim: the shortest row is a shortest lattice vector, and stage 2's tests hold you to module 12's values exactly. For the fixpoint guarantees no such thing — the minimum may be a combination of three or more rows — and that is not a defect of Algorithm R but the NP-hardness of §1 showing through. The fixpoint's real product is small numbers: rows of norm instead of , which is what makes the next section's box tiny.
4. Hand trace: RANDU's basis collapses onto
Do this on paper once; it is the module's Fibonacci-trace moment. RANDU: , , ( — the algebra of doom, since ). Start:
One sweep order (ours; any order reaches a fixpoint):
| # | action | new row | new | |
|---|---|---|---|---|
| 1 | 2 147 221 514 | |||
| 2 | 118 | |||
| 3 | 100 107 354 | |||
| 4 | 1 346 790 354 | |||
| 5 | 391 228 824 |
then no pair fires: fixpoint. Two things to savor. Step 1 produced the vector padded — RANDU's exact witness, so 2-D reduction is embedded in the sweep. Step 2 is the catastrophe made mechanical: , norm 118 — the single division is the discovery that . Module 12 needed number-theoretic insight to spot that identity; Algorithm R finds it by rote. Meanwhile (silently) each step also sheared a row of , and holds at every line of the table — the stage tests replay this.
Note the fixpoint is unbalanced: rows of norm 118, , coexist. That is allowed — size reduction never promised balance — and it is why the certification below must not assume near-orthogonality.
5. Algorithm E: certified enumeration
After reduction, is the best row really ? For , only an enumeration can say. The problem: integer combinations are infinite in every direction. The fix, as in module 12's stage 3, is a bound that certifies itself — but the centered-residue trick doesn't survive the climb in dimension, so we need a better one. It comes from linear algebra we can do in exact integers.
Algorithm E (certified shortest vector on a basis ).
E1. [Seed.] best ← min_i v_i·v_i; d ← det V, computed exactly
(Bareiss fraction-free elimination: every division in the
elimination is exact, so no rationals ever appear).
E2. [Bounds.] For each i: g_i ← column i of adj(V) (cofactors),
z_i ← ⌊ √( best·|g_i|² / d² ) ⌋.
E3. [Scan.] For every integer vector x ≠ 0 with |x_i| ≤ z_i for all
i: best ← min(best, |x·V|²).
E4. [Return.] best.
Theorem (the certificate). The value returned by Algorithm E is exactly .
Proof. Let be the seed. Suppose is any nonzero lattice vector with (in particular any vector shorter than whatever E returns, since the returned value is ). Multiply on the right by and use :
so by Cauchy–Schwarz
Since is an integer, (flooring the inner quotient first is harmless: for positive integers and integer , ). So lies in the scanned box, E3 evaluates it, and nothing outside the box can be shorter than — i.e. than anything the box contains. The returned minimum is the true one. The zero vector never wins: is skipped and the seed is a genuine row norm. ∎
Read the proof again and notice what it does not use: no orthogonality, no reduction quality, no property of beyond invertibility. The bound is rigorous for any basis. Reduction enters only as an economic force — it makes small so the box is small. Concretely, with the pipeline of stage 4 (measured, not estimated):
| generator | box bounds | candidates scanned | |
|---|---|---|---|
| RANDU | 3 | 3 | |
| RANDU | 6 | 81 | |
| 16807, | 6 | 9 | |
| 48271, | 6 | 729 |
Three candidates to certify RANDU's 118. The same box computed on the unreduced would have edges in the millions — reduction is the whole economy. (A of 0 is the certificate saying: no vector shorter than the best row uses row at all.)
Where U went. From and you can derive (exercise H1): the cofactor columns are the rows of the primal basis, up to sign. So E2's bounds are Knuth's step-S8 bounds , and the "abstract inverse" in Cramer's rule has been sitting in the algorithm as honest integers since stage 1. That is the second, deeper reason Algorithm S carries the pair.
6. Figures of merit: for
Raw values are incomparable across and . Knuth's normalization (§3.3.4, Eq. (37)) is the volume of the -ball of radius per lattice point of the dual (one point per volume ):
The half-integer Gamma values are exact for — from and :
| closed form | ||
|---|---|---|
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | ||
| 6 |
and are module 12's and — your mu_t must reproduce
those values to nine decimals. Knuth's rule of thumb stands:
passes, is excellent; Hermite's constants (,
, , continuing module 12's ) cap
at and hence cap at a small constant per .
The verdicts you will compute (compare Knuth's Table 1):
| generator | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| toy, , | 274 | 30 | 14 | 6 | 4 | 3.36 | 2.69 | 3.78 | 1.81 | 1.29 |
| RANDU, | 2 147 221 514 | 118 | 116 | 116 | 116 | 3.14 | ||||
| 16807, | 282 475 250 | 408 197 | 21 682 | 4 439 | 895 | 0.41 | 0.51 | 1.08 | 3.22 | 1.73 |
| 48271, | 1 990 735 345 | 1 433 881 | 47 418 | 4 404 | 1 402 | 2.91 | 3.35 | 5.17 | 3.15 | 6.63 |
RANDU never heals. Pad with zeros and it stays dual, so for every . In fact at something slightly shorter appears: think of dual vectors as polynomials — is dual iff satisfies . Since , every polynomial multiple of is dual, and
norm . Then it freezes: . The creep upward () only because the ball-volume yardstick weakens with while stays flat — RANDU fails Knuth's 0.1 rule in every dimension from 3 up, forever.
The multiplier market. 16807 (the 1969 "minimal standard") passes
everywhere but never shines: hovers between 0.4 and 3.2. Its 1993
replacement 48271 — same cost per step, found by spectral search — is
excellent at every (min , peak 6.63). This is why C++'s
std::minstd_rand uses 48271 and why "which multiplier?" is answered by
tables, not folklore. (Note the one wrinkle at , where 16807's 3.22
nudges past 48271's 3.15: good multipliers are chosen by their worst
dimension, not any single one.) And notice always (pad a
dual vector with a zero — module 12's exercise W1), but is not
monotone in ; the stage-4 tests deliberately refuse to assert any such
law.
Honest scope. Knuth's full Algorithm S adds refinements we deliberately simplify away: it grows t incrementally (each dimension's reduced pair seeds the next), orders and interleaves its transformations more cleverly than our sweep, and in careful implementations uses floating-point scaffolding for speed with exact integer verification of every step; its enumeration prunes with Cholesky-style partial sums (an ellipsoid) where we scan a box. Same architecture, better constants; was in reach on 1970s hardware. Exercises H6 and H7 walk toward it.
LLL, the polynomially-guaranteed cousin. In 1982 Lenstra, Lenstra and Lovász turned size reduction into an algorithm with a proof: LLL interleaves the pairwise shears you built with adjacent-row swaps governed by the Lovász condition, and guarantees, in polynomial time in both and the bit size, a basis whose first vector is within of the true minimum. The guarantee is exponentially loose in — yet usually far better in practice, and its base case () is exactly Gauss–Lagrange. LLL doesn't replace enumeration when you need the exact minimum (we still enumerate after reducing; so does every SVP solver), but it made "reduce first" a theorem instead of a hope. We implement none of it; you have already built its two ingredients.
7. Why it's done this way
- Exact
i128end to end. A minimum over a lattice certified by floating point certifies nothing: one misrounded cofactor and the box silently excludes the true minimum. Entries stay below , norms below , adjugate-times-norm products comfortably inside i128 for reduced bases. Floating point appears exactly once, in , after the integer answer is frozen. - A pair of bases, not one. gives (1) a machine-checkable invariant threading every transformation, and (2) the exact inverse of — the search bounds — maintained by integer shears instead of computed by fragile elimination at the end. One identity, two jobs.
- Reduce-then-enumerate. Reduction is cheap and uncertified; enumeration is certified and priced by the quality of the basis. Composed, each pays for the other's weakness. This is Knuth's Algorithm S, and it is also, three decades later, the architecture of every serious SVP solver (LLL/BKZ + enumeration or sieving).
- Sweep-to-fixpoint with the strict trigger . The strict inequality makes a strictly decreasing positive integer — a termination proof, not a hope — and makes the fixpoint stable (reduce twice, get the same pair), which the tests exploit.
- Cramer/adjugate bounds, not Gram–Schmidt. The textbook enumeration bound uses Gram–Schmidt norms, which are rationals and invite either floats (unsound) or big-rational bookkeeping (heavy). The adjugate bound is a bit looser but integer-exact by construction, provable in three lines, and — via — literally already in memory.
- Stop at . stays a five-entry table, i128 bounds are trivially safe, and every test certifies in milliseconds. The architecture is -ready; the bookkeeping to go there is exercise material, not lesson material.
8. Stage-by-stage lab guide
Open labs/module-16-spectral-hd/src/lab.rs. Run ./grade 16; stages
run in order and stop at the first failure. Everything is i128;
bases are Vec<Vec<i128>>, rows are basis vectors.
Stage 1 — dual_basis, primal_basis, check_duality
Transcribe §2's matrices. Keep powers of reduced mod as you multiply
(pow = pow * a % m in i128) — the tests pin the reduced entries, e.g.
RANDU's third dual row is , not .
check_duality computes all dot products and also rejects mismatched
shapes without panicking. The tests verify the identity for the four
benchmark generators through , check every V-row against an
independent dual-vector predicate, and expand by cofactors for
to see the on the diagonal.
Stage 2 — reduce_basis
Algorithm R verbatim. The two classic bugs: transforming on the tie
(never terminates — see §3), and mis-pairing the mirror update
(it is , indices crossed, sign plus; anything else breaks
and the very first test). Module 12's rounding helper
(2*d + n).div_euclid(2*n) is still the right nearest-integer quotient
for . The tests check the invariant after reduction for all four
generators up to , demand strictly dropped, reproduce
module 12's exactly at (both pinned values and a brute-force
grid), verify the fixpoint condition pairwise, and reduce twice to
detect idling.
Stage 3 — shortest_vector_squared
Algorithm E verbatim, on the basis alone (recompute det and adjugate —
don't assume the caller has U). Bareiss elimination for the determinant:
eliminate below the diagonal with the fraction-free update
m[i][j] = (m[i][j]·m[k][k] − m[i][k]·m[k][j]) / prev, dividing by the
previous pivot — the division is exact; remember to swap in a nonzero
pivot (flipping the sign) and to return 0 if none exists. Cofactors by
deleting a row and column and recursing into the same determinant.
For the scan, skip , and optionally halve the work by forcing the
first nonzero coefficient positive. Keep the partial sum
incremental if you like speed; the boxes are so small after reduction
that correctness is the only real concern. Tests: the grid against
module 12's brute force, RANDU's 118, a full 4-D brute force on tiny
moduli, hand-checkable bases (identity, shears), and permuted/negated
bases — the answer is a property of the lattice, not the basis.
Stage 4 — nu_t_squared and mu_t
Ten lines each. The pipeline: build the pair, reduce, search; assert . For , implement the table of §6 exactly — the tests check against module 12's frozen nine-decimal values and against independently expanded closed forms, so a wrong Gamma constant cannot hide. Then the verdicts: RANDU forever (and exactly 116 from ), 48271 above 0.29 at every .
9. In the real world
- TestU01 and BigCrush. L'Ecuyer & Simard's TestU01 (2007) is the de-facto certification battery for RNGs; BigCrush runs ~160 statistical tests over ~ outputs. The spectral test is the theoretical member of the same family: it examines the entire period at once, no sampling, but only for generators with lattice structure. The two are used together in practice — a new LCG/MRG design is first screened by spectral figures (cheap, exact, covers all up to 8–32), then battery-tested empirically; and when a plain LCG fails BigCrush, the failing tests are overwhelmingly the lattice-shaped ones (serial/ birthday-spacings collisions in dimensions — the empirical shadow of small ). PCG and xoshiro pass BigCrush precisely because a nonlinear output permutation stands between their linear state lattice and you.
- L'Ecuyer's good-multiplier tables. "Which a?" is answered by
exhaustive spectral search, and has been since Fishman–Moore's 1986
census of . L'Ecuyer's tables (Mathematics of
Computation, 1999) list multipliers for moduli from to ranked
by the worst normalized spectral value over (up to 32) — the
criterion this module taught you to compute. That is where 48271 earned
its place in
std::minstd_rand, where MRG32k3a's component multipliers come from, and where PCG's 64-bit default multiplier (Knuth's MMIX constant 6364136223846793005, beyond this module's i128-comfortable range) was vetted. When someone ships a new multiplier today, the accompanying evidence is a spectral table computed by exactly the reduce-then-enumerate machine you just built. - LLL and cryptanalysis: the same idea, weaponized. Lattice reduction escaped random-number testing long ago. LLL broke the Merkle–Hellman knapsack cryptosystem (recovering the secret from a "hard" subset-sum by finding a short vector); Coppersmith's method turns "find small roots of a polynomial mod " into lattice reduction, yielding practical attacks on low-exponent and partially-leaked RSA; biased or partially known ECDSA nonces become a hidden-number-problem lattice whose short vector is the private key (attacks that have emptied real wallets). And the defense mirrors the offense: NIST's post-quantum standards (ML-KEM/Kyber, ML-DSA/Dilithium) are lattice schemes whose parameters are sized against the best known reduce-then-enumerate/sieve pipelines — BKZ block reduction plus exact search, i.e. industrial-strength Algorithm S. The invariant-carrying dual pair, the size reduction, the certified enumeration: you have now implemented, in miniature, the standard toolkit of modern lattice cryptanalysis.
10. Check your understanding
Answer before moving on (hints follow each item):
- The mirror update in R3 is . Why not , which looks more symmetric? Hint: write both as matrix multiplications and compute for each candidate ; only one gives . Symmetry is exactly what the identity does not want.
- Where does the certification proof of Algorithm E use Cauchy–Schwarz, and why does the argument need no assumption that the basis is reduced? What would a badly non-reduced basis cost you instead? Hint: ; correctness vs. box volume.
- Exhibit the infinite loop that R2's strict inequality prevents: find two vectors with where transforming with swaps you between two bases of equal total norm. Hint: try , ... or module 12's boundary.
- Prove (one line), then explain why still may go up with — as it does for every generator in §6's table. Hint: pad with a zero; then look at how and the exponent on trade off against the fixed determinant .
- For RANDU, every polynomial multiple of gives a dual vector in the dimension matching its degree. Why can't some degree-5 multiple of beat norm 116 — how does your computation prove none exists, without reasoning about polynomials at all? Hint: what does the certificate of Algorithm E quantify over?
11. Exercises
Ratings use Knuth's scale: 00 immediate · 10 a minute · 20 fifteen
minutes to an hour · 30 hours · 40 term project. M = mathematically
oriented. Log attempts in course/module-16-spectral-hd/exercises.md.
(If you own Vol. 2, the exercises of §3.3.4 continue: exercise 7 is the
theory behind step S8's bound.)
| Ex. | Rating | Statement |
|---|---|---|
| ▶H1 | M15 | From and , prove , so E2's cofactor columns are the primal rows. (Hence Knuth's bound in S8 and ours are identical integers.) |
| H2 | M18 | Prove the floor lemma used by E2: for positive integers and integer , ; conclude . Where would a naive float sqrt break this? |
| ▶H3 | M25 | RANDU's polynomial ideal: show is dual for whenever its polynomial is a multiple of ; derive from ; then search all multiples with small and verify none beats 116 — matching your . |
| H4 | 22 | Instrument Algorithm E: print and the box volume for the four benchmark generators at , unreduced vs. reduced. Quantify "reduction is the whole economy". |
| ▶H5 | M28 | Prove the fixpoint claim of §3: pairwise-reduced in both orders the shorter row is a shortest lattice vector. (Adapt module 12's $\alpha^2 - |
| H6 | 30 | Extend the pipeline to (Knuth's full range): grow the table (, ), watch i128 headroom in the adjugate products, and reproduce a Table-1-style row for 48271. |
| H7 | M32 | Replace the box of E3 with ellipsoid pruning: compute exact Gram–Schmidt data as integer pairs (numerator, denominator) via the Gram matrix, and prune partial sums Cholesky-style as Knuth's S9–S10 do. Measure candidates scanned vs. the box. |
| H8 | M20 | From and Hermite's , , , derive the caps on and check §6's table respects them. |
| H9 | 25 | Fishman–Moore in miniature: for , search all multipliers maximizing and print the ten best. How lonely is the top? |
12. Proof techniques you practiced
- Invariant preservation by explicit matrix identity — the two-line computation (§3) upgraded "we also update U" from ritual to theorem; every stage-2 test is this identity made executable. The same move — conjugate your transformation so a bilinear form is preserved — recurs from Gram–Schmidt to symplectic integrators.
- Termination by a strictly decreasing positive integer — under the strict trigger (§3); module 01's oldest pattern, with the new lesson that the strictness of the trigger is what converts "usually terminates" into "terminates".
- Certified enumeration via an a-posteriori bound — Cramer plus Cauchy–Schwarz turned the best-so-far value into a finite, provably sufficient search region (§5), generalizing module 12's trick from a square to a box shaped by the basis. This is the correctness skeleton of branch-and-bound, exact SVP solvers, and every "prune by bound" argument you will ever write.
- Duality as computation — (§5, exercise H1): the abstract inverse in Cramer's rule recognized as an object the algorithm already maintains in exact integers. Recognizing a needed quantity as an existing invariant is a proof technique and a performance technique.
- Integrality/floor lemmas — (§5, exercise H2) let integer arithmetic stand in for real square roots without losing a single lattice point; the humble cousin of module 12's centered-residue argument.
- Reduction to a pinned lower-dimensional case — the fixpoint identified with Gauss–Lagrange (§3) imported module 12's optimality theorem wholesale, and the cross-module pinned constants (118, 282 475 250, ...) turned that identification into regression tests.
13. Where this leads
- Module 12 → here → Knuth's full Algorithm S. You now hold the entire architecture; what remains in §3.3.4 is engineering — grown- bookkeeping, transformation ordering, ellipsoid pruning (exercises H6, H7) — plus Knuth's beautiful worked Table 1.
- LLL and BKZ. Your reducer is greedy with no quality guarantee; LLL adds the Lovász swap condition and earns -approximation in polynomial time; BKZ interpolates between LLL and exact SVP by running enumeration on blocks. The reduce-then-enumerate pattern is unchanged all the way up.
- Lattice cryptography and cryptanalysis (§9): Coppersmith, hidden number problems, NIST's ML-KEM/ML-DSA — the offense and defense both priced by how well the pipeline you built scales.
- The statistical companion: §3.3.2's serial test and TestU01's batteries measure empirically what bounds structurally; module 12's §9 told that story, and it continues wherever a new generator ships.