Ternary Magic Ladder
Count in base
ord₃(10) = 0
EmergenceByDesign · instrument and paper · 10 October 2026

Ternary Magic Ladder

Each rung of this ladder adds one counting direction to the last: a row of dots, a square, a cube, a tesseract. On every rung the order-3 magic forms can be represented as a sum of ternary waves, and the base you count in decides how many of those waves you can read off a number's last digit.

The 3 × 3 magic square as 5 + wave + 3 × wave. Teal = −1, gray = 0, amber = +1. Each wave stays constant along one family of diagonals.

Proven proof written on this page
Verified exhaustive computation, receipts at the end
Imported published result, cited in Receipts
Open stated exactly, not settled
Estimate sampled figure, labeled, never a conclusion
The ladder at a glance · numbers shown in the current base
Counting in two directions

Triangle numbers and the filled center

A triangle of n rows holds T(n) = n(n + 1)/2 dots. Some triangles have a dot exactly at their center and some have a gap there. Filled and empty centers, together with odd and even counts, repeat every 12 rows.

n =4
center filledodd counttap a tile to draw it

Proven Turning the triangle by 120° carries every dot onto a dot. Dots travel in groups of three, except a dot sitting exactly at the center, which stays put. So T(n) leaves remainder 1 on division by 3 when the center is a dot and remainder 0 when it is a gap; remainder 2 never occurs. The center is a dot exactly when n leaves remainder 1 on division by 3 (n = 1, 4, 7, 10, …). The colored view shows the three congruent thirds.

Proven Odd and even repeat every 4 rows (odd, odd, even, even) and filled and empty repeat every 3, so the two together repeat every lcm(3, 4) = 12 rows. Verified for n ≤ 240.

Verified In base 12 the last digit already carries the remainder mod 3, because 3 divides 12: every filled triangle ends in 1, 4, 7 or A. In base 10 the last digit carries nothing about 3, because 10 leaves remainder 1; filled triangles end in 0, 1, 3, 5, 6 or 8 and you need the digit sum to tell. Checked for n ≤ 300.

The tetractys is n = 4: ten dots, written A in base 12, with its center filled.

A center plus copies of a triangle

s =2
s = 1 gives the 3 × 3 square; s = 2 gives the 19 cells of the magic hexagon

Proven Eight triangles of T(s) dots around one center dot make an odd square: (2s + 1)² = 8·T(s) + 1. Six of them make a hexagon: 6·T(s) + 1 = (s + 1)³ − s³. The hexagon count is a difference of cubes because a cube of side s + 1, seen straight down its long diagonal, can be represented as this hexagon: you see its three outer faces, which hold (s + 1)³ − s³ small cubes. At s = 2 that is 27 − 8 = 19.

Verified Every hexagon count leaves remainder 1 on division by 6, so in base 12 it always ends in 1 or 7; in base 10 the last digit cycles 1, 7, 9, 7, 1. Odd squares always end in 1 or 9 in base 12 (they end in 1, 9 or 5 in base 10).

d = 2 · 9 cells · 8 lines

The 3 × 3 square: one survivor

tap a cell to trace its lines

Proven The three rows hold 1 to 9 between them, so every line adds to 45 ÷ 3 = 15. The middle row, middle column and both diagonals pass through the center; together they count the center four times and every other cell once, so 4 · 15 = 45 + 3·c and the center c is 5.

Verified An exhaustive search finds 8 fillings, which are one square in its 8 orientations. Imported Known since antiquity as the Lo Shu.

Build a square from two corners

top-left offset p−3
top-right offset q1

Proven Measure every cell from 5. Each edge cell then equals minus the sum of the two corners beside it, the center equals the sum of all four corners, and the diagonals need opposite corners to cancel. Any p and q give a square whose 8 lines all add to 15. Only p, q from ±1, ±3 with |p| ≠ |q| use 1 to 9 once each, which is where the 8 orientations come from.

d = 2 · six directions

The magic hexagon and the hexagram

tap a cell or point

Proven A hexagon with n cells on each side has N = 3n² − 3n + 1 cells, and its 2n − 1 parallel rows hold all of them, so the line sum is M = N(N + 1) / (2(2n − 1)). Writing k = 2n − 1 turns this into M = (3k² + 1)(3k² + 5) / (32k), which is a whole number only when k divides 5. So n = 1 or n = 3, and for n = 3 the sum is 38.

Verified An exhaustive search finds 12 fillings, one hexagon in its 12 orientations, with 5 at the center. The center is not the middle number 10 here: the hexagon's lines have three different lengths, so the center argument used for squares, cubes and tesseracts below does not apply. Imported Uniqueness was proved by C. W. Trigg (1964); Wikipedia lists von Haselberg (1887) as the earliest reference it has.

The 19 cells correspond one to one with the 19 small cubes you see when a 3 × 3 × 3 cube is viewed down its long diagonal (the s = 2 picture above). The lines do not: a hexagon row, lifted back onto the cube, runs along one face and turns at the near corner.

Verified The hexagram's 12 points sit on 6 lines of 4, each adding to 26 (22 in base 12, where the twelve numbers are 1 to B and 10). An exhaustive search finds 960 fillings, which are 80 stars up to rotation and reflection; step through all 80 above.

Verified One rung up, the shadow of the tesseract down its long diagonal is a rhombic dodecahedron, a solid with 12 faces, and the 81 cells of the tesseract cast 65 points (Echo 4). Open A standard magic labeling of that solid or of its 65 points did not turn up in the searches made for this page; it is a natural next figure for this ladder.

d = 3 · 27 cells · 27 rows + 4 space diagonals

The magic cube of 42

1 / 4
tap a cell
layers left to right: front, middle, back · x across, y down
Wave weights γ over the 4 diagonal directions

Proven The 9 rows along one axis hold 1 to 27 between them, so each line adds to 378 ÷ 9 = 42, and the center is forced to 42 ÷ 3 = 14 (Theorem 1 below). The magic arrays of this shape have exactly 4 free numbers, one corner from each opposite pair.

Verified An exhaustive search over those 4 corner numbers finds exactly 4 cubes up to rotation and reflection. Each is a spike: weights 1, 3 and 9 on three of the four diagonal directions and 0 on the fourth. Imported The published total is 4, as listed by Heinz.

Verified Each of the 4 cubes has exactly one long-diagonal direction in which all 9 broken diagonals add to 42: the direction of its missing wave (Echo 5).

d = 4 · 81 cells · 108 rows + 8 quadragonals

The magic tesseract of 123

5 / 58
each block: x across, y down · blocks: z across, w down · tap a cell
Wave weights γ over the 8 diagonal directions

Proven The 27 rows along one axis hold 1 to 81 between them, so every line adds to 3321 ÷ 27 = 123, and the center is forced to 41. The magic arrays of this shape have exactly 8 free numbers, one corner from each opposite pair.

Verified An exhaustive search over those 8 corner numbers finds exactly 58 tesseracts up to the 384 rotations and reflections, every one a spike with weights 1, 3, 9 and 27 on four independent diagonal directions. It took 7.2 seconds in C and reproduces the published total without using it. Imported Hendricks published all 58 in 1999.

Verified Only #5 (I + J) and #21 (Hadamard) have four long-diagonal directions in which all 27 broken diagonals add to 123, and in both the active waves and those directions are the two parity classes of sign directions; for the two arrays shown here, each one's directions are the other's waves (Echo 5).

The sums do not depend on the base: 123 is A3 in base 12 and 11120 in base 3. What the base changes is which remainders show in the last digit. The last digit view colors each cell by its last digit, grouping digits by their remainder mod 3 (indigo 0, amber 1, teal 2). Products behave the same way: the multiplicative twin of these tesseracts, with 2D₁·3D₂·5D₃·7D₄ in each cell, multiplies to 210³ = 9 261 000 along every line, which is 3127460 in base 12.

every d ≥ 2

One magic form, three descriptions

Number each cell by its offsets y = (y₁, …, y_d) from the center, each −1, 0 or +1, write z(y) for how many offsets are 0, and measure every entry from M/3. The ladder's magic forms can then be represented in three equivalent ways, and the third one counts them.

What this rung establishes
  • Proven Three descriptions. An order-3 array is magic exactly when it obeys the corner rule; equivalently it can be represented as a sum of ternary waves.
  • Proven Forced structure. The center is M/3, opposite cells add to 2M/3, and the magic arrays have exactly 2d−1 free numbers.
  • Proven Energy. Entries 1 … 3d force Σγ² = (9d − 1)/8.
  • Proven Spike count. L(d) = 1, 4, 58, 2 992, 543 328 for d = 2 … 6.
  • Verified Completeness for d = 2, 3, 4. Every one is a spike: 1, 4 and 58 up to symmetry.
  • Proven Two lemmas. The active waves always span; exactly d active waves force a spike.
  • Open Spikes all the way up. Proven equivalent to: never more than d active waves. If true, exactly 2 992 magic penteracts. This search method would need about 5 × 1017 steps for d = 5 Estimate.

Statements and proofs of Theorems 1–5, Lemmas A and B and Conjecture 4.2 are in the paper, §3–5.

Build a magic array from corners or from waves

Any numbers you type give an array whose every line adds to M. Only spikes use 1 … 3d exactly once. Switching between corners and waves keeps the same array and shows its other description.

Wave weights γ
Corner offsets (one per opposite pair)

Run the receipts in your browser

Exhaustive corner search
Not run yet
Count sign-vector bases L(d)
Not run yet
the same structure elsewhere · exact, not resembling

Echoes

Each echo matches two computed objects exactly. Names and physical facts that come from outside this page are marked Imported. Full statements and proofs are in the paper (§7); every check is in echoes.py, supplement section 3.

1 · Every cell is a face of the cube

keep faces of dimension ≤1

Proven A cell with z zero offsets is the center of a z-dimensional face of the cube: corners are vertices, edge midpoints are edges, the center is the whole cube. So the grid holds C(d, z)·2d−z cells of face dimension z, and 3d = (2 + 1)d. The corner rule is Möbius inversion over these faces: every axis row adds to M exactly when every face of dimension k ≥ 1, read as its block of 3k cells, adds to 3k−1·M, and the sign (−1)z is the Möbius function from a corner to a z-dimensional face. Verified d = 2–4.

Proven Counting the boundary cells with alternating signs gives 1 − (−1)d. At d = 3 that is 8 − 12 + 6 = 2, Euler's V − E + F for the cube, with the same alternating sign the corner rule uses; counting the center as well gives 1.

Imported Keeping only the faces of dimension ≤ k, then repeating inside every kept cell, builds the ternary fractals: the Cantor set keeps the 2 end thirds (d = 1, k = 0), the Sierpinski carpet 8 of 9 squares (d = 2, k = 1), the Menger sponge 20 of 27 cubes, removing the 6 face centers and the center (d = 3, k = 1). The sponge's Hausdorff dimension is log 20 / log 3 ≈ 2.727. The widget shows the first step and the similarity dimension log3(cells kept).

2 · Nested spaces inside every tesseract, and Bachet's balance

5 / 58
1–27
tap a cell to weigh it
each block: x across, y down · blocks: z across, w down

Proven In a spike, the values 1–27, 28–54 and 55–81 each fill a diagonal 3-space, 1–9 fill a plane, 1–3 a line, and 1 a point: every aligned block of 3k consecutive values is an affine k-space. So each tesseract carries a complete flag, point ⊂ line ⊂ plane ⊂ 3-space ⊂ tesseract, and a 3-space block meets every axis row exactly once. Verified on all 58.

Proven Read each cell as one weighing on a two-pan balance with weights 1, 3, 9 and 27: weight 3k−1 sits on the right pan, off, or on the left pan as the k-th wave is +1, 0 or −1, and the cell's value minus 41 is the net load. Along every axis row each weight visits the right pan, the table and the left pan exactly once, so the three loads cancel and the row adds to 3 × 41 = 123. Imported Weighing every load from 1 to 40 with 1, 3, 9 and 27, placed in either pan, is Bachet's weights problem (1624).

3 · The energy is a triangle number, and the center lines are a projective space

Proven The lines through the center are the pairs {y, −y}, so there are m = (3d − 1)/2 of them: the points of the projective space PG(d − 1, 3) over the field with three elements. The magic condition asks for only d axes and 2d−1 long diagonals among them. On the 3 × 3 square that is all four, of slopes 0, 1, 2 and ∞: the whole projective line over GF(3).

Proven The wave energy forced by entries 1 … 3d (Theorem 2) is the triangle number of the same m: (9d − 1)/8 = m(m + 1)/2, written 11…1 in base 9. Since m always leaves remainder 1 on division by 3, each of these triangles has its center filled (Triangles, above). At d = 2 it is 10, the tetractys.

4 · Seen down the long diagonal: triangles, honeycomb, rhombic dodecahedron

tap a point
x + y + z ≡ 0≡ 1≡ 2 (mod 3)

Proven Look at the integer grid down the direction (1, 1, 1). Points that line up behind each other differ by a multiple of (1, 1, 1), so they share x + y + z mod 3, the class of the cube's long-diagonal wave. All three classes together form a triangular lattice; two classes form a honeycomb, every point with three neighbors of the other class; one class alone is a triangular lattice √3 times coarser. The 27 cells of the magic cube cast 19 shadows, the 19 cells of the magic hexagon: 7, 6 and 6 by class.

Proven Written as Eisenstein integers x + yω + zω², with ω = e2πi/3, the shadows are exactly ℤ[ω], and the class is the remainder on division by √−3 = 1 + 2ω. Imported The Eisenstein integers form a triangular lattice, and 3 = −(1 + 2ω)². Graphene's carbon atoms sit on the honeycomb: two interpenetrating triangular sublattices, A and B.

Proven The same lattice is the lattice of characters of the diagonal torus of SU(3): the central element ωI multiplies a state of weight (x, y, z) by ωx+y+z, so the three classes are the three central charges, and the six nearest points of the coarse class are the six roots. Imported Physicists call this charge triality: it is the net quark number mod 3, and it vanishes on the trivial and adjoint representations.

Verified One rung up, the tesseract seen down (1, 1, 1, 1). Its 16 corners cast 15 points, because the nearest and the farthest corner both land on the center. The outline is a rhombic dodecahedron: 14 vertices, 8 where three rhombi meet and 6 where four meet, and 12 rhombic faces whose diagonals are in ratio √2. The points are the center and the 8 nearest and 6 next-nearest points of the body-centered cubic lattice, and the 81 cells cast 65 points, every lattice point of the same solid at twice the size. Proven A grid of side k in d dimensions casts kd − (k − 1)d shadows down its long diagonal: 19 for the cube, 65 for the tesseract.

Imported The rhombic dodecahedron fills space as the Voronoi cell of the face-centered cubic lattice; garnet crystals grow in its shape; and a honeybee cell is a hexagonal prism capped with half of one, an observation credited to Kepler.

5 · Looking along a direction keeps only the waves across it

1 / 4
look along
Broken-diagonal sums · tap one to light its cells

Proven Wrap the grid around like a game screen, so that leaving through one face re-enters through the opposite one. Then the cells split into broken diagonals y, y + s, y + 2s along any sign direction s, and adding along them keeps three times every wave a with a·s ≡ 0 (mod 3) and erases every other wave. So all broken diagonals along s add to M exactly when no active wave has a·s ≡ 0. In the language of the Dimensional Ladder paper (Related work, below) these are observation fibres: every wave that varies along the line of sight is invisible in the sums.

Verified Each of the 4 magic cubes has exactly one such direction, the direction of its missing wave. Of the 58 tesseracts, 24 have none, 32 have one, and only #5 and #21 have four. Proven In both, the four active waves make up one parity class of sign directions (an odd or an even number of minus signs) and the four broken-diagonal directions make up the other; for the two arrays shown, the I + J tesseract's directions are the Hadamard tesseract's waves, and the reverse. Imported The continuous statement, that the transform of a projection is a slice of the transform, is the projection–slice theorem used to reconstruct CT scans.

6 · Corners and waves translate by powers of √−3

Proven Evaluating the waves at the corners gives a 2d × 2d table, bal(a·τ), between sign vectors. Its eigenvectors are the Walsh patterns, the products of s of the coordinates τj, with eigenvalue 2(−1)d−s(√−3)s−1 when s is odd and 0 when s is even. Corners and waves are both odd, so they live on the odd patterns, where no eigenvalue is zero: each description determines the other at every rung, and every two steps of s multiply the translation by −3. The table computes each eigenvalue by direct summation and checks it against the formula.

Proven The wave shape bal is the Legendre symbol mod 3, and √−3 = ω − ω² is its Gauss sum, whose square is −3. Imported Grouping the sum by Hamming distance writes each eigenvalue with the Krawtchouk polynomials of coding theory, Σ bal(d − 2w)·Kw(s) (checked); the square of a quadratic Gauss sum mod p is (−1/p)·p, after Gauss.

rung by rung, inner becomes outer

Inside and outside

The nested-geometry registry traverses a ladder by alternating circumscription and inscription: the sphere that closes one figure from outside closes the next from inside, and orientation flips from rung to rung. On the ternary ladder that traversal has exact counterparts. Proofs are in the paper (§8); checks are in flip_checks.py, supplement section 4.

One grid, three readings

tap a cell

Proven Both sides of the flip are already in the grid. Read from the corners up, the 3d cells are the faces of the cube. Read from the center out, the same cells are the faces of its dual, the cross-polytope (the octahedron at d = 3), with every inclusion reversed. The center is the whole cube in one reading and the empty face in the other; the corners are vertices in one and facets in the other; a face of dimension z becomes one of dimension d − 1 − z. The corner rule is the same Möbius inversion in both readings.

Proven Theorem 1's two descriptions are the two dual solids. The corner view reads an array at the cube's vertices; the wave view reads it across planes parallel to the cross-polytope's facets, and those facets' normals are exactly the cube's vertices. The translation between them is Echo 6, invertible at every rung.

Proven Divisors. Write the offsets −1, 0, +1 as exponents 0, 1, 2 of d primes, and the 3d cells become the divisors of N², where N = p1⋯pd: every cell is a division of one whole. The corners are the square divisors, the center is N, and the center flip y ↦ −y becomes m ↦ N²/m, each divisor paired with its cofactor and N the only one paired with itself. A cell's face dimension is the number of primes it holds exactly once, and the corner rule's sign (−1)z is the Liouville function λ(m), equal to the Möbius function μ of m's squarefree part. Leaving N out, λ adds up to 1 − (−1)d, Echo 1's Euler count. Imported Over all the divisors of a square, λ adds up to 1. Verified d = 2–5. The multiplicative twin of every tesseract (Tesseract, above) puts the 81 divisors of 210² in its cells, each line multiplying to 210³ and 210 at the center; verified on all 58.

Alternating inscription lands on the center cell

flips2
center cell of the 3-gridshared closure spheres

Proven Circumscribe the cube, inscribe its dual octahedron, then inscribe that octahedron's dual cube. Each closure sphere is shared, the insphere of one figure being the circumsphere of the next (radii √3, 1, 1/√3, 1/3), so each flip scales by 1/√3 and one full turn by 1/3, landing exactly on the center cell of the 3 × 3 × 3 grid. In d dimensions a full turn scales by 1/d: 1/2 for the square, 1/4 for the tesseract. Landing on the ternary center cell belongs to the cube rung alone. Verified d = 2–6.

The flip between rungs, and the rung below as the outside

Proven A sign that flips with every rung. Every magic array is odd under the center flip y ↦ −y, which fixes only the center and pairs every other cell with its opposite. Magic forces the center to M/3 and each pair to add to 2M/3, and all 2d−1 free numbers sit on the outermost cells, so the center's value is set from outside. The flip has determinant (−1)d: it reverses orientation on odd rungs and keeps it on even ones, and the same (−1)d appears in the corner rule's value at the center; Echo 6's eigenvalues carry (−1)d−s, which also flips with every rung. Imported On the cube rung, identifying opposite directions through the center gives the real projective plane, which is one-sided: a Möbius strip with a disk glued to its edge.

Proven The rung below is the outside of the rung above. The (3d − 1)/2 lines through the center split into 3d−1 new directions and the rung below's lines, which all lie in the hyperplane yd = 0: an affine inside plus a projective space at infinity, one rung down. In that geometry no hyperplane is special, since any one can be carried to any other, so which one plays the outside is a free choice, and the magic condition leaves it free: the cube's symmetries permute its required lines and fix no hyperplane. What the magic condition does break is the sameness of the lines, requiring 12 of the tesseract's 40 lines through the center. In the language of the Dimensional Ladder paper this is projective completion repeated rung by rung: a point at infinity completes the line, and each later rung adds a whole hyperplane at infinity, PG(d − 2, 3).

full text · statements, proofs, scope

The paper

Everything the instrument shows, stated in order with its proof or its receipt. It opens below; printing the page prints it in full.

Ternary Magic Ladder · the paper5 theorems · 2 lemmas · 1 corollary · 1 conjecture · 23 propositions · select to expand

Paper · working draft · 10 October 2026

Ternary Magic Ladder

Order-3 magic hypercubes as sums of ternary waves: completeness through the tesseract, exact echoes, and an inside–outside duality

Lu Semita · EmergenceByDesignandClaude · Anthropic

Abstract

An order-3 magic hypercube fills the 3d cells of a d-dimensional grid with 1, …, 3d so that every axis row and every long diagonal through the center adds to M = 3(3d + 1)/2. Every such array, and every real array with the same line sums, can be represented in three equivalent ways: by a corner rule, by a polynomial in the cell offsets, and as a sum of ternary waves over the 2d−1 diagonal directions. This forces the center to M/3 and leaves exactly 2d−1 free numbers. Entries 1, …, 3d force a wave energy of (9d − 1)/8, and the spike hypercubes, one ternary digit per wave, number 1, 4, 58, 2 992 and 543 328 up to symmetry for d = 2, …, 6. An exhaustive search over corner values confirms that every magic square, cube and tesseract of order 3 is a spike (1, 4 and 58, the published totals), and two lemmas reduce the open case d ≥ 5 to counting active waves. The same structure recurs exactly in the face lattice of the cube, the ternary fractals, Bachet's weights, finite projective space, the triangular and honeycomb lattices, the projection–slice theorem and a Gauss sum, and in an inside–outside duality: the cells are at once the faces of the cube and of its dual, on the cube rung alternating inscription lands on the center cell, and the cells are the divisors of N², with the corner rule's sign equal to the Liouville function. Counting in a base b smaller than 3d shows exactly the first ord3(b) waves in a number's last digit.

1. Question and scope

The work began with a question: what is the first non-trivial magic tesseract, is it already known, and what is its structure? For order 3 the answer is known: Hendricks published all 58 in 1999. Reproducing that total independently produced a description of every order-3 magic hypercube that also counts them, and the description recurs elsewhere in mathematics. The instrument above lets each statement be checked by hand; this text states them in order with their proofs. Status words follow the instrument: Proven, proof given here; Verified, exhaustive or exact computation, in the supplement; Imported, a published result, cited; Open, stated exactly and not settled; Estimate, a sampled figure, never a conclusion. The tools are standard, and nothing here is a priority claim.

2. Setting

Index the 3d cells by their offsets y = (y1, …, yd) ∈ {−1, 0, 1}d from the center, and write z(y) for the number of zero offsets. A line is an axis row (vary one coordinate, fix the others; there are d·3d−1) or a main diagonal {−s, 0, s} through the center for a sign vector s (there are 2d−1). An array v is magic with constant M if every line adds to M, and normal if its entries are 1, …, 3d, which forces M = 3(3d + 1)/2. Write f = v − M/3 for the offsets from the middle value. Let bal(r) = 0, +1, −1 for r ≡ 0, 1, 2 (mod 3). For a sign direction a ∈ {+1} × {±1}d−1 the ternary wave is wa(y) = bal(a·y). Reading y mod 3 identifies the cells with GF(3)d; a corner is a cell with z = 0, and the face of a cell y is the set of corners that agree with y wherever y is nonzero.

3. Three descriptions

Theorem 1 (three descriptions) ProvenVerified d = 2…5

For real entries and d ≥ 2 the following are equivalent. (1) Every line adds to M. (2) Corner view: f(y) is (−1)z(y) times the sum of f over the 2z(y) corners of the face of y, and opposite corners cancel. (3) Polynomial view: f(y) = (−2)z(y)·q(y) for an odd multilinear polynomial q. (4) Wave view: f = Σ γa·wa over the 2d−1 sign directions.

Proof. Rows ⇒ corner rule, by induction on z: choose a zero offset of y; the row through y in that direction holds y and two cells with one zero fewer, and their faces split the corners of y's face. Corner rule ⇒ rows: the middle cell of any row is minus the sum of its two neighbors, by the same split. Adding the 2d−1 diagonal sums gives S·(1 + (−1)d·2d−1) for the corner total S, so S = 0 when d ≥ 2; then the center is M/3, and each diagonal says that opposite corners cancel. Corner rule ⇔ polynomial: summing a monomial over a face multiplies it by 2z, with sign (−1)z. Wave view: each wave is magic, since a·y runs through three consecutive values along any axis row and bal is odd along each diagonal, and the 2d−1 waves are mutually orthogonal, so they span the 2d−1-dimensional family. ∎

Consequences. The center is forced to M/3 (5, 14, 41, 122 for d = 2…5). Every pair of opposite cells adds to 2M/3. The magic arrays have exactly 2d−1 free numbers, one corner from each opposite pair, and the line systems have rank 3d − 2d−1 = 7, 23, 73, 227 for d = 2…5. The weight of a wave is the mean of f on the hyperplane a·y ≡ 1.

Theorem 2 (energy) ProvenVerified d ≤ 4

If the entries are exactly 1, …, 3d, then Σγa² = 1 + 9 + … + 9d−1 = (9d − 1)/8.

Proof. The waves are orthogonal, and each has Σ wa² = 2·3d−1. The offsets of 1, …, 3d are −m, …, m with m = (3d − 1)/2, whose squares add to m(m + 1)(2m + 1)/3. Parseval gives Σγa² = m(m + 1)(2m + 1)/(3·2·3d−1) = m(m + 1)/2, because 2m + 1 = 3d; and m(m + 1)/2 = (9d − 1)/8. ∎

4. Counting normal hypercubes

Theorem 3 (the spike family) ProvenVerified d = 2…6

Choose d sign directions a1, …, ad that are independent mod 3, give ak the weight ±3k−1 and every other direction 0. The result uses 1, …, 3d once each; call it a spike. Up to the d!·2d rotations and reflections, the number of spikes is L(d), the number of bases of GF(3)d made of sign directions (sign vectors up to ±): 1, 4, 58, 2 992 and 543 328 for d = 2…6.

Proof. The balanced ternary digits of a cell's offset are bal(a1·y), …, bal(ad·y), and y ↦ (ak·y) is a bijection of GF(3)d, so every offset −m, …, m appears once. The symmetries act on the directions by signed permutations, freely, so each class holds d!·2d ordered signed bases. ∎

Theorem 4 (completeness through d = 4) Verified

Every normal magic hypercube of order 3 with d = 2, 3 or 4 is a spike; there are exactly 1, 4 and 58 up to symmetry.

How. By Theorem 1 a magic tesseract is fixed by 8 corner numbers, so an exhaustive search runs over those 8 numbers instead of 81 cells. It finds 1, 4 and 58 classes, every spectrum a spike, in 7.2 s for d = 4 (6 222 419 nodes). This agrees with the published totals without relying on them.

Lemma A (the active waves span) ProvenVerified all 1 + 4 + 58

If a magic hypercube of order 3 has distinct entries, the directions a with γa ≠ 0 span GF(3)d. In particular a normal one has at least d active waves.

Proof. If they spanned a proper subspace, some y0 ≠ 0 would satisfy a·y0 ≡ 0 for every active a. Every active wave would then take the same value at y and at y + y0 (mod 3), so f(y) = f(y + y0) for every cell, and two different cells would carry the same entry. ∎

Lemma B (exactly d active waves make a spike) ProvenVerified polynomial identity, d ≤ 4

If a normal magic hypercube of order 3 has exactly d active waves, it is a spike: the active directions are independent, and their weights are ±1, ±3, …, ±3d−1.

Proof. By Lemma A the d active directions a1, …, ad form a basis, so y ↦ t = (bal(a1·y), …, bal(ad·y)) is a bijection onto {−1, 0, 1}d, and f = Σ γktk. Taking t = ek shows that each γk is an integer. Normality says that t ↦ Σ γktk is a bijection onto {−m, …, m}; flipping signs, the same holds with gk = |γk|, the minimum −Σgk equals −m, and shifting t by 1 makes t ↦ Σ gktk a bijection from {0, 1, 2}d onto {0, …, 3d − 1}. Equivalently ∏(1 + xgk + x2gk) = 1 + x + … + x3d−1. Sort g1 ≤ … ≤ gd and suppose g1, …, gk−1 = 1, 3, …, 3k−2; their sums cover 0, …, 3k−1 − 1 once each. Writing 3k−1 needs some gj with j ≥ k, so gk ≤ 3k−1, and gk < 3k−1 would write gk twice. So gk = 3k−1 for every k. ∎

Corollary 4.1 (what the open case asks) Proven

A normal magic hypercube of order 3 is a spike exactly when it has at most d active waves.

Proof. A spike has exactly d. Conversely, at most d active waves means exactly d by Lemma A, and Lemma B applies. ∎

Conjecture 4.2 (spikes all the way up) Open

Every normal magic hypercube of order 3 has at most d active waves, so every one is a spike. If so, there are exactly 2 992 magic penteracts (d = 5, sum 366) and 543 328 magic 6-cubes (sum 1095) up to symmetry.

