ogdoad
Clifford algebras — with nilpotents: the quadratic form may be degenerate
(q[i] = 0 ⇒ eᵢ² = 0; all-zero q is the exterior algebra) — over the
commutative scalar worlds adjacent to Conway's combinatorial games. A pure Rust
engine, generic over a Scalar trait, whose backends are the nimbers, the
surreals and surcomplex numbers, and a bench of comparison worlds: p-adics and
Witt vectors, Laurent series, finite fields, the exact global function field
F_q(t), and the plain rationals and integers. On top sit a quadratic-forms
classification layer across the characteristic trichotomy, an integral-lattice
wing, a combinatorial-games pillar, and per-backend Python bindings.
The claim behind the collection is that these worlds are not a curiosity
cabinet. They are cells of one table: read the table one way and you get
Clifford algebras, read it the other way and you get the classification of
quadratic forms, and the same structures keep surfacing cell after cell with
the characteristic and the place politely swapped. The eightfold periodicity of
the real Clifford table, BW(ℝ) ≅ ℤ/8, Bott, E₈ — it is all one spine, and
the code is laid out to make the rhyming visible.
One honest caveat up front, because it shaped everything. Conway's games, under disjunctive sum, form an abelian group but not a ring: you can add games freely, but multiplication only makes sense on the numbers and nimbers hiding inside them. A Clifford algebra demands a commutative ring of scalars. So this is emphatically not "Clifford algebras over all games." It is a generic Clifford engine over the commutative worlds that live next door to game theory, plus a forms layer to classify whatever it builds.
Two readings of one table
Every backend is a cell in a table with two axes:
- place — where a number lives (Archimedean, p-adic, finite, transfinite), and
whether it is a field or a ring of integers. This is how
src/scalar/is grouped. - characteristic — which classification theory applies (char 0 / odd / 2). This
is how
src/forms/is grouped.
The axes are independent. The place axis is what pairs each field with its ring of integers — four such pairs, plus the finite worlds that are their own:
| field | ring of integers | |
|---|---|---|
| Archimedean (char 0) | Rational ℚ |
Integer ℤ |
| transfinite | Surreal (No) |
Omnific (Oz) |
| p-adic (char 0) | Qp, Qq |
Zp, WittVec |
| function field (char p) | RationalFunction F_q(t) |
Poly F_q[t] |
| finite | Fp, Fpn, Nimber |
— (already their own) |
The pairing is structural, not decorative. The HasFractionField / HasRingOfIntegers
trait pair makes ℤ⊂ℚ, Oz⊂No, Zp⊂Qp, W_N⊂Qq, and F_q[t]⊂F_q(t) explicit in the type
system (with ℤ[i]⊂ℚ[i] following for free via the surcomplex transport). The rest of
the local-field furniture is type-level too — the valuation and uniformizer (Valued),
and the residue field k = 𝒪/𝔪 with its angular component and Teichmüller section
(ResidueField) — so the whole package (K, 𝒪, 𝔪, k, Γ, ϖ) lives in the types rather
than the comments.
The symmetries
The project is built around a handful of these rhymes. Each is the same theorem seen twice, once on each side of a mirror.
char 0 ↔ char 2. Classifying a quadratic form is one theorem wearing three hats,
sorted by char F. Over a real-closed field it is the famous 8-fold periodic Cl(p,q)
table, M_n(ℝ/ℂ/ℍ) marching around the Bott clock. Drop to characteristic 2 and the
quadratic form and its polar form file for divorce; the Arf invariant and the
Brauer–Wall group take over custody. On the finite char-2 legs (Nimber, generated
Fpn<2,N>, the documented finite ordinal windows) a nonsingular form carries both the
Arf bit and the BW(F_{2^m}) ≅ ℤ/2 class, under the same XOR law. metric.classify() /
.bw_class() pick the right leg from the scalar type at compile time. Over ℚ, the graded
Brauer–Wall story is separate and exact-sequence-shaped: bw_class_rational records
dimension parity, signed discriminant, and the ungraded Clifford class c(q), with
scalar extension to ℝ recovering the same Bott clock.
No ↔ On₂. The surreals (a char-0 field) and the ordinal nimbers (a char-2
non-field) are the same Cantor-normal-form tower seen in two mirrors — both are
finite-support towers over recursive exponents, sharing one canonicalizer. They differ
in exactly three places: how exponents order, whether coefficients add or XOR, and
what counts as zero. That is why the shared machinery is a function, not a type —
forcing No and On₂ into one type would assert a field equals a non-field. The mirror
reads out again at the games layer: NumberGame (a surreal-valued game) and
NimberGame (a transfinite Nim heap ⋆α) are the two views, one per characteristic.
four ways to grow a field. A 2×2 of (algebraic | transcendental) × (residue-extending | value-extending), and all four corners are filled:
| residue-extending | value-extending | |
|---|---|---|
| algebraic | Surcomplex (adjoin i) |
Ramified (adjoin π = ϖ^{1/e}) |
| transcendental | Gauss (adjoin a unit t) |
Laurent (adjoin a uniformizer t) |
Laurent over a finite field is the equal-characteristic twin of Qp; Ramified is
the ramified twin of the unramified Qq. The separable extensions among these share one
relative trace/norm (FieldExtension) feeding Hilbert symbols, the Brauer–Wall group,
and Hermitian forms; the cyclic-Galois refinement (CyclicGaloisExtension) feeds the
twisted trace form Tr(x·σ^k(x)), which lands back in the classifiers — and over
the nim-fields becomes the Arf-classified Gold form Tr(x^{1+2^a}). The same Galois
data builds Frobenius linear maps in clifford::frobenius, so scalar trace maps and
Clifford outermorphism spectra share one computation.
local ↔ global. Springer's decomposition appears over every complete valued field,
and the value group decides how much survives: over the surreals it is 2-divisible, so
W(No) = W(ℝ) = ℤ, but over Q_p, Q_q, and F_q((t)) it is ℤ, so two residue layers
live (W(Q_p) = W(F_p)²). The discretely-valued legs share one generic engine keyed
on ResidueField; the surreal leg keeps its own, precisely because its value group is
divisible — that mismatch is the symmetry, not a gap. Glue the local data and you get
Hasse–Minkowski over ℚ and Hilbert reciprocity ∏_v (a,b)_v = +1; the per-prime residues
also assemble into Milnor's exact sequence 0 → W(ℤ) → W(ℚ) → ⊕_p W(F_p) → 0. The whole
package re-runs in equal characteristic over F_q(t) — tame Hilbert symbols at every
place, reciprocity, Hasse–Minkowski, the split Milnor map — and there it is exact, no
precision model, the char-p mirror of the ℚ stack. Both global fields answer one
interface: the GlobalField trait, with ℚ and F_q(t) as its two implementors.
the games bridge. Red/blue/green Hackenbush is the showpiece: the same picture reads
out as a surreal (blue − red), a nimber (all-green is Nim), or a general partizan game —
and nim-multiplication itself is realized by Conway's Turning-Corners coin game. The game
pillar even reaches the lattice world: a greedy binary lexicode is built by the
mex rule, so the Conway–Sloane codes are Sprague–Grundy P-sets, feeding straight into
the integral lattices — turning game → mex → lexicode → Golay → Construction A → theta,
one chain across three pillars. And thermography turns out to be tropical arithmetic
in disguise: the option-folds are the tropical ⊕, cooling is the tropical ⊗, and the
two scaffold walls live in the dual (max,+)/(min,+) semirings — named in
scalar/tropical.rs and machine-checked equal to the golden thermograph.
the lattice wing. The mod-8 spine surfaces one more place: integral lattices. E₈ is
the unique rank-8 even unimodular lattice, and from it the wing fans out — discriminant
forms with their Weil S/T matrices and the Brown ℤ/8 invariant; Conway–Sloane
p-adic genus symbols and explicit Kneser neighbors with mass-closed reports; codes
feeding Construction A/D up to BW16 and D16+; ADE roots acting as Clifford Pin
versors and replaying the Weyl reflections; exact theta series identified inside
ℂ[E₄, E₆]; Leech pinned by rootlessness in weight 12; and the 24-class Niemeier
catalogue checking the rank-24 mass against E₁₂ and the 691. Lattice signature, real
Brauer–Wall mod-8 cycle, and Clifford classifier all become directly comparable in the
core.
The char-2 point
This is the load-bearing technical detail, so it gets its own heading. In characteristic 2 the quadratic form and its polar form carry different data, and the engine stores them separately:
e_i^2 = q_i # the quadratic form
e_i e_j + e_j e_i = b_ij # the polar / anticommutator (alternating: b_ii = 0)
For nimbers −1 = 1, so an orthogonal basis (b = 0) gives a commutative Clifford
product; a nonzero off-diagonal b[(i,j)] is what makes a characteristic-2 example
noncommutative. Collapse q and b into one symmetric form and you have silently thrown
away the entire point of the nimber backend. (An optional third field a lifts the
engine to a general, non-symmetric bilinear form.)
The spinor module has its own characteristic-2 route — no ½(1+w) idempotent, blade
idempotents like e_i e_j when they shrink a left ideal, otherwise an honest fallback to
the full left-regular action. In characteristic 0, general-bilinear metrics are handled
by transporting through the antisymmetric a gauge to the matching ordinary (q,b)
metric; characteristic 2 keeps the explicit nonzero-a boundary.
Quickstart
Requires Rust and Python ≥ 3.9.
# characteristic-2 nimber Clifford: non-orthogonal => noncommutative
=
, = ,
* + * # *1 (the anticommutator b[(0,1)])
# surreal metric: infinite and infinitesimal squares are exact
=
** 2 # -1
# the games bridge: Hackenbush reads out as a surreal OR a nimber
, = ,
# a surreal number
# a nimber (all-green = Nim)
# char 0 <-> char 2: a classification on each leg
# Cl(1,3) over R, the 8-fold table
# the char-2 mirror invariant
# the char-2 Brauer-Wall class, if nonsingular
# local <-> global: Hasse-Minkowski + Hilbert reciprocity over Q
# False (anisotropic over Q)
# +1 (reciprocity)
The Python surface is runtime-friendly parity: every backend that is a plain runtime
type is bound, while open-ended const-generic families (arbitrary Qp<P,K>, Qq<P,N,F>,
…) stay Rust-only unless they get an explicit fixed dispatch slice. See
src/py/AGENTS.md for the
full bound surface and the policy.
Prefer no Python? The Rust tour needs none:
Layout
A pure Rust math core, generic over a Scalar trait, with PyO3 per-backend bindings on
top. Each src/ pillar has its own AGENTS.md with the file-by-file breakdown:
src/scalar/— theScalartrait and every coefficient world, grouped by place.src/clifford/— the multivector engine, geometric product, and the GA layer (versors, outermorphisms, Hopf/divided-power structures, conformal/projective GA, spinors, Frobenius maps, including the characteristic-2 nimber spinors).src/forms/— the quadratic-form classifiers across the characteristic trichotomy, plus Witt/Brauer–Wall, the Springer trio,local_global/for Hasse–Minkowski and Hilbert symbols, andintegral/for lattices, genus, Kneser neighbors, Weyl-versor reports, discriminant forms and Weil matrices, codes, theta/modular forms,BW16,D16+, Leech, and the Niemeier catalogue.src/games/— normal-, misère-, and loopy-play impartial games, finite loopy-partizan graphs, short partizan games, thermography/atomic weight, Hackenbush, the exterior algebra of the game group, and the checked integer Clifford deformation surface.src/py/— the optional PyO3 bindings behind thepythonfeature.src/linalg/— crate-private shared linear algebra (exact integer HNF/Smith, F₂/nim rank, generic field solves).
Beside the published crate sits grundy/ — the grundy expression-language crate over the
core (lexer/parser/AST/unparser, fixed-world evaluator, error taxonomy, conformance
corpus). It is an unpublished workspace member (publish = false) while the language is
pre-release; it ships separately when it stabilizes.
See AGENTS.md for the working-notes summary, docs/OPEN.md for the genuine open
problems, the other docs/ ledgers for the cross-pillar bookkeeping, grundy/docs/ for
the language contract, and writeups/ for the draft notes.
The bridges — a traveller's catalog
The pillars are joined by named bridges (summarized in the AGENTS.md files; the
catalog below walks them). Five islands: Scalar, Clifford, Forms (the
classifier core), the Integral wing, Games. Eighteen crossings — Bridge N is four
footbridges — each listed with its banks. A bridge with both feet on one island is a
loop; crossing it counts like any other.
| bridge | banks | what it carries |
|---|---|---|
| A | I–C | even lattice → Clifford metric; bounded FQM Witt class and Milgram phase = signature mod 8 |
clifford-lattices |
C–I | BW16 from Clifford/spinor module rows; Aut(BW16) as the index-2 real Clifford subgroup |
| B | C–F | char-2 Arf/Brauer–Wall classification over the Fpn<2,N> coefficient fields |
| C | S–C | Frobenius/Galois maps as outermorphisms, with flat exterior spectrum |
| D | S–C | Ordinal as a checked Clifford scalar — genuinely transfinite char-2 squares |
| E | I–I | theta series identified in ℂ[E₄,E₆]; the Milnor isospectral pair, executable |
| F | C–F | the rational Clifford invariant c(q) = s(q) + δ(n mod 8, disc), corrected, and its graded BW(ℚ) lift via dimension parity + signed discriminant |
| H | I–I | Construction A: codes ↔ lattices; MacWilliams ↔ the theta transformation |
| I | I–F | the Weil representation of the discriminant form; a third route to σ mod 8 |
| J | S–F | the valuation as (lax) tropicalization; Newton slopes are Springer layers |
| K | S–F | the full ℚ/ℤ cyclic-algebra Brauer invariant, unramified plus tame Kummer; reciprocity over F_q(t) |
| M | F–I | the Brown ℤ/8 invariant — the char-2 cell of the mod-8 spine, float-free |
| N.1 | F–I | Milnor's exact sequence: the Springer residues go global over ℚ and F_q(t) |
| N.2 | S–F | the Scharlau transfer, named and tested |
| N.3 | I–I | Nikulin: genus and existence via signature + discriminant form |
| N.4 | I–I | one Bernoulli source for the Eisenstein constants and the mass formula |
| O | G–I | lexicodes: the turning-game P-set is greedy = mex; the [24,12,8] lexicode is Golay |
game-clifford-checked |
C–G | checked integer Clifford data on game generators; quotient-compatible, not game-native |
(G and L were never built under those letters — they became the deferred stars *1
(spinor genus, docs/COMPLETENESS.md) and *2 (the char-p Drinfeld mirror,
docs/CONTINUATIONS.md). The alphabet still has two pontoons missing;
game-clifford-checked is the later unlettered C–G span and clifford-lattices is the
later unlettered C–I return span.)
The traveller's question (Euler, 1736): can you cross every bridge exactly once and end where you began? Count the bridge-ends per island:
| island | S | C | F | I | G |
|---|---|---|---|---|---|
| degree | 5 | 7 | 8 | 14 | 2 |
An Euler circuit needs every island even. Forms — the island the mod-8 spine runs
through — stays balanced, at degree exactly 8. The Integral wing, long the lone odd
island, is even now too: the clifford-lattices return span (C–I) is the bridge that
balanced it (13 → 14). But the very same span tipped Clifford odd (6 → 7), so the
obstruction did not vanish — it moved. Today the odd islands are Scalar and
Clifford, so an open Euler stroll exists (Scalar → Clifford), but the closed grand
tour still does not. The integral wing, with its four loops (E, H, N.3, N.4), remains the
one place a traveller may wander in circles.
Closing the tour now wants a third Scalar–Clifford span — bridges C and D are the two
it already has. None of the pending threads supplies one: *2 (S–I), the
Drinfeld/Carlitz mirror, would even Scalar but tip the Integral wing odd in turn;
*1 (the spinor genus) and *4 (the wild local symbol) matter on their own terms
but land elsewhere. under is closed rather than pending: the game filtration has
genuine numeric filtered transports, but its [*] 2-torsion and the exact Norton
composition defect prove that it cannot carry the place axis's full dyadic
coefficient object, even in graded initial-form form. It likewise lands elsewhere
on the map. The round trip stays open —
and the obstruction has simply walked from the Integral shore to the Clifford one.
The research threads
The genuine open problems live in docs/OPEN.md, each named by a loopy game
value — an open problem is a game played without a termination guarantee. The
flagship, tis, is not a claim of a new Clifford classification theorem. It is
an investigation of game-built quadratic forms in the nimber backend:
- Turning-Corners games realize nim multiplication.
- Frobenius squaring and traces are built from nim multiplication and XOR.
- Gold-style trace forms
Tr(λ · x^{1+2^a})are therefore expressible from game-value operations. - The Arf invariant gives the standard zero-count bias for a quadratic zero set.
- The open question: is there a natural, non-tautological game rule whose P-positions are exactly such a zero set?
The current frontier (writeups/goldarf.tex): the linear case is both floor and
ceiling — lexicodes show natural rules realize rich linear codes as P-sets,
and Theorem A shows every Winning Ways coin-turning P-set is the kernel of an
F₂-linear map — so tis asks precisely whether that phenomenon admits a
quadratic refinement. A no-go ladder kills the frame-blind tier (Sp(B)-invariant
rules see only orbit unions) and shows the known normal-play realizers are
clocks. The one verified positive object is σ-valued: the echo-fifo+dummy
realizer computes Q as a forced terminal charge, checked exhaustively at
m = 8 (391,680/391,680, adversarial review), and its mechanism reduces to an
odd-close parity game whose isolated-coin linking theorem is machine-verified
on all 12,346 graph classes through k = 8. The two load-bearing open steps:
recast that charge readout into normal/misère/loopy outcome semantics, and prove
the general-m linking theorem.
The rest of the board, briefly:
tisn— a game-native quadratic deformation of the game exterior algebra. The torsion obstruction is now a theorem (integer-valued deformations are blind to torsion:2* = 0forcesQ(*)and all pairings with*to zero), and the surviving escapes are tautological or off-core; what is missing is a directed/noncommutative coefficient source whose squaring remembers the first-/second-player asymmetry — the same obstructiontishit in misère form.on— transfinite nim multiplication beyond the verified excess table. Conway's Kummer carry belowω^(ω^ω)isα_p = κ_{f(p)} + m_pwithm_pLenstra's finite excess; every source-pinned row obeys an unproved0/1/4rule, andwriteups/excess.texreduces that rule exactly to four universal order statements — the zero, ordinary-odd-spine, cubic, and exceptional arms. The next unsupported carry isα₇₁₉.off— what, if anything, replaces the finite Arf/Brauer–Wall bit for Clifford metrics with genuinely transfinite ordinal-nimber coefficients, where no finite trace toF₂exists.over— whether the Brownℤ/8invariant has a game reading the way the Arf bit does: a natural four-class outcome census whose Gauss-sum phase isζ₈^β, lifting the two-class win-bias fromℤ/2toℤ/8.under— resolved 2026-07-20: thermography and the Newton-polygon stack are two tropical objects. Temperature is an honest tropical valuation and every positive dyadic Norton unitu = m/2^ktransports the temperature filtration exactly (gr_τ → gr_{uτ+u−δ}), butgr₀'s[*]2-torsion and the exact Norton composition defect forbid any faithful dyadic-unital graded ring (writeups/thermo_newton.tex).
If you want to play along, the open-problem examples (interactive_kernel, octal_hunt,
loopy_quadric, misere_quotient, bent_route) are the doors in.
Status and limits
Active research code with tests, examples, and experiments. Treat green tests as
regression evidence, not as proof of the mathematical program. CI runs cargo fmt --check, cargo clippy --all-targets (warning-clean), cargo test, cargo check --features python, cargo check --examples, and cargo doc --no-deps.
Scope boundaries, stated plainly:
Nimber(u128)is exactlyF_{2^128}. It holds the nim subfields of degree dividing 128; it is not the proper-class field of all nimbers.Ordinalnim-addition is general on the represented CNF terms, and it implementsScalarfor Clifford experiments inside the checked Kummer boundary. Nim-multiplication works belowω^(ω^ω)whenever every carry uses a verified finite Lenstra excess row (OEIS A380496 b-file, 126 rows, odd primes3..=709); a carry needing a prime past that table (the first unknown is719) returnsNone. Finite ordinal-nimber metrics classify through their detectedF_{2^m}; genuinely transfinite metrics stay outside the classifier.Surrealuses finite support and rational coefficients — the honest truncation of true CNF. Non-monomial inverses are infinite Hahn series and are not represented.Qp,Qq,Laurent,Ramified,Gauss, andAdeleare finite-precision (capped-relative) models, not exact infinite-memory local fields. They are useful for local/global form experiments and excluded from the exact-ring fuzz.ExactScalar/ExactFieldScalar/PrecisionScalarname that boundary explicitly — opt-in markers, notScalarsupertraits.- Fixed-width integer payloads are consistently
u128/i128for arithmetic carriers, residues, invariants, counts, and budgets.usizeis for indices, dimensions, and ABI hooks. - The Gold/Arf game thread is conditional: if a game has P-set
{Q = 0}, Arf predicts the win-bias. No non-tautological natural game with that P-set has been found.
License: AGPL-3.0-or-later (see LICENSE).