Skip to main content

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 16

This module extends module 12, which built the spectral test for t=2t = 2 and t=3t = 3. 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 t=3t = 3. 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 t=3t = 3 and collapse at t=5t = 5, and Knuth's Table 1 grades every multiplier through t=6t = 6 or beyond for exactly that reason. By the end of this module you will compute νt\nu_t and μt\mu_t exactly for 2≤t≤62 \le t \le 6, 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:

νt2=min⁡{ u12+⋯+ut2:u1+au2+⋯+at−1ut≡0(modm), u≠0 },\nu_t^2 = \min\{\, u_1^2 + \cdots + u_t^2 : u_1 + a u_2 + \cdots + a^{t-1} u_t \equiv 0 \pmod{m},\ u \ne 0 \,\},

the squared length of the shortest nonzero vector of the dual lattice; the widest gap between adjacent hyperplanes covering the tt-tuple point set is m/νtm/\nu_t.


1. Recap — and why higher dimensions are a different animal​

Module 12 solved t=2t = 2 and t=3t = 3 with two ad-hoc weapons:

  • t=2t = 2: 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 α2−∣αβ∣+β2≥1\alpha^2 - |\alpha\beta| + \beta^2 \ge 1 — a rank-2 miracle.
  • t=3t = 3: a certified plane scan. Choose (u2,u3)(u_2, u_3) freely, pin u1u_1 to its centered residue, scan a square of side 2B2B, and grow BB until B2≥bestB^2 \ge \text{best}. Cost ≈B2\approx B^2, with B≈ν3≤21/6m1/3B \approx \nu_3 \le 2^{1/6} m^{1/3} — a few thousand for m≈231m \approx 2^{31}. Cheap.

Both weapons break at higher t, for different and instructive reasons.

The scan explodes. The same centered-residue scan at dimension tt costs Bt−1B^{t-1} with B≈νt≈m1/tB \approx \nu_t \approx m^{1/t}. At t=6t = 6, m=231−1m = 2^{31} - 1 that is (2⋅46+1)5≈7×109(2 \cdot 46 + 1)^5 \approx 7 \times 10^9 norm evaluations for a single multiplier — and squaring the modulus doubles the exponent's pain. The polynomial got the wrong variable: exponential in tt, and tt 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 tt up to 8 by refusing to choose between the weapons and instead composing them:

  1. Reduce the basis by cheap, exact, unimodular transformations. This does not prove anything about the minimum, but it makes every basis vector short.
  2. 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 t=6t = 6, between 9 and 729 candidates. Total.

Fixed tt means fixed, small exponent: the exponential lives in tt, and for t≤8t \le 8 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 t×tt \times t integer matrices, transformed in lockstep. (Rows are basis vectors throughout.)

The V-basis (dual side). The dual lattice Lt∗={u∈Zt:u1+au2+⋯+at−1ut≡0(modm)}L^*_t = \{ u \in \mathbb{Z}^t : u_1 + a u_2 + \cdots + a^{t-1} u_t \equiv 0 \pmod{m} \} is generated by the rows

v1=( m, 0, 0, …, 0 )v2=( −(a mod m), 1, 0, …, 0 )v3=( −(a2 mod m), 0, 1, …, 0 )  ⋮vt=( −(at−1 mod m), 0, …, 0, 1 )\begin{aligned} v_1 &= (\,m,\ 0,\ 0,\ \ldots,\ 0\,) \\ v_2 &= (\,-(a \bmod m),\ 1,\ 0,\ \ldots,\ 0\,) \\ v_3 &= (\,-(a^2 \bmod m),\ 0,\ 1,\ \ldots,\ 0\,) \\ &\ \ \vdots \\ v_t &= (\,-(a^{t-1} \bmod m),\ 0,\ \ldots,\ 0,\ 1\,) \end{aligned}

Lemma D. These rows generate exactly Lt∗L^*_t, and det⁡V=m\det V = m.