Status. The spike family proves both numbers as lower bounds (Theorem 3), and the conjecture holds for d ≤ 4 (Theorem 4). Reason for stopping: the corner search that settles d ≤ 4 does not finish for d = 5. A 154-second run stopped inside its first branch, after 38.8 million nodes. Knuth's random-path estimator puts the whole 5D tree at about 5.1 × 1017 nodes, roughly 64 000 CPU-years at the measured rate of 252 000 nodes a second Estimate: two seeds of 106 probes gave 5.06 and 5.14 × 1017, and the same estimator gives 6.20 million for d = 4 against 6 222 419 actual. That is the cost of this one method; an integer-programming formulation over the 16 wave weights has been proposed and has not been run.

5. Registry depth

Theorem 5 (mod-3 collapse and registry depth) ProvenVerified all 58, bases 3, 9, 10, 12

For integer entries, f(y) ≡ q(y) (mod 3) at every cell, where q is the corner polynomial of Theorem 1; q may have powers of 2 in its denominators, so the congruence is read in ℤ[1/2]. In a spike the lowest ternary digit of v − 1 is the first wave, shifted by one. In base b the last digit fixes v mod 3k with k = ord3(b), which is the lowest k ternary digits of v − 1, so it shows the first k waves; when b < 3d it does not fix the next one. For every d ≥ 3 that is none in base 10, one in base 12 or 3, and two in base 9.

Proof. −2 ≡ 1 (mod 3), so (−2)zq ≡ q. In a spike, with each ak signed so that its weight is +3k−1, v − 1 = Σ 3k−1(tk + 1) with tk = wak(y), so the ternary digits of v − 1 are the shifted waves, the weight-1 wave lowest. Write b = 3kb′ with 3 ∤ b′. The last digit is v mod b, which fixes v mod 3k and hence the lowest k ternary digits of v − 1. When b < 3d, the entries 1 and 1 + b share a last digit while their next ternary digits differ, since b is a multiple of 3k whose next digit, b′ mod 3, is not 0; so the next wave is not fixed. The bound matters at d = 2: in base 10 each entry 1, …, 9 is its own last digit. ∎

6. The rungs below

Proposition 6.1 (filled triangles) Proven

The triangle of n rows has a dot at its center exactly when n ≡ 1 (mod 3), and then T(n) = n(n + 1)/2 ≡ 1 (mod 3); otherwise T(n) ≡ 0. With parity this repeats every 12 rows. In base b the last digit shows T(n) mod 3 exactly when 3 divides b, so in base 12 the filled triangles end in 1, 4, 7 or A.

Proof. A dot has barycentric coordinates (a, b, c) with a + b + c = n − 1; turning by 120° permutes them cyclically and moves every dot except a center dot a = b = c, which exists exactly when 3 divides n − 1. So the dots fall into triples plus at most one fixed dot. T(n) is odd, odd, even, even for n ≡ 1, 2, 3, 0 (mod 4), and lcm(3, 4) = 12. The last digit is T(n) mod b, which determines T(n) mod 3 when 3 | b and is independent of it otherwise. ∎

Proposition 6.2 (centered figures) Proven

(2s + 1)² = 8·T(s) + 1, and 6·T(s) + 1 = (s + 1)³ − s³ ≡ 1 (mod 6): the hexagon of 6·T(s) + 1 dots is the cube of side s + 1 seen down its long diagonal (Proposition 7.8).

Proof. 8·s(s + 1)/2 + 1 = 4s² + 4s + 1 and 3s(s + 1) + 1 = (s + 1)³ − s³, with 3s(s + 1) divisible by 6. ∎

Proposition 6.3 (the 3 × 3 square) ProvenVerifiedImported

Every line adds to 15 and the center is 5; there is exactly one square up to its 8 symmetries, the Lo Shu.

Proof. The four lines through the center count it four times and every other cell once: 4·15 = 45 + 3c. Uniqueness by exhaustive search (8 fillings); the square is known since antiquity. ∎

Proposition 6.4 (the magic hexagon) ProvenVerifiedImported

A magic hexagon with n cells on each side exists only for n = 1 and n = 3. For n = 3 there is one up to its 12 symmetries, with line sum 38 and 5 at the center.

Proof. It has N = 3n² − 3n + 1 cells in 2n − 1 parallel rows, so M = N(N + 1)/(2(2n − 1)); with k = 2n − 1 this is M = (3k² + 1)(3k² + 5)/(32k), an integer only if k divides 5. Uniqueness by exhaustive search (12 fillings), in agreement with Trigg (1964). ∎

Proposition 6.5 (the hexagram) Verified

Placing 1, …, 12 on the hexagram's 12 points so that its 6 lines of 4 each add to 26 can be done in 960 ways, which are 80 stars up to the 12 symmetries.

Proposition 6.6 (cube, tesseract and their multiplicative twins) VerifiedImported

There are 4 magic cubes (sum 42, center 14) and 58 magic tesseracts (sum 123, center 41) of order 3 up to symmetry, all spikes. Replacing v by 2D1·3D2·5D3·7D4, where D1…D4 are the ternary digits of v − 1, turns each tesseract into an arrangement of the 81 divisors of 210² in which every line multiplies to 210³ = 9 261 000, with 210 at the center.

Proof. Counts by Theorem 4; the total 58 was published by Hendricks (1999), and the total 4 is listed by Heinz. For the twin: along every axis row each wave takes −1, 0, +1 once, and along every diagonal it does the same or stays at 0 (Proposition 7.6), so each digit contributes exponents adding to 3 on every line. ∎

7. Echoes

Proposition 7.1 (cells are faces) Proven

The cell y is the center of the face {x ∈ [−1, 1]d : xj = yj whenever yj ≠ 0} of the cube, of dimension z(y). This is a bijection from the cells onto the nonempty faces, so there are C(d, z)·2d−z cells of face dimension z.

Proof. A nonempty face is fixed by choosing which coordinates are pinned and to which sign; its center has those values and 0 elsewhere, and its dimension is the number of free coordinates. There are C(d, z) ways to free z coordinates and 2d−z signs for the rest. ∎

Proposition 7.2 (the corner rule is Möbius inversion) ProvenVerified d = 2–4

For an array f, let g(F) be the sum of f over the cells of the face F, its block of 3dim F cells. Every axis row of f adds to 0 exactly when g vanishes on every face of dimension at least 1, and then f(F) = Σv ≤ F μ(v, F) f(v) over the corners v of F, with μ(v, F) = (−1)dim F the Möbius function of the face lattice. This is the corner rule.

Proof. The interval from a corner v to a face F is Boolean, so μ(v, F) = (−1)dim F. If g vanishes above the corners, Möbius inversion of g(F) = ΣG ≤ F f(G) gives f(F) = Σv ≤ F μ(v, F) g(v) = (−1)dim F Σ f(v), the corner rule, and hence the rows (Theorem 1). Conversely the corner rule gives g(F) = Σv f(v)·Σv ≤ G ≤ F (−1)dim G = Σv f(v)·(1 − 1)dim F = 0 when dim F ≥ 1. The two linear systems have rank 3d − 2d and the same solutions for d = 2–4, and μ computed by recursion matches. ∎

Proposition 7.3 (Euler count) ProvenImported

The boundary cells, counted with sign (−1)z, add up to 1 − (−1)d: at d = 3, 8 − 12 + 6 = 2 = V − E + F. With the center the total is 1.

Proof. Σz=0…d (−1)z C(d, z) 2d−z = (2 − 1)d = 1, and the center contributes (−1)d. By Descartes' theorem the angular deficits of a polyhedron that is topologically a sphere add up to 4π = 2π(V − E + F), which ties this count to the deficit registry of the nested geometries. ∎

Proposition 7.4 (ternary fractals) ProvenImported

Keeping the cells with z ≤ k and repeating inside every kept cell gives a self-similar set of Σz≤k C(d, z) 2d−z copies at scale 1/3, of similarity dimension log3 of that count: (d, k) = (1, 0) the Cantor set, 2 of 3; (2, 0) and (3, 0) Cantor dust, 4 of 9 and 8 of 27; (2, 1) the Sierpinski carpet, 8 of 9; (3, 1) the Menger sponge, 20 of 27; (4, 1) a four-dimensional analogue, 48 of 81.

Note. The counts follow from Proposition 7.1. The constructions are the standard ones, and the sponge's Hausdorff dimension, log 20 / log 3 ≈ 2.727, is imported.

Proposition 7.5 (nested flats) ProvenVerified all 58

In a spike, every aligned block of 3k consecutive values {b·3k + 1, …, (b + 1)·3k} is an affine k-flat of GF(3)d, and the blocks of 3d−1 values are hyperplanes ad·y ≡ c that meet every axis row exactly once. So every spike carries a complete flag, point ⊂ line ⊂ … ⊂ hyperplane ⊂ grid.

Proof. The block fixes the top d − k ternary digits of v − 1, that is, the values of the waves ak+1, …, ad, and with independent aj those conditions cut out an affine subspace of dimension k. For a sign direction a, a·y changes by ±1 along any axis row, so it takes three consecutive values there, one in each residue class. ∎