Proof. Each row satisfies the congruence: −(ai−1 mod m)+ai−1⋅1≡0(modm)-(a^{i-1} \bmod m) + a^{i-1} \cdot 1 \equiv 0 \pmod{m}. Conversely take any u∈Lt∗u \in L^*_t. Subtract u2v2+⋯+utvtu_2 v_2 + \cdots + u_t v_t from uu: all coordinates after the first become zero, and the first becomes u1+∑i≥2ui(ai−1 mod m)u_1 + \sum_{i \ge 2} u_i (a^{i-1} \bmod m), which the congruence forces to be a multiple kmk m — that is kv1k v_1. So u=kv1+u2v2+⋯+utvtu = k v_1 + u_2 v_2 + \cdots + u_t v_t. The matrix is lower triangular with diagonal (m,1,…,1)(m, 1, \ldots, 1), so det⁡V=m\det V = m — which is right: one congruence mod mm cuts Zt\mathbb{Z}^t down to a sublattice of index mm. ∎

This is module 12's basis (m,0)(m, 0), (−a,1)(-a, 1) grown up: one row to absorb the modulus, one row per free coordinate.

The U-basis (primal side). Pair VV with

u1=( 1, a mod m, a2 mod m, …, at−1 mod m )ui=m eifor i=2,…,t.\begin{aligned} u_1 &= (\,1,\ a \bmod m,\ a^2 \bmod m,\ \ldots,\ a^{t-1} \bmod m\,) \\ u_i &= m\, e_i \qquad \text{for } i = 2, \ldots, t. \end{aligned}

u1u_1 is the generating tuple from module 12's lattice theorem (the tt consecutive outputs starting at x=1x = 1) and the meim e_i absorb the mod-mm wraparounds; the rows generate m⋅m \cdot (t-tuple lattice), determinant mt−1m^{t-1}.

Theorem (the anchor identity). UV⊤=mIU V^\top = m I.

Proof. Entry (i,j)(i, j) is ui⋅vju_i \cdot v_j. Three cases: u1⋅v1=1⋅m=mu_1 \cdot v_1 = 1 \cdot m = m; for j≥2j \ge 2, u1⋅vj=1⋅(−(aj−1 mod m))+(aj−1 mod m)⋅1=0u_1 \cdot v_j = 1 \cdot (-(a^{j-1} \bmod m)) + (a^{j-1} \bmod m) \cdot 1 = 0; and for i≥2i \ge 2, ui⋅vj=m(vj)i=mδiju_i \cdot v_j = m (v_j)_i = m \delta_{ij}, because beyond its first coordinate vjv_j has a single nonzero entry, the 1 in position jj. ∎

Why carry U at all, when V alone defines the problem? Two reasons, and they structure the whole module:

  1. UV⊤=mIU V^\top = m I 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 O(t2)O(t^2) integer check that certifies, at any moment, that UU and VV still describe the same exact dual pair. Every stage test hammers it.
  2. UU's rows are the search bounds. The enumeration box of stage 3 is computed from the inverse of VV — and UU is m(V−1)⊤m (V^{-1})^\top, 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 VV, UU with UV⊤=mIU V^\top = m I, shorten VV'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 VV-update is V←EVV \leftarrow E V with E=I−q eiej⊤E = I - q\, e_i e_j^\top; the UU-update is U←FUU \leftarrow F U with F=I+q ejei⊤F = I + q\, e_j e_i^\top. Then

FE⊤=(I+q ejei⊤)(I−q ejei⊤)=I−q2 ej(ei⊤ej)ei⊤=I,F E^\top = (I + q\, e_j e_i^\top)(I - q\, e_j e_i^\top) = I - q^2\, e_j (e_i^\top e_j) e_i^\top = I,

because the two linear terms cancel and ei⊤ej=0e_i^\top e_j = 0 (i≠ji \ne j). Hence

U′V′⊤=FU(EV)⊤=F(UV⊤)E⊤=m FE⊤=mI.■U' V'^\top = F U (E V)^\top = F (U V^\top) E^\top = m\, F E^\top = m I. \qquad \blacksquare

Note the shape of the pairing: shearing row ii of VV by row jj is undone, on the other side of the identity, by shearing row j of UU by row i — indices crossed, sign flipped. Get either wrong and the product picks up a q⋅(…)q \cdot (\ldots) term; the tests will catch it, but the matrix identity tells you why it must be exactly this update. Both EE and FF 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 2∣d∣>n2|d| > n. Writing the new norm out:

∣vi−qvj∣2=∣vi∣2−2qd+q2n=∣vi∣2+q(qn−2d).|v_i - q v_j|^2 = |v_i|^2 - 2 q d + q^2 n = |v_i|^2 + q (q n - 2 d).

With q=round⁡(d/n)q = \operatorname{round}(d/n) and ∣d/n∣>1/2|d/n| > 1/2 we have q≠0q \ne 0 and ∣q−d/n∣≤1/2|q - d/n| \le 1/2, so qnq n lies within n/2n/2 of dd, whence qn−2dq n - 2 d has the opposite sign of qq (e.g. q≥1q \ge 1 gives qn−2d≤(d+n/2)−2d=n/2−d<0q n - 2 d \le (d + n/2) - 2 d = n/2 - d < 0). The correction term q(qn−2d)q (q n - 2 d) is therefore strictly negative: every applied transformation strictly decreases ∑i∣vi∣2\sum_i |v_i|^2, 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 round⁡(d/n)≠0\operatorname{round}(d/n) \ne 0, the tie 2∣d∣=n2|d| = n lets q=±1q = \pm 1 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 2∣vi⋅vj∣≤min⁡(∣vi∣2,∣vj∣2)2|v_i \cdot v_j| \le \min(|v_i|^2, |v_j|^2). For t=2t = 2 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 ν22\nu_2^2 values exactly. For t≥3t \ge 3 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 ≈m1/t\approx m^{1/t} instead of mm, which is what makes the next section's box tiny.


4. Hand trace: RANDU's t=3t = 3 basis collapses onto (9,−6,1)(9, -6, 1)​

Do this on paper once; it is the module's Fibonacci-trace moment. RANDU: a=65539a = 65539, m=231=2147483648m = 2^{31} = 2147483648, a2 mod m=393225a^2 \bmod m = 393225 (=6a−9= 6a - 9 — the algebra of doom, since (a−3)2=232(a - 3)^2 = 2^{32}). Start:

v1=(2147483648, 0, 0)∥v1∥2≈4.6×1018v2=(−65539, 1, 0)∥v2∥2≈4.3×109v3=(−393225, 0, 1)∥v3∥2≈1.5×1011\begin{aligned} v_1 &= (2147483648,\ 0,\ 0) & \lVert v_1 \rVert^2 &\approx 4.6 \times 10^{18} \\ v_2 &= (-65539,\ 1,\ 0) & \lVert v_2 \rVert^2 &\approx 4.3 \times 10^{9} \\ v_3 &= (-393225,\ 0,\ 1) & \lVert v_3 \rVert^2 &\approx 1.5 \times 10^{11} \end{aligned}

One sweep order (ours; any order reaches a fixpoint):

#actionqqnew rownew ∥⋅∥2\lVert \cdot \rVert^2
1v1←v1−qv2v_1 \leftarrow v_1 - q v_2−32767-32767(−32765,32767,0)(-32765, 32767, 0)2 147 221 514
2v3←v3−qv2v_3 \leftarrow v_3 - q v_266(9,−6,1)\mathbf{(9, -6, 1)}118
3v1←v1−qv3v_1 \leftarrow v_1 - q v_3−4165-4165(4720,7777,4165)(4720, 7777, 4165)100 107 354
4v2←v2−qv3v_2 \leftarrow v_2 - q v_3−4999-4999(−20548,−29993,4999)(-20548, -29993, 4999)1 346 790 354
5v2←v2−qv1v_2 \leftarrow v_2 - q v_1−3-3(−6388,−6662,17494)(-6388, -6662, 17494)391 228 824

then no pair fires: fixpoint. Two things to savor. Step 1 produced the vector (−32765,32767)(-32765, 32767) padded — RANDU's exact ν2\nu_2 witness, so 2-D reduction is embedded in the sweep. Step 2 is the catastrophe made mechanical: v3−6v2=(−393225+6⋅65539,−6,1)=(9,−6,1)v_3 - 6 v_2 = (-393225 + 6 \cdot 65539, -6, 1) = (9, -6, 1), norm2^2 118 — the single division 393225/65539≈6.0393225/65539 \approx 6.0 is the discovery that a2≡6a−9a^2 \equiv 6a - 9. 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 UU, and UV⊤=mIU V^\top = m I holds at every line of the table — the stage tests replay this.