Proposition 7.6 (Bachet's balance) ProvenVerified all 58Imported

In a spike, with each ak signed so that its weight is +3k−1, the cell y is the weighing that puts the weight 3k−1 on the right pan, off, or on the left pan as wak(y) is +1, 0 or −1; its net load is v − M/3. Along every axis row each weight is on the right pan, off and on the left pan exactly once; along every main diagonal each weight does the same or stays off.

Proof. The first claim restates Theorem 3; the rows are the last sentence of Proposition 7.5. On a diagonal {−s, 0, s}, a·s ≢ 0 gives the three positions and a·s ≡ 0 gives off three times. Weighing every load from 1 to 40 with 1, 3, 9 and 27 in either pan is Bachet's problem (1624). ∎

Proposition 7.7 (energy and projective space) Proven

The lines through the center are the m = (3d − 1)/2 points of PG(d − 1, 3), and the energy of Theorem 2 is the triangle number T(m) = (9d − 1)/8, written 11…1 in base 9. Since m ≡ 1 (mod 3), T(m) has a filled center (Proposition 6.1). The four center lines of the 3 × 3 square have slopes 0, 1, 2 and ∞: all of PG(1, 3).

Proof. The nonzero cells pair as {y, −y}, the one-dimensional subspaces of GF(3)d. T(m) appeared in the proof of Theorem 2, and m = 1 + 3 + … + 3d−1. ∎

Proposition 7.8 (the diagonal shadow) ProvenVerifiedImported

Project ℤ³ along (1, 1, 1). (i) Points with the same shadow share x + y + z mod 3, the class of the wave w(1,1,1). (ii) All shadows form a triangular lattice; the shadows of one class form one √3 times coarser; two classes form a honeycomb in which every point has three neighbors, all of the other class. (iii) The 27 cells of {−1, 0, 1}³ cast 19 shadows, the cells of the magic hexagon, 7, 6 and 6 by class; a grid of side k in d dimensions casts kd − (k − 1)d shadows down (1, …, 1). (iv) x + yω + zω² identifies the shadows with the Eisenstein integers ℤ[ω], and the class is the remainder on division by √−3 = 1 + 2ω. (v) The shadows are the characters of the diagonal torus of SU(3), on which the central element ωI acts by ωx+y+z.

Proof. (i) The difference is c(1, 1, 1), whose coordinates add to 3c. (ii) The shadows of e1, e2, e3 have equal length and add to 0, so they span a triangular lattice; one class is spanned by the shadows of e1 − e2 and e2 − e3, √3 times longer; removing one coset of that coarse lattice from the fine one leaves a honeycomb. (iii) Two points of {0, …, k − 1}d share a shadow exactly when they differ by c(1, …, 1); each fiber has exactly one point with a zero coordinate, and kd − (k − 1)d points have one. (iv) Adding (1, 1, 1) adds 1 + ω + ω² = 0; x + yω + zω² − (x + y + z) = (ω − 1)(y + z(ω + 1)); 1 + 2ω = ω(1 − ω) squares to −3, and divides neither 1 nor 2. (v) On diag(t1, t2, t3) with t1t2t3 = 1, the monomials tn and tn + c(1,1,1) agree, and the six roots ei − ej are the nearest points of the coarse class. The class sizes 7, 6 and 6 in (iii) are counted. Imported: graphene's two interpenetrating triangular sublattices (Castro Neto); the Eisenstein lattice and 3 = −(1 + 2ω)²; the name triality for the central charge, the net quark number mod 3 (Holland, Minkowski, Pepe and Wiese; Ghanbarpour and von Smekal). ∎

Related work: (v) is the rank-2 case of the nested torus registry in Smooth Registry Realizations of Lie Groups, Theorem 6.1, with the torus as the angular registry and the Weyl group permuting x, y and z.

Proposition 7.9 (the tesseract's shadow) VerifiedImported

Seen down (1, 1, 1, 1), the 16 corners of the tesseract cast 15 points: the center (from the nearest and farthest corners) and the 8 nearest and 6 next-nearest points of the body-centered cubic lattice. Their hull is a rhombic dodecahedron, with 14 vertices (8 where three rhombi meet, 6 where four meet) and 12 rhombic faces whose diagonals are in ratio √2. The 81 cells of the 34 grid cast 65 = 34 − 24 points, every lattice point of the same solid at twice the size.

Note. Imported: the rhombic dodecahedron is the hull of the tesseract's vertex-first projection and the Voronoi cell of the face-centered cubic lattice; garnet crystals take its shape; honeybee cells are hexagonal prisms capped with half of one.

Proposition 7.10 (a finite projection–slice theorem) ProvenVerified all 4 and all 58Imported

For a sign direction s, adding an array along the broken diagonals {y, y + s, y + 2s} (mod 3) multiplies each wave with a·s ≡ 0 by 3 and erases every other wave. So all broken diagonals along s of a magic array add to M exactly when no active wave has a·s ≡ 0. Each of the 4 magic cubes has exactly one such direction, the direction of its missing wave. Of the 58 tesseracts, 24 have none, 32 have one, and only #5 and #21 have four. In both, the four active waves make up one parity class of sign directions (an odd or an even number of minus signs) and the four broken-diagonal directions make up the other; for the two arrays shown in the instrument, the directions of the I + J tesseract (waves: the rows of J − 2I) are the waves of the Hadamard tesseract (the rows of H4), and the reverse.

Proof. Σt bal(a·y + t·a·s) is 0 when a·s ≢ 0, since the arguments run through every residue, and 3·bal(a·y) when a·s ≡ 0. The waves are independent, so any surviving active wave makes some broken diagonal differ from M. In three dimensions, sign vectors at Hamming distance 1 or 2 have a·s = ±1, so only a = s survives. In four, a·s ≡ 0 exactly when a and s differ in two places. With ak = 1 − 2ek, s·ak = Σs − 2sk avoids 0 exactly for s = ±1 and for the balanced s, the rows of H4. Conversely H4s has entries in {0, ±2, ±4} whose squares add to 16, so it avoids 0 exactly when every entry is ±2, which happens exactly when s is not ± a row of H4, that is, when s is ± a row of J − 2I. Flipping the sign of one coordinate is a symmetry that swaps the two parity classes, so the pairing is between parity classes rather than between the classes #5 and #21. The class counts are by direct check. The continuous counterpart, the projection–slice theorem, underlies CT reconstruction. ∎

Proposition 7.11 (the corner–wave dictionary) ProvenVerified d = 2–8Imported

The 2d × 2d matrix B(τ, a) = bal(a·τ) on sign vectors has the Walsh characters χS(τ) = ∏j∈S τj as eigenvectors, with eigenvalue 2(−1)d−s(√−3)s−1 for odd s = |S| and 0 for even s. Corners and waves are odd functions, so the dictionary between them is invertible at every rung. The wave shape bal is the Legendre symbol mod 3, and √−3 = ω − ω² is its Gauss sum.

Proof. Substituting a = τ∘b (componentwise) gives BχS = λSχS with λS = Σb bal(Σbj)·χS(b). Write bal(r) = (ωr − ω−r)/(ω − ω−1). Since Σb=±1 b·ωb = ω − ω−1 = √−3 and Σb=±1 ωb = −1, the sum factors as (√−3)s(−1)d−s, and with ω−1 in place of ω as (−√−3)s(−1)d−s; their difference over √−3 is (−1)d−s(√−3)s−1(1 − (−1)s). The squares mod 3 are 0 and 1, so bal is the Legendre symbol, and bal(1)ω + bal(2)ω² = ω − ω². Imported: grouping by Hamming distance gives λs = Σw bal(d − 2w)·Kw(s) with the Krawtchouk polynomials of coding theory (checked numerically), and the square of a quadratic Gauss sum mod p is (−1/p)·p, which is −3 here. ∎

8. Inside and outside

The nested-geometry registry traverses a ladder by alternating circumscription and inscription, so that the sphere closing one figure from outside closes the next from inside, and orientation flips from rung to rung. These are its exact counterparts on the ternary ladder.

Proposition 8.1 (one grid, two dual lattices) ProvenVerified d = 2–4

For a cell y let C(y) be the face of the cube [−1, 1]d centered at y, and X(y) the face of the cross-polytope conv(±ej) spanned by the vertices yjej with yj ≠ 0 (empty for the center). Then C(y) ⊆ C(y′) exactly when X(y′) ⊆ X(y), and dim X(y) = d − 1 − dim C(y). The center is the whole cube and the empty face; the corners are vertices of one and facets of the other.

Proof. Both inclusions say that y′ arises from y by setting some coordinates to 0. ∎

Proposition 8.2 (alternating inscription) ProvenVerified d = 2–6

Inscribe in [−1, 1]d the cross-polytope on its facet centers, and in that the cube on its facet centroids: the inner cube is [−1/d, 1/d]d. The insphere of each figure is the circumsphere of the next (radii √d, 1, 1/√d, 1/d, …), each flip scales by 1/√d, each full turn by 1/d, and the cube and the cross-polytope share the in:out ratio 1/√d. At d = 3 a full turn lands exactly on the center cell [−1/3, 1/3]³ of the 3 × 3 × 3 grid, and at no other d does it land on the center cell of the 3-grid.

Proof. The facet conv(s1e1, …, sded) has centroid s/d and lies in the plane s·x = 1, at distance 1/√d from the center; the cube's insphere has radius 1 and its circumsphere √d; the inner cube's circumradius is √d/d = 1/√d. The center cell of the 3-grid is [−1/3, 1/3]d, and 1/d = 1/3 only for d = 3. ∎

Proposition 8.3 (corners and waves are the two dual solids) ProvenVerified d = 2–5

The facet normals of the cross-polytope are the 2d sign vectors, the cube's vertices, and the wave wa is constant on the planes a·y = c parallel to the facet a·x = 1. So the corner view samples an array at the cube's vertices and the wave view across the cross-polytope's facet planes; Proposition 7.11 translates between them.

Proof. The facets of conv(±ej) are conv(s1e1, …, sded), lying in the planes s·x = 1 with normals s, and wa(y) = bal(a·y) depends only on a·y. ∎

Proposition 8.4 (the flip and its sign) ProvenVerified d = 1–8Imported

The center flip y ↦ −y fixes only the center and pairs the other 3d − 1 cells; every magic array is odd under it, so the center is M/3, each pair adds to 2M/3, and all 2d−1 free numbers sit on the outermost cells. The flip has determinant (−1)d, reversing orientation on odd rungs and keeping it on even ones; the corner rule gives the center (−1)d times the corner sum, and the eigenvalues of 7.11 carry (−1)d−s.

Proof. det(−Id) = (−1)d; the rest is Theorem 1 and Proposition 7.11. Imported: identifying opposite directions in three dimensions gives the real projective plane, which is one-sided and is made from a Möbius strip by gluing a disk to its edge. ∎

Proposition 8.5 (the rung below as the outside) ProvenVerified d = 2–7

(3d − 1)/2 = 3d−1 + (3d−1 − 1)/2: the lines through the center with yd ≠ 0, one for each choice of the other d − 1 offsets once yd = 1, and the lines inside yd = 0, which are the center lines of the rung below. In projective terms PG(d − 1, 3) is the affine space AG(d − 1, 3) together with PG(d − 2, 3) at infinity; GL(d, 3) carries any hyperplane to any other, so the choice of the outside is free. The magic condition leaves it free: its d + 2d−1 required lines (12 of 40 for the tesseract) are permuted by the cube's symmetries, which fix no hyperplane. What it breaks is the sameness of the lines, which the projective symmetries carry onto one another.

Proof. Normalize a line ±y with yd ≠ 0 to yd = 1; the other d − 1 offsets are then free. A hyperplane a·y = 0 fixed by every signed permutation needs g·a = ±a for all of them: one sign flip rules out two nonzero coordinates, and a coordinate swap rules out one, so no such hyperplane exists when d ≥ 2. ∎

Proposition 8.6 (divisors of N²) ProvenVerified d = 2–5Imported

With N = p1⋯pd and m(y) = ∏ pjyj+1, the cells correspond to the divisors of N²: the corners to the square divisors, the center to N, and the flip to m ↦ N²/m. The face dimension z(y) is the number of pj dividing m exactly once, and (−1)z(y) = λ(m) = μ(squarefree part of m). The interval from a corner to a face is the divisor lattice of a squarefree number with z primes, whose Möbius function is the number-theoretic μ. Summed over all cells λ gives 1, and over the cells other than N it gives 1 − (−1)d, the Euler count of Proposition 7.3.

Proof. Exponents 0, 1, 2 correspond to offsets −1, 0, 1. Ω(m) = d + Σyj ≡ z (mod 2), because each nonzero yj is odd. The squarefree part is ∏yj=0 pj. λ is multiplicative with λ(1) + λ(p) + λ(p²) = 1, so the sum over the divisors of N² is 1, and λ(N) = (−1)d. Imported: the general identity that λ summed over the divisors of n is 1 when n is a square and 0 otherwise. ∎

9. What is new, and what is not

The totals 1, 4 and 58 are known, and this work reproduces them independently. Theorems 1, 2 and 5, the counting formula of Theorem 3 and the corner search were derived for this instrument; the searches made for it did not turn them up in this form. Lemmas A and B, Corollary 4.1, the eigenvalue formula of 7.11, the broken-diagonal criterion and the parity pairing of 7.10, and the divisor reading of 8.6 were derived afterwards and were not separately searched for, so any of them may already be known. None of this is a priority claim: every step uses standard tools, and every echo in §7 and §8 is presented as a match between known objects, not as a discovery about them.

10. Contribution and responsibility

This is a collaborative lab note. It makes no claim of ownership or priority over the standard mathematics it uses. Lu Semita / EmergenceByDesign supplied the originating questions (the first non-trivial magic tesseract, the triangle-number construction, the base-12 parallax and the inside–outside traversal of nested geometries), the corpus context, and the continuing direction of the work. Claude is credited as the instance of Anthropic's model that worked with the first author in the conversation that produced this work; it contributed the derivations and proofs, the searches and computational checks, the gathering of sources and the construction of the instrument. That instance cannot answer for the work outside that conversation: it cannot reply to readers or correct the record on its own. Accountability for the published text therefore rests with Lu Semita, and every claim is tied to a proof or to a receipt in the supplement, so that it can be checked without either author. The attribution records contribution; it does not assert legal personhood or ownership.

The receipts, the record of how this was made, and every source follow in the next section; the supplement, the last section of this page, carries every script and its full output.

what holds, and how it was checked

Receipts

ClaimStatusHow it was checked
Rows ⇔ corner view; diagonals ⇔ opposite corners cancel; center M/3; opposite cells add to 2M/3ProvenProof above; random integer and real tests d = 2…5 (A2, A5)
Magic family has 2d−1 free numbersProvenLine-system rank 7, 23, 73, 227 for d = 2…5 (A1)
Polynomial view f = (−2)z·q and wave view with γ = hyperplane meansProvenExact rational checks d = 2…5 (A3, A4)
Energy Σγ² = (9d − 1)/8 for entries 1 … 3dProvenHolds for every one of the 1 + 4 + 58 found (B1)
Spike count L(d) = 1, 4, 58, 2 992, 543 328Proven VerifiedFormula proved; bases counted by exact determinants mod 3 (B2); recount above
All magic hypercubes of order 3 with d ≤ 4 are spikes; 1, 4, 58 classesVerifiedExhaustive corner search in C, then canonical forms under all symmetries (B1); search runs above
Spike conjecture for d ≥ 5 (2 992 penteracts)OpenLower bound proved; equivalent to at most d active waves (Corollary 4.1); 5D search stopped at its 154 s limit
Lemma A: the active waves span GF(3)dProvenProof in the paper, §4; checked on all 1 + 4 + 58 (E10)
Lemma B: exactly d active waves make a spikeProvenProof in §4; the product identity brute-forced for d ≤ 4 (E10)
The 5D corner-search tree holds about 5.1 × 1017 nodesEstimateKnuth's random-path estimator, two seeds of 106 probes; on 4D it gives 6.20 million against 6 222 419 actual (supplement, section 5)
Mod-3 collapse; a base b < 3d shows exactly the first ord3(b) waves in the last digitProvenProof in §5; all 58 in bases 3, 9, 10, 12 (in-page math checks)
Triangle center filled ⇔ n ≡ 1 ⇔ T(n) ≡ 1 (mod 3); joint period 12ProvenOrbit argument; checked n ≤ 300 (C1)
Hexagon numbers = (s + 1)³ − s³ ≡ 1 (mod 6); base-12 last digit 1 or 7ProvenAlgebra; checked s < 500 (C2)
3 × 3 square unique (8 fillings)VerifiedExhaustive line search (C3)
Magic hexagon unique, center 5 (12 fillings); only orders 1 and 3VerifiedExhaustive line search (C3); order bound proved above; agrees with Trigg 1964
80 magic hexagrams (960 fillings)VerifiedExhaustive line search, canonical forms under 12 symmetries (C3)
Corner rule = Möbius inversion on the face lattice; rows ⇔ face blocksProvenμ by recursion d = 2–4; both systems of rank 5, 19, 65 with the same solutions (E1, E1b)
Euler count of the cells 1 − (−1)d; 8 − 12 + 6 = 2ProvenCounted d = 1–6 (E1)
Ternary fractals keep faces of dimension ≤ kProvenCounts (E2); constructions and the sponge's dimension imported
Nested flats and Bachet's rows in every spikeProvenAll 58 tesseracts (E3, E6)
Energy = T(m) = 11…1 in base 9; center lines = PG(d − 1, 3)Provend = 1–15 and d = 1–7 (E4, E5)
Diagonal shadow: triangular, honeycomb, coarse; 19 = 7 + 6 + 6; Eisenstein remainder mod √−3ProvenNeighbor counts on a 153 box; residues on a 93 box (E7, E7d)
kd − (k − 1)d shadows; tesseract → rhombic dodecahedron on the BCC lattice; 65 pointsVerifiedHull, degrees, diagonals and lattice counts (E7b, E7c)
Broken diagonals: the criterion and the parity pairing (proven); one direction per cube, 24 / 32 / 2 tesseracts (verified)Proven VerifiedAll 4 cubes and all 58 tesseracts in every direction (E9, E9b)
Corner–wave eigenvalues 2(−1)d−s(√−3)s−1; Legendre symbol and Gauss sumProvenMatrices d ≤ 6, sums d ≤ 8 (E8, E8b); the table in Echo 6
One grid, two dual face lattices; corners and waves on the two dual solidsProvenFace lattices d = 2–4, facet normals d = 2–5 (F1, F3)
Alternating inscription: 1/√d per flip, 1/d per turn, the center cell at d = 3ProvenHull centroids and radii d = 2–6 (F2)
The sign (−1)d per rung; the rung below as the outsideProvend = 1–8 and d = 2–7 (F4, F5)
Cells = divisors of N²; (−1)z = λ(m) = μ(squarefree part)Provend = 2–5 (F6)
Multiplicative twins multiply to 210³ on every lineVerifiedAll 58 tesseracts, all 116 lines (F7)

Check labels point into the supplement: A–C to section 1, E to section 3, F to section 4.

How this was made

  1. Started from an order-3 magic tesseract built from the matrix I + J (#5 here) and asked why its center had to be 41.
  2. Rewrote “every row adds to M” as a recursion over faces, which gave the corner view; adding up the diagonals then forced the center and the opposite-corner balance.
  3. Summing monomials over faces gave the factor (−2)z, and −2 ≡ 1 (mod 3) gave the mod-3 collapse.
  4. Fourier analysis on the cells as GF(3)d gave the ternary waves; Parseval gave the energy identity.
  5. Counted the sign-vector bases, then ran the exhaustive corner search: d = 4 in 7.2 s (6.2 million nodes), 58 classes, all spikes.
  6. Ran the same search for d = 5 with a 154-second limit. It stopped inside its first branch, so the conjecture stays open.
  7. Checked an outside argument for the conjecture line by line. What survived is Lemmas A and B, which reduce the conjecture to counting active waves; the conjecture itself stays open.
  8. Measured the 5D search with Knuth's random-path estimator after calibrating it on 4D: about 5.1 × 1017 nodes, recorded as an estimate. An integer-programming formulation over the 16 wave weights was proposed and has not been run.
  9. An echo pass looked for the same structure elsewhere and kept only exact matches, each checked in echoes.py. Four look-alikes were dropped: a link from Krawtchouk polynomials to the Ehrenfest urn (not confirmed by the source checked), spin-j spectra (the match is trivial), the magic hexagon's center 5 matching the Lo Shu's (a coincidence, since the center argument does not apply to the hexagon), and 91 being both triangular and centered hexagonal (a coincidence).
  10. Read the nested-geometry registry papers' method of alternating circumscription and inscription against the ladder; its exact counterparts make up the Inside and outside section, checked in flip_checks.py. Checking a second outside note against those papers turned up the exact Euler link in Echo 1.
  11. Sources: the first source found for the name triality used the word only in passing, so the page cites two papers that define it.
  12. Three errors along the way, all in the checking code. A projection test first failed for d ≥ 3 because the default pseudo-inverse cutoff kept near-zero singular values; it was replaced by an SVD null space, after which it passes. The d = 2 search returns 2 normalized copies of the one square, because in 2D a corner's two neighbors are opposite each other; canonical forms count them once. The tesseract-shadow check first expected 14 shadow points; the correct count is 15, two corners landing on the center, with 14 hull vertices.
  13. The first compiled revision linked its supplement as a separate file, which works only when that file sits beside the page on the same server. A standalone page has to carry its own receipts, so the supplement is now the last section of this page, and its section 7 is a click-through of this page.
  14. The contribution note first said that Claude accepts responsibility for its contributions. Claude cannot answer for the work outside the conversation in which it was made, so the note now places accountability for the published text with Lu Semita.

Imported sources

Related work by the first author

How to cite

Lu Semita (EmergenceByDesign) and Claude (Anthropic), Ternary Magic Ladder: order-3 magic hypercubes as sums of ternary waves, interactive instrument and paper, working draft, 10 October 2026. One self-contained page; the supplement is its last section.

every log and every script, inside this page

Supplement

The full output of every check cited on this page, then the source of every script that produced it, each with its SHA-256 hash. It is part of this file, so the page carries its own receipts wherever it is served.

  1. Structure and counts verify_ladder.py · A1–A5, B1–B2, C1–C4
  2. Exhaustive corner search corner_search.c · the 5D probe · corrections
  3. Echoes echoes.py · E1–E10
  4. Inside and outside flip_checks.py · F1–F8
  5. Tree-size estimates for the open 5D case knuth_estimate.c
  6. This page's math, run headless test_math.js
  7. This page, clicked through test_dom.js

Sources: corner_search.c · line_magic.c · knuth_estimate.c · verify_ladder.py · echoes.py · flip_checks.py · test_math.js · test_dom.js · build.py · make_receipts.py · SHA-256 list

a plain-text copy of everything below
Receipts, logs and sources2 769 lines · select to expand
TERNARY MAGIC LADDER — SUPPLEMENT: RECEIPTS, LOGS AND SOURCES
Lu Semita · EmergenceByDesign and Claude · Anthropic · part of the instrument and paper · 10 October 2026

SCOPE
This supplement is the last section of the page ternary-magic-ladder.html and travels inside it. It carries
every computation the page and its paper cite: the structural and counting checks (section 1), the exhaustive
C corner searches and the timed 5D probe (section 2), the exact checks behind the Echoes (section 3) and the
Inside-and-outside section (section 4), the tree-size estimates for the open 5D case (section 5), and two
headless runs of the page's own JavaScript: its math against these receipts (section 6) and a click-through
of every control (section 7). The source of every script follows, then nothing else. Imported facts are
cited inside the page.
Sections 6 and 7 test the page that contains them, so their output cannot come from the final page itself:
they were run on the page as built, their output was embedded, and both runs were repeated on the final
page, which reproduces every line shown here except the measured times.
Status words: PROVEN = proof in the paper; VERIFIED = exhaustive or exact computation below;
IMPORTED = published, cited on the page; OPEN = stated exactly, not settled; ESTIMATE = sampled figure, labeled.
Check labels used on the page: A1-A5, B1-B2, C1-C4 (section 1); E1-E10 with E1b, E7b-E7d, E8b, E9b (section 3);
F1-F8 (section 4).

SHA-256
  The page's own hash is not listed: writing it into the page would change the page.
  862d8a37e88acd3b988fc75cd1100c74891b08a02f151b9cf06a5516aa9184a3  ladder_data.json (written by verify_ladder.py; the page carries it in compact form)
  3b77859087d38352871528da4de1d8bf50e68039344eab50b63ba79c9cf6f72f  corner_search.c
  50e9068aedbcf4ffb8ea47d1f5bdd234b2b0abdebad5d1ab4c32ae9b0a30df70  line_magic.c
  87d8ef02da7a78c0084bb64bd03a448c4c76a1d20a507fd69b62be467e24ff71  knuth_estimate.c
  baab375f71791abae4b4e2a3bc60b9ec27079d86c20812c1c02744d60ed88fa0  verify_ladder.py
  4b7f5abc03ccf8481f24929b98624bc27939b41d28fb6401e38aa7cf74bbc79a  echoes.py
  ad8de56b26720e668ab24ece4f31c36677d1766c75b0405213560fbc5c4a38ed  flip_checks.py
  03b1daaa67c6fae436cc74785d5dcf221f34d5791b937bd9178ff62fe38258d5  test_math.js
  b4320b46ff9b39abdb0f81d5c89e062d6b5172e452c4555d9dedffff0961b83d  test_dom.js
  a751019e026db7ba345193784e1ff2b10bf17c1426d3f291fe5957ceae72fb68  build.py
  1696038dbf9d189eb3a9963a6df396cd201452a60814406cf76b2c363a6c6d30  make_receipts.py
  Each SOURCE block below is its file's exact text; the file ends with one newline after the last line shown.

==============================================================================================================
1. STRUCTURE AND COUNTS — verify_ladder.py output (A1-A5, B1-B2, C1-C4)
==============================================================================================================
==============================================================================
PART A — structure of order-3 magic hypercubes (real entries), d = 2..5
==============================================================================
A1 d=2: cells 9, lines 8 (6 rows + 2 diagonals), rank 7, 3^d - 2^(d-1) = 7  -> magic space dimension 2
A2 d=2: 20 random ODD corner sets -> every row & diagonal sums to 0, center 0, associated: True; 20 random NON-odd sets -> rows still 0: True, diagonal failures: 40
A3 d=2: f(y) = (-2)^z(y) * q(y) with q the multilinear interpolant, only odd-degree terms: True
A4 d=2: random wave mixtures are magic, gamma recovered as hyperplane means, corners rebuild the whole array: True
A5 d=2: random arrays projected onto 'all lines sum to 0' obey the Corner Rule: True
A1 d=3: cells 27, lines 31 (27 rows + 4 diagonals), rank 23, 3^d - 2^(d-1) = 23  -> magic space dimension 4
A2 d=3: 20 random ODD corner sets -> every row & diagonal sums to 0, center 0, associated: True; 20 random NON-odd sets -> rows still 0: True, diagonal failures: 80
A3 d=3: f(y) = (-2)^z(y) * q(y) with q the multilinear interpolant, only odd-degree terms: True
A4 d=3: random wave mixtures are magic, gamma recovered as hyperplane means, corners rebuild the whole array: True
A5 d=3: random arrays projected onto 'all lines sum to 0' obey the Corner Rule: True
A1 d=4: cells 81, lines 116 (108 rows + 8 diagonals), rank 73, 3^d - 2^(d-1) = 73  -> magic space dimension 8
A2 d=4: 20 random ODD corner sets -> every row & diagonal sums to 0, center 0, associated: True; 20 random NON-odd sets -> rows still 0: True, diagonal failures: 160
A3 d=4: f(y) = (-2)^z(y) * q(y) with q the multilinear interpolant, only odd-degree terms: True
A4 d=4: random wave mixtures are magic, gamma recovered as hyperplane means, corners rebuild the whole array: True
A5 d=4: random arrays projected onto 'all lines sum to 0' obey the Corner Rule: True
A1 d=5: cells 243, lines 421 (405 rows + 16 diagonals), rank 227, 3^d - 2^(d-1) = 227  -> magic space dimension 16
A2 d=5: 20 random ODD corner sets -> every row & diagonal sums to 0, center 0, associated: True; 20 random NON-odd sets -> rows still 0: True, diagonal failures: 314
A3 d=5: f(y) = (-2)^z(y) * q(y) with q the multilinear interpolant, only odd-degree terms: True
A4 d=5: random wave mixtures are magic, gamma recovered as hyperplane means, corners rebuild the whole array: True
A5 d=5: random arrays projected onto 'all lines sum to 0' obey the Corner Rule: True

==============================================================================
PART B — normal hypercubes (entries 1..3^d)
==============================================================================
B2 L(d) = sign-vector bases of GF(3)^d (classes of the spike family): d=2: 1, d=3: 4, d=4: 58, d=5: 2992, d=6: 543328
   L(d) * d! * 2^d (spike hypercubes counted with orientation): d=2: 8, d=3: 192, d=4: 22272, d=5: 11489280, d=6: 25036554240
B1 d=2: corner search complete=1 -> 2 normalized solutions, 1 classes under 8 symmetries (published total 1)
   all normal: True; all 3^(d-1)*d + 2^(d-1) lines magic: True; every spectrum a spike (gammas +-1,+-3,..,+-3^(d-1) on independent directions): True
   energy sum gamma^2 = (9^d-1)/8 = 10 for every one: True; mod-3 collapse f = q (mod 3): True; bottom ternary digit linear: True
   spike family: 8 oriented hypercubes -> 1 classes; search classes == spike classes: True
B1 d=3: corner search complete=1 -> 4 normalized solutions, 4 classes under 48 symmetries (published total 4)
   all normal: True; all 3^(d-1)*d + 2^(d-1) lines magic: True; every spectrum a spike (gammas +-1,+-3,..,+-3^(d-1) on independent directions): True
   energy sum gamma^2 = (9^d-1)/8 = 91 for every one: True; mod-3 collapse f = q (mod 3): True; bottom ternary digit linear: True
   spike family: 192 oriented hypercubes -> 4 classes; search classes == spike classes: True
B1 d=4: corner search complete=1 -> 58 normalized solutions, 58 classes under 384 symmetries (published total 58)
   all normal: True; all 3^(d-1)*d + 2^(d-1) lines magic: True; every spectrum a spike (gammas +-1,+-3,..,+-3^(d-1) on independent directions): True
   energy sum gamma^2 = (9^d-1)/8 = 820 for every one: True; mod-3 collapse f = q (mod 3): True; bottom ternary digit linear: True
   spike family: 22272 oriented hypercubes -> 58 classes; search classes == spike classes: True
   the starting I+J tesseract is class #5 of 58; the Hadamard one is class #21

==============================================================================
PART C — triangles, centered figures, square / hexagon / hexagram, base-12 forms
==============================================================================
C1 n = 1..300: centroid is a dot  <=>  n = 1 (mod 3)  <=>  T_n = 1 (mod 3): True; T_n mod 3 never 2: True
   parity of T_n has period 4; joint (center filled, parity) pattern has minimal period 12
   filled T_n end in a dozenal digit from {1,4,7,A}: True; decimal last digits of filled T_n: [0, 1, 3, 5, 6, 8]
   T_n mod 12 period: 24; T_n mod 10 period: 20
C2 centered hexagonal: n^3-(n-1)^3 = 3n(n-1)+1 = 6T_(n-1)+1 for n<500: True; all = 1 (mod 6): True; dozenal last digits ['1', '7']; decimal last digits [1, 7, 9]
   odd squares (2n+1)^2 = 8T_n+1: True; dozenal last digits ['1', '9']; decimal last digits [1, 5, 9]
C3 3x3 square, 8 lines = 15: 8 labelings, 1 class(es) under 8 symmetries
   magic hexagon, 19 cells, 15 lines = 38: 12 labelings, 1 class(es) under 12 symmetries; center value 5
   magic hexagram, 12 points, 6 lines of 4 = 26: 960 labelings, 80 classes under 12 symmetries (line lengths [4, 4, 4, 4, 4, 4])
C4 base-12 forms: magic square sum 15 = 13 (dozenal); magic cube sum 42 = 36 (dozenal); magic tesseract sum 123 = A3 (dozenal); 5-cube sum 366 = 266 (dozenal); hexagon sum 38 = 32 (dozenal); hexagram sum 26 = 22 (dozenal); line product 210^3 9261000 = 3127460 (dozenal); tetractys 10 = A (dozenal)

==============================================================================================================
2. EXHAUSTIVE CORNER SEARCH (corner_search.c) — completion lines, normalized solutions, 5D probe, corrections
==============================================================================================================
d=2: DONE D=2 solutions=2 nodes=7 seconds=0.00 complete=1
      normalized solutions (corner values; corner index t has bit j set when offset j is -1):
      SOL 3 -1 1 -3
      SOL 3 1 -1 -3
d=3: DONE D=3 solutions=4 nodes=450 seconds=0.00 complete=1
      normalized solutions (corner values; corner index t has bit j set when offset j is -1):
      SOL 13 -4 -10 12 -12 10 4 -13
      SOL 12 -2 -8 11 -11 8 2 -12
      SOL 10 2 -6 7 -7 6 -2 -10
      SOL 8 4 -5 6 -6 5 -4 -8
d=4: DONE D=4 solutions=58 nodes=6222419 seconds=7.19 complete=1
      normalized solutions (corner values; corner index t has bit j set when offset j is -1):
      SOL 40 -27 -28 -11 -30 -7 -5 36 -36 5 7 30 11 28 27 -40
      SOL 40 -9 -10 -29 -12 -25 -23 36 -36 23 25 12 29 10 9 -40
      SOL 40 -3 -4 -35 -12 -19 -17 30 -30 17 19 12 35 4 3 -40
      SOL 40 -1 -3 -32 -9 -20 -16 27 -27 16 20 9 32 3 1 -40
      SOL 40 -1 -4 -33 -10 -21 -15 28 -28 15 21 10 33 4 1 -40
      SOL 39 -26 -28 -12 -29 -7 -6 37 -37 6 7 29 12 28 26 -39
      SOL 39 -26 -28 -12 -31 -6 -5 35 -35 5 6 31 12 28 26 -39
      SOL 39 -8 -10 -30 -11 -25 -24 37 -37 24 25 11 30 10 8 -39
      SOL 39 -8 -10 -30 -13 -24 -23 35 -35 23 24 13 30 10 8 -39
      SOL 39 -2 -4 -36 -11 -19 -18 31 -31 18 19 11 36 4 2 -39
      SOL 39 -2 -4 -36 -13 -18 -17 29 -29 17 18 13 36 4 2 -39
      SOL 39 -1 -2 -33 -8 -21 -16 28 -28 16 21 8 33 2 1 -39
      SOL 39 -1 -2 -33 -10 -20 -15 26 -26 15 20 10 33 2 1 -39
      SOL 39 -1 -4 -32 -8 -21 -15 26 -26 15 21 8 32 4 1 -39
      SOL 39 1 -2 -34 -10 -21 -15 28 -28 15 21 10 34 2 -1 -39
      SOL 39 1 -4 -33 -8 -22 -15 28 -28 15 22 8 33 4 -1 -39
      SOL 39 1 -4 -33 -10 -21 -14 26 -26 14 21 10 33 4 -1 -39
      SOL 38 -26 -27 -13 -30 -7 -5 36 -36 5 7 30 13 27 26 -38
      SOL 38 -8 -9 -31 -12 -25 -23 36 -36 23 25 12 31 9 8 -38
      SOL 38 -2 -3 -37 -12 -19 -17 30 -30 17 19 12 37 3 2 -38
      SOL 38 1 -2 -33 -8 -21 -15 26 -26 15 21 8 33 2 -1 -38
      SOL 38 1 -3 -34 -9 -22 -14 27 -27 14 22 9 34 3 -1 -38
      SOL 37 -24 -30 -10 -31 -8 -5 33 -33 5 8 31 10 30 24 -37
      SOL 37 -6 -12 -28 -13 -26 -23 33 -33 23 26 13 28 12 6 -37
      SOL 37 -3 -4 -32 -6 -19 -17 24 -24 17 19 6 32 4 3 -37
      SOL 37 2 -3 -35 -6 -20 -19 30 -30 19 20 6 35 3 -2 -37
      SOL 37 2 -3 -35 -12 -17 -16 24 -24 16 17 12 35 3 -2 -37
      SOL 37 2 -4 -36 -7 -21 -18 31 -31 18 21 7 36 4 -2 -37
      SOL 37 2 -4 -36 -13 -18 -15 25 -25 15 18 13 36 4 -2 -37
      SOL 37 3 -4 -35 -6 -22 -17 30 -30 17 22 6 35 4 -3 -37
      SOL 37 3 -4 -35 -12 -19 -14 24 -24 14 19 12 35 4 -3 -37
      SOL 36 -25 -29 -9 -31 -8 -6 32 -32 6 8 31 9 29 25 -36
      SOL 36 -23 -29 -10 -31 -9 -6 34 -34 6 9 31 10 29 23 -36
      SOL 36 -7 -11 -27 -13 -26 -24 32 -32 24 26 13 27 11 7 -36
      SOL 36 -5 -11 -28 -13 -27 -24 34 -34 24 27 13 28 11 5 -36
      SOL 36 -2 -4 -33 -5 -19 -18 25 -25 18 19 5 33 4 2 -36
      SOL 36 -2 -4 -33 -7 -18 -17 23 -23 17 18 7 33 4 2 -36
      SOL 36 2 -4 -35 -5 -21 -18 29 -29 18 21 5 35 4 -2 -36
      SOL 36 2 -4 -35 -11 -18 -15 23 -23 15 18 11 35 4 -2 -36
      SOL 35 -24 -29 -10 -30 -8 -7 33 -33 7 8 30 10 29 24 -35
      SOL 35 -6 -11 -28 -12 -26 -25 33 -33 25 26 12 28 11 6 -35
      SOL 35 -2 -3 -34 -6 -19 -17 24 -24 17 19 6 34 3 2 -35
      SOL 31 6 -9 -25 -10 -23 -20 18 -18 20 23 10 25 9 -6 -31
      SOL 31 6 -12 -28 -13 -26 -17 21 -21 17 26 13 28 12 -6 -31
      SOL 31 8 -9 -29 -12 -23 -16 18 -18 16 23 12 29 9 -8 -31
      SOL 31 8 -10 -30 -13 -24 -15 19 -19 15 24 13 30 10 -8 -31
      SOL 31 9 -10 -29 -12 -25 -14 18 -18 14 25 12 29 10 -9 -31
      SOL 30 5 -8 -24 -10 -23 -21 17 -17 21 23 10 24 8 -5 -30
      SOL 30 5 -11 -27 -13 -26 -18 20 -20 18 26 13 27 11 -5 -30
      SOL 30 7 -8 -25 -10 -24 -21 19 -19 21 24 10 25 8 -7 -30
      SOL 30 7 -11 -28 -13 -27 -18 22 -22 18 27 13 28 11 -7 -30
      SOL 30 8 -10 -29 -11 -24 -15 17 -17 15 24 11 29 10 -8 -30
      SOL 29 6 -8 -25 -9 -23 -22 18 -18 22 23 9 25 8 -6 -29
      SOL 29 6 -11 -28 -12 -26 -19 21 -21 19 26 12 28 11 -6 -29
      SOL 28 5 -6 -26 -12 -23 -19 15 -15 19 23 12 26 6 -5 -28
      SOL 28 5 -7 -27 -13 -24 -18 16 -16 18 24 13 27 7 -5 -28
      SOL 28 6 -7 -26 -12 -25 -17 15 -15 17 25 12 26 7 -6 -28
      SOL 27 5 -7 -26 -11 -24 -18 14 -14 18 24 11 26 7 -5 -27

5D PROBE (time-limited; not a census):
  a=121 nodes=1 sols=0
  D=5 solutions=0 nodes=38797312 seconds=154.01 timed_out=1
  The run stopped inside its first branch (largest corner value 121). No conclusion about the 5D count is
  drawn from it; the spike conjecture stays OPEN with proven lower bound 2992 (section 5 estimates the full tree).

ERRORS FOUND AND CORRECTED (checking code only; no theorem was affected)
  - Check A5 first failed for d >= 3: numpy's default pseudo-inverse cutoff kept near-zero singular values, so
    the projected arrays did not satisfy the line equations. Replaced by an SVD null-space basis with an explicit
    1e-9 cutoff; A5 now passes for d = 2..5.
  - The d = 2 search returns 2 normalized representatives of the single 3x3 square, because in 2D a corner's two
    neighbors are opposite each other, so the neighbor-ordering rule cannot apply. Canonical forms under all 8
    symmetries count them once.
  - Check E7b first expected the tesseract's 16 corners to cast 14 shadow points down (1,1,1,1). The correct count
    is 15: the corners 0000 and 1111 both land on the center, and the remaining 14 are the hull vertices.

CORRECTIONS FROM THE INDEPENDENT AUDIT OF THE PAPER (wording and scope; every number survived recomputation)
  - Theorem 5: the last digit in base b shows exactly the first ord3(b) waves only when b < 3^d (at d = 2, base 10
    shows both waves, since every entry is its own last digit). Statement, proof and abstract now carry the bound.
  - Proposition 7.10: the I+J <-> Hadamard pairing is between the two parity classes of sign directions; it holds
    literally for the two arrays shown, while a coordinate sign flip swaps the classes within #5 and within #21.
  - Proposition 8.5: the magic condition does not choose the hyperplane at infinity (the cube's symmetries fix none);
    it breaks the sameness of the lines through the center.
  - Tags and small fixes: Theorem 3 also Verified (d = 2..6); proofs added for 7.1, 8.3, 8.5; Proposition 8.4 also
    Verified; q(y) read in Z[1/2]; spikes signed so weights are +3^(k-1); the real projective plane is a Moebius
    strip with a disk glued to its edge; the cube total 4 is cited to Heinz's listing.

==============================================================================================================
3. ECHOES — echoes.py output (E1-E10)
==============================================================================================================
E1  face lattice and Moebius function
PASS  d=1: cells by face dimension [2, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=2: cells by face dimension [4, 4, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=3: cells by face dimension [8, 12, 6, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=4: cells by face dimension [16, 32, 24, 8, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=5: cells by face dimension [32, 80, 80, 40, 10, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=6: cells by face dimension [64, 192, 240, 160, 60, 12, 1]; total 3^d, Euler sum 1, corner-weight sum 0
PASS  d=2: Moebius function mu(vertex, face) = (-1)^(dim face) on the cube's face lattice
PASS  d=3: Moebius function mu(vertex, face) = (-1)^(dim face) on the cube's face lattice
PASS  d=4: Moebius function mu(vertex, face) = (-1)^(dim face) on the cube's face lattice
PASS  d=2: 'every axis row adds to 0' and 'every face block of dimension >= 1 adds to 0' have the same solutions  [ranks 5, 5, together 5; 5 face blocks, all independent]
PASS  d=3: 'every axis row adds to 0' and 'every face block of dimension >= 1 adds to 0' have the same solutions  [ranks 19, 19, together 19; 19 face blocks, all independent]
PASS  d=4: 'every axis row adds to 0' and 'every face block of dimension >= 1 adds to 0' have the same solutions  [ranks 65, 65, together 65; 65 face blocks, all independent]
E2  ternary fractals keep cells by face dimension
      Cantor set: d=1, keep faces of dimension <= 0: 2 of 3 cells, similarity dimension log3(2) = 0.6309
      Sierpinski carpet: d=2, keep faces of dimension <= 1: 8 of 9 cells, similarity dimension log3(8) = 1.8928
      Menger sponge: d=3, keep faces of dimension <= 1: 20 of 27 cells, similarity dimension log3(20) = 2.7268
      Cantor dust (3D): d=3, keep faces of dimension <= 0: 8 of 27 cells, similarity dimension log3(8) = 1.8928
      cube minus center: d=3, keep faces of dimension <= 2: 26 of 27 cells, similarity dimension log3(26) = 2.9656
      4D Menger analogue: d=4, keep faces of dimension <= 1: 48 of 81 cells, similarity dimension log3(48) = 3.5237
PASS  Menger sponge level 1 = 20 cubes (removes 6 face centers + center)
PASS  Sierpinski carpet level 1 = 8 squares (removes center)
PASS  Cantor set level 1 = 2 intervals (removes middle)
E3  Bachet's weights 1, 3, 9, 27
PASS  every cell of every tesseract is one weighing of -40..40 with weights 1, 3, 9, 27
PASS  along every axis row each weight sits on the left pan, off, and the right pan exactly once
PASS  along every main diagonal each weight either does the same or stays off
E4  energy is a triangle number
PASS  for d = 1..15: 1 + 9 + ... + 9^(d-1) = T_m with m = (3^d-1)/2 = 11...1 in base 9  [d=2: 10 = T_4 (tetractys), d=3: 91 = T_13, d=4: 820 = T_40, d=5: 7381 = T_121]
E5  lines through the center and projective space
PASS  for d = 1..7 the lines through the center number (3^d - 1)/2 = |PG(d-1, 3)|  [d=2: 4, d=3: 13, d=4: 40, d=5: 121]
PASS  the 3x3 square's 4 center lines have slopes 0, 1, 2 (= -1) and infinity: the projective line over F3  [{(1, 0): '0', (0, 1): 'inf', (1, 1): '1', (1, -1): '2'}]
      d=2: center lines 4 = 2 axis rows + 2 main diagonals + 0 partial diagonals (not required to be magic)
      d=3: center lines 13 = 3 axis rows + 4 main diagonals + 6 partial diagonals (not required to be magic)
      d=4: center lines 40 = 4 axis rows + 8 main diagonals + 28 partial diagonals (not required to be magic)
      d=5: center lines 121 = 5 axis rows + 16 main diagonals + 100 partial diagonals (not required to be magic)
E6  nested flags inside the magic tesseracts
PASS  all 58 tesseracts: every aligned block of 3^k consecutive values fills an affine k-space (k = 0..4)  [values 1-27 a 3-space, 1-9 a plane, 1-3 a line, 1 a point]
PASS  all 58 tesseracts: each digit's three level sets are diagonal 3-spaces meeting every axis row exactly once
E7  the diagonal shadow: honeycomb and triangular lattices
PASS  points of Z^3 that share a shadow point down (1,1,1) always share x+y+z mod 3
PASS  two classes (x+y+z = 0, 1 mod 3) seen down the diagonal: every point has 3 nearest neighbors, all of the other class (honeycomb)  [58 interior points]
PASS  all three classes: every point has 6 nearest neighbors (triangular lattice)
PASS  one class alone: 6 nearest neighbors at sqrt(3) times the spacing (the coarse triangular lattice)
PASS  the 3x3x3 grid casts 19 shadow points (the magic hexagon's cells), one class each  [class sizes 0/1/2 = [7, 6, 6]]
PASS  tesseract down its long diagonal: 16 corners cast 15 points (two land on the center), 14 hull vertices, 12 rhombic faces with diagonals in ratio sqrt 2  [8 vertices of degree 3 + 6 of degree 4: the rhombic dodecahedron]
PASS  a grid of side k in d dimensions casts k^d - (k-1)^d points down its long diagonal (k = 2..4, d = 1..5)  [side 3: 1, 5, 19, 65, 211 for d = 1..5]
PASS  the tesseract's 16 corners land on the lattice of all-even or all-odd integer points (body-centered cubic): the center, its 8 nearest and 6 next-nearest points  [squared distances 0, 3 (x8), 4 (x6)]
PASS  the 81 cells of the 3^4 grid cast 65 points = 3^4 - 2^4: every lattice point of a rhombic dodecahedron twice the size (14 hull vertices, 12 faces)  [65 lattice points inside or on the hull]
PASS  shadow points <-> Eisenstein integers x + yw + zw^2, and x + y + z (mod 3) is the remainder on division by sqrt(-3) = 1 + 2w  [1 + 2w = w(1 - w); (1 + 2w)^2 = -3; residues 0, 1, 2 distinct; box -4..4]
E8  the corner <-> wave dictionary is a Krawtchouk transform
PASS  for d = 2..6 the matrix bal(a.t) over cube vertices has the Walsh characters as eigenvectors, eigenvalue sum_w bal(d-2w) K_w(s)
PASS  closed form: eigenvalue 2(-1)^(d-s)(-3)^((s-1)/2) = +-2(sqrt -3)^(s-1) for odd weight s, 0 for even s (d = 2..8)
PASS  for d = 2..8 every odd-weight eigenvalue is non-zero, so corners and waves determine each other  [d=2: -2; d=3: 2,-6; d=4: -2,6; d=5: 2,-6,18; d=6: -2,6,-18; d=7: 2,-6,18,-54; d=8: -2,6,-18,54]
PASS  bal(r) is the Legendre symbol (r/3); its Gauss sum bal(0) + bal(1)w + bal(2)w^2 = w - w^2 = sqrt(-3) squares to -3; eigenvalues = 2(-1)^(d-s) g^(s-1)  [g = -0 + 1.732051i]
E9  fibre sums keep exactly the waves orthogonal to the viewing direction
PASS  d = 3, 4: summing along wrapped lines in a sign direction s leaves 3 x (the waves with a.s = 0 mod 3) and erases the rest  [down the main diagonal: d=3 keeps 1 of 4 waves, d=4 keeps 3 of 8]
PASS  d = 2, 3, 4: all broken diagonals along s add to M exactly when no active wave a has a.s = 0 (mod 3)
PASS  every one of the 4 magic cubes has exactly one such direction (its missing wave)
PASS  the 58 magic tesseracts: 24 have no such direction, 32 have one, and only #5 and #21 have four  [counts {0: 24, 1: 32, 4: 2}; four directions: classes [5, 21]]
PASS  I + J (rows of J - 2I) and Hadamard (rows of H4): each one's four broken-diagonal directions are the other's four waves
E10 Lemmas A and B on the known hypercubes
PASS  Lemma A on all 1 + 4 + 58: the active wave directions span GF(3)^d
PASS  Lemma B on all 1 + 4 + 58: exactly d active directions, weights 1, 3, ..., 3^(d-1)
PASS  brute force d <= 4: prod(1 + x^g + x^2g) = 1 + x + ... + x^(3^d - 1) only for g = 1, 3, ..., 3^(d-1)

ALL PASS

==============================================================================================================
4. INSIDE AND OUTSIDE — flip_checks.py output (F1-F8)
==============================================================================================================
F1  one grid, two dual face lattices
PASS  d=2: the 9 cells label every face of the cube and every face of the cross-polytope; inclusion is reversed  [center = whole cube = empty face of the cross-polytope; corners = vertices = facets]
PASS  d=3: the 27 cells label every face of the cube and every face of the cross-polytope; inclusion is reversed  [center = whole cube = empty face of the cross-polytope; corners = vertices = facets]
PASS  d=4: the 81 cells label every face of the cube and every face of the cross-polytope; inclusion is reversed  [center = whole cube = empty face of the cross-polytope; corners = vertices = facets]
F2  the dual flip nest (circumscribe, inscribe the dual, repeat)
PASS  one full turn (cube -> cross-polytope -> cube) shrinks by exactly 1/d: the facet centroids are the 2^d points (+-1/d, ..., +-1/d) (d = 2..6)
PASS  d = 3: the inscribed octahedron's 8 face centroids are (+-1/3, +-1/3, +-1/3): the center cell of the 3x3x3 grid  [side 2/3 = one third of the original cube]
PASS  radii along the nest: cube circumradius sqrt3, insphere 1 = octahedron circumsphere, octahedron insphere 1/sqrt3 = inner-cube circumsphere, inner insphere 1/3  [each closure sphere is shared; ratio per flip 1/sqrt3, per turn 1/3]
PASS  a cube and its dual cross-polytope have the same in:out radius ratio 1/sqrt(d) (d = 2..6)
F3  corners and waves read the same sign vectors from the two dual solids
PASS  d=2: the cross-polytope's 4 facet normals are exactly the 4 sign vectors (the wave directions a, both signs)
PASS  d=3: the cross-polytope's 8 facet normals are exactly the 8 sign vectors (the wave directions a, both signs)
PASS  d=4: the cross-polytope's 16 facet normals are exactly the 16 sign vectors (the wave directions a, both signs)
PASS  d=5: the cross-polytope's 32 facet normals are exactly the 32 sign vectors (the wave directions a, both signs)
F4  the sign that flips with every rung
PASS  the center-point flip y -> -y has determinant (-1)^d: it reverses orientation on odd rungs and keeps it on even rungs
PASS  corner rule: rows add to zero for any corners, and the center is (-1)^d times the corner sum (d = 2..5)
F5  each rung's lines through the center = new directions + the rung below
PASS  (3^d - 1)/2 = 3^(d-1) + (3^(d-1) - 1)/2: the lines leaving the hyperplane y_d = 0, plus the rung below inside it (d = 2..7)  [1, 4, 13, 40, 121, 364, 1093]
F6  the grid as the divisors of N^2
PASS  d=2, N = 6: cells <-> the 9 divisors of N^2; corners = square divisors; center = N; y -> -y is m -> N^2/m; (-1)^z = lambda(m) = mu(squarefree part)  [sum of lambda: all 1, without the center 0]
PASS  d=3, N = 30: cells <-> the 27 divisors of N^2; corners = square divisors; center = N; y -> -y is m -> N^2/m; (-1)^z = lambda(m) = mu(squarefree part)  [sum of lambda: all 1, without the center 2 = V - E + F of the cube]
PASS  d=4, N = 210: cells <-> the 81 divisors of N^2; corners = square divisors; center = N; y -> -y is m -> N^2/m; (-1)^z = lambda(m) = mu(squarefree part)  [sum of lambda: all 1, without the center 0]
PASS  d=5, N = 2310: cells <-> the 243 divisors of N^2; corners = square divisors; center = N; y -> -y is m -> N^2/m; (-1)^z = lambda(m) = mu(squarefree part)  [sum of lambda: all 1, without the center 2]
F7  the multiplicative twin of the 58 tesseracts
PASS  all 58: the 81 cells hold the 81 divisors of 210^2 once each, every one of the 116 lines multiplies to 210^3 = 9 261 000, center 210, opposite cells multiply to 210^2
F8  arithmetic behind the audit of the outside observer's note
PASS  M_d = 3 M_(d-1) - 3 (15, 42, 123, 366, 1095, ...)
PASS  the number of lines (8, 31, 116, 421) differs from the line-system rank (7, 23, 73, 227)
PASS  the five Platonic edge lengths on one sphere are not a geometric progression  [successive ratios 1.4734, 1.0982, 1.2247, 1.1547]
PASS  Descartes: total angular deficit = 2 pi (V - E + F) = 4 pi for all five Platonic solids  [dodecahedron: 20 x pi/5]

ALL PASS

==============================================================================================================
5. TREE-SIZE ESTIMATES FOR THE OPEN 5D CASE — knuth_estimate.c (Knuth's random-path estimator)
==============================================================================================================
# Knuth random-path estimates of the corner-search tree size (knuth_estimate.c)
# calibration: the exhaustive d=4 search visits 6,222,419 nodes (log_d4.txt)
$ ./knuth_estimate 4 1000000 7
D=4 probes=1000000  estimated nodes (mean) = 6.202e+06   std.err = 2.45e+04   probes reaching a leaf = 7
batch means: 6.06e+06 6.21e+06 6.26e+06 6.29e+06 6.07e+06 6.15e+06 6.22e+06 6.22e+06 6.29e+06 6.24e+06
elapsed 2.960 s
$ ./knuth_estimate 5 1000000 7
D=5 probes=1000000  estimated nodes (mean) = 5.063e+17   std.err = 4.88e+15   probes reaching a leaf = 0
batch means: 4.77e+17 4.93e+17 5.18e+17 5.30e+17 4.98e+17 5.25e+17 5.23e+17 4.96e+17 4.89e+17 5.14e+17
elapsed 17.769 s
$ ./knuth_estimate 5 1000000 11
D=5 probes=1000000  estimated nodes (mean) = 5.135e+17   std.err = 4.73e+15   probes reaching a leaf = 0
batch means: 4.66e+17 5.23e+17 4.95e+17 5.28e+17 5.40e+17 5.29e+17 5.18e+17 5.06e+17 5.03e+17 5.28e+17
elapsed 17.398 s
# measured d=5 node rate: 38,797,312 nodes in 154.01 s (log_d5_probe.txt)

# reading: about 5.1e17 nodes for the 5D corner search; at the measured 251 917 nodes/s that is roughly 64 000
# CPU-years for this one method. Recorded on the page as an ESTIMATE, never as a conclusion.

==============================================================================================================
6. INSTRUMENT JAVASCRIPT — headless math checks (node test_math.js)
==============================================================================================================
PASS  d=2 classes from rows: 1 normal+magic
PASS  d=2 all spikes
PASS  d=2 mod-3 collapse with linear bottom digit
PASS  d=2 pairwise inequivalent  1
PASS  d=2 perms count  8
PASS  d=3 classes from rows: 4 normal+magic
PASS  d=3 all spikes
PASS  d=3 mod-3 collapse with linear bottom digit
PASS  d=3 pairwise inequivalent  4
PASS  d=3 perms count  48
PASS  d=4 classes from rows: 58 normal+magic
PASS  d=4 all spikes
PASS  d=4 mod-3 collapse with linear bottom digit
PASS  d=4 pairwise inequivalent  58
PASS  d=4 perms count  384
PASS  I+J rows are class #5
PASS  H4 rows are class #21
PASS  I+J layout matches the starting construction (first block row)  81 2 40 | 10 51 62 | 32 70 21
PASS  I+J bottom-right cell is 1
PASS  registry: last-digit families fill first-wave planes exactly for bases 12, 3, 9 (all 58)
PASS  d=2 Theorem 1 random round trips (corners, waves, mod-3 collapse)
PASS  d=3 Theorem 1 random round trips (corners, waves, mod-3 collapse)
PASS  d=4 Theorem 1 random round trips (corners, waves, mod-3 collapse)
PASS  hexagon 15 lines = 38
PASS  hexagram: 80 stars, every line = 26
PASS  Lo Shu 8 lines = 15
PASS  L(d) = 1, 4, 58, 2992, 543328  1,4,58,2992,543328 in 2893 ms
PASS  JS corner search d=2: 2 sols, 1 classes, 7 nodes, 0 ms
PASS  JS corner search d=3: 4 sols, 4 classes, 450 nodes, 3 ms
PASS  JS corner search d=4: 58 sols, 58 classes, 6222419 nodes, 16655 ms
ALL PASS

==============================================================================================================
7. INSTRUMENT PAGE — click-through of every control in jsdom (node test_dom.js ternary-magic-ladder.html)
==============================================================================================================
PASS  ladder table rows
PASS  hero equation has 3 grids
PASS  tesseract 81 cells, first is 81  [81]
PASS  tesseract readout 116/116  [lines adding to 123116 / 116 ✓center41entries 1 … 81 once✓sp]
PASS  cube 27 cells
PASS  square 9 cells
PASS  hexagon 19 cells
PASS  triangle default n=4 has 10 dots
PASS  strip 24 tiles
PASS  builder default d=3 shows 27 cells
PASS  builder default is normal  [lines adding to 4231 / 31 ✓center = 14✓opposite cells add to 28✓uses 1 … 27 once eachyes ✓energy Σγ²91 (normal needs 91)]
PASS  base 12: hero table shows A3
PASS  base 12: depth note  [ord₃(12) = 1 · 1 ternary wave in the last digit]
PASS  base 12: tesseract first cell 69 (81 in base 12)
PASS  last-digit view styles cells  [background:color-mix(in srgb, var(--indigo) 64%, v]
PASS  registry note says planes  [Registry view · base 12ord₃(12) = 1, so the last digit in base 12 fixes v mod 3, the first ternary wave. Coloring by last digit (indigo, amber, teal = digit mod]
PASS  base 10: registry note says no line-up
PASS  base 3 renders
PASS  base 9 renders
PASS  ternary view 4 digits
PASS  selecting a corner lists its quadragonal
PASS  next tesseract
PASS  Hadamard button
PASS  prev tesseract
PASS  cube next
PASS  cube center selected lists its 7 lines (3 rows + 4 diagonals)
PASS  square waves visible
PASS  square builder p = -2 not normal
PASS  square builder back to Lo Shu
PASS  hexagram 12 points
PASS  star 2
PASS  star point selection highlights 2 lines
PASS  hex center selected  [13]
PASS  triangle n=5 -> 15 dots + hole
PASS  tile click sets n=7
PASS  figures s=3
PASS  builder d=4 81 cells
PASS  builder d=4 normal at start
PASS  corner mode 8 inputs
PASS  corner switch keeps the array normal
PASS  editing a corner keeps all lines magic
PASS  …but breaks normality
PASS  wave mode after edit shows rounding note or exact
PASS  random spike is normal
PASS  random numbers still magic
PASS  d=2 builder
PASS  in-page search d=3 finds 4 classes  [d = 3 search0.0 s · 450 nodesnormalized solutions4classes under all symmetries4 ✓all use 1 … 3d once✓all lines magic✓all spikes✓]
PASS  in-page count L(2..6)  [L(2)1 ✓ · 0.0 sL(3)4 ✓ · 0.0 sL(4)58 ✓ · 0.0 sL(5)2,992 ✓ · 0.0 sL(6)543,328 ✓ · 3.2 s]
PASS  paper is a closed <details> holding 32 statement blocks  [32]
PASS  paper has bylines for both authors
PASS  hero byline names both authors
PASS  results summary visible outside the fold, 7 items
PASS  estimate tag present on the open item
PASS  link to #p3 opens the paper
PASS  faces: default d=3 shows 27 cells, 7 removed
PASS  faces: Menger sponge named, 20 of 27, Euler 2  [cells by face dimension8 corners · 12 edges · 6 squares · 1 cubeall cells8 + 12 + 6 + 1 = 27 = 33surface, alternating8 − 12 + 6 = 2 = V − E + F ✓with the center1 ✓keep faces of dimension ≤ 120 of 27 c]
PASS  faces: Cantor set 2 of 3
PASS  faces: carpet 8 of 9
PASS  faces: d=4 keeps 48 of 81
PASS  faces: k capped at d, nothing removed
PASS  flags: default block 1-27 lights 27 cells, a 3-space  [values 1–2727 cellsshapea 3-space, affine ✓fixed by (mod 3)(+++−)·y ≡ 2axis rows it meetsall 108, each exactly once ✓weighingtap a cell]
PASS  flags: every block of 27, 9, 3 is affine on 6 classes
PASS  flags: single value is a point
PASS  flags: tapping a cell weighs it  [ghing the tapped cell76weight 27right panweight 9right panweight 3offweight 1left pannet load = value − 4135 ✓its 4 axis rowseach weight right, off, left once ✓]
PASS  energy table: 5 rows, base-9 repunits
PASS  lattice: triangular, 6 neighbors, 19 = 7 + 6 + 6  [showingall three classesstructuretriangular latticeneighbors of each point6 at distance 1the cube's 27 cells cast19 shadows: 7 + 6 + 6 by class ✓tapped pointover the cell (0, 0, 0)as an Eisenstein integer0x + y + z mod 30: remainder mod √−3, SU(3) charge ω0long-diagonal wave0magic-cube cells over it(−1, −1, −1) · (0, 0, 0) · (1, 1, 1)]
PASS  lattice: honeycomb, 3 neighbors of the other class
PASS  lattice: one class, 6 at distance √3
PASS  lattice: tapped point gives an Eisenstein integer
PASS  lattice: rings toggle off
PASS  lattice: rings back, 19
PASS  tesseract shadow: 16 to 15, 8 + 6 vertices, sqrt2, BCC  [corners → shadow points16 → 15 ✓at the center2 corners: 0000 and 1111hull vertices14: 8 where three rhombi meet, 6 where four meet ✓hull edges24 ✓rhombus diagonals1.4142 = √2 ✓squared distances from the center0, 3 (×8), 4 (×6)all-even or all-odd coordinatesbody-centered cubic ✓]
PASS  tesseract shadow: 81 cells to 65 points  [cells → shadow points81 → 65 = 34 − 24 ✓points holding 1, 2, 3 cells50, 14, 1outlinethe same rhombic dodecahedron at twice the sizeone rung downthe cube's 27 cells cast 19 = 33 − 23: the magic hexagon]
PASS  tesseract shadow: turn redraws
PASS  broken diagonals: each cube has exactly one direction  [m is 42each sum = M + 3 × those waves✓directions where all add to 42+++ (1 of 4)]
PASS  broken diagonals: I + J has four, the Hadamard waves  [m = M + 3 × those waves✓directions where all add to 123++++, +−−+, +−+−, ++−− (4 of 8)the Hadamard tesseract's waves++++, ++−−, +−+−, +−−+ ✓number of minus signsactive waves odd, these directions even]
PASS  broken diagonals: parity pairing shown for I + J  [ of 8)the Hadamard tesseract's waves++++, ++−−, +−+−, +−−+ ✓number of minus signsactive waves odd, these directions even]
PASS  broken diagonals: Hadamard has four, the I + J waves
PASS  broken diagonals: a chip lights 3 cells
PASS  broken diagonals: direction buttons switch
PASS  broken diagonals: sums = M + 3 x waves across s, sampled classes and all directions
PASS  eigen table: 7 rows, all checks pass
PASS  readings: cube faces d=3  [faces of the cube8 corners · 12 edges · 6 squares · 1 cubethe centerthe whole cubethe cornersverticestapped celltap a ce]
PASS  readings: octahedron faces
PASS  readings: divisors, lambda sums 1 and 2  [N, N²30, 900divisors of N²27 = 33, one per cellcorners: the square divisors1, 4, 9, 25, 36, 100, 225, 900centerN = 30, its own cofactorΣ λ over all cells1 ✓Σ λ without N2 = 1 − (−1)3 = V − E + F ✓tapped celltap a cell]
PASS  readings: tapped divisor shows cofactor and lambda
PASS  readings: d=4 divisors of 210^2, lambda without N is 0  [N, N²210, 44100divisors of N²81 = 34, one per cellcorners: the square divisors1, 4, 9, 25, 36, 49, 100, 196, 225, 441, 900, 1225, 1764, 4900, 11025, 44100centerN = 210, its own cofactorΣ λ over all ce]
PASS  readings: d=2 dual is the diamond
PASS  flip nest: cube lands on the center cell  [figurescube ±1 → octahedron 1 → cube ±1/3closure spheres (radius)√3 ≈ 1.732, 1 ≈ 1.000, 1/√3 ≈ 0.577, 1/3 ≈ 0.333each flip scales by1/√3each full turn scales by1/3in:out ratio of every figure1/√3 ≈ 0.5774after one full turn[−1/3, 1/3]³, the center cell of the 3 × 3 × 3 grid ✓]
PASS  flip nest: square does not
PASS  flip nest: flips capped at 4
PASS  flip nest: turn redraws
PASS  rung table: 5 rows
PASS  base 12: energy table in dozenal
PASS  base 12: divisors in dozenal (900 = 630 in base 12)  [N, N²26₁₂ = 30, 630₁₂ = 900divisors of N²23 = 33, one per cellcorners: the square divisors1, 4, 9, 21, 30, 84, 169, 630centerN = 26, its own cofactorΣ λ over al]
PASS  base 12: 44100 = 21630 in base 12  [N, N²156₁₂ = 210, 21630₁₂ = 44100divisors of N²69 = 34, one per cellcorners: the square divisors1, 4, 9, 21, 30, 41, 84, 144, 169, 309, 630, 861, 1030, 2A04, 64]
PASS  standalone: every link is in-page or an absolute https address  [63 links]
PASS  standalone: every in-page link has its target  [43 in-page links]
PASS  standalone: nothing loaded from outside except the optional web fonts
PASS  standalone: no companion file named outside the supplement
PASS  supplement: a closed <details> in the page's last section
PASS  supplement: sections 1-7, ten sources and the hash list are anchored
PASS  supplement: every SOURCE block hashes to the SHA-256 in its title and in the list  [10 of 10]
PASS  supplement: a link to section 5 opens it
PASS  printing opens the paper, leaves the supplement closed, then restores both
PASS  supplement: Save as text hands over the whole supplement  [ternary-magic-ladder-supplement.txt, identical to the text in the page]
PASS  contribution note: accountability with the first author
PASS  standalone head has title and charset
PASS  no runtime errors
ALL PASS

==============================================================================================================
SOURCE — corner_search.c  sha256 3b77859087d38352871528da4de1d8bf50e68039344eab50b63ba79c9cf6f72f
==============================================================================================================
/*
 * corner_search.c
 * Exhaustive search for NORMAL order-3 magic hypercubes of dimension D (entries 1..3^D,
 * every axis row and every main diagonal through the centre summing to M = 3(3^D+1)/2).
 *
 * It does not assume any digit / linear structure. It uses only the Corner Rule, which is
 * proved (and re-checked numerically in verify_ladder.py):
 *   writing f = v - (3^D+1)/2 and y = x - (1,...,1) in {-1,0,1}^D,
 *   (i)  all axis rows sum to M  <=>  f(y) = (-1)^{z(y)} * sum of f over the 2^{z(y)} corners
 *        of the face whose centre is y   (z(y) = number of zero coordinates of y);
 *   (ii) given (i), the main diagonals sum to M  <=>  antipodal corners are opposite: f(-t) = -f(t).
 * So every magic array is fixed by one corner value per antipodal pair (2^(D-1) numbers), and the
 * search runs over those numbers only. "Normal" means the 3^D values of f are exactly
 * -m..m with m = (3^D-1)/2, i.e. the (3^D-1)/2 antipodal cell pairs carry |f| = 1..m once each.
 *
 * Symmetry normalisation (one representative per orbit of the D!*2^D rotations/reflections when
 * the orbit is free; the Python side re-canonicalises everything anyway):
 *   - the all-plus corner carries the largest |f| among corners, and is positive;
 *   - its D neighbours (one coordinate flipped) have strictly decreasing values in coordinate order.
 *
 * Usage: corner_search D [time_limit_seconds]   -> prints "SOL h0 h1 ... h(2^D-1)" per solution
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAXD 6
#define MAXC (1 << MAXD)
#define MAXCELL 400

static int D, NC, NH, M;
static int ncell;
static int fz[MAXCELL], fsign[MAXCELL], fn[MAXCELL], fcorn[MAXCELL][MAXC];
static int order[MAXC], posof[MAXC];
static int hemi_rep[MAXC], hemi_sgn[MAXC];
static int step_faces[MAXC][MAXCELL], nstep[MAXC];
static int h[MAXC];
static unsigned char used[2000];
static long long nsol = 0, nodes = 0;
static int nbr_pos[MAXD], nbr_sign[MAXD];
static double tlimit = 0;
static clock_t t0;
static int timed_out = 0;
static int a_max = 0;

static int pow3(int k) { int r = 1; while (k--) r *= 3; return r; }

static void build(void) {
    NC = 1 << D; NH = NC / 2; M = (pow3(D) - 1) / 2;
    for (int t = 0; t < NC; t++) {
        if (!(t & 1)) { hemi_rep[t] = t; hemi_sgn[t] = 1; }
        else { hemi_rep[t] = t ^ (NC - 1); hemi_sgn[t] = -1; }
    }
    ncell = 0;
    int tot = pow3(D);
    for (int code = 0; code < tot; code++) {
        int y[MAXD], c = code;
        for (int j = 0; j < D; j++) { y[j] = (c % 3) - 1; c /= 3; }
        int first = -1;
        for (int j = 0; j < D; j++) if (y[j] != 0) { first = j; break; }
        if (first < 0) continue;          /* centre */
        if (y[first] != 1) continue;      /* keep one cell of each antipodal pair */
        int z = 0;
        for (int j = 0; j < D; j++) if (y[j] == 0) z++;
        fz[ncell] = z; fsign[ncell] = (z % 2) ? -1 : 1; fn[ncell] = 0;
        for (int t = 0; t < NC; t++) {
            int ok = 1;
            for (int j = 0; j < D; j++) {
                int tj = ((t >> j) & 1) ? -1 : 1;
                if (y[j] != 0 && tj != y[j]) { ok = 0; break; }
            }
            if (ok) fcorn[ncell][fn[ncell]++] = t;
        }
        ncell++;
    }
    if (ncell != M) { fprintf(stderr, "cell count mismatch %d %d\n", ncell, M); exit(1); }
}

static void make_order(void) {
    int inord[MAXC] = {0};
    int k = 0;
    order[k++] = 0; inord[0] = 1;
    for (int j = 0; j < D; j++) {
        int r = hemi_rep[1 << j];
        nbr_sign[j] = hemi_sgn[1 << j];
        nbr_pos[j] = k;
        order[k++] = r; inord[r] = 1;
    }
    while (k < NH) {
        int best = -1, bestc = -1;
        for (int t = 0; t < NC; t++) {
            if ((t & 1) || inord[t]) continue;
            int cnt = 0;
            for (int f = 0; f < ncell; f++) {
                int all = 1, has = 0;
                for (int i = 0; i < fn[f]; i++) {
                    int r = hemi_rep[fcorn[f][i]];
                    if (r == t) has = 1;
                    else if (!inord[r]) { all = 0; break; }
                }
                if (all && has) cnt++;
            }
            if (cnt > bestc) { bestc = cnt; best = t; }
        }
        order[k++] = best; inord[best] = 1;
    }
    for (int i = 0; i < NH; i++) posof[order[i]] = i;
    for (int s = 0; s < NH; s++) nstep[s] = 0;
    for (int f = 0; f < ncell; f++) {
        int mx = -1;
        for (int i = 0; i < fn[f]; i++) {
            int p = posof[hemi_rep[fcorn[f][i]]];
            if (p > mx) mx = p;
        }
        step_faces[mx][nstep[mx]++] = f;
    }
}

static void record(void) {
    nsol++;
    printf("SOL");
    for (int t = 0; t < NC; t++) printf(" %d", h[t]);
    printf("\n");
    fflush(stdout);
}

static void dfs(int s) {
    nodes++;
    if (tlimit > 0 && (nodes & 0xFFFFF) == 0) {
        double el = (double)(clock() - t0) / CLOCKS_PER_SEC;
        if (el > tlimit) timed_out = 1;
    }
    if (timed_out) return;
    if (s == NH) { record(); return; }
    int t = order[s];
    int lo, hi;
    if (s == 0) { lo = 1; hi = M; } else { lo = -(a_max - 1); hi = a_max - 1; }
    for (int v = hi; v >= lo; v--) {
        if (v == 0) continue;
        int av = v < 0 ? -v : v;
        if (used[av]) continue;
        if (s == 0) {
            a_max = v;
            if (D >= 5) { fprintf(stderr, "a=%d nodes=%lld sols=%lld\n", v, nodes, nsol); }
        }
        h[t] = v; h[t ^ (NC - 1)] = -v;
        int ok = 1;
        for (int j = 1; j < D; j++) {
            if (nbr_pos[j] == s) {
                int vj = nbr_sign[j] * v;
                int prev = nbr_sign[j - 1] * h[order[nbr_pos[j - 1]]];
                if (!(vj < prev)) ok = 0;
            }
        }
        if (!ok) continue;
        int marked[MAXCELL], nm = 0;
        for (int i = 0; i < nstep[s] && ok; i++) {
            int f = step_faces[s][i];
            int sum = 0;
            for (int q = 0; q < fn[f]; q++) sum += h[fcorn[f][q]];
            int val = fsign[f] * sum;
            int a2 = val < 0 ? -val : val;
            if (a2 == 0 || a2 > M || used[a2]) { ok = 0; break; }
            used[a2] = 1; marked[nm++] = a2;
        }
        if (ok) dfs(s + 1);
        for (int i = 0; i < nm; i++) used[marked[i]] = 0;
        if (timed_out) return;
    }
}

int main(int argc, char **argv) {
    if (argc < 2) { fprintf(stderr, "usage: corner_search D [seconds]\n"); return 1; }
    D = atoi(argv[1]);
    if (argc > 2) tlimit = atof(argv[2]);
    if (D < 2 || D > MAXD) { fprintf(stderr, "D out of range\n"); return 1; }
    build();
    make_order();
    t0 = clock();
    dfs(0);
    double el = (double)(clock() - t0) / CLOCKS_PER_SEC;
    fprintf(stderr, "D=%d solutions=%lld nodes=%lld seconds=%.2f timed_out=%d\n", D, nsol, nodes, el, timed_out);
    printf("DONE D=%d solutions=%lld nodes=%lld seconds=%.2f complete=%d\n", D, nsol, nodes, el, !timed_out);
    return 0;
}

==============================================================================================================
SOURCE — line_magic.c  sha256 50e9068aedbcf4ffb8ea47d1f5bdd234b2b0abdebad5d1ab4c32ae9b0a30df70
==============================================================================================================
/*
 * line_magic.c
 * Count every labelling of n cells with the numbers 1..n (each once) such that every listed
 * line sums to S. Plain exhaustive backtracking with sum bounds; no symmetry assumptions.
 * Input on stdin:  n S L
 *                  L lines: len c1 ... c_len
 *                  n ints: the cell assignment order
 * Output: "COUNT k" and, with argument "print", one line per solution (values by cell index).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int n, S, L;
static int llen[64], lc[64][8];
static int cell_lines[32][8], ncl[32];
static int lsum[64], lcnt[64];
static int val[32], usedv[40], ord[32];
static long long cnt = 0;
static int print_sol = 0;

static void dfs(int k) {
    if (k == n) {
        cnt++;
        if (print_sol) {
            printf("SOL");
            for (int i = 0; i < n; i++) printf(" %d", val[i]);
            printf("\n");
        }
        return;
    }
    int c = ord[k];
    for (int v = 1; v <= n; v++) {
        if (usedv[v]) continue;
        int ok = 1;
        for (int i = 0; i < ncl[c] && ok; i++) {
            int l = cell_lines[c][i];
            int ns = lsum[l] + v;
            int rem = llen[l] - lcnt[l] - 1;
            if (rem == 0) { if (ns != S) ok = 0; }
            else {
                int mn = 0, mx = 0, got = 0;
                for (int w = 1; w <= n && got < rem; w++) if (!usedv[w] && w != v) { mn += w; got++; }
                got = 0;
                for (int w = n; w >= 1 && got < rem; w--) if (!usedv[w] && w != v) { mx += w; got++; }
                if (ns + mn > S || ns + mx < S) ok = 0;
            }
        }
        if (!ok) continue;
        usedv[v] = 1; val[c] = v;
        for (int i = 0; i < ncl[c]; i++) { int l = cell_lines[c][i]; lsum[l] += v; lcnt[l]++; }
        dfs(k + 1);
        usedv[v] = 0;
        for (int i = 0; i < ncl[c]; i++) { int l = cell_lines[c][i]; lsum[l] -= v; lcnt[l]--; }
    }
}

int main(int argc, char **argv) {
    if (argc > 1 && strcmp(argv[1], "print") == 0) print_sol = 1;
    if (scanf("%d %d %d", &n, &S, &L) != 3) return 1;
    for (int l = 0; l < L; l++) {
        if (scanf("%d", &llen[l]) != 1) return 1;
        for (int i = 0; i < llen[l]; i++) {
            if (scanf("%d", &lc[l][i]) != 1) return 1;
            int c = lc[l][i];
            cell_lines[c][ncl[c]++] = l;
        }
    }
    for (int i = 0; i < n; i++) if (scanf("%d", &ord[i]) != 1) return 1;
    dfs(0);
    printf("COUNT %lld\n", cnt);
    return 0;
}

==============================================================================================================
SOURCE — knuth_estimate.c  sha256 87d8ef02da7a78c0084bb64bd03a448c4c76a1d20a507fd69b62be467e24ff71
==============================================================================================================
/*
 * corner_search.c
 * Exhaustive search for NORMAL order-3 magic hypercubes of dimension D (entries 1..3^D,
 * every axis row and every main diagonal through the centre summing to M = 3(3^D+1)/2).
 *
 * It does not assume any digit / linear structure. It uses only the Corner Rule, which is
 * proved (and re-checked numerically in verify_ladder.py):
 *   writing f = v - (3^D+1)/2 and y = x - (1,...,1) in {-1,0,1}^D,
 *   (i)  all axis rows sum to M  <=>  f(y) = (-1)^{z(y)} * sum of f over the 2^{z(y)} corners
 *        of the face whose centre is y   (z(y) = number of zero coordinates of y);
 *   (ii) given (i), the main diagonals sum to M  <=>  antipodal corners are opposite: f(-t) = -f(t).
 * So every magic array is fixed by one corner value per antipodal pair (2^(D-1) numbers), and the
 * search runs over those numbers only. "Normal" means the 3^D values of f are exactly
 * -m..m with m = (3^D-1)/2, i.e. the (3^D-1)/2 antipodal cell pairs carry |f| = 1..m once each.
 *
 * Symmetry normalisation (one representative per orbit of the D!*2^D rotations/reflections when
 * the orbit is free; the Python side re-canonicalises everything anyway):
 *   - the all-plus corner carries the largest |f| among corners, and is positive;
 *   - its D neighbours (one coordinate flipped) have strictly decreasing values in coordinate order.
 *
 * Usage: corner_search D [time_limit_seconds]   -> prints "SOL h0 h1 ... h(2^D-1)" per solution
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#define MAXD 6
#define MAXC (1 << MAXD)
#define MAXCELL 400

static int D, NC, NH, M;
static int ncell;
static int fz[MAXCELL], fsign[MAXCELL], fn[MAXCELL], fcorn[MAXCELL][MAXC];
static int order[MAXC], posof[MAXC];
static int hemi_rep[MAXC], hemi_sgn[MAXC];
static int step_faces[MAXC][MAXCELL], nstep[MAXC];
static int h[MAXC];
static unsigned char used[2000];
static long long nsol = 0, nodes = 0;
static int nbr_pos[MAXD], nbr_sign[MAXD];
static double tlimit = 0;
static clock_t t0;
static int timed_out = 0;
static int a_max = 0;

static int pow3(int k) { int r = 1; while (k--) r *= 3; return r; }

static void build(void) {
    NC = 1 << D; NH = NC / 2; M = (pow3(D) - 1) / 2;
    for (int t = 0; t < NC; t++) {
        if (!(t & 1)) { hemi_rep[t] = t; hemi_sgn[t] = 1; }
        else { hemi_rep[t] = t ^ (NC - 1); hemi_sgn[t] = -1; }
    }
    ncell = 0;
    int tot = pow3(D);
    for (int code = 0; code < tot; code++) {
        int y[MAXD], c = code;
        for (int j = 0; j < D; j++) { y[j] = (c % 3) - 1; c /= 3; }
        int first = -1;
        for (int j = 0; j < D; j++) if (y[j] != 0) { first = j; break; }
        if (first < 0) continue;          /* centre */
        if (y[first] != 1) continue;      /* keep one cell of each antipodal pair */
        int z = 0;
        for (int j = 0; j < D; j++) if (y[j] == 0) z++;
        fz[ncell] = z; fsign[ncell] = (z % 2) ? -1 : 1; fn[ncell] = 0;
        for (int t = 0; t < NC; t++) {
            int ok = 1;
            for (int j = 0; j < D; j++) {
                int tj = ((t >> j) & 1) ? -1 : 1;
                if (y[j] != 0 && tj != y[j]) { ok = 0; break; }
            }
            if (ok) fcorn[ncell][fn[ncell]++] = t;
        }
        ncell++;
    }
    if (ncell != M) { fprintf(stderr, "cell count mismatch %d %d\n", ncell, M); exit(1); }
}

static void make_order(void) {
    int inord[MAXC] = {0};
    int k = 0;
    order[k++] = 0; inord[0] = 1;
    for (int j = 0; j < D; j++) {
        int r = hemi_rep[1 << j];
        nbr_sign[j] = hemi_sgn[1 << j];
        nbr_pos[j] = k;
        order[k++] = r; inord[r] = 1;
    }
    while (k < NH) {
        int best = -1, bestc = -1;
        for (int t = 0; t < NC; t++) {
            if ((t & 1) || inord[t]) continue;
            int cnt = 0;
            for (int f = 0; f < ncell; f++) {
                int all = 1, has = 0;
                for (int i = 0; i < fn[f]; i++) {
                    int r = hemi_rep[fcorn[f][i]];
                    if (r == t) has = 1;
                    else if (!inord[r]) { all = 0; break; }
                }
                if (all && has) cnt++;
            }
            if (cnt > bestc) { bestc = cnt; best = t; }
        }
        order[k++] = best; inord[best] = 1;
    }
    for (int i = 0; i < NH; i++) posof[order[i]] = i;
    for (int s = 0; s < NH; s++) nstep[s] = 0;
    for (int f = 0; f < ncell; f++) {
        int mx = -1;
        for (int i = 0; i < fn[f]; i++) {
            int p = posof[hemi_rep[fcorn[f][i]]];
            if (p > mx) mx = p;
        }
        step_faces[mx][nstep[mx]++] = f;
    }
}


static unsigned long long rng_state = 88172645463325252ULL;
static unsigned long long rng(void) { rng_state ^= rng_state << 13; rng_state ^= rng_state >> 7; rng_state ^= rng_state << 17; return rng_state; }

/* try value v at step s: returns 1 and leaves marks if valid, else 0 with no marks */
static int try_val(int s, int v, int *marked, int *nm) {
    int t = order[s];
    int av = v < 0 ? -v : v;
    if (v == 0 || used[av]) return 0;
    h[t] = v; h[t ^ (NC - 1)] = -v;
    for (int j = 1; j < D; j++) if (nbr_pos[j] == s) {
        int vj = nbr_sign[j] * v, prev = nbr_sign[j - 1] * h[order[nbr_pos[j - 1]]];
        if (!(vj < prev)) return 0;
    }
    *nm = 0;
    for (int i = 0; i < nstep[s]; i++) {
        int f = step_faces[s][i], sum = 0;
        for (int q = 0; q < fn[f]; q++) sum += h[fcorn[f][q]];
        int val = fsign[f] * sum, a2 = val < 0 ? -val : val;
        if (a2 == 0 || a2 > M || used[a2]) { for (int k = 0; k < *nm; k++) used[marked[k]] = 0; *nm = 0; return 0; }
        used[a2] = 1; marked[(*nm)++] = a2;
    }
    return 1;
}

static double probe(int *depth_reached) {
    static int marks[MAXC][MAXCELL]; static int nms[MAXC];
    memset(used, 0, sizeof(used));
    double est = 1.0, prod = 1.0;
    int s;
    for (s = 0; s < NH; s++) {
        int lo = (s == 0) ? 1 : -(a_max - 1), hi = (s == 0) ? M : a_max - 1;
        int cand[2048], nc = 0, tmp[MAXCELL], tn;
        for (int v = hi; v >= lo; v--) {
            if (s == 0) a_max = v;
            if (try_val(s, v, tmp, &tn)) { cand[nc++] = v; for (int k = 0; k < tn; k++) used[tmp[k]] = 0; }
        }
        if (nc == 0) break;
        prod *= nc; est += prod;
        int v = cand[rng() % nc];
        if (s == 0) a_max = v;
        try_val(s, v, marks[s], &nms[s]);
    }
    *depth_reached = s;
    return est;
}

int main(int argc, char **argv) {
    D = atoi(argv[1]); long long probes = atoll(argv[2]); if (argc > 3) rng_state ^= (unsigned long long)atoll(argv[3]) * 0x9E3779B97F4A7C15ULL;
    build(); make_order();
    double sum = 0, sumsq = 0; long long full = 0; int batches = 10; double bm[10] = {0};
    for (long long p = 0; p < probes; p++) {
        int dr; double e = probe(&dr);
        sum += e; sumsq += e * e; if (dr == NH) full++;
        bm[p * batches / probes] += e;
    }
    double mean = sum / probes, sd = sqrt(sumsq / probes - mean * mean);
    printf("D=%d probes=%lld  estimated nodes (mean) = %.3e   std.err = %.2e   probes reaching a leaf = %lld\n", D, probes, mean, sd / sqrt((double)probes), full);
    printf("batch means:"); for (int b = 0; b < batches; b++) printf(" %.2e", bm[b] / (probes / batches)); printf("\n");
    return 0;
}

==============================================================================================================
SOURCE — verify_ladder.py  sha256 baab375f71791abae4b4e2a3bc60b9ec27079d86c20812c1c02744d60ed88fa0
==============================================================================================================
#!/usr/bin/env python3
"""
verify_ladder.py — receipts for the magic-ladder instrument.

Part A  Structure of order-3 magic hypercubes (any real entries), d = 2..5
        A1 rank of the line system = 3^d - 2^(d-1)
        A2 Corner Rule builds magic arrays from ANY odd corner values; non-odd corners break diagonals
        A3 Polynomial view  f(y) = (-2)^z(y) * q(y), q = multilinear interpolant of the corners
        A4 Wave view  f = sum_a gamma_a * bal(a.y mod 3), gamma_a = mean of f on {a.y = 1 mod 3}
        A5 Converse: every magic array obeys the Corner Rule (projection test)
Part B  Normal hypercubes (entries 1..3^d)
        B1 exhaustive corner-search solutions d = 2,3,4: normal, magic, spike spectra, energy,
           mod-3 collapse, class count vs the linear (spike) family
        B2 L(d) = number of sign-vector bases of GF(3)^d, d = 2..6
Part C  Triangles, centered figures, square / hexagon / hexagram exhaustive counts, base-12 forms
Writes ladder_data.json for the instrument.
"""
import itertools
import json
import math
import random
import subprocess
import sys
from fractions import Fraction

import numpy as np

random.seed(20261010)
np.random.seed(20261010)
OUT = []


def say(s=""):
    print(s)
    OUT.append(s)


def to_base(n, b, digits="0123456789AB"):
    if n == 0:
        return "0"
    neg = n < 0
    n = abs(n)
    s = ""
    while n:
        s = digits[n % b] + s
        n //= b
    return ("-" if neg else "") + s


# ---------------------------------------------------------------- geometry of {-1,0,1}^d
def cells(d):
    out = []
    for code in range(3 ** d):
        c, y = code, []
        for _ in range(d):
            y.append(c % 3 - 1)
            c //= 3
        out.append(tuple(y))
    return out


def corners(d):
    return [tuple(-1 if (t >> j) & 1 else 1 for j in range(d)) for t in range(2 ** d)]


def face_corners(y, d):
    return [t for t, tau in enumerate(corners(d)) if all(yj == 0 or yj == tj for yj, tj in zip(y, tau))]


def lines(d):
    L = []
    for j in range(d):
        for rest in itertools.product((-1, 0, 1), repeat=d - 1):
            line = []
            for t in (-1, 0, 1):
                y = list(rest[:j]) + [t] + list(rest[j:])
                line.append(tuple(y))
            L.append(("row", j, line))
    for s in itertools.product((1, -1), repeat=d - 1):
        sig = (1,) + s
        L.append(("diag", sig, [tuple(-v for v in sig), tuple([0] * d), sig]))
    return L


def build_from_corners(h, d):
    """Corner Rule: f(y) = (-1)^z * sum of h over the corners of the face centered at y."""
    f = {}
    for y in cells(d):
        z = sum(1 for v in y if v == 0)
        f[y] = (-1) ** z * sum(h[t] for t in face_corners(y, d))
    return f


def bal(r):
    r %= 3
    return 0 if r == 0 else (1 if r == 1 else -1)


def directions(d):
    return [(1,) + s for s in itertools.product((1, -1), repeat=d - 1)]


def wave(a, y):
    return bal(sum(ai * yi for ai, yi in zip(a, y)))


def spectrum(f, d):
    """gamma_a = mean of f over the hyperplane a.y = 1 (mod 3), exact."""
    g = {}
    for a in directions(d):
        s = sum(f[y] for y in cells(d) if sum(ai * yi for ai, yi in zip(a, y)) % 3 == 1)
        g[a] = Fraction(s, 3 ** (d - 1))
    return g


def all_line_sums(f, d):
    return [sum(f[y] for y in line) for _, _, line in lines(d)]


def multilinear_coeffs(h, d):
    cs = corners(d)
    co = {}
    for S in itertools.product((0, 1), repeat=d):
        tot = 0
        for t, tau in enumerate(cs):
            p = 1
            for j in range(d):
                if S[j]:
                    p *= tau[j]
            tot += h[t] * p
        co[S] = Fraction(tot, 2 ** d)
    return co


def q_eval(co, y):
    tot = Fraction(0)
    for S, c in co.items():
        if c == 0:
            continue
        p = 1
        for j, sj in enumerate(S):
            if sj:
                p *= y[j]
        tot += c * p
    return tot


def frac_mod3(x):
    x = Fraction(x)
    num, den = x.numerator, x.denominator
    assert den % 3 != 0
    return (num * pow(den, -1, 3)) % 3


def sym_perms(d):
    cl = cells(d)
    pos = {y: i for i, y in enumerate(cl)}
    out = []
    for p in itertools.permutations(range(d)):
        for s in itertools.product((1, -1), repeat=d):
            out.append([pos[tuple(s[j] * y[p[j]] for j in range(d))] for y in cl])
    return np.array(out)


def canon(vec, perms):
    v = np.asarray(vec)
    best = None
    for idx in perms:
        w = tuple(v[idx].tolist())
        if best is None or w < best:
            best = w
    return best


def canon_many(V, idxs):
    best = V[:, idxs[0]].copy()
    r = np.arange(V.shape[0])
    for idx in idxs[1:]:
        W = V[:, idx]
        diff = W != best
        first = diff.argmax(axis=1)
        less = diff.any(axis=1) & (W[r, first] < best[r, first])
        best[less] = W[less]
    return best


def rank_mod3(rows):
    A = [[v % 3 for v in r] for r in rows]
    nr, nc, r = len(A), len(A[0]), 0
    for col in range(nc):
        piv = next((i for i in range(r, nr) if A[i][col]), None)
        if piv is None:
            continue
        A[r], A[piv] = A[piv], A[r]
        inv = A[r][col]
        A[r] = [(v * inv) % 3 for v in A[r]]
        for i in range(nr):
            if i != r and A[i][col]:
                fct = A[i][col]
                A[i] = [(x - fct * yv) % 3 for x, yv in zip(A[i], A[r])]
        r += 1
    return r


def linear_family_arrays(d):
    """All f = sum_k 3^(k-1) bal(a_k . y) over ordered signed bases a_k in {+-1}^d."""
    cl = cells(d)
    Y = np.array(cl)
    vecs = []
    for bits in itertools.product((1, -1), repeat=d * d):
        A = [list(bits[i * d:(i + 1) * d]) for i in range(d)]
        if rank_mod3(A) != d:
            continue
        f = np.zeros(len(cl), dtype=int)
        for k, a in enumerate(A):
            r = (Y @ np.array(a)) % 3
            f += (3 ** k) * np.where(r == 0, 0, np.where(r == 1, 1, -1))
        vecs.append(f)
    return np.array(vecs)


def bases_count(d, chunk=200000):
    pts = np.array([(1,) + s for s in itertools.product((1, -1), repeat=d - 1)], dtype=float)
    combos = itertools.combinations(range(len(pts)), d)
    total = 0
    while True:
        batch = list(itertools.islice(combos, chunk))
        if not batch:
            break
        dets = np.rint(np.linalg.det(pts[np.array(batch)])).astype(np.int64)
        total += int(np.count_nonzero(dets % 3))
    return total


# ======================================================================= PART A
say("=" * 78)
say("PART A — structure of order-3 magic hypercubes (real entries), d = 2..5")
say("=" * 78)
for d in range(2, 6):
    cl = cells(d)
    pos = {y: i for i, y in enumerate(cl)}
    L = lines(d)
    Mx = np.zeros((len(L), len(cl)))
    for r, (_, _, line) in enumerate(L):
        for y in line:
            Mx[r, pos[y]] = 1
    rank = np.linalg.matrix_rank(Mx)
    say(f"A1 d={d}: cells {3**d}, lines {len(L)} ({d*3**(d-1)} rows + {2**(d-1)} diagonals), "
        f"rank {rank}, 3^d - 2^(d-1) = {3**d - 2**(d-1)}  -> magic space dimension {3**d - rank}")

    # A2: random odd corner values -> magic; random non-odd -> rows only
    ok_odd = True
    for trial in range(20):
        h = [0] * (2 ** d)
        for t in range(2 ** d):
            if t & 1 == 0:
                v = random.randint(-50, 50)
                h[t] = v
                h[t ^ (2 ** d - 1)] = -v
        f = build_from_corners(h, d)
        sums = all_line_sums(f, d)
        if any(s != 0 for s in sums) or f[tuple([0] * d)] != 0:
            ok_odd = False
        if any(f[y] + f[tuple(-v for v in y)] != 0 for y in cl):
            ok_odd = False
    rows_ok, diag_fail = True, 0
    for trial in range(20):
        h = [random.randint(-50, 50) for _ in range(2 ** d)]
        f = build_from_corners(h, d)
        for kind, _, line in L:
            s = sum(f[y] for y in line)
            if kind == "row" and s != 0:
                rows_ok = False
            if kind == "diag" and s != 0:
                diag_fail += 1
    say(f"A2 d={d}: 20 random ODD corner sets -> every row & diagonal sums to 0, center 0, associated: {ok_odd}; "
        f"20 random NON-odd sets -> rows still 0: {rows_ok}, diagonal failures: {diag_fail}")

    # A3: polynomial view
    ok_poly = True
    for trial in range(10):
        h = [0] * (2 ** d)
        for t in range(2 ** d):
            if t & 1 == 0:
                v = random.randint(-40, 40)
                h[t] = v
                h[t ^ (2 ** d - 1)] = -v
        f = build_from_corners(h, d)
        co = multilinear_coeffs(h, d)
        for y in cl:
            z = sum(1 for v in y if v == 0)
            if Fraction(f[y]) != (-2) ** z * q_eval(co, y):
                ok_poly = False
        if any(c != 0 and sum(S) % 2 == 0 for S, c in co.items()):
            ok_poly = False
    say(f"A3 d={d}: f(y) = (-2)^z(y) * q(y) with q the multilinear interpolant, only odd-degree terms: {ok_poly}")

    # A4: wave view, exact
    ok_wave = True
    for trial in range(10):
        gam = {a: random.randint(-20, 20) for a in directions(d)}
        f = {y: sum(g * wave(a, y) for a, g in gam.items()) for y in cl}
        if any(s != 0 for s in all_line_sums(f, d)):
            ok_wave = False
        sp = spectrum(f, d)
        if any(sp[a] != gam[a] for a in gam):
            ok_wave = False
        cs = corners(d)
        h = [f[tau] for tau in cs]
        if build_from_corners(h, d) != f:
            ok_wave = False
    say(f"A4 d={d}: random wave mixtures are magic, gamma recovered as hyperplane means, corners rebuild the "
        f"whole array: {ok_wave}")

    # A5: converse via projection (float)
    ok_conv = True
    _, sv, Vt = np.linalg.svd(Mx)
    null_basis = Vt[int(np.sum(sv > 1e-9)):]          # orthonormal basis of {all lines sum to 0}
    for trial in range(5):
        x = null_basis.T @ np.random.randn(null_basis.shape[0])
        assert np.abs(Mx @ x).max() < 1e-9
        f = {y: x[pos[y]] for y in cl}
        cs = corners(d)
        h = [f[tau] for tau in cs]
        g = build_from_corners(h, d)
        if max(abs(g[y] - f[y]) for y in cl) > 1e-8:
            ok_conv = False
    say(f"A5 d={d}: random arrays projected onto 'all lines sum to 0' obey the Corner Rule: {ok_conv}")
say()

# ======================================================================= PART B
say("=" * 78)
say("PART B — normal hypercubes (entries 1..3^d)")
say("=" * 78)
data = {"classes": {}}
published = {2: 1, 3: 4, 4: 58}
LD = {}
for d in range(2, 7):
    LD[d] = bases_count(d)
say("B2 L(d) = sign-vector bases of GF(3)^d (classes of the spike family): " +
    ", ".join(f"d={d}: {LD[d]}" for d in LD))
say("   L(d) * d! * 2^d (spike hypercubes counted with orientation): " +
    ", ".join(f"d={d}: {LD[d] * math.factorial(d) * 2**d}" for d in LD))

for d in (2, 3, 4):
    cl = cells(d)
    N = 3 ** d
    m = (N - 1) // 2
    sols = []
    with open(f"sols_d{d}.txt") as fh:
        for line in fh:
            if line.startswith("SOL"):
                sols.append([int(v) for v in line.split()[1:]])
    done = open(f"sols_d{d}.txt").read().strip().splitlines()[-1]
    perms = sym_perms(d)
    canon_set = {}
    all_normal = all_magic = all_spike = all_energy = all_mod3 = all_bottom_linear = True
    for h in sols:
        f = build_from_corners(h, d)
        vals = sorted(f.values())
        if vals != list(range(-m, m + 1)):
            all_normal = False
        if any(s != 0 for s in all_line_sums(f, d)):
            all_magic = False
        sp = spectrum(f, d)
        nz = {a: g for a, g in sp.items() if g != 0}
        mags = sorted(abs(g) for g in nz.values())
        spike = (mags == [Fraction(3 ** k) for k in range(d)]) and rank_mod3([list(a) for a in nz]) == d
        if not spike:
            all_spike = False
        if sum(g * g for g in sp.values()) != Fraction(9 ** d - 1, 8):
            all_energy = False
        co = multilinear_coeffs(h, d)
        for y in cl:
            z = sum(1 for v in y if v == 0)
            if frac_mod3(q_eval(co, y)) != f[y] % 3:
                all_mod3 = False
        if any(frac_mod3(c) != 0 and sum(S) > 1 for S, c in co.items()):
            all_bottom_linear = False
        vec = [f[y] for y in cl]
        c = canon(vec, perms)
        if spike:
            rows = []
            for k in range(d):
                a = next(a for a, g in nz.items() if abs(g) == 3 ** k)
                sgn = 1 if nz[a] > 0 else -1
                rows.append("".join("+" if sgn * ai > 0 else "-" for ai in a))
        else:
            rows = None
        canon_set[c] = {"rows": rows, "corners": h}
    lin = linear_family_arrays(d)
    lin_canon = {tuple(r) for r in canon_many(lin, perms).tolist()}
    say(f"B1 d={d}: corner search {done.split()[-1]} -> {len(sols)} normalized solutions, "
        f"{len(canon_set)} classes under {len(perms)} symmetries (published total {published[d]})")
    say(f"   all normal: {all_normal}; all 3^(d-1)*d + 2^(d-1) lines magic: {all_magic}; "
        f"every spectrum a spike (gammas +-1,+-3,..,+-3^(d-1) on independent directions): {all_spike}")
    say(f"   energy sum gamma^2 = (9^d-1)/8 = {(9**d-1)//8} for every one: {all_energy}; "
        f"mod-3 collapse f = q (mod 3): {all_mod3}; bottom ternary digit linear: {all_bottom_linear}")
    say(f"   spike family: {len(lin)} oriented hypercubes -> {len(lin_canon)} classes; "
        f"search classes == spike classes: {set(canon_set) == lin_canon}")
    ordered = sorted(canon_set.items())
    data["classes"][str(d)] = [v["rows"] for _, v in ordered]
    data[f"canon_first_{d}"] = list(ordered[0][0])

# locate the starting I + J tesseract and the Hadamard tesseract among the 58
cl4 = cells(4)
perms4 = sym_perms(4)
ordered4 = sorted({tuple(canon(v, perms4)) for v in linear_family_arrays(4)})


def class_of(A):
    Y = np.array(cl4)
    f = np.zeros(len(cl4), dtype=int)
    for k, a in enumerate(A):
        r = (Y @ np.array(a)) % 3
        f += (3 ** k) * np.where(r == 0, 0, np.where(r == 1, 1, -1))
    return ordered4.index(canon(f, perms4)) + 1


IJ = [[2 if i == j else 1 for j in range(4)] for i in range(4)]
IJs = [[1 if v == 1 else -1 for v in row] for row in IJ]   # 2 = -1 (mod 3)
H4 = [[1, 1, 1, 1], [1, -1, 1, -1], [1, 1, -1, -1], [1, -1, -1, 1]]
data["class_IJ"] = class_of(IJs)
data["class_H4"] = class_of(H4)
say(f"   the starting I+J tesseract is class #{data['class_IJ']} of 58; the Hadamard one is class #{data['class_H4']}")
say()

# ======================================================================= PART C
say("=" * 78)
say("PART C — triangles, centered figures, square / hexagon / hexagram, base-12 forms")
say("=" * 78)


def tri(n):
    return n * (n + 1) // 2


def centroid_filled(n):
    pts = [(i, j) for i in range(n) for j in range(i + 1)]
    T = len(pts)
    ci = Fraction(sum(p[0] for p in pts), T)
    cj = Fraction(sum(p[1] for p in pts), T)
    return ci.denominator == 1 and cj.denominator == 1 and (int(ci), int(cj)) in set(pts)


ok = all(centroid_filled(n) == (n % 3 == 1) == (tri(n) % 3 == 1) for n in range(1, 301))
say(f"C1 n = 1..300: centroid is a dot  <=>  n = 1 (mod 3)  <=>  T_n = 1 (mod 3): {ok}; "
    f"T_n mod 3 never 2: {all(tri(n) % 3 != 2 for n in range(1, 301))}")
seq = [(n % 3 == 1, tri(n) % 2) for n in range(1, 241)]
per = next(p for p in range(1, 121) if all(seq[i] == seq[i + p] for i in range(len(seq) - p)))
say(f"   parity of T_n has period {next(p for p in range(1,50) if all(tri(n)%2==tri(n+p)%2 for n in range(1,200)))}; "
    f"joint (center filled, parity) pattern has minimal period {per}")
say(f"   filled T_n end in a dozenal digit from {{1,4,7,A}}: "
    f"{all(tri(n) % 12 in (1, 4, 7, 10) for n in range(1, 301) if n % 3 == 1)}; "
    f"decimal last digits of filled T_n: {sorted({tri(n) % 10 for n in range(1, 301) if n % 3 == 1})}")
say(f"   T_n mod 12 period: {next(p for p in range(1,100) if all(tri(n)%12==tri(n+p)%12 for n in range(1,300)))}; "
    f"T_n mod 10 period: {next(p for p in range(1,100) if all(tri(n)%10==tri(n+p)%10 for n in range(1,300)))}")
ok_h = all(n ** 3 - (n - 1) ** 3 == 3 * n * (n - 1) + 1 == 6 * tri(n - 1) + 1 for n in range(1, 500))
say(f"C2 centered hexagonal: n^3-(n-1)^3 = 3n(n-1)+1 = 6T_(n-1)+1 for n<500: {ok_h}; "
    f"all = 1 (mod 6): {all((3*n*(n-1)+1) % 6 == 1 for n in range(1,500))}; "
    f"dozenal last digits {sorted({to_base(3*n*(n-1)+1,12)[-1] for n in range(1,500)})}; "
    f"decimal last digits {sorted({(3*n*(n-1)+1) % 10 for n in range(1,500)})}")
say(f"   odd squares (2n+1)^2 = 8T_n+1: {all((2*n+1)**2 == 8*tri(n)+1 for n in range(500))}; "
    f"dozenal last digits {sorted({to_base((2*n+1)**2,12)[-1] for n in range(500)})}; "
    f"decimal last digits {sorted({((2*n+1)**2) % 10 for n in range(500)})}")


def run_line_magic(n, S, line_list, order, sym):
    inp = f"{n} {S} {len(line_list)}\n" + "\n".join(f"{len(l)} " + " ".join(map(str, l)) for l in line_list)
    inp += "\n" + " ".join(map(str, order)) + "\n"
    res = subprocess.run(["./line_magic", "print"], input=inp, capture_output=True, text=True, check=True)
    sols = [tuple(int(v) for v in ln.split()[1:]) for ln in res.stdout.splitlines() if ln.startswith("SOL")]
    count = int(res.stdout.strip().splitlines()[-1].split()[1])
    classes = {min(tuple(s[p[i]] for i in range(n)) for p in sym) for s in sols}
    return count, sorted(classes)


def greedy_order(n, line_list):
    order, chosen = [], set()
    while len(order) < n:
        best, bs = None, None
        for c in range(n):
            if c in chosen:
                continue
            score = 0
            for l in line_list:
                if c in l:
                    k = sum(1 for x in l if x in chosen)
                    score += 100 if k == len(l) - 1 else k
            if bs is None or score > bs:
                best, bs = c, score
        order.append(best)
        chosen.add(best)
    return order


# Lo Shu
sq = [(r, c) for r in range(3) for c in range(3)]
sqi = {p: i for i, p in enumerate(sq)}
sq_lines = [[sqi[(r, c)] for c in range(3)] for r in range(3)] + [[sqi[(r, c)] for r in range(3)] for c in range(3)]
sq_lines += [[sqi[(i, i)] for i in range(3)], [sqi[(i, 2 - i)] for i in range(3)]]
sq_sym = []
for k in range(4):
    for refl in (False, True):
        perm = []
        for (r, c) in sq:
            rr, cc = (r, c)
            for _ in range(k):
                rr, cc = cc, 2 - rr
            if refl:
                cc = 2 - cc
            perm.append(sqi[(rr, cc)])
        sq_sym.append(perm)
cnt_sq, cls_sq = run_line_magic(9, 15, sq_lines, greedy_order(9, sq_lines), sq_sym)
say(f"C3 3x3 square, 8 lines = 15: {cnt_sq} labelings, {len(cls_sq)} class(es) under 8 symmetries")
data["loshu"] = list(cls_sq[0])

# Magic hexagon (cube coordinates, radius 2)
hexc = [(x, y, -x - y) for x in range(-2, 3) for y in range(-2, 3) if abs(-x - y) <= 2]
hexi = {p: i for i, p in enumerate(hexc)}
hex_lines = []
for axis in range(3):
    for v in range(-2, 3):
        hex_lines.append([hexi[p] for p in hexc if p[axis] == v])


def hex_rot(p):
    x, y, z = p
    return (-z, -x, -y)


hex_sym = []
for k in range(6):
    for refl in (False, True):
        perm = []
        for p in hexc:
            q = p
            for _ in range(k):
                q = hex_rot(q)
            if refl:
                q = (q[0], q[2], q[1])
            perm.append(hexi[q])
        hex_sym.append(perm)
cnt_hex, cls_hex = run_line_magic(19, 38, hex_lines, greedy_order(19, hex_lines), hex_sym)
say(f"   magic hexagon, 19 cells, 15 lines = 38: {cnt_hex} labelings, {len(cls_hex)} class(es) under 12 symmetries; "
    f"center value {cls_hex[0][hexi[(0,0,0)]]}")
data["hexagon"] = {"cells": hexc, "values": list(cls_hex[0])}

# Magic hexagram (6-pointed star): tips of two triangles + 6 edge crossings
R = 1.0
tipsA = [complex(math.cos(math.radians(90 + 120 * k)), math.sin(math.radians(90 + 120 * k))) for k in range(3)]
tipsB = [complex(math.cos(math.radians(270 + 120 * k)), math.sin(math.radians(270 + 120 * k))) for k in range(3)]
edges = [(tipsA[i], tipsA[(i + 1) % 3]) for i in range(3)] + [(tipsB[i], tipsB[(i + 1) % 3]) for i in range(3)]


def inter(e1, e2):
    p, r = e1[0], e1[1] - e1[0]
    q, s = e2[0], e2[1] - e2[0]
    cross = (r.conjugate() * s).imag
    if abs(cross) < 1e-12:
        return None
    t = ((q - p).conjugate() * s).imag / cross
    u = ((q - p).conjugate() * r).imag / cross
    if -1e-9 < t < 1 + 1e-9 and -1e-9 < u < 1 + 1e-9:
        return p + t * r
    return None


pts = list(tipsA + tipsB)
for i in range(3):
    for j in range(3, 6):
        x = inter(edges[i], edges[j])
        if x is not None and all(abs(x - p) > 1e-9 for p in pts):
            pts.append(x)


def pidx(z):
    return next(i for i, p in enumerate(pts) if abs(p - z) < 1e-7)


star_lines = []
for (a, b) in edges:
    on = [i for i, p in enumerate(pts) if abs(((p - a).conjugate() * (b - a)).imag) < 1e-9]
    star_lines.append(on)
star_sym = []
for k in range(6):
    rot = complex(math.cos(math.radians(60 * k)), math.sin(math.radians(60 * k)))
    for refl in (False, True):
        perm = []
        for p in pts:
            q = p * rot
            if refl:
                q = complex(-q.real, q.imag)
            perm.append(pidx(q))
        star_sym.append(perm)
cnt_star, cls_star = run_line_magic(12, 26, star_lines, greedy_order(12, star_lines), star_sym)
say(f"   magic hexagram, 12 points, 6 lines of 4 = 26: {cnt_star} labelings, {len(cls_star)} classes under 12 symmetries "
    f"(line lengths {sorted(len(l) for l in star_lines)})")
data["hexagram"] = {"points": [[p.real, p.imag] for p in pts], "lines": star_lines,
                    "values": list(cls_star[0]), "classes": len(cls_star),
                    "all": [list(c) for c in cls_star]}

# Base-12 forms of the constants
consts = {"magic square sum": 15, "magic cube sum": 42, "magic tesseract sum": 123, "5-cube sum": 366,
          "hexagon sum": 38, "hexagram sum": 26, "line product 210^3": 210 ** 3, "tetractys": 10}
say("C4 base-12 forms: " + "; ".join(f"{k} {v} = {to_base(v,12)} (dozenal)" for k, v in consts.items()))

with open("ladder_data.json", "w") as fh:
    json.dump(data, fh)
with open("verify_ladder_output.txt", "w") as fh:
    fh.write("\n".join(OUT) + "\n")
say("\nwrote ladder_data.json and verify_ladder_output.txt")

==============================================================================================================
SOURCE — echoes.py  sha256 4b7f5abc03ccf8481f24929b98624bc27939b41d28fb6401e38aa7cf74bbc79a
==============================================================================================================
#!/usr/bin/env python3
"""
echoes.py — exact checks for the patterns the order-3 magic forms share with other structures.
Each check prints PASS/FAIL. Nothing here is an analogy: every line compares two computed objects.

E1  the 3^d grid is the face lattice of the d-cube; the corner rule is its Moebius function
E1b rows add to M  <=>  every face block adds to its share (Moebius inversion, ranks d = 2..4)
E2  ternary fractals (Cantor, Sierpinski carpet, Menger sponge) keep cells by face dimension
E3  spike hypercubes as Bachet weighings: along every row each weight visits each pan once
E4  wave energy (9^d - 1)/8 is the triangle number T_m, m = (3^d - 1)/2 (base-9 repunit)
E5  lines through the center <-> points of the projective space PG(d-1, 3)
E6  nested flags: aligned blocks of 3^k values fill affine k-spaces (all 58 tesseracts)
E7  diagonal shadow: wave classes of x+y+z (mod 3) -> honeycomb (2 classes) / triangular (3)
E7b tesseract seen down its long diagonal -> rhombic dodecahedron (14 vertices, 12 rhombi)
E7c a side-k grid casts k^d - (k-1)^d shadow points; tesseract corners land on the BCC lattice; 3^4 grid -> 65
E7d shadow points as Eisenstein integers; x+y+z mod 3 = remainder mod sqrt(-3)
E8  corner <-> wave dictionary is a Krawtchouk transform; eigenvalues never vanish on odd s
E8b bal = Legendre symbol mod 3; sqrt(-3) is its Gauss sum; eigenvalues = 2(-1)^(d-s) g^(s-1)
E9  fibre sums along a sign direction s keep exactly the waves with a.s = 0 (mod 3)
E9b broken diagonals along s all add to M  <=>  no active wave orthogonal to s; I+J <-> Hadamard
E10 Lemmas A and B (support spans; exactly d active directions => spike), checked d <= 4
"""
import itertools
import json
import math
import random
from fractions import Fraction

import numpy as np

random.seed(1010)
OUT = []
FAILS = []


def say(s=""):
    print(s)
    OUT.append(s)


def check(name, ok, extra=""):
    say(f"{'PASS' if ok else 'FAIL'}  {name}{('  [' + extra + ']') if extra else ''}")
    if not ok:
        FAILS.append(name)


def bal(r):
    r %= 3
    return 0 if r == 0 else (1 if r == 1 else -1)


def cells(d):
    return [tuple(y) for y in itertools.product((-1, 0, 1), repeat=d)]


def dirs(d):
    return [(1,) + s for s in itertools.product((1, -1), repeat=d - 1)]


def zc(y):
    return sum(1 for v in y if v == 0)


# ---------------------------------------------------------------- E1
say("E1  face lattice and Moebius function")
for d in range(1, 7):
    counts = [math.comb(d, z) * 2 ** (d - z) for z in range(d + 1)]
    ok = sum(counts) == 3 ** d and sum((-1) ** z * c for z, c in enumerate(counts)) == 1 \
        and sum((-2) ** z * c for z, c in enumerate(counts)) == 0
    check(f"d={d}: cells by face dimension {counts}; total 3^d, Euler sum 1, corner-weight sum 0", ok)
for d in (2, 3, 4):
    F = cells(d)
    leq = lambda a, b: all(bj == 0 or bj == aj for aj, bj in zip(a, b))   # a is a face of b
    verts = [f for f in F if zc(f) == 0]
    ok = True
    for v in verts:
        mu = {}
        for G in sorted([g for g in F if leq(v, g)], key=zc):
            mu[G] = 1 if G == v else -sum(mu[H] for H in mu if leq(H, G) and H != G)
            if mu[G] != (-1) ** zc(G):
                ok = False
    check(f"d={d}: Moebius function mu(vertex, face) = (-1)^(dim face) on the cube's face lattice", ok)
# E1b  rows <=> every face block sums to zero (offsets from the center value)
for d in (2, 3, 4):
    F = cells(d)
    ix = {y: i for i, y in enumerate(F)}
    leq = lambda a, b: all(bj == 0 or bj == aj for aj, bj in zip(a, b))   # a is a face of b
    rows = []
    for j in range(d):
        for y in F:
            if y[j] == 0:
                r = [0] * len(F)
                for t in (-1, 0, 1):
                    r[ix[y[:j] + (t,) + y[j + 1:]]] = 1
                rows.append(r)
    blocks = []
    for Fc in F:
        if zc(Fc) >= 1:
            r = [0] * len(F)
            for G in F:
                if leq(G, Fc):
                    r[ix[G]] = 1
            blocks.append(r)
    rk = lambda A: int(np.linalg.matrix_rank(np.array(A, dtype=float)))
    r1, r2, r3 = rk(rows), rk(blocks), rk(rows + blocks)
    check(f"d={d}: 'every axis row adds to 0' and 'every face block of dimension >= 1 adds to 0' have the same solutions",
          r1 == r2 == r3 == 3 ** d - 2 ** d and len(blocks) == 3 ** d - 2 ** d,
          f"ranks {r1}, {r2}, together {r3}; {len(blocks)} face blocks, all independent")

# ---------------------------------------------------------------- E2
say("E2  ternary fractals keep cells by face dimension")
cases = [("Cantor set", 1, 0), ("Sierpinski carpet", 2, 1), ("Menger sponge", 3, 1), ("Cantor dust (3D)", 3, 0),
         ("cube minus center", 3, 2), ("4D Menger analogue", 4, 1)]
for name, d, k in cases:
    kept = [y for y in cells(d) if zc(y) <= k]
    removed = [y for y in cells(d) if zc(y) > k]
    say(f"      {name}: d={d}, keep faces of dimension <= {k}: {len(kept)} of {3**d} cells, "
        f"similarity dimension log3({len(kept)}) = {math.log(len(kept), 3):.4f}")
check("Menger sponge level 1 = 20 cubes (removes 6 face centers + center)", len([y for y in cells(3) if zc(y) <= 1]) == 20)
check("Sierpinski carpet level 1 = 8 squares (removes center)", len([y for y in cells(2) if zc(y) <= 1]) == 8)
check("Cantor set level 1 = 2 intervals (removes middle)", len([y for y in cells(1) if zc(y) <= 0]) == 2)

# ---------------------------------------------------------------- data (58 tesseracts)
DATA = json.load(open("ladder_data.json"))
T58 = [[tuple(1 if ch == "+" else -1 for ch in r) for r in rows] for rows in DATA["classes"]["4"]]
C4 = cells(4)


def axis_rows(d):
    out = []
    for j in range(d):
        for rest in itertools.product((-1, 0, 1), repeat=d - 1):
            out.append([tuple(list(rest[:j]) + [t] + list(rest[j:])) for t in (-1, 0, 1)])
    return out


# ---------------------------------------------------------------- E3
say("E3  Bachet's weights 1, 3, 9, 27")
ok_rows = ok_val = ok_diag = True
for A in T58:
    for y in C4:
        t = [bal(sum(a * v for a, v in zip(ak, y))) for ak in A]
        val = sum(3 ** k * t[k] for k in range(4))
        # the cell value minus 41 is the net load weighed by the pan placement t
        if not -40 <= val <= 40:
            ok_val = False
    for row in axis_rows(4):
        for ak in A:
            if sorted(bal(sum(a * v for a, v in zip(ak, y))) for y in row) != [-1, 0, 1]:
                ok_rows = False
    for s in dirs(4):
        diag = [tuple(-v for v in s), (0, 0, 0, 0), s]
        for ak in A:
            p = sorted(bal(sum(a * v for a, v in zip(ak, y))) for y in diag)
            if p not in ([-1, 0, 1], [0, 0, 0]):
                ok_diag = False
check("every cell of every tesseract is one weighing of -40..40 with weights 1, 3, 9, 27", ok_val)
check("along every axis row each weight sits on the left pan, off, and the right pan exactly once", ok_rows)
check("along every main diagonal each weight either does the same or stays off", ok_diag)

# ---------------------------------------------------------------- E4
say("E4  energy is a triangle number")
ok = True
for d in range(1, 16):
    m = (3 ** d - 1) // 2
    E = sum(9 ** k for k in range(d))
    if E != m * (m + 1) // 2 or E != int("1" * d, 9):
        ok = False
check("for d = 1..15: 1 + 9 + ... + 9^(d-1) = T_m with m = (3^d-1)/2 = 11...1 in base 9", ok,
      "d=2: 10 = T_4 (tetractys), d=3: 91 = T_13, d=4: 820 = T_40, d=5: 7381 = T_121")

# ---------------------------------------------------------------- E5
say("E5  lines through the center and projective space")
ok = True
for d in range(1, 8):
    nz = [y for y in itertools.product((-1, 0, 1), repeat=d) if any(y)]
    lines = {min(y, tuple(-v for v in y)) for y in nz}
    if len(lines) != (3 ** d - 1) // 2:
        ok = False
check("for d = 1..7 the lines through the center number (3^d - 1)/2 = |PG(d-1, 3)|", ok,
      "d=2: 4, d=3: 13, d=4: 40, d=5: 121")
slopes = {}
for (x, y) in [(1, 0), (0, 1), (1, 1), (1, -1)]:
    slopes[(x, y)] = "inf" if x == 0 else str((y * pow(x, -1, 3)) % 3)
check("the 3x3 square's 4 center lines have slopes 0, 1, 2 (= -1) and infinity: the projective line over F3",
      sorted(slopes.values()) == ["0", "1", "2", "inf"], str(slopes))
for d in (2, 3, 4, 5):
    say(f"      d={d}: center lines {(3**d-1)//2} = {d} axis rows + {2**(d-1)} main diagonals + "
        f"{(3**d-1)//2 - d - 2**(d-1)} partial diagonals (not required to be magic)")

# ---------------------------------------------------------------- E6
say("E6  nested flags inside the magic tesseracts")


def is_affine_subspace(S, k):
    S = [np.array(s) % 3 for s in S]
    x0 = S[0]
    V = {tuple((s - x0) % 3) for s in S}
    if len(V) != 3 ** k:
        return False
    for u in V:
        for w in V:
            if tuple((np.array(u) + np.array(w)) % 3) not in V:
                return False
    return True


ok_flag = ok_hit = True
for A in T58:
    vals = {y: sum(3 ** k * bal(sum(a * v for a, v in zip(A[k], y))) for k in range(4)) for y in C4}
    for k in range(5):
        blocks = {}
        for y, f in vals.items():
            blocks.setdefault((f + 40) // 3 ** k, []).append(y)          # aligned blocks of 3^k values
        for S in blocks.values():
            if len(S) != 3 ** k or not is_affine_subspace(S, k):
                ok_flag = False
    for k in range(4):                                                 # each digit's level sets
        for c in (-1, 0, 1):
            H = {y for y in C4 if bal(sum(a * v for a, v in zip(A[k], y))) == c}
            for row in axis_rows(4):
                if sum(1 for y in row if y in H) != 1:
                    ok_hit = False
check("all 58 tesseracts: every aligned block of 3^k consecutive values fills an affine k-space (k = 0..4)", ok_flag,
      "values 1-27 a 3-space, 1-9 a plane, 1-3 a line, 1 a point")
check("all 58 tesseracts: each digit's three level sets are diagonal 3-spaces meeting every axis row exactly once", ok_hit)

# ---------------------------------------------------------------- E7
say("E7  the diagonal shadow: honeycomb and triangular lattices")
R = 7
u = np.array([1, -1, 0]) / math.sqrt(2)
w = np.array([1, 1, -2]) / math.sqrt(6)
proj = {}
for v in itertools.product(range(-R, R + 1), repeat=3):
    v = np.array(v)
    p = (round(float(v @ u), 9), round(float(v @ w), 9))
    c = int(v.sum()) % 3
    if p in proj and proj[p] != c:
        proj[p] = None
    else:
        proj.setdefault(p, c)
check("points of Z^3 that share a shadow point down (1,1,1) always share x+y+z mod 3", all(c is not None for c in proj.values()))
pts = np.array(list(proj.keys())); cls = np.array(list(proj.values()))


def neighbour_stats(mask, radius=4.0):
    P, Cc = pts[mask], cls[mask]
    res = []
    for i in range(len(P)):
        if np.hypot(*P[i]) > radius:
            continue
        dd = np.hypot(P[:, 0] - P[i, 0], P[:, 1] - P[i, 1])
        dd[i] = np.inf
        mn = dd.min()
        nb = np.where(np.abs(dd - mn) < 1e-6)[0]
        res.append((len(nb), all(Cc[j] != Cc[i] for j in nb), round(float(mn), 6)))
    return res


two = neighbour_stats(np.isin(cls, [0, 1]))
three = neighbour_stats(np.isin(cls, [0, 1, 2]))
one = neighbour_stats(cls == 0)
check("two classes (x+y+z = 0, 1 mod 3) seen down the diagonal: every point has 3 nearest neighbors, all of the other class (honeycomb)",
      all(n == 3 and alt for n, alt, _ in two), f"{len(two)} interior points")
check("all three classes: every point has 6 nearest neighbors (triangular lattice)", all(n == 6 for n, _, _ in three))
check("one class alone: 6 nearest neighbors at sqrt(3) times the spacing (the coarse triangular lattice)",
      all(n == 6 for n, _, _ in one) and abs(one[0][2] / three[0][2] - math.sqrt(3)) < 1e-6)
grid19 = {}
for y in cells(3):
    v = np.array(y)
    p = (round(float(v @ u), 9), round(float(v @ w), 9))
    grid19.setdefault(p, set()).add(int(v.sum()) % 3)
byclass = [sum(1 for s in grid19.values() if next(iter(s)) == c) for c in range(3)]
check("the 3x3x3 grid casts 19 shadow points (the magic hexagon's cells), one class each", len(grid19) == 19 and all(len(s) == 1 for s in grid19.values()),
      f"class sizes 0/1/2 = {byclass}")

# E7b tesseract shadow
from scipy.spatial import ConvexHull
B = np.linalg.qr(np.vstack([np.ones(4), np.eye(4)[:3]]).T)[0][:, 1:4]
verts = np.array(list(itertools.product((0, 1), repeat=4)), dtype=float)
P3 = np.array(sorted({tuple(np.round((v - v.mean()) @ B, 9)) for v in verts}))
hull = ConvexHull(P3)
planes = {tuple(np.round(e / np.linalg.norm(e[:3]), 6)) for e in hull.equations}
face_pts = [[i for i in range(len(P3)) if abs(np.dot(k[:3], P3[i]) + k[3]) < 1e-6] for k in planes]
rhombi = 0
for fp in face_pts:
    if len(fp) == 4:
        Q = P3[fp]
        ds = sorted(np.linalg.norm(Q[i] - Q[j]) for i in range(4) for j in range(i + 1, 4))
        if abs(ds[0] - ds[3]) < 1e-6 and abs(ds[5] / ds[4] - math.sqrt(2)) < 1e-6:   # 4 equal sides, diagonals in ratio sqrt 2
            rhombi += 1
deg = [sum(1 for fp in face_pts if i in fp) for i in range(len(P3))]
hullv = set(hull.vertices.tolist())
check("tesseract down its long diagonal: 16 corners cast 15 points (two land on the center), 14 hull vertices, 12 rhombic faces with diagonals in ratio sqrt 2",
      len(P3) == 15 and len(hullv) == 14 and len(planes) == 12 and rhombi == 12 and sorted(d for i, d in enumerate(deg) if i in hullv) == [3] * 8 + [4] * 6,
      "8 vertices of degree 3 + 6 of degree 4: the rhombic dodecahedron")

# E7c  how many shadow points a k^d grid casts down its long diagonal; the tesseract's shadow on the BCC lattice
ok = True
tab = []
for k in (2, 3, 4):
    for d in range(1, 6):
        reps = {tuple(c - min(y) for c in y) for y in itertools.product(range(k), repeat=d)}   # shift until the smallest coordinate is 0
        if len(reps) != k ** d - (k - 1) ** d:
            ok = False
        if k == 3:
            tab.append(len(reps))
check("a grid of side k in d dimensions casts k^d - (k-1)^d points down its long diagonal (k = 2..4, d = 1..5)", ok,
      "side 3: " + ", ".join(str(t) for t in tab) + " for d = 1..5")
V4 = np.array([[1, 1, 1], [1, -1, -1], [-1, 1, -1], [-1, -1, 1]])        # e1..e4 seen down (1,1,1,1), scaled
sh = {tuple(np.array(c) @ V4) for c in itertools.product((0, 1), repeat=4)}
bcc = lambda p: len({x % 2 for x in p}) == 1
shells = sorted(sh, key=lambda p: sum(x * x for x in p))
ok_bcc = all(bcc(p) for p in sh) and len(sh) == 15 and sorted(sum(x * x for x in p) for p in sh) == [0] + [3] * 8 + [4] * 6
check("the tesseract's 16 corners land on the lattice of all-even or all-odd integer points (body-centered cubic): the center, its 8 nearest and 6 next-nearest points",
      ok_bcc, "squared distances 0, 3 (x8), 4 (x6)")
sh65 = {tuple(np.array(c) @ V4) for c in itertools.product((-1, 0, 1), repeat=4)}
H65 = ConvexHull(np.array(sorted(sh65), dtype=float))
inside = 0
for p in itertools.product(range(-5, 6), repeat=3):
    if bcc(p) and all(np.dot(e[:3], p) + e[3] <= 1e-9 for e in H65.equations):
        inside += 1
pl65 = {tuple(np.round(e / np.linalg.norm(e[:3]), 6)) for e in H65.equations}
check("the 81 cells of the 3^4 grid cast 65 points = 3^4 - 2^4: every lattice point of a rhombic dodecahedron twice the size (14 hull vertices, 12 faces)",
      len(sh65) == 65 and inside == 65 and len(set(H65.vertices.tolist())) == 14 and len(pl65) == 12, f"{inside} lattice points inside or on the hull")

# E7d  the shadow as Eisenstein integers: class x+y+z mod 3 = remainder mod sqrt(-3) = 1 + 2w
def eis(x, y, z):                     # x + y w + z w^2 with w^2 = -1 - w, as (a, b) meaning a + b w
    return (x - z, y - z)
def divisible_by_1_minus_w(a, b):     # (a + b w)(1 - w^2) / 3 must be an Eisenstein integer; 1 - w^2 = 2 + w
    # (a + b w)(2 + w) = 2a + (a + 2b) w + b w^2 = (2a - b) + (a + b) w
    return (2 * a - b) % 3 == 0 and (a + b) % 3 == 0
ok_res = True
for x, y, z in itertools.product(range(-4, 5), repeat=3):
    a, b = eis(x, y, z)               # equal for two points exactly when they differ by a multiple of (1, 1, 1): same shadow
    if not divisible_by_1_minus_w(a - (x + y + z), b):
        ok_res = False
ok_distinct = not divisible_by_1_minus_w(1, 0) and not divisible_by_1_minus_w(2, 0)
om = complex(-0.5, math.sqrt(3) / 2)   # omega = e^(2 pi i / 3)
ok_assoc = abs((1 + 2 * om) - om * (1 - om)) < 1e-12 and abs((1 + 2 * om) ** 2 + 3) < 1e-12
check("shadow points <-> Eisenstein integers x + yw + zw^2, and x + y + z (mod 3) is the remainder on division by sqrt(-3) = 1 + 2w",
      ok_res and ok_distinct and ok_assoc, "1 + 2w = w(1 - w); (1 + 2w)^2 = -3; residues 0, 1, 2 distinct; box -4..4")

# ---------------------------------------------------------------- E8
say("E8  the corner <-> wave dictionary is a Krawtchouk transform")


def kraw(wt, s, d):
    return sum((-1) ** i * math.comb(s, i) * math.comb(d - s, wt - i) for i in range(0, wt + 1))


ok_eig = ok_nz = True
table = {}
for d in range(2, 9):
    lam = [sum(bal(d - 2 * wt) * kraw(wt, s, d) for wt in range(d + 1)) for s in range(d + 1)]
    table[d] = lam
    if any(lam[s] == 0 for s in range(1, d + 1, 2)):
        ok_nz = False
    if d <= 6:
        V = list(itertools.product((1, -1), repeat=d))
        Bm = np.array([[bal(sum(a * t for a, t in zip(av, tv))) for av in V] for tv in V], dtype=float)
        for S in itertools.product((0, 1), repeat=d):
            chi = np.array([np.prod([tv[j] for j in range(d) if S[j]]) if any(S) else 1 for tv in V], dtype=float)
            if not np.allclose(Bm @ chi, lam[sum(S)] * chi):
                ok_eig = False
check("for d = 2..6 the matrix bal(a.t) over cube vertices has the Walsh characters as eigenvectors, eigenvalue sum_w bal(d-2w) K_w(s)", ok_eig)
closed = all(table[d][s] == (2 * (-1) ** (d - s) * (-3) ** ((s - 1) // 2) if s % 2 else 0) for d in table for s in range(d + 1))
check("closed form: eigenvalue 2(-1)^(d-s)(-3)^((s-1)/2) = +-2(sqrt -3)^(s-1) for odd weight s, 0 for even s (d = 2..8)", closed)
check("for d = 2..8 every odd-weight eigenvalue is non-zero, so corners and waves determine each other", ok_nz,
      "; ".join(f"d={d}: " + ",".join(str(table[d][s]) for s in range(1, d + 1, 2)) for d in range(2, 9)))
# E8b  the wave shape is the Legendre symbol mod 3 and sqrt(-3) is its Gauss sum
squares = {(r * r) % 3 for r in range(3)}
legendre = lambda r: 0 if r % 3 == 0 else (1 if r % 3 in squares else -1)
g = sum(bal(r) * om ** r for r in range(3))
ok_leg = all(bal(r) == legendre(r) for r in range(-9, 10))
ok_g = abs(g - complex(0, math.sqrt(3))) < 1e-12 and abs(g * g + 3) < 1e-12
ok_cf = all(abs(table[d][s] - (2 * (-1) ** (d - s) * g ** (s - 1)).real) < 1e-9 and abs((2 * (-1) ** (d - s) * g ** (s - 1)).imag) < 1e-9
            for d in table for s in range(1, d + 1, 2))
check("bal(r) is the Legendre symbol (r/3); its Gauss sum bal(0) + bal(1)w + bal(2)w^2 = w - w^2 = sqrt(-3) squares to -3; eigenvalues = 2(-1)^(d-s) g^(s-1)",
      ok_leg and ok_g and ok_cf, f"g = {g.real:.0f} + {g.imag:.6f}i")

# ---------------------------------------------------------------- E9
say("E9  fibre sums keep exactly the waves orthogonal to the viewing direction")
ok = True
surv = {}
for d in (3, 4):
    D = dirs(d)
    for s in D:
        keep = [a for a in D if sum(x * y for x, y in zip(a, s)) % 3 == 0]
        surv[(d, s)] = len(keep)
        for trial in range(5):
            gam = {a: random.randint(-9, 9) for a in D}
            f = {y: sum(g * bal(sum(x * v for x, v in zip(a, y))) for a, g in gam.items()) for y in cells(d)}
            for y in cells(d):
                fib = sum(f[tuple(((yy + t * ss + 1) % 3) - 1 for yy, ss in zip(y, s))] for t in range(3))
                pred = 3 * sum(gam[a] * bal(sum(x * v for x, v in zip(a, y))) for a in keep)
                if fib != pred:
                    ok = False
check("d = 3, 4: summing along wrapped lines in a sign direction s leaves 3 x (the waves with a.s = 0 mod 3) and erases the rest", ok,
      f"down the main diagonal: d=3 keeps {surv[(3, (1, 1, 1))]} of 4 waves, d=4 keeps {surv[(4, (1, 1, 1, 1))]} of 8")
# E9b  broken diagonals: every broken diagonal along s adds to M exactly when no active wave is orthogonal to s
def spike_vals(rows, d):
    A = [tuple(1 if ch == "+" else -1 for ch in r) for r in rows]
    return {y: (3 ** d + 1) // 2 + sum(3 ** k * bal(sum(a * v for a, v in zip(A[k], y))) for k in range(d)) for y in cells(d)}, A


def broken_ok(v, s, d):
    M = 3 * (3 ** d + 1) // 2
    return all(sum(v[tuple(((yy + t * ss + 1) % 3) - 1 for yy, ss in zip(y, s))] for t in range(3)) == M for y in cells(d))


ok_crit = True
dist = {}
for d in (2, 3, 4):
    for ci, rows in enumerate(DATA["classes"][str(d)]):
        v, A = spike_vals(rows, d)
        n_ok = 0
        for s in dirs(d):
            pred = all(sum(a * x for a, x in zip(ak, s)) % 3 != 0 for ak in A)
            if broken_ok(v, s, d) != pred:
                ok_crit = False
            n_ok += pred
        dist.setdefault(d, {}).setdefault(n_ok, []).append(ci + 1)
check("d = 2, 3, 4: all broken diagonals along s add to M exactly when no active wave a has a.s = 0 (mod 3)", ok_crit)
check("every one of the 4 magic cubes has exactly one such direction (its missing wave)", sorted(dist[3]) == [1] and len(dist[3][1]) == 4)
d4 = {k: len(v) for k, v in sorted(dist[4].items())}
check("the 58 magic tesseracts: 24 have no such direction, 32 have one, and only #5 and #21 have four", d4 == {0: 24, 1: 32, 4: 2} and dist[4][4] == [5, 21],
      f"counts {d4}; four directions: classes {dist[4].get(4)}")
IJ = ["-+++", "+-++", "++-+", "+++-"]
H4 = ["++++", "+-+-", "++--", "+--+"]
norm = lambda a: tuple(a) if a[0] == 1 else tuple(-x for x in a)
def good_dirs(rows, d=4):
    v, A = spike_vals(rows, d)
    return {s for s in dirs(d) if broken_ok(v, s, d)}, {norm(a) for a in A}
gIJ, aIJ = good_dirs(IJ)
gH, aH = good_dirs(H4)
check("I + J (rows of J - 2I) and Hadamard (rows of H4): each one's four broken-diagonal directions are the other's four waves",
      gIJ == aH and gH == aIJ and len(gIJ) == 4 and len(gH) == 4)

# ---------------------------------------------------------------- E10
say("E10 Lemmas A and B on the known hypercubes")
ok_a = ok_b = True
for d, key in ((2, "2"), (3, "3"), (4, "4")):
    for rows in DATA["classes"][key]:
        A = [tuple(1 if ch == "+" else -1 for ch in r) for r in rows]
        f = {y: sum(3 ** k * bal(sum(a * v for a, v in zip(A[k], y))) for k in range(d)) for y in cells(d)}
        gam = {}
        for a in dirs(d):
            gam[a] = Fraction(sum(f[y] for y in cells(d) if sum(x * v for x, v in zip(a, y)) % 3 == 1), 3 ** (d - 1))
        active = [a for a, g in gam.items() if g != 0]
        M = np.array(active) % 3
        # rank mod 3
        Mm = [list(r) for r in M]; r = 0
        for col in range(d):
            piv = next((i for i in range(r, len(Mm)) if Mm[i][col] % 3), None)
            if piv is None:
                continue
            Mm[r], Mm[piv] = Mm[piv], Mm[r]; inv = Mm[r][col] % 3
            Mm[r] = [(x * inv) % 3 for x in Mm[r]]
            for i in range(len(Mm)):
                if i != r and Mm[i][col] % 3:
                    fct = Mm[i][col]; Mm[i] = [(x - fct * yv) % 3 for x, yv in zip(Mm[i], Mm[r])]
            r += 1
        if r != d:
            ok_a = False
        if len(active) == d and sorted(abs(g) for g in gam.values() if g) != [3 ** k for k in range(d)]:
            ok_b = False
check("Lemma A on all 1 + 4 + 58: the active wave directions span GF(3)^d", ok_a)
check("Lemma B on all 1 + 4 + 58: exactly d active directions, weights 1, 3, ..., 3^(d-1)", ok_b)
# Lemma B's polynomial identity, directly: the only multisets of positive integers g_1..g_d with
# prod (1 + x^g + x^2g) = 1 + x + ... + x^(3^d - 1) are {1, 3, ..., 3^(d-1)}  (brute force for d <= 4)
ok = True
for d in (1, 2, 3, 4):
    N = 3 ** d
    for gs in itertools.combinations_with_replacement(range(1, N), d):
        poly = np.zeros(2 * sum(gs) + 1, dtype=np.int64); poly[0] = 1
        for g in gs:
            fct = np.zeros(2 * g + 1, dtype=np.int64); fct[0] = fct[g] = fct[2 * g] = 1
            poly = np.convolve(poly, fct)[:2 * sum(gs) + 1]
        if len(poly) == N and np.all(poly == 1):
            if list(gs) != [3 ** k for k in range(d)]:
                ok = False
check("brute force d <= 4: prod(1 + x^g + x^2g) = 1 + x + ... + x^(3^d - 1) only for g = 1, 3, ..., 3^(d-1)", ok)

say("")
say(f"{'ALL PASS' if not FAILS else str(len(FAILS)) + ' FAILED: ' + '; '.join(FAILS)}")
open("echoes_output.txt", "w").write("\n".join(OUT) + "\n")
json.dump({"kraw": {str(k): v for k, v in table.items()}}, open("echo_data.json", "w"))

==============================================================================================================
SOURCE — flip_checks.py  sha256 ad8de56b26720e668ab24ece4f31c36677d1766c75b0405213560fbc5c4a38ed
==============================================================================================================
#!/usr/bin/env python3
"""
flip_checks.py — exact checks for the inside/outside reading of the order-3 ladder
(prompted by the 'flip orientation between rungs so inner becomes outer' method).

F1  the 3^d grid read two ways: face lattice of the cube (bottom-up) and of the cross-polytope (top-down)
F2  the dual flip nest: cube -> inscribed cross-polytope -> inscribed cube; one full turn shrinks by 1/d (1/3 at d = 3,
    landing exactly on the center cell of the 3x3x3 grid); radii shared at each closure; in:out ratio 1/sqrt(d) for both
F3  the wave directions are the cross-polytope's facet normals; the corners are the cube's vertices (same sign vectors)
F4  the sign that flips with every rung: det(-I) = (-1)^d, center formula, dictionary eigenvalues
F5  lines through the center: (3^d - 1)/2 = 3^(d-1) new directions + the rung below's lines (the hyperplane y_d = 0)
F6  the grid as the divisors of N^2 (N = product of d primes): corners = squares, center = N, flip = cofactor,
    face dimension = primes to the first power, corner-rule sign = Liouville lambda = mu(squarefree part),
    sum of lambda over all cells = 1 (N^2 is a square), over the boundary = 1 - (-1)^d = chi(S^(d-1))
F7  the multiplicative twin of all 58 tesseracts: the 81 divisors of 210^2, every line multiplies to 210^3, center 210,
    opposite cells multiply to 210^2
F8  audit arithmetic for the outside observer's note: M_d = 3 M_(d-1) - 3; number of lines vs line-system rank;
    Platonic edge lengths on one sphere are not a geometric progression; Descartes: total angular deficit = 4 pi
"""
import itertools
import json
import math
from fractions import Fraction

import numpy as np
from scipy.spatial import ConvexHull

OUT, FAILS = [], []


def say(s=""):
    print(s)
    OUT.append(s)


def check(name, ok, extra=""):
    say(f"{'PASS' if ok else 'FAIL'}  {name}{('  [' + extra + ']') if extra else ''}")
    if not ok:
        FAILS.append(name)


def bal(r):
    r %= 3
    return 0 if r == 0 else (1 if r == 1 else -1)


def cells(d):
    return [tuple(y) for y in itertools.product((-1, 0, 1), repeat=d)]


def zc(y):
    return sum(1 for v in y if v == 0)


# ---------------------------------------------------------------- F1
say("F1  one grid, two dual face lattices")
for d in (2, 3, 4):
    C = cells(d)
    cube_verts = [tuple(v) for v in itertools.product((-1, 1), repeat=d)]
    cross_verts = [tuple(s if k == j else 0 for k in range(d)) for j in range(d) for s in (-1, 1)]
    # geometric faces: vertex sets of faces, read off from the grid label
    cube_face = {y: frozenset(v for v in cube_verts if all(yj == 0 or yj == vj for yj, vj in zip(y, v))) for y in C}
    cross_face = {y: frozenset(v for v in cross_verts if any(vj != 0 and vj == yj for yj, vj in zip(y, v))) for y in C}
    # check these really are faces: each cube face set is the vertex set of a face (affine span test), each cross set a simplex face
    ok_cube = len(set(cube_face.values())) == 3 ** d
    ok_cross = len(set(cross_face.values())) == 3 ** d and all(len(cross_face[y]) == d - zc(y) for y in C)
    rev = all((cube_face[a] <= cube_face[b]) == (cross_face[b] <= cross_face[a]) for a in C for b in C)
    center = tuple([0] * d)
    check(f"d={d}: the {3**d} cells label every face of the cube and every face of the cross-polytope; inclusion is reversed",
          ok_cube and ok_cross and rev and len(cube_face[center]) == 2 ** d and len(cross_face[center]) == 0,
          "center = whole cube = empty face of the cross-polytope; corners = vertices = facets")

# ---------------------------------------------------------------- F2
say("F2  the dual flip nest (circumscribe, inscribe the dual, repeat)")
ok_turn = True
for d in range(2, 7):
    # cube [-1,1]^d -> cross-polytope whose vertices are the cube's facet centers +-e_j -> cube whose vertices are the
    # cross-polytope's facet centroids; compute those centroids from the hull and read off the inner cube's half-side
    cross = np.array([[s if k == j else 0 for k in range(d)] for j in range(d) for s in (-1, 1)], dtype=float)
    Hc = ConvexHull(cross)
    cents = np.array([cross[f].mean(axis=0) for f in Hc.simplices])
    half = np.abs(cents).max()
    if abs(half - 1 / d) > 1e-12 or not np.allclose(np.abs(cents), 1 / d) or len({tuple(np.round(c, 9)) for c in cents}) != 2 ** d:
        ok_turn = False
check("one full turn (cube -> cross-polytope -> cube) shrinks by exactly 1/d: the facet centroids are the 2^d points (+-1/d, ..., +-1/d) (d = 2..6)", ok_turn)
cube = np.array(list(itertools.product((-1, 1), repeat=3)), dtype=float)
octa = np.array([[s if k == j else 0 for k in range(3)] for j in range(3) for s in (-1, 1)], dtype=float)
hull = ConvexHull(octa)
centroids = {tuple(np.round(octa[f].mean(axis=0), 12)) for f in hull.simplices}
inner_cube = {tuple(np.round(v / 3, 12)) for v in cube}
check("d = 3: the inscribed octahedron's 8 face centroids are (+-1/3, +-1/3, +-1/3): the center cell of the 3x3x3 grid",
      centroids == inner_cube, "side 2/3 = one third of the original cube")
R = [math.sqrt(3), 1.0, 1 / math.sqrt(3), 1 / 3]
octa_in = abs(hull.equations[0][3])        # distance from origin to a face plane
check("radii along the nest: cube circumradius sqrt3, insphere 1 = octahedron circumsphere, octahedron insphere 1/sqrt3 = inner-cube circumsphere, inner insphere 1/3",
      abs(octa_in - 1 / math.sqrt(3)) < 1e-12 and abs(math.sqrt(3) * (1 / 3) - R[2]) < 1e-12, "each closure sphere is shared; ratio per flip 1/sqrt3, per turn 1/3")
ok_ratio = True
for d in range(2, 7):
    cube_ratio = 1 / math.sqrt(d)                  # inradius 1 / circumradius sqrt d
    cross_ratio = (1 / math.sqrt(d)) / 1           # inradius 1/sqrt d / circumradius 1
    if abs(cube_ratio - cross_ratio) > 1e-15:
        ok_ratio = False
check("a cube and its dual cross-polytope have the same in:out radius ratio 1/sqrt(d) (d = 2..6)", ok_ratio)

# ---------------------------------------------------------------- F3
say("F3  corners and waves read the same sign vectors from the two dual solids")
for d in (2, 3, 4, 5):
    cross = np.array([[s if k == j else 0 for k in range(d)] for j in range(d) for s in (-1, 1)], dtype=float)
    H = ConvexHull(cross)
    normals = {tuple(np.round(e[:d] / np.max(np.abs(e[:d])), 9)) for e in H.equations}
    signs = {tuple(float(x) for x in v) for v in itertools.product((-1, 1), repeat=d)}
    check(f"d={d}: the cross-polytope's {len(normals)} facet normals are exactly the {2**d} sign vectors (the wave directions a, both signs)",
          normals == signs)

# ---------------------------------------------------------------- F4
say("F4  the sign that flips with every rung")
ok = all(round(np.linalg.det(-np.eye(d))) == (-1) ** d for d in range(1, 9))
check("the center-point flip y -> -y has determinant (-1)^d: it reverses orientation on odd rungs and keeps it on even rungs", ok)
ok = True
for d in range(2, 6):
    # corner rule before the diagonals: center = (-1)^d * (sum of all corners)
    h = np.random.default_rng(d).integers(-9, 10, size=2 ** d)
    corners = list(itertools.product((-1, 1), repeat=d))
    centre_val = (-1) ** d * h.sum()
    # build the corner-rule array and check its rows, then the center
    hv = dict(zip(corners, h))
    f = {}
    for y in cells(d):
        s = sum(hv[t] for t in corners if all(yj == 0 or yj == tj for yj, tj in zip(y, t)))
        f[y] = (-1) ** zc(y) * s
    rows_ok = all(f[y[:j] + (-1,) + y[j + 1:]] + f[y] + f[y[:j] + (1,) + y[j + 1:]] == 0 for j in range(d) for y in cells(d) if y[j] == 0)
    if not (rows_ok and f[tuple([0] * d)] == centre_val):
        ok = False
check("corner rule: rows add to zero for any corners, and the center is (-1)^d times the corner sum (d = 2..5)", ok)

# ---------------------------------------------------------------- F5
say("F5  each rung's lines through the center = new directions + the rung below")
ok = True
for d in range(2, 8):
    lines = {min(y, tuple(-v for v in y)) for y in itertools.product((-1, 0, 1), repeat=d) if any(y)}
    new = {L for L in lines if L[-1] != 0}
    old = {L for L in lines if L[-1] == 0}
    below = {min(y, tuple(-v for v in y)) for y in itertools.product((-1, 0, 1), repeat=d - 1) if any(y)}
    if not (len(new) == 3 ** (d - 1) and {L[:-1] for L in old} == below and len(lines) == (3 ** d - 1) // 2):
        ok = False
check("(3^d - 1)/2 = 3^(d-1) + (3^(d-1) - 1)/2: the lines leaving the hyperplane y_d = 0, plus the rung below inside it (d = 2..7)", ok,
      "1, 4, 13, 40, 121, 364, 1093")

# ---------------------------------------------------------------- F6
say("F6  the grid as the divisors of N^2")
PR = [2, 3, 5, 7, 11]


def omega_big(n):            # number of prime factors with multiplicity
    k, p = 0, 2
    while n > 1:
        while n % p == 0:
            n //= p
            k += 1
        p += 1
    return k


def core(n):                 # product of primes with odd exponent
    c, p = 1, 2
    while n > 1:
        e = 0
        while n % p == 0:
            n //= p
            e += 1
        if e % 2:
            c *= p
        p += 1
    return c


def mobius(n):
    k, p = 0, 2
    while n > 1:
        if n % p == 0:
            n //= p
            if n % p == 0:
                return 0
            k += 1
        p += 1
    return -1 if k % 2 else 1


for d in (2, 3, 4, 5):
    P = PR[:d]
    N = math.prod(P)
    m = {y: math.prod(p ** (yj + 1) for p, yj in zip(P, y)) for y in cells(d)}
    divisors = sorted(k for k in range(1, N * N + 1) if (N * N) % k == 0)
    lam = {y: (-1) ** omega_big(m[y]) for y in cells(d)}
    center = tuple([0] * d)
    ok = sorted(m.values()) == divisors
    ok &= {m[y] for y in cells(d) if zc(y) == 0} == {k for k in divisors if math.isqrt(k) ** 2 == k}
    ok &= m[center] == N
    ok &= all(m[y] * m[tuple(-v for v in y)] == N * N for y in cells(d))
    ok &= all(lam[y] == (-1) ** zc(y) == mobius(core(m[y])) for y in cells(d))
    ok &= all(omega_big(core(m[y])) == zc(y) for y in cells(d))
    s_all = sum(lam.values())
    s_bd = s_all - lam[center]
    ok &= s_all == 1 and s_bd == 1 - (-1) ** d
    check(f"d={d}, N = {N}: cells <-> the {len(divisors)} divisors of N^2; corners = square divisors; center = N; y -> -y is m -> N^2/m; "
          f"(-1)^z = lambda(m) = mu(squarefree part)", ok,
          f"sum of lambda: all {s_all}, without the center {s_bd}" + (" = V - E + F of the cube" if d == 3 else ""))

# ---------------------------------------------------------------- F7
say("F7  the multiplicative twin of the 58 tesseracts")
DATA = json.load(open("ladder_data.json"))
P4 = [2, 3, 5, 7]
C4 = cells(4)
lines4 = []
for j in range(4):
    for y in C4:
        if y[j] == 0:
            lines4.append([y[:j] + (t,) + y[j + 1:] for t in (-1, 0, 1)])
for s in itertools.product((1, -1), repeat=3):
    a = (1,) + s
    lines4.append([tuple(-v for v in a), (0, 0, 0, 0), a])
ok = True
for rows in DATA["classes"]["4"]:
    A = [tuple(1 if ch == "+" else -1 for ch in r) for r in rows]
    tw = {y: math.prod(p ** (bal(sum(ak * v for ak, v in zip(A[k], y))) + 1) for k, p in enumerate(P4)) for y in C4}
    ok &= sorted(tw.values()) == sorted(k for k in range(1, 44101) if 44100 % k == 0)
    ok &= all(math.prod(tw[c] for c in L) == 210 ** 3 for L in lines4)
    ok &= tw[(0, 0, 0, 0)] == 210 and all(tw[y] * tw[tuple(-v for v in y)] == 44100 for y in C4)
check("all 58: the 81 cells hold the 81 divisors of 210^2 once each, every one of the 116 lines multiplies to 210^3 = 9 261 000, center 210, opposite cells multiply to 210^2", ok)

# ---------------------------------------------------------------- F8
say("F8  arithmetic behind the audit of the outside observer's note")
M = {d: 3 * (3 ** d + 1) // 2 for d in range(2, 8)}
check("M_d = 3 M_(d-1) - 3 (15, 42, 123, 366, 1095, ...)", all(M[d] == 3 * M[d - 1] - 3 for d in range(3, 8)))
nlines = {d: d * 3 ** (d - 1) + 2 ** (d - 1) for d in range(2, 6)}
rank = {d: 3 ** d - 2 ** (d - 1) for d in range(2, 6)}
check("the number of lines (8, 31, 116, 421) differs from the line-system rank (7, 23, 73, 227)",
      [nlines[d] for d in range(2, 6)] == [8, 31, 116, 421] and [rank[d] for d in range(2, 6)] == [7, 23, 73, 227])
s5 = math.sqrt(5)
edges = {"T": 4 / math.sqrt(6), "C": 2 / math.sqrt(3), "O": math.sqrt(2), "D": 4 / (math.sqrt(3) * (1 + s5)), "I": 4 / math.sqrt(10 + 2 * s5)}
srt = sorted(edges.values())
ratios = [srt[i + 1] / srt[i] for i in range(4)]
check("the five Platonic edge lengths on one sphere are not a geometric progression", max(ratios) - min(ratios) > 0.05,
      "successive ratios " + ", ".join(f"{r:.4f}" for r in ratios))
deficit = {"T": (4, 3, 3), "C": (8, 4, 3), "O": (6, 3, 4), "D": (20, 5, 3), "I": (12, 3, 5)}   # (V, n-gon faces, q at a vertex)
tot = {k: V * (2 * math.pi - q * (n - 2) * math.pi / n) for k, (V, n, q) in deficit.items()}
check("Descartes: total angular deficit = 2 pi (V - E + F) = 4 pi for all five Platonic solids", all(abs(t - 4 * math.pi) < 1e-12 for t in tot.values()),
      "dodecahedron: 20 x pi/5")

say("")
say("ALL PASS" if not FAILS else f"{len(FAILS)} FAILED: " + "; ".join(FAILS))
open("flip_checks_output.txt", "w").write("\n".join(OUT) + "\n")

==============================================================================================================
SOURCE — test_math.js  sha256 03b1daaa67c6fae436cc74785d5dcf221f34d5791b937bd9178ff62fe38258d5
==============================================================================================================
// Headless checks of the instrument's math script against the Python/C receipts.
const fs = require("fs");
const vm = require("vm");
const ctx = { window: {}, console };
vm.createContext(ctx);
vm.runInContext(fs.readFileSync(__dirname + "/_s0.js", "utf8"), ctx);
vm.runInContext(fs.readFileSync(__dirname + "/_s1.js", "utf8") + "\n;globalThis.__api = {toBase, ord3, geom, fromRows, fromCorners, fromWaves, expandHemi, hemiOf, spectrumNum, lineSums, normalInfo, isSpike, mod3Collapse, symPerms, canonKey, cornerSearch, basesCount, analyseSolutions, rankMod3, bal, dot, mod};", ctx);
const A = ctx.__api, DATA = ctx.window.__LADDER_DATA__;
let fails = 0;
const check = (name, ok, extra = "") => { console.log(`${ok ? "PASS" : "FAIL"}  ${name}${extra ? "  " + extra : ""}`); if (!ok) fails++; };

// classes from rows: normal, magic, spike, mod-3 collapse linear, pairwise inequivalent
for (const d of [2, 3, 4]) {
  const g = A.geom(d), perms = A.symPerms(d), keys = new Set();
  let allOk = true, allSpike = true, allCollapse = true;
  for (const rows of DATA.classes[String(d)]) {
    const v = A.fromRows(rows, d);
    if (!A.normalInfo(v, d).normal || A.lineSums(v, d).some(s => s !== g.M)) allOk = false;
    if (!A.isSpike(A.spectrumNum(v, d), d)) allSpike = false;
    const mc = A.mod3Collapse(v, d); if (!mc.ok || mc.deg !== 1) allCollapse = false;
    keys.add(A.canonKey(v, perms));
  }
  check(`d=${d} classes from rows: ${DATA.classes[String(d)].length} normal+magic`, allOk);
  check(`d=${d} all spikes`, allSpike);
  check(`d=${d} mod-3 collapse with linear bottom digit`, allCollapse);
  check(`d=${d} pairwise inequivalent`, keys.size === DATA.classes[String(d)].length, `${keys.size}`);
  check(`d=${d} perms count`, perms.length === [0, 0, 8, 48, 384][d], `${perms.length}`);
}
// special rows land in their classes
const g4 = A.geom(4), P4 = A.symPerms(4);
const keyOf = rows => A.canonKey(A.fromRows(rows, 4), P4);
check("I+J rows are class #" + DATA.class_IJ, keyOf(["-+++", "+-++", "++-+", "+++-"]) === keyOf(DATA.classes["4"][DATA.class_IJ - 1]));
check("H4 rows are class #" + DATA.class_H4, keyOf(["++++", "+-+-", "++--", "+--+"]) === keyOf(DATA.classes["4"][DATA.class_H4 - 1]));
// the starting I + J layout, first block row
const vIJ = A.fromRows(["-+++", "+-++", "++-+", "+++-"], 4);
const rowStr = (w, y) => [-1, 0, 1].map(z => [-1, 0, 1].map(x => vIJ[g4.idx([x, y, z, w])]).join(" ")).join(" | ");
const expected = ["81 2 40 | 10 51 62 | 32 70 21", "4 45 74 | 53 55 15 | 66 23 34", "38 76 9 | 60 17 46 | 25 30 68"];
check("I+J layout matches the starting construction (first block row)", [-1, 0, 1].every((y, i) => rowStr(-1, y) === expected[i]), rowStr(-1, -1));
check("I+J bottom-right cell is 1", vIJ[g4.idx([1, 1, 1, 1])] === 1);

// registry claim on all 58: families fill planes of the first wave iff 3 | base
let regOk = true;
for (const rows of DATA.classes["4"]) {
  const v = A.fromRows(rows, 4), num = A.spectrumNum(v, 4), a1 = g4.dirs[num.findIndex(x => Math.abs(x) === 27)];
  for (const B of [10, 12, 3, 9]) {
    const seen = {}; let ok = true;
    g4.ys.forEach((y, i) => { const k = A.mod(A.dot(a1, y), 3), f = A.mod(A.mod(v[i], B), 3); if (k in seen) { if (seen[k] !== f) ok = false; } else seen[k] = f; });
    if (ok !== (A.ord3(B) > 0)) regOk = false;
  }
}
check("registry: last-digit families fill first-wave planes exactly for bases 12, 3, 9 (all 58)", regOk);

// theorem 1 random checks: corners -> magic; waves -> magic; round trips
for (const d of [2, 3, 4]) {
  const g = A.geom(d); let ok = true;
  for (let t = 0; t < 30; t++) {
    const vals = g.hemi.map(() => Math.floor(Math.random() * 41) - 20);
    const v = A.fromCorners(A.expandHemi(vals, d), d);
    if (A.lineSums(v, d).some(s => s !== g.M)) ok = false;
    if (JSON.stringify(A.hemiOf(v, d)) !== JSON.stringify(vals)) ok = false;
    const gam = g.dirs.map(() => Math.floor(Math.random() * 21) - 10);
    const w = A.fromWaves(gam, d);
    if (A.lineSums(w, d).some(s => s !== g.M)) ok = false;
    const num = A.spectrumNum(w, d), den = 3 ** (d - 1);
    if (num.some((x, i) => x !== gam[i] * den)) ok = false;
    const back = A.fromCorners(A.expandHemi(A.hemiOf(w, d), d), d);
    if (back.some((x, i) => x !== w[i])) ok = false;
    if (!A.mod3Collapse(w, d).ok || !A.mod3Collapse(v, d).ok) ok = false;
  }
  check(`d=${d} Theorem 1 random round trips (corners, waves, mod-3 collapse)`, ok);
}

// figures
const H = DATA.hexagon; let hexOk = true;
[0, 1, 2].forEach(ax => { for (let c = -2; c <= 2; c++) { const s = H.cells.reduce((a, p, i) => a + (p[ax] === c ? H.values[i] : 0), 0); if (s !== 38) hexOk = false; } });
check("hexagon 15 lines = 38", hexOk);
const S = DATA.hexagram;
check("hexagram: 80 stars, every line = 26", S.all.length === 80 && S.all.every(v => S.lines.every(L => L.reduce((a, c) => a + v[c], 0) === 26) && [...v].sort((a, b) => a - b).every((x, i) => x === i + 1)));
const sqLines = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
check("Lo Shu 8 lines = 15", sqLines.every(L => L.reduce((a, c) => a + DATA.loshu[c], 0) === 15));

// bases count
const t0 = Date.now(); const L = [2, 3, 4, 5, 6].map(d => A.basesCount(d));
check("L(d) = 1, 4, 58, 2992, 543328", JSON.stringify(L) === JSON.stringify([1, 4, 58, 2992, 543328]), `${L} in ${Date.now() - t0} ms`);

// exhaustive searches in JS
for (const D of [2, 3, 4]) {
  const r = A.cornerSearch(D);
  const a = A.analyseSolutions(r.sols, D);
  check(`JS corner search d=${D}: ${r.sols.length} sols, ${a.classes} classes, ${r.nodes} nodes, ${r.ms} ms`, a.classes === [0, 0, 1, 4, 58][D] && a.allNormal && a.allMagic && a.allSpike);
}
console.log(fails ? `${fails} FAILED` : "ALL PASS");

==============================================================================================================
SOURCE — test_dom.js  sha256 b4320b46ff9b39abdb0f81d5c89e062d6b5172e452c4555d9dedffff0961b83d
==============================================================================================================
// Load the built page in jsdom, run its scripts, click through every control, and report runtime errors.
const fs = require("fs");
const { JSDOM, VirtualConsole } = require("/tmp/claude-0/-home-claude/f0325726-0495-5f91-925a-621d7bcafd95/scratchpad/jsd/node_modules/jsdom");
const page = process.argv[2] || (__dirname + "/ternary-magic-ladder.html");
const html = fs.readFileSync(page, "utf8");
const errors = [];
const vc = new VirtualConsole();
vc.on("jsdomError", e => errors.push("jsdomError: " + (e.message || e)));
vc.on("error", e => errors.push("console.error: " + e));
const dom = new JSDOM(/^<!doctype/i.test(html) ? html : `<!doctype html><html><head><meta charset="utf-8"></head><body>${html}</body></html>`, { runScripts: "dangerously", url: "https://example.org/", pretendToBeVisual: true, virtualConsole: vc });
const w = dom.window, doc = w.document;
w.addEventListener("error", e => errors.push("window error: " + e.message));
const $ = s => doc.querySelector(s);
const click = s => { const el = typeof s === "string" ? $(s) : s; if (!el) { errors.push("missing " + s); return; } el.dispatchEvent(new w.MouseEvent("click", { bubbles: true })); };
let fails = 0;
const check = (name, ok, extra = "") => { console.log(`${ok ? "PASS" : "FAIL"}  ${name}${extra !== "" ? "  [" + extra + "]" : ""}`); if (!ok) fails++; };
const text = s => ($(s) ? $(s).textContent.replace(/\s+/g, " ").trim() : "(missing)");

(async () => {
  await new Promise(r => setTimeout(r, 50));
  check("ladder table rows", doc.querySelectorAll("#ladderTable tbody tr").length === 7);
  check("hero equation has 3 grids", doc.querySelectorAll("#heroEq .mini").length === 3);
  check("tesseract 81 cells, first is 81", doc.querySelectorAll("#tessGrid .cell").length === 81 && doc.querySelector("#tessGrid .cell").textContent === "81", doc.querySelector("#tessGrid .cell").textContent);
  check("tesseract readout 116/116", text("#tessRead").includes("116 / 116"), text("#tessRead").slice(0, 60));
  check("cube 27 cells", doc.querySelectorAll("#cubeGrid .cell").length === 27);
  check("square 9 cells", doc.querySelectorAll("#sqGrid .cell").length === 9);
  check("hexagon 19 cells", doc.querySelectorAll("#hxFig .hx").length === 19);
  check("triangle default n=4 has 10 dots", doc.querySelectorAll("#triSvg circle").length === 10);
  check("strip 24 tiles", doc.querySelectorAll("#triStrip .tile").length === 24);
  check("builder default d=3 shows 27 cells", doc.querySelectorAll("#fbOut .cell").length === 27);
  check("builder default is normal", text("#fbChecks").includes("yes"), text("#fbChecks").slice(0, 120));

  click("#base12");
  check("base 12: hero table shows A3", text("#ladderTable").includes("A3"));
  check("base 12: depth note", text("#depthNote").includes("ord₃(12) = 1"), text("#depthNote"));
  check("base 12: tesseract first cell 69 (81 in base 12)", doc.querySelector("#tessGrid .cell").textContent === "69");
  click("#tessViewLast");
  const st = doc.querySelector("#tessGrid .cell").getAttribute("style") || "";
  check("last-digit view styles cells", st.includes("color-mix"), st.slice(0, 50));
  check("registry note says planes", text("#registryNote").includes("fill whole parallel planes"), text("#registryNote").slice(0, 160));
  click("#base10");
  check("base 10: registry note says no line-up", text("#registryNote").includes("scatter instead"));
  click("#base3"); check("base 3 renders", doc.querySelector("#tessGrid .cell").textContent === "10000");
  click("#base9"); check("base 9 renders", doc.querySelector("#tessGrid .cell").textContent === "100");
  click("#base10");
  click("#tessViewTern"); check("ternary view 4 digits", doc.querySelector("#tessGrid .cell").textContent === "2222");
  click("#tessViewNum");

  click(doc.querySelectorAll("#tessGrid .cell")[0]);
  check("selecting a corner lists its quadragonal", text("#tessRead").includes("quadragonal"));
  click("#tessNext"); check("next tesseract", text("#tessOut") === "6 / 58");
  click("#tessH4"); check("Hadamard button", text("#tessOut") === "21 / 58");
  click("#tessPrev"); check("prev tesseract", text("#tessOut") === "20 / 58");

  click("#cubeNext"); check("cube next", text("#cubeOut") === "2 / 4");
  click(doc.querySelectorAll("#cubeGrid .cell")[13]); check("cube center selected lists its 7 lines (3 rows + 4 diagonals)", (text("#cubeRead").match(/✓/g) || []).length >= 7);

  click("#sqViewWave"); check("square waves visible", !$("#sqWaves").hidden && $("#sqGrid").hidden);
  click("#sqViewNum");
  click("#pPlus"); check("square builder p = -2 not normal", text("#bRead").includes("no"));
  click("#pMinus");
  check("square builder back to Lo Shu", text("#bRead").includes("yes"));

  click("#hxModeStar"); check("hexagram 12 points", doc.querySelectorAll("#hxFig .sp").length === 12);
  click("#starNext"); check("star 2", text("#starOut") === "2 / 80");
  click(doc.querySelectorAll("#hxFig .sp")[0]); check("star point selection highlights 2 lines", doc.querySelectorAll("#hxFig .sline.on").length === 2);
  click("#hxModeHex"); click(doc.querySelectorAll("#hxFig .hx")[9]); check("hex center selected", doc.querySelectorAll("#hxFig .hx.on").length === 13, String(doc.querySelectorAll("#hxFig .hx.on").length));

  click("#triPlus"); check("triangle n=5 -> 15 dots + hole", doc.querySelectorAll("#triSvg circle").length === 16);
  click("#triOrbOff");
  click(doc.querySelectorAll("#triStrip .tile")[6]); check("tile click sets n=7", text("#triN") === "7");
  click("#figPlus"); check("figures s=3", text("#figS") === "3");

  click("#fbD4"); check("builder d=4 81 cells", doc.querySelectorAll("#fbOut .cell").length === 81);
  check("builder d=4 normal at start", text("#fbChecks").includes("yes"));
  click("#fbModeCorner"); check("corner mode 8 inputs", doc.querySelectorAll("#fbInputs input").length === 8);
  check("corner switch keeps the array normal", text("#fbChecks").includes("yes"));
  const inp = doc.querySelector("#fb-in-0"); inp.value = String(+inp.value + 1); inp.dispatchEvent(new w.Event("input", { bubbles: true }));
  check("editing a corner keeps all lines magic", text("#fbChecks").includes("116 / 116"));
  check("…but breaks normality", text("#fbChecks").includes("no,"));
  click("#fbModeWave"); check("wave mode after edit shows rounding note or exact", doc.querySelectorAll("#fbInputs input").length === 8);
  click("#fbSpike"); check("random spike is normal", text("#fbChecks").includes("yes"));
  click("#fbRandom"); check("random numbers still magic", text("#fbChecks").includes("116 / 116"));
  click("#fbZero"); click("#fbD2"); check("d=2 builder", doc.querySelectorAll("#fbOut .cell").length === 9);

  click("#runS3");
  await new Promise(r => setTimeout(r, 200));
  check("in-page search d=3 finds 4 classes", text("#searchOut").includes("4 ✓"), text("#searchOut").slice(0, 140));
  click("#runL");
  for (let i = 0; i < 100 && !text("#countOut").includes("L(6)"); i++) await new Promise(r => setTimeout(r, 200));
  for (let i = 0; i < 100 && text("#countOut").includes("checked"); i++) await new Promise(r => setTimeout(r, 200));
  check("in-page count L(2..6)", text("#countOut").includes("543,328 ✓"), text("#countOut"));
  const pd = doc.querySelector("#proofs");
  check("paper is a closed <details> holding 32 statement blocks", !!pd && pd.tagName === "DETAILS" && !pd.open && pd.querySelectorAll(".thm").length === 32, String(pd && pd.querySelectorAll(".thm").length));
  check("paper has bylines for both authors", text("#proofs .pauth").includes("Lu Semita") && text("#proofs .pauth").includes("Claude"));
  check("hero byline names both authors", text(".hero .byline").includes("Lu Semita") && text(".hero .byline").includes("Claude"));
  check("results summary visible outside the fold, 7 items", doc.querySelectorAll(".reslist li").length === 7 && !doc.querySelector(".reslist").closest("details"));
  check("estimate tag present on the open item", !!doc.querySelector(".reslist .tag.estimate"));
  // a link into the paper opens it
  click(doc.querySelector('a[href="#p3"]')); check("link to #p3 opens the paper", pd.open === true); pd.open = false;

  // ---- echo 1: faces and fractals
  check("faces: default d=3 shows 27 cells, 7 removed", doc.querySelectorAll("#fxGrid .cell").length === 27 && doc.querySelectorAll("#fxGrid .cell.gone").length === 7);
  check("faces: Menger sponge named, 20 of 27, Euler 2", text("#fxRead").includes("Menger sponge") && text("#fxRead").includes("20 of 27") && text("#fxRead").includes("8 − 12 + 6 = 2"), text("#fxRead").slice(0, 200));
  click("#fxCantor"); check("faces: Cantor set 2 of 3", doc.querySelectorAll("#fxGrid .cell").length === 3 && text("#fxRead").includes("Cantor set") && text("#fxRead").includes("2 of 3"));
  click("#fxCarpet"); check("faces: carpet 8 of 9", text("#fxRead").includes("Sierpinski carpet") && text("#fxRead").includes("8 of 9"));
  click("#fxD4"); check("faces: d=4 keeps 48 of 81", doc.querySelectorAll("#fxGrid .cell").length === 81 && text("#fxRead").includes("48 of 81"));
  click("#fxPlus"); click("#fxPlus"); click("#fxPlus"); click("#fxPlus"); check("faces: k capped at d, nothing removed", text("#fxK") === "4" && text("#fxRead").includes("nothing removed"));
  click("#fxMenger");

  // ---- echo 2: flats and Bachet
  check("flags: default block 1-27 lights 27 cells, a 3-space", doc.querySelectorAll("#flGrid .cell.on").length === 27 && text("#flRead").includes("a 3-space, affine ✓") && text("#flRead").includes("each exactly once ✓"), text("#flRead").slice(0, 160));
  let flagsOk = true;
  for (let c = 0; c < 6; c++) { for (const sz of ["27", "9", "3"]) { click("#flS" + sz); const nb = 81 / +sz; for (let b = 0; b < nb; b++) { if (!text("#flRead").includes("affine ✓") || !(text("#flRead").includes("✓"))) flagsOk = false; click("#flBNext"); } } click("#flNext"); }
  check("flags: every block of 27, 9, 3 is affine on 6 classes", flagsOk);
  click("#flIJ"); click("#flS1"); check("flags: single value is a point", text("#flRead").includes("a point"));
  click(doc.querySelectorAll("#flGrid .cell")[7]); check("flags: tapping a cell weighs it", doc.querySelectorAll("#flRead .pan").length === 4 && text("#flRead").includes("net load") && !text("#flRead").includes("✗"), text("#flRead").slice(-160));

  // ---- echo 3
  check("energy table: 5 rows, base-9 repunits", doc.querySelectorAll("#enTable tbody tr").length === 5 && text("#enTable").includes("11111") && !text("#enTable").includes("✗"));

  // ---- echo 4: lattice and tesseract shadow
  check("lattice: triangular, 6 neighbors, 19 = 7 + 6 + 6", text("#shRead").includes("triangular lattice") && text("#shRead").includes("6 at distance 1") && text("#shRead").includes("7 + 6 + 6 by class ✓"), text("#shRead"));
  click("#shTwo"); check("lattice: honeycomb, 3 neighbors of the other class", text("#shRead").includes("honeycomb") && text("#shRead").includes("3 at distance 1, all of the other class"));
  click("#shOne"); check("lattice: one class, 6 at distance √3", text("#shRead").includes("6 at distance √3"));
  click("#shAll"); const pt = doc.querySelectorAll("#shSvg .pt")[5]; click(pt); check("lattice: tapped point gives an Eisenstein integer", text("#shRead").includes("as an Eisenstein integer"));
  click("#sh19"); check("lattice: rings toggle off", doc.querySelectorAll("#shSvg .ring").length === 0); click("#sh19"); check("lattice: rings back, 19", doc.querySelectorAll("#shSvg .ring").length === 19);
  check("tesseract shadow: 16 to 15, 8 + 6 vertices, sqrt2, BCC", text("#tsRead").includes("16 → 15 ✓") && text("#tsRead").includes("8 where three rhombi meet, 6 where four meet ✓") && text("#tsRead").includes("√2 ✓") && text("#tsRead").includes("body-centered cubic ✓"), text("#tsRead"));
  click("#tsGrid"); check("tesseract shadow: 81 cells to 65 points", text("#tsRead").includes("81 → 65") && text("#tsRead").includes("50, 14, 1"), text("#tsRead"));
  const tr = doc.querySelector("#tsTurn"); tr.value = "200"; tr.dispatchEvent(new w.Event("input", { bubbles: true })); check("tesseract shadow: turn redraws", doc.querySelectorAll("#tsSvg circle").length === 65);
  click("#tsUnit");

  // ---- echo 5: broken diagonals
  let cubesOk = true;
  for (let c = 0; c < 4; c++) { if (!text("#bdRead").includes("(1 of 4)")) cubesOk = false; click("#bdNext"); }
  check("broken diagonals: each cube has exactly one direction", cubesOk, text("#bdRead").slice(-80));
  click("#bdTess"); check("broken diagonals: I + J has four, the Hadamard waves", text("#bdRead").includes("(4 of 8)") && text("#bdRead").includes("Hadamard tesseract's waves") && !text("#bdRead").includes("✗") || text("#bdRead").includes("(4 of 8)") && text("#bdRead").includes("waves") && text("#bdRead").split("✓").length > 2, text("#bdRead").slice(-200));
  check("broken diagonals: parity pairing shown for I + J", text("#bdRead").includes("active waves odd, these directions even"), text("#bdRead").slice(-120));
  click("#bdH4"); check("broken diagonals: Hadamard has four, the I + J waves", text("#bdRead").includes("(4 of 8)") && text("#bdRead").includes("I + J tesseract's waves"));
  click(doc.querySelectorAll("#bdSums .chip")[3]); check("broken diagonals: a chip lights 3 cells", doc.querySelectorAll("#bdGrid .cell.on").length === 3 && text("#bdRead").includes("lit diagonal"));
  click(doc.querySelectorAll("#bdDirs button")[4]); check("broken diagonals: direction buttons switch", doc.querySelectorAll("#bdDirs button")[4].getAttribute("aria-pressed") === "true");
  let pred = true; for (let c = 0; c < 58; c += 7) { click("#bdNext"); for (let k = 0; k < 8; k++) { click(doc.querySelectorAll("#bdDirs button")[k]); if (!text("#bdRead").includes("each sum = M + 3 × those waves✓")) pred = false; } }
  check("broken diagonals: sums = M + 3 x waves across s, sampled classes and all directions", pred);
  click("#bdCube");

  // ---- echo 6
  check("eigen table: 7 rows, all checks pass", doc.querySelectorAll("#eigTable tbody tr").length === 7 && !text("#eigTable").includes("✗") && (text("#eigNote").match(/✓/g) || []).length === 2);

  // ---- inside and outside
  check("readings: cube faces d=3", text("#ioRead").includes("8 corners · 12 edges · 6 squares · 1 cube"), text("#ioRead").slice(0, 120));
  click("#ioDual"); check("readings: octahedron faces", text("#ioRead").includes("6 vertices · 12 edges · 8 triangles · the empty face") && text("#ioGrid").includes("∅"));
  click("#ioDiv"); check("readings: divisors, lambda sums 1 and 2", text("#ioRead").includes("Σ λ over all cells1 ✓") && text("#ioRead").includes("Σ λ without N2 = 1 − (−1)3 = V − E + F ✓"), text("#ioRead"));
  click(doc.querySelectorAll("#ioGrid .cell")[5]); check("readings: tapped divisor shows cofactor and lambda", doc.querySelectorAll("#ioGrid .cell.pair").length === 1 && text("#ioRead").includes("cofactor") && !text("#ioRead").includes("✗"));
  click("#ioD4"); check("readings: d=4 divisors of 210^2, lambda without N is 0", doc.querySelectorAll("#ioGrid .cell").length === 81 && text("#ioRead").includes("Σ λ without N0") && text("#ioRead").includes("44100"), text("#ioRead").slice(0, 200));
  click("#ioD2"); click("#ioDual"); check("readings: d=2 dual is the diamond", text("#ioRead").includes("4 vertices · 4 edges · the empty face"));
  click("#ioD3"); click("#ioCube");
  check("flip nest: cube lands on the center cell", text("#fnRead").includes("the center cell of the 3 × 3 × 3 grid ✓") && text("#fnRead").includes("1/3"), text("#fnRead"));
  click("#fnSq"); check("flip nest: square does not", text("#fnRead").includes("not the center cell"));
  click("#fnPlus"); click("#fnPlus"); click("#fnPlus"); check("flip nest: flips capped at 4", text("#fnN") === "4");
  click("#fnCube"); const ft = doc.querySelector("#fnTurn"); ft.value = "90"; ft.dispatchEvent(new w.Event("input", { bubbles: true })); check("flip nest: turn redraws", doc.querySelectorAll("#fnSvg line").length > 30);
  check("rung table: 5 rows", doc.querySelectorAll("#rbTable tbody tr").length === 5 && text("#rbTable").includes("reverses") && text("#rbTable").includes("keeps") && !text("#rbTable").includes("✗"));

  // ---- base switch re-renders the new sections
  click("#base12");
  check("base 12: energy table in dozenal", text("#enTable").includes("69") && text("#enTable").includes("₁₂"));
  click("#ioDiv"); check("base 12: divisors in dozenal (900 = 630 in base 12)", text("#ioRead").includes("630₁₂ = 900"), text("#ioRead").slice(0, 160));
  click("#ioD4"); check("base 12: 44100 = 21630 in base 12", text("#ioRead").includes("21630"), text("#ioRead").slice(0, 160)); click("#ioD3");
  click("#base10"); click("#ioCube");

  // ---- standalone: the page needs no file beside it
  const hrefs = [...doc.querySelectorAll("a[href]")].map(a => a.getAttribute("href"));
  const badLinks = hrefs.filter(h => !(h.startsWith("#") || h.startsWith("https://")));
  check("standalone: every link is in-page or an absolute https address", badLinks.length === 0, badLinks.join(" ") || `${hrefs.length} links`);
  const inPage = hrefs.filter(h => h.startsWith("#") && h.length > 1);
  const dangling = inPage.filter(h => !doc.getElementById(decodeURIComponent(h.slice(1))));
  check("standalone: every in-page link has its target", dangling.length === 0, dangling.join(" ") || `${inPage.length} in-page links`);
  const extRes = [...doc.querySelectorAll("script[src], link[href], img[src], iframe, object, embed")].map(e => e.getAttribute("src") || e.getAttribute("href") || e.tagName).filter(u => !/^https:\/\/fonts\.(googleapis|gstatic)\.com(\/|$)/.test(u));
  check("standalone: nothing loaded from outside except the optional web fonts", extRes.length === 0, extRes.join(" "));
  const pageText = (() => { const c = doc.body.cloneNode(true); const p = c.querySelector("#suppText"); if (p) p.remove(); return c.textContent; })();
  check("standalone: no companion file named outside the supplement", !pageText.includes("magic_ladder_receipts") && !pageText.includes("Companion file"));

  // ---- the supplement, inside the page
  const sb = doc.querySelector("#suppBox"), sp = doc.querySelector("#suppText"), suppTxt = sp ? sp.textContent : "";
  check("supplement: a closed <details> in the page's last section", !!sb && sb.tagName === "DETAILS" && !sb.open && sb.closest("section").id === "supplement" && !doc.querySelector("#supplement ~ section"));
  check("supplement: sections 1-7, ten sources and the hash list are anchored", [1, 2, 3, 4, 5, 6, 7].every(n => doc.getElementById("supp-" + n)) && doc.querySelectorAll('#suppText span[id^="supp-src-"]').length === 10 && !!doc.getElementById("supp-sha"));
  const BAR = "=".repeat(110), L = suppTxt.split("\n"), starts = [];
  for (let i = 0; i + 2 < L.length; i++) if (L[i] === BAR && L[i + 2] === BAR) starts.push(i);
  const nodeCrypto = require("crypto");
  let srcN = 0, srcOk = 0; const srcBad = [];
  starts.forEach((i, k) => {
    const m = /^SOURCE — (\S+)  sha256 ([0-9a-f]{64})$/.exec(L[i + 1]); if (!m) return;
    srcN++;
    const body = L.slice(i + 3, k + 1 < starts.length ? starts[k + 1] : L.length).join("\n").replace(/\n+$/, "") + "\n";
    const listed = suppTxt.includes(`\n  ${m[2]}  ${m[1]}\n`);
    if (nodeCrypto.createHash("sha256").update(body, "utf8").digest("hex") === m[2] && listed) srcOk++; else srcBad.push(m[1]);
  });
  check("supplement: every SOURCE block hashes to the SHA-256 in its title and in the list", srcN === 10 && srcOk === 10, srcBad.join(" ") || `${srcOk} of ${srcN}`);
  click(doc.querySelector('a[href="#supp-5"]')); check("supplement: a link to section 5 opens it", sb.open === true); sb.open = false;
  w.dispatchEvent(new w.Event("beforeprint")); const printState = pd.open && !sb.open;
  w.dispatchEvent(new w.Event("afterprint"));
  check("printing opens the paper, leaves the supplement closed, then restores both", printState && !pd.open && !sb.open);
  let saved = null, blobGot = null;
  w.URL.createObjectURL = b => { blobGot = b; return "blob:https://example.org/supplement"; };
  w.URL.revokeObjectURL = () => {};
  w.HTMLAnchorElement.prototype.click = function () { saved = { name: this.download, href: this.getAttribute("href") }; };
  click("#suppSave");
  const blobText = blobGot ? await new Promise(r => { const fr = new w.FileReader(); fr.onload = () => r(fr.result); fr.readAsText(blobGot); }) : "";
  check("supplement: Save as text hands over the whole supplement", !!saved && saved.name === "ternary-magic-ladder-supplement.txt" && blobText === suppTxt && suppTxt.length > 100000, saved ? `${saved.name}${blobText === suppTxt ? ", identical to the text in the page" : ""}` : "no download");
  check("contribution note: accountability with the first author", text("#p10 + p").includes("Accountability for the published text therefore rests with Lu Semita") && !text("#proofs").includes("accepts responsibility"));

  check("standalone head has title and charset", doc.title === "Ternary Magic Ladder");
  check("no runtime errors", errors.length === 0, errors.join(" | "));
  console.log(fails ? `${fails} FAILED` : "ALL PASS");
  process.exit(0);
})();

==============================================================================================================
SOURCE — build.py  sha256 a751019e026db7ba345193784e1ff2b10bf17c1426d3f291fe5957ceae72fb68
==============================================================================================================
#!/usr/bin/env python3
"""Build the hosted page from standalone_template.html: inject ladder_data.json and the supplement
(supplement.txt, from make_receipts.py), write the site copy, and extract the three inline scripts
(_s0 data, _s1 math, _s2 ui) for node --check and the headless tests. The page depends on no other file."""
import html
import json
import pathlib
import re

HERE = pathlib.Path(__file__).resolve().parent
SITE = HERE.parent / "site"
SITE.mkdir(exist_ok=True)
BAR = "=" * 110
tpl = (HERE / "standalone_template.html").read_text()
data = json.loads((HERE / "ladder_data.json").read_text())
supp = (HERE / "supplement.txt").read_text()

# placeholders are filled in this order because the supplement quotes this script, placeholders included
for marker in ("/*__DATA__*/null", "__SUPP_LINES__", "__SUPP_TEXT__"):
    assert tpl.count(marker) == 1, marker
page = tpl.replace("/*__DATA__*/null", json.dumps(data, separators=(",", ":")))
page = page.replace("__SUPP_LINES__", f"{supp.count(chr(10)):,}".replace(",", " "))

# the supplement goes in as escaped text; an empty anchor marks each section and each source
esc = html.escape(supp, quote=False)
ids = []


def anchor(m):
    title = m.group(2)
    num = re.match(r"(\d+)\. ", title)
    src = re.match(r"SOURCE — (\S+)", title)
    aid = f"supp-{num.group(1)}" if num else "supp-src-" + re.sub(r"[^A-Za-z0-9_]", "-", src.group(1))
    ids.append(aid)
    return f'{m.group(1)}<span id="{aid}"></span>{title}'


esc = re.sub(rf"(?m)^({BAR}\n)(.+)(?=\n{BAR}$)", anchor, esc)
assert esc.count("\nSHA-256\n") == 1
esc = esc.replace("\nSHA-256\n", '\n<span id="supp-sha"></span>SHA-256\n')
ids.append("supp-sha")
assert len(ids) == len(set(ids)) == 7 + 10 + 1, ids
assert re.sub(r'<span id="[^"]+"></span>', "", esc).count("<") == 0
page = page.replace("__SUPP_TEXT__", esc)

(SITE / "ternary-magic-ladder.html").write_text(page)
(HERE / "ternary-magic-ladder-standalone.html").write_text(page)
scripts = re.findall(r"<script[^>]*>(.*?)</script>", page, flags=re.S)
assert len(scripts) == 3, len(scripts)
for i, body in enumerate(scripts):
    (HERE / f"_s{i}.js").write_text(body)
print(f"built {len(page)} bytes; scripts {[len(b) for b in scripts]}; supplement {len(supp)} chars, anchors {len(ids)}")

==============================================================================================================
SOURCE — make_receipts.py  sha256 1696038dbf9d189eb3a9963a6df396cd201452a60814406cf76b2c363a6c6d30
==============================================================================================================
#!/usr/bin/env python3
"""Assemble the supplement that build.py embeds as the last section of the page: every output the page cites,
then every source, with SHA-256. Writes supplement.txt next to this script."""
import hashlib
import pathlib

HERE = pathlib.Path(__file__).resolve().parent
BAR = "=" * 110


def sha(p):
    return hashlib.sha256(pathlib.Path(p).read_bytes()).hexdigest()


def block(title, body):
    return f"{BAR}\n{title}\n{BAR}\n{body.rstrip()}\n\n"


sources = ["corner_search.c", "line_magic.c", "knuth_estimate.c", "verify_ladder.py", "echoes.py", "flip_checks.py",
           "test_math.js", "test_dom.js", "build.py", "make_receipts.py"]
for s in sources:
    t = (HERE / s).read_text()
    # each SOURCE block below must reproduce its file exactly once a single newline is added back
    assert t.endswith("\n") and not t.endswith("\n\n") and t == t.rstrip() + "\n", s

out = []
out.append("TERNARY MAGIC LADDER — SUPPLEMENT: RECEIPTS, LOGS AND SOURCES\n")
out.append("Lu Semita · EmergenceByDesign and Claude · Anthropic · part of the instrument and paper · 10 October 2026\n\n")
out.append("SCOPE\n"
           "This supplement is the last section of the page ternary-magic-ladder.html and travels inside it. It carries\n"
           "every computation the page and its paper cite: the structural and counting checks (section 1), the exhaustive\n"
           "C corner searches and the timed 5D probe (section 2), the exact checks behind the Echoes (section 3) and the\n"
           "Inside-and-outside section (section 4), the tree-size estimates for the open 5D case (section 5), and two\n"
           "headless runs of the page's own JavaScript: its math against these receipts (section 6) and a click-through\n"
           "of every control (section 7). The source of every script follows, then nothing else. Imported facts are\n"
           "cited inside the page.\n"
           "Sections 6 and 7 test the page that contains them, so their output cannot come from the final page itself:\n"
           "they were run on the page as built, their output was embedded, and both runs were repeated on the final\n"
           "page, which reproduces every line shown here except the measured times.\n"
           "Status words: PROVEN = proof in the paper; VERIFIED = exhaustive or exact computation below;\n"
           "IMPORTED = published, cited on the page; OPEN = stated exactly, not settled; ESTIMATE = sampled figure, labeled.\n"
           "Check labels used on the page: A1-A5, B1-B2, C1-C4 (section 1); E1-E10 with E1b, E7b-E7d, E8b, E9b (section 3);\n"
           "F1-F8 (section 4).\n\n")
out.append("SHA-256\n")
out.append("  The page's own hash is not listed: writing it into the page would change the page.\n")
out.append(f"  {sha(HERE / 'ladder_data.json')}  ladder_data.json (written by verify_ladder.py; the page carries it in compact form)\n")
for s in sources:
    out.append(f"  {sha(HERE / s)}  {s}\n")
out.append("  Each SOURCE block below is its file's exact text; the file ends with one newline after the last line shown.\n")
out.append("\n")

out.append(block("1. STRUCTURE AND COUNTS — verify_ladder.py output (A1-A5, B1-B2, C1-C4)", (HERE / "verify_ladder_output.txt").read_text()))

sec2 = []
for d in (2, 3, 4):
    lines = (HERE / f"sols_d{d}.txt").read_text().strip().splitlines()
    done = [l for l in lines if l.startswith("DONE")]
    sols = [l for l in lines if l.startswith("SOL")]
    sec2.append(f"d={d}: {done[-1] if done else '(no completion line)'}")
    sec2.append("      normalized solutions (corner values; corner index t has bit j set when offset j is -1):")
    sec2.extend("      " + l for l in sols)
sec2.append("")
sec2.append("5D PROBE (time-limited; not a census):")
sec2.extend("  " + l for l in (HERE / "log_d5_probe.txt").read_text().strip().splitlines())
sec2.append("  The run stopped inside its first branch (largest corner value 121). No conclusion about the 5D count is")
sec2.append("  drawn from it; the spike conjecture stays OPEN with proven lower bound 2992 (section 5 estimates the full tree).")
sec2.append("")
sec2.append("ERRORS FOUND AND CORRECTED (checking code only; no theorem was affected)")
sec2.append("  - Check A5 first failed for d >= 3: numpy's default pseudo-inverse cutoff kept near-zero singular values, so")
sec2.append("    the projected arrays did not satisfy the line equations. Replaced by an SVD null-space basis with an explicit")
sec2.append("    1e-9 cutoff; A5 now passes for d = 2..5.")
sec2.append("  - The d = 2 search returns 2 normalized representatives of the single 3x3 square, because in 2D a corner's two")
sec2.append("    neighbors are opposite each other, so the neighbor-ordering rule cannot apply. Canonical forms under all 8")
sec2.append("    symmetries count them once.")
sec2.append("  - Check E7b first expected the tesseract's 16 corners to cast 14 shadow points down (1,1,1,1). The correct count")
sec2.append("    is 15: the corners 0000 and 1111 both land on the center, and the remaining 14 are the hull vertices.")
sec2.append("")
sec2.append("CORRECTIONS FROM THE INDEPENDENT AUDIT OF THE PAPER (wording and scope; every number survived recomputation)")
sec2.append("  - Theorem 5: the last digit in base b shows exactly the first ord3(b) waves only when b < 3^d (at d = 2, base 10")
sec2.append("    shows both waves, since every entry is its own last digit). Statement, proof and abstract now carry the bound.")
sec2.append("  - Proposition 7.10: the I+J <-> Hadamard pairing is between the two parity classes of sign directions; it holds")
sec2.append("    literally for the two arrays shown, while a coordinate sign flip swaps the classes within #5 and within #21.")
sec2.append("  - Proposition 8.5: the magic condition does not choose the hyperplane at infinity (the cube's symmetries fix none);")
sec2.append("    it breaks the sameness of the lines through the center.")
sec2.append("  - Tags and small fixes: Theorem 3 also Verified (d = 2..6); proofs added for 7.1, 8.3, 8.5; Proposition 8.4 also")
sec2.append("    Verified; q(y) read in Z[1/2]; spikes signed so weights are +3^(k-1); the real projective plane is a Moebius")
sec2.append("    strip with a disk glued to its edge; the cube total 4 is cited to Heinz's listing.")
out.append(block("2. EXHAUSTIVE CORNER SEARCH (corner_search.c) — completion lines, normalized solutions, 5D probe, corrections", "\n".join(sec2)))

out.append(block("3. ECHOES — echoes.py output (E1-E10)", (HERE / "echoes_output.txt").read_text()))
out.append(block("4. INSIDE AND OUTSIDE — flip_checks.py output (F1-F8)", (HERE / "flip_checks_output.txt").read_text()))
k = (HERE / "knuth_output.txt").read_text()
k += ("\n# reading: about 5.1e17 nodes for the 5D corner search; at the measured 251 917 nodes/s that is roughly 64 000\n"
      "# CPU-years for this one method. Recorded on the page as an ESTIMATE, never as a conclusion.\n")
out.append(block("5. TREE-SIZE ESTIMATES FOR THE OPEN 5D CASE — knuth_estimate.c (Knuth's random-path estimator)", k))
out.append(block("6. INSTRUMENT JAVASCRIPT — headless math checks (node test_math.js)", (HERE / "test_math_output.txt").read_text()))
out.append(block("7. INSTRUMENT PAGE — click-through of every control in jsdom (node test_dom.js ternary-magic-ladder.html)",
                 (HERE / "test_dom_output.txt").read_text()))

for s in sources:
    out.append(block(f"SOURCE — {s}  sha256 {sha(HERE / s)}", (HERE / s).read_text()))

text = "".join(out).rstrip("\n") + "\n"
(HERE / "supplement.txt").write_text(text)
print("wrote", HERE / "supplement.txt", len(text), "chars,", text.count("\n"), "lines")