Note the fixpoint is unbalanced: rows of norm2^2 118, 10810^8, 4×1084 \times 10^8 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 νt\nu_t? For t≥3t \ge 3, only an enumeration can say. The problem: integer combinations xVx V 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 VV).

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 min⁡{∣xV∣2:x∈Zt, x≠0}\min\{ |x V|^2 : x \in \mathbb{Z}^t,\ x \ne 0 \}.

Proof. Let best0\mathrm{best}_0 be the seed. Suppose w=xVw = x V is any nonzero lattice vector with ∣w∣2≤best0|w|^2 \le \mathrm{best}_0 (in particular any vector shorter than whatever E returns, since the returned value is ≤best0\le \mathrm{best}_0). Multiply xV=wx V = w on the right by adj⁡(V)\operatorname{adj}(V) and use Vadj⁡(V)=(det⁡V)IV \operatorname{adj}(V) = (\det V) I:

xidet⁡V=w⋅gi(Cramer’s rule, row form),x_i \det V = w \cdot g_i \qquad \text{(Cramer's rule, row form)},

so by Cauchy–Schwarz

∣xi∣=∣w⋅gi∣/∣det⁡V∣≤∣w∣⋅∣gi∣/∣d∣≤best0⋅∣gi∣/∣d∣.|x_i| = |w \cdot g_i| / |\det V| \le |w| \cdot |g_i| / |d| \le \sqrt{\mathrm{best}_0} \cdot |g_i| / |d|.

Since xix_i is an integer, ∣xi∣≤⌊best0⋅∣gi∣2/d2⌋=zi|x_i| \le \lfloor \sqrt{\mathrm{best}_0 \cdot |g_i|^2 / d^2} \rfloor = z_i (flooring the inner quotient first is harmless: for positive integers N,DN, D and integer xx, x2≤N/D  ⟺  x2≤⌊N/D⌋x^2 \le N/D \iff x^2 \le \lfloor N/D \rfloor). So xx lies in the scanned box, E3 evaluates it, and nothing outside the box can be shorter than best0\mathrm{best}_0 — i.e. than anything the box contains. The returned minimum is the true one. The zero vector never wins: x=0x = 0 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 VV beyond invertibility. The bound is rigorous for any basis. Reduction enters only as an economic force — it makes ∣gi∣/∣d∣|g_i|/|d| small so the box is small. Concretely, with the pipeline of stage 4 (measured, not estimated):

generatorttbox bounds zzcandidates scanned
RANDU3(0,0,1)(0, 0, 1)3
RANDU6(0,0,1,1,1,1)(0, 0, 1, 1, 1, 1)81
16807, m=231−1m = 2^{31}-16(0,0,0,1,0,1)(0, 0, 0, 1, 0, 1)9
48271, m=231−1m = 2^{31}-16(1,1,1,1,1,1)(1, 1, 1, 1, 1, 1)729

Three candidates to certify RANDU's 118. The same box computed on the unreduced VV would have edges in the millions — reduction is the whole economy. (A ziz_i of 0 is the certificate saying: no vector shorter than the best row uses row ii at all.)

Where U went. From UV⊤=mIU V^\top = m I and det⁡V=±m\det V = \pm m you can derive adj⁡(V)=±U⊤\operatorname{adj}(V) = \pm U^\top (exercise H1): the cofactor columns gig_i are the rows of the primal basis, up to sign. So E2's bounds are Knuth's step-S8 bounds zk=⌊⌊uk⋅uk⋅s/m2⌋⌋z_k = \lfloor \sqrt{\lfloor u_k \cdot u_k \cdot s / m^2 \rfloor} \rfloor, 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: μt\mu_t for 2≤t≤62 \le t \le 6​

Raw νt\nu_t values are incomparable across tt and mm. Knuth's normalization (§3.3.4, Eq. (37)) is the volume of the tt-ball of radius νt\nu_t per lattice point of the dual (one point per volume mm):

μt=πt/2νtt/(Γ(t/2+1)⋅m).\mu_t = \pi^{t/2} \nu_t^t / (\Gamma(t/2 + 1) \cdot m).

The half-integer Gamma values are exact for t≤6t \le 6 — from Γ(1/2)=π\Gamma(1/2) = \sqrt{\pi} and Γ(x+1)=xΓ(x)\Gamma(x+1) = x \Gamma(x):

ttΓ(t/2+1)\Gamma(t/2 + 1)μt\mu_t closed form
2Γ(2)=1\Gamma(2) = 1πν2/m\pi \nu^2 / m
3Γ(5/2)=3π/4\Gamma(5/2) = 3\sqrt{\pi}/4(4π/3)ν3/m(4\pi/3) \nu^3 / m
4Γ(3)=2\Gamma(3) = 2π2ν4/(2m)\pi^2 \nu^4 / (2m)
5Γ(7/2)=15π/8\Gamma(7/2) = 15\sqrt{\pi}/8(8π2/15)ν5/m(8\pi^2/15) \nu^5 / m
6Γ(4)=6\Gamma(4) = 6π3ν6/(6m)\pi^3 \nu^6 / (6m)

t=2t = 2 and t=3t = 3 are module 12's μ2\mu_2 and μ3\mu_3 — your mu_t must reproduce those values to nine decimals. Knuth's rule of thumb stands: μt≥0.1\mu_t \ge 0.1 passes, μt≥1\mu_t \ge 1 is excellent; Hermite's constants (γ4=2\gamma_4 = \sqrt{2}, γ5=81/5\gamma_5 = 8^{1/5}, γ6=(64/3)1/6\gamma_6 = (64/3)^{1/6}, continuing module 12's γ2,γ3\gamma_2, \gamma_3) cap νt2\nu_t^2 at γtm2/t\gamma_t m^{2/t} and hence cap μt\mu_t at a small constant per tt.

The verdicts you will compute (compare Knuth's Table 1):

generatorν22\nu_2^2ν32\nu_3^2ν42\nu_4^2ν52\nu_5^2ν62\nu_6^2μ2\mu_2μ3\mu_3μ4\mu_4μ5\mu_5μ6\mu_6
toy, a=137a = 137, m=256m = 2562743014643.362.693.781.811.29
RANDU, m=231m = 2^{31}2 147 221 5141181161161163.142.5×10−62.5 \times 10^{-6}3.1×10−53.1 \times 10^{-5}3.6×10−43.6 \times 10^{-4}3.8×10−33.8 \times 10^{-3}
16807, m=231−1m = 2^{31}-1282 475 250408 19721 6824 4398950.410.511.083.221.73
48271, m=231−1m = 2^{31}-11 990 735 3451 433 88147 4184 4041 4022.913.355.173.156.63

RANDU never heals. Pad (9,−6,1)(9, -6, 1) with zeros and it stays dual, so νt2≤118\nu_t^2 \le 118 for every t≥3t \ge 3. In fact at t=4t = 4 something slightly shorter appears: think of dual vectors as polynomials — uu is dual iff P(x)=u1+u2x+⋯+utxt−1P(x) = u_1 + u_2 x + \cdots + u_t x^{t-1} satisfies P(a)≡0(modm)P(a) \equiv 0 \pmod{m}. Since (a−3)2=232≡0(a - 3)^2 = 2^{32} \equiv 0, every polynomial multiple of (x−3)2(x - 3)^2 is dual, and

(x−3)2(x+1)=x3−5x2+3x+9  ⟹  u=(9,3,−5,1),(x - 3)^2 (x + 1) = x^3 - 5x^2 + 3x + 9 \implies u = (9, 3, -5, 1),

norm2^2 81+9+25+1=116<11881 + 9 + 25 + 1 = 116 < 118. Then it freezes: ν42=ν52=ν62=116\nu_4^2 = \nu_5^2 = \nu_6^2 = 116. The μt\mu_t creep upward (2.5×10−6→3.8×10−32.5 \times 10^{-6} \to 3.8 \times 10^{-3}) only because the ball-volume yardstick weakens with tt while ν\nu 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: μ\mu hovers between 0.4 and 3.2. Its 1993 replacement 48271 — same cost per step, found by spectral search — is excellent at every tt (min μ=2.91\mu = 2.91, 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 t=5t = 5, 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 νt+1≤νt\nu_{t+1} \le \nu_t always (pad a dual vector with a zero — module 12's exercise W1), but μ\mu is not monotone in tt; 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; t≤8t \le 8 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 tt and the bit size, a basis whose first vector is within 2(t−1)/22^{(t-1)/2} of the true minimum. The guarantee is exponentially loose in tt — yet usually far better in practice, and its base case (t=2t = 2) 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 i128 end 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 m≤231m \le 2^{31}, norms below m2<262m^2 < 2^{62}, adjugate-times-norm products comfortably inside i128 for reduced bases. Floating point appears exactly once, in μt\mu_t, after the integer answer is frozen.
  • A pair of bases, not one. UV⊤=mIU V^\top = m I gives (1) a machine-checkable invariant threading every transformation, and (2) the exact inverse of VV — 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 2∣d∣>n2|d| > n. The strict inequality makes ∑∣vi∣2\sum |v_i|^2 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 adj⁡(V)=±U⊤\operatorname{adj}(V) = \pm U^\top — literally already in memory.
  • Stop at t=6t = 6. Γ(t/2+1)\Gamma(t/2 + 1) stays a five-entry table, i128 bounds are trivially safe, and every test certifies in milliseconds. The architecture is t=8t = 8-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 aa reduced mod mm as you multiply (pow = pow * a % m in i128) — the tests pin the reduced entries, e.g. RANDU's third dual row is (−393225,0,1)(-393225, 0, 1), not (−4295360521,0,1)(-4295360521, 0, 1). check_duality computes all t2t^2 dot products and also rejects mismatched shapes without panicking. The tests verify the identity for the four benchmark generators through t=6t = 6, check every V-row against an independent dual-vector predicate, and expand det⁡V\det V by cofactors for t≤3t \le 3 to see the mm on the diagonal.

Stage 2 — reduce_basis​

Algorithm R verbatim. The two classic bugs: transforming on the tie 2∣d∣=n2|d| = n (never terminates — see §3), and mis-pairing the mirror update (it is uj+=quiu_j \mathrel{+}= q u_i, indices crossed, sign plus; anything else breaks UV⊤=mIU V^\top = m I 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 n>0n > 0. The tests check the invariant after reduction for all four generators up to t=6t = 6, demand ∑∣vi∣2\sum |v_i|^2 strictly dropped, reproduce module 12's ν22\nu_2^2 exactly at t=2t = 2 (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 x=0x = 0, and optionally halve the work by forcing the first nonzero coefficient positive. Keep the partial sum ∑xlvl\sum x_l v_l incremental if you like speed; the boxes are so small after reduction that correctness is the only real concern. Tests: the t=2t = 2 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 2≤t≤62 \le t \le 6. For μt\mu_t, implement the Γ\Gamma table of §6 exactly — the tests check t=2,3t = 2, 3 against module 12's frozen nine-decimal values and t=4,5,6t = 4, 5, 6 against independently expanded closed forms, so a wrong Gamma constant cannot hide. Then the verdicts: RANDU ≤118\le 118 forever (and exactly 116 from t=4t = 4), 48271 above 0.29 at every tt.


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 ~2382^{38} 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 tt 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 tt dimensions — the empirical shadow of small νt\nu_t). 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 m=231−1m = 2^{31} - 1. L'Ecuyer's tables (Mathematics of Computation, 1999) list multipliers for moduli from 282^8 to 2642^{64} ranked by the worst normalized spectral value over t≤8t \le 8 (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 m≤231m \le 2^{31} 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 NN" 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):

  1. The mirror update in R3 is uj+=quiu_j \mathrel{+}= q u_i. Why not ui−=quju_i \mathrel{-}= q u_j, which looks more symmetric? Hint: write both as matrix multiplications U←FUU \leftarrow F U and compute FE⊤F E^\top for each candidate FF; only one gives II. Symmetry is exactly what the identity does not want.
  2. 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: ∣w⋅gi∣≤∣w∣⋅∣gi∣|w \cdot g_i| \le |w| \cdot |g_i|; correctness vs. box volume.
  3. Exhibit the infinite loop that R2's strict inequality prevents: find two vectors with 2∣v1⋅v2∣=∣v2∣22|v_1 \cdot v_2| = |v_2|^2 where transforming with q=±1q = \pm 1 swaps you between two bases of equal total norm. Hint: try v1=(1,1)v_1 = (1, 1), v2=(0,2)v_2 = (0, 2)... or module 12's α=β=12\alpha = \beta = \tfrac{1}{2} boundary.
  4. Prove νt+1≤νt\nu_{t+1} \le \nu_t (one line), then explain why μt\mu_t still may go up with tt — as it does for every generator in §6's table. Hint: pad with a zero; then look at how πt/2/Γ(t/2+1)\pi^{t/2}/\Gamma(t/2+1) and the exponent on ν\nu trade off against the fixed determinant mm.
  5. For RANDU, every polynomial multiple of (x−3)2(x - 3)^2 gives a dual vector in the dimension matching its degree. Why can't some degree-5 multiple of (x−3)2(x - 3)^2 beat norm2^2 116 — how does your ν62\nu_6^2 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.RatingStatement
▶H1M15From UV⊤=mIU V^\top = m I and det⁡V=±m\det V = \pm m, prove adj⁡(V)=±U⊤\operatorname{adj}(V) = \pm U^\top, so E2's cofactor columns are the primal rows. (Hence Knuth's zkz_k bound in S8 and ours are identical integers.)
H2M18Prove the floor lemma used by E2: for positive integers N,DN, D and integer x≥0x \ge 0, x2≤N/D  ⟺  x2≤⌊N/D⌋x^2 \le N/D \iff x^2 \le \lfloor N/D \rfloor; conclude ⌊N/D⌋=⌊⌊N/D⌋⌋\lfloor \sqrt{N/D} \rfloor = \lfloor \sqrt{\lfloor N/D \rfloor} \rfloor. Where would a naive float sqrt break this?
▶H3M25RANDU's polynomial ideal: show uu is dual for (a,m)=(65539,231)(a, m) = (65539, 2^{31}) whenever its polynomial is a multiple of (x−3)2(x-3)^2; derive (9,3,−5,1)(9, 3, -5, 1) from (x−3)2(x+1)(x-3)^2(x+1); then search all multiples (x−3)2(c0+c1x+c2x2)(x-3)^2 (c_0 + c_1 x + c_2 x^2) with small cic_i and verify none beats 116 — matching your ν52,ν62\nu_5^2, \nu_6^2.
H422Instrument Algorithm E: print zz and the box volume for the four benchmark generators at t=2..6t = 2..6, unreduced vs. reduced. Quantify "reduction is the whole economy".
▶H5M28Prove the t=2t = 2 fixpoint claim of §3: pairwise-reduced in both orders   ⟹  \implies the shorter row is a shortest lattice vector. (Adapt module 12's $\alpha^2 -
H630Extend the pipeline to t=7,8t = 7, 8 (Knuth's full range): grow the Γ\Gamma table (Γ(9/2)=105π/16\Gamma(9/2) = 105\sqrt{\pi}/16, Γ(5)=24\Gamma(5) = 24), watch i128 headroom in the adjugate products, and reproduce a Table-1-style row for 48271.
H7M32Replace 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.
H8M20From det⁡V=m\det V = m and Hermite's γ4=2\gamma_4 = \sqrt{2}, γ5=81/5\gamma_5 = 8^{1/5}, γ6=(64/3)1/6\gamma_6 = (64/3)^{1/6}, derive the caps on μ4,μ5,μ6\mu_4, \mu_5, \mu_6 and check §6's table respects them.
H925Fishman–Moore in miniature: for m=214−3m = 2^{14} - 3, search all multipliers maximizing min⁡(μ2,…,μ6)\min(\mu_2, \ldots, \mu_6) 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 FE⊤=IF E^\top = I (§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 — ∑∣vi∣2\sum |v_i|^2 under the strict trigger 2∣d∣>n2|d| > n (§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 B2≥bestB^2 \ge \text{best} 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 — adj⁡(V)=±U⊤\operatorname{adj}(V) = \pm U^\top (§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 — x2≤N/D  ⟺  x2≤⌊N/D⌋x^2 \le N/D \iff x^2 \le \lfloor N/D \rfloor (§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 t=2t = 2 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-tt 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 2(t−1)/22^{(t-1)/2}-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 νt\nu_t bounds structurally; module 12's §9 told that story, and it continues wherever a new generator ships.