src/2-geometry — geometry.py

The validation package of Geometry and Constants in Finite Ring Continuum: one script, geometry.py, 4 blocks, standard library only, one check per predicate of the paper's predicate ledger — 18 of 18 checks passing. The script is rendered below with an anchor per line; its markers (# 2:D1 (p02013): the predicate and its key) are the checks the ledger predicates cite, and the block index opens each block at its definition. The notebook frc-2-geometry.ipynb runs one cell per predicate on Google Colab (its generator make_notebook.py). Installed from this site as a named requirement: pip install frc-2-geometry --find-links https://finitering.space/pkg/ (the sdist frc-2-geometry-20261012.tar.gz).

Blocks: A (9) · B (4) · C (3) · D (5) — the predicates each decides in parentheses.

2-geometry ledgersrc/2-geometry/geometry.py · 26.5 KBgeometry.pyColab
1"""2geometry.py — the validation package of "Geometry and Constants in Finite Ring Continuum" (Akhtman, Symmetry 2026, 18, 751;3doi 10.3390/sym18050751), the paper 2-geometry of the FRC corpus (finite-ring-space/src/2-geometry), added with the paper's4predicate ledger (Appendix A, 17 September 2026, from the corpus script validation/verify_geometry.py; one script since524 September 2026, the four block scripts merged).6========================================================================================================================78One script, four blocks, eighteen checks, standard library only. Each check names the predicate(s) of the paper's ledger it9witnesses (LEDGER below; predicates cited as 2:XN) under a `# 2:XN (<key>)` marker, the key the predicate's accession key,, and the ledger's source column links the10marker of the check that decides each predicate (PREDICATES below; finitering.space/src/2-geometry/#<key>).11Where a predicate is proved in Lean (lean/FrcLedger/Geometry.lean on Mathlib, or lean/FrcCore with no axioms), the check here12is the instance the reader can run; the witnesses decide the same statements at different generality.1314    python3 geometry.py           every block, results.json written; exit 1 if a check fails (≈ 2 s)15    python3 geometry.py C         one block (A, B, C or D); no results.json16    from frc_2_geometry import predicate; predicate("2:D1")    one predicate: its block runs once per session1718Blocks:  A  the Euclidean datum and the involutions                 EXACT          (2:B3, D1–D8)19         B  the orbital shell and its spherical completion          EXACT          (2:B4, C2–C4)20         C  the external spherical comparison                       CHART / EXACT  (2:E2–E4)21         D  Fourier duality on the phase cycle                      EXACT / CHART  (2:F1, F3–F6)2223Everything the blocks share:24  * the shell datum: p = 4κ+1 prime, the primitive generators of F_p, the oriented quarter-turn i = −g^κ,25    the half-period π = 2κ, the phase cycle of length n = p − 1;26  * exact arithmetic in F_p (python int, reduced mod p) and in Q (fractions.Fraction);27  * the PASS/FAIL registry every block reports into (results.json).2829Kinds: EXACT checks are integer-pinned computations in F_p or exact rationals (a pass is a proof on the tested30instances); CHART checks decide a [chart] predicate — the reading of the shell against R or C — in floats with the31closed forms checked.32"""33import os, json, sys, math, random34from collections import Counter, defaultdict35from fractions import Fraction3637SCRIPT = os.path.splitext(os.path.basename(__file__))[0]        # "geometry": the one script, the name results.json and the site pages carry3839# The shells (p, κ): p = 4κ+1 prime.40SHELLS = [(5, 1), (13, 3), (17, 4), (29, 7), (37, 9), (41, 10)]4142# ----------------------------------------------------------------------------- registry43RESULTS = []4445# The paper's predicate ledger (Appendix A, predicates cited as 2:XN): the predicate(s) each check witnesses.46LEDGER = {47    "A1": "2:D1, 2:D2, 2:D3, 2:D4", "A2": "2:B3", "A3": "2:D5", "A4": "2:D6", "A5": "2:D7", "A6": "2:D8",48    "B1": "2:C2", "B2": "2:C3", "B3": "2:B4, 2:C4",49    "C1": "2:E2", "C2": "2:E3", "C2b": "2:E3", "C3": "2:E4",50    "D1": "2:F1", "D2": "2:F3", "D3": "2:F4", "D4": "2:F5", "D5": "2:F6",51}5253BLOCK = {"A": "the Euclidean datum and the involutions",                 # check-id prefix -> the block (the function block_<letter> below)54         "B": "the orbital shell and its spherical completion",55         "C": "the external spherical comparison",56         "D": "Fourier duality on the phase cycle"}5758# the deciding check of each witnessed predicate: the one whose verdict decides the predicate's statement (the other checks that59# touch it are corroboration, listed by predicate() from the records)60PREDICATES = {61    "2:B3": "A2", "2:B4": "B3",62    "2:C2": "B1", "2:C3": "B2", "2:C4": "B3",63    "2:D1": "A1", "2:D2": "A1", "2:D3": "A1", "2:D4": "A1", "2:D5": "A3", "2:D6": "A4", "2:D7": "A5", "2:D8": "A6",64    "2:E2": "C1", "2:E3": "C2", "2:E4": "C3",65    "2:F1": "D1", "2:F3": "D2", "2:F4": "D3", "2:F5": "D4", "2:F6": "D5",66}67_RAN = set()                                            # blocks already run in this session (predicate() runs each once)6869def check(pid, label, ok, detail="", kind="EXACT"):70    """Record one predicate check. pid = package check id; LEDGER[pid] = the paper statement(s) decided."""71    ok = bool(ok)72    rows = LEDGER.get(pid, "")73    RESULTS.append({"id": pid, "rows": rows, "block": pid[0], "script": SCRIPT, "label": label, "ok": ok, "detail": detail, "kind": kind})74    print(f"  [{'PASS' if ok else 'FAIL'}] {pid:4s} {kind:6s} [{rows}] {label}" + (f"  --  {detail}" if detail else ""))75    return ok7677def summary(write=True):78    n_ok = sum(r["ok"] for r in RESULTS)79    print(f"\nSUMMARY: {n_ok}/{len(RESULTS)} checks passed" + ("" if n_ok == len(RESULTS) else "  <-- FAILURES"))80    if write:81        with open("results.json", "w") as f:82            json.dump(RESULTS, f, indent=1)83    return n_ok == len(RESULTS)8485def markers():86    """predicate label -> (script file, line) of its marker `# <paper>:<label> (<key>)`: the line of the check that decides it."""87    import re88    out = {}89    for i, line in enumerate(open(os.path.abspath(__file__), encoding="utf-8"), 1):90        m = re.match(r"\s*# ((?:\d+:[A-Z]+\d+[a-z]?(?: \(p\d{5}\))?)(?:, \d+:[A-Z]+\d+[a-z]?(?: \(p\d{5}\))?)*)\s*$", line)91        if m:92            for lab in re.findall(r"\d+:[A-Z]+\d+[a-z]?", m.group(1)): out.setdefault(lab, (SCRIPT + ".py", i))93    return out9495def _run_block(letter):96    if letter not in _RAN:97        globals()[f"block_{letter}"](); _RAN.add(letter)9899def predicate(label, lines=14):100    """Verify one ledger predicate: run the block of the check that decides it (once per session), print that check's source101    (from its marker) and every record that cites the predicate, and return True iff all pass."""102    pid = PREDICATES.get(label)103    if pid is None:104        print(f"{label}: no python witness (see the predicate's Lean witness or its source)"); return None105    citing = {i[0] for i, rows in LEDGER.items() if label in [t.strip() for t in rows.split(",")]}106    for b in sorted({pid[0]} | citing): _run_block(b)          # the deciding block and every block whose checks cite the predicate107    mk = markers().get(label)108    if mk:109        src = open(os.path.abspath(__file__), encoding="utf-8").read().split("\n")110        print(f"— {mk[0]}:{mk[1]} (the check that decides {label}: {pid})")111        for j in range(mk[1] - 1, min(mk[1] - 1 + lines, len(src))): print(f"{j + 1:5d}  {src[j]}")112    recs = [r for r in RESULTS if label in [t.strip() for t in r["rows"].split(",")]]113    ok = all(r["ok"] for r in recs)114    for r in recs:115        role = "(deciding)" if r["id"] == pid else "(corroborating)"116        print(f"  [{'PASS' if r['ok'] else 'FAIL'}] {r['id']:4s} {role:16s} {r['detail'][:150]}")117    print(f"{label}: {'VERIFIED' if ok and recs else 'FAILED'} — {len(recs)} record(s)")118    return ok119120def verify_all():121    """Run every block (those already run in this session are not re-run) and print the summary; True iff every check passed."""122    for b in sorted(BLOCK): _run_block(b)123    return summary(write=False)124125# ----------------------------------------------------------------------------- the shell datum126def is_prime(n):127    return n > 1 and all(n % q for q in range(2, int(n ** .5) + 1))128129def order(x, p):130    """The multiplicative order of x in F_p^x."""131    k, y = 1, x % p132    while y != 1:133        y = y * x % p; k += 1134    return k135136def generators(p):137    """The primitive generators of F_p^x, in increasing order."""138    return [g for g in range(2, p) if order(g, p) == p - 1]139140def units(n):141    """The units of Z_n: u in [1, n) coprime to n."""142    return [u for u in range(1, n) if math.gcd(u, n) == 1]143144def kappa(p):145    assert is_prime(p) and p % 4 == 1, p146    return (p - 1) // 4147148def quarter_turn(p, g):149    """The oriented quarter-turn i = −g^κ (00:C7, 2:A2)."""150    return (-pow(g, kappa(p), p)) % p151152for _p, _k in SHELLS:153    assert is_prime(_p) and _p == 4 * _k + 1, (_p, _k)154155# ------------------------------------------------------------------------------------------------------------156# block A: the Euclidean datum and the involutions (2:B3, D1–D8)157# Section 2 (the frame) and Section 4 (symmetry axes and structural data) of the paper, on the shells158# p ∈ {5, 13, 17, 29, 37, 41} and every primitive generator.  \label(s): prop:half-period,159# def:quarter-turn-subgroup, prop:quarter-turn, rem:observer-canonical-e, rem:exponential-unit (A1);160# prop:primitive-orbit, prop:no-canonical-e (A2); rem:frame-transport (A3); the Euler identity of 00:C14 (A4);161# prop:shell-negation, prop:k4-shell (A5); rem:euclidean-conjugation (A6).162def block_A():163    """Block A — the Euclidean datum and the involutions (EXACT): A1–A6."""164    # A1 half-period, quarter-turn of order four, i = −g^κ with i² = −1, 2π = −1, e = g^i (2:D1–D4)165    ok = True; det = []166    for p, k in SHELLS:167        pi = 2 * k168        for g in generators(p):169            i = quarter_turn(p, g)170            ok &= pow(g, pi, p) == p - 1 and (i * i) % p == p - 1 and pow(g, 4 * k, p) == 1 and order(pow(g, k, p), p) == 4171            ok &= (2 * pi) % p == p - 1172            Q = {x for x in range(1, p) if pow(x, 4, p) == 1}173            ok &= Q == {1, p - 1, i, (-i) % p}174        g = generators(p)[0]; i = quarter_turn(p, g)175        det.append(f"p={p}: g={g}, i={i}, e=g^i={pow(g, i, p)}, pi={pi}")176    # 2:D1 (p02013), 2:D2 (p02014), 2:D3 (p02015), 2:D4 (p02016)177    check("A1", "half-period g^pi = -1; i = -g^k of order 4 with i^2 = -1; Q_p = {±1, ±i}; 2pi = -1; e = g^i", ok, "; ".join(det[:3]))178179    # A2 the primitive generators form one orbit g^u, u a unit mod p−1 (2:B3)180    ok = True181    for p, k in SHELLS:182        gens = set(generators(p)); g = min(gens)183        orbit = {pow(g, u, p) for u in units(p - 1)}184        ok &= orbit == gens and len(gens) == len(units(p - 1))185    # 2:B3 (p02007)186    check("A2", "generators = {g^u : gcd(u, p-1) = 1}, one Aut(C_{p-1})-torsor", ok)187188    # A3 orientation classes: i' = i iff u ≡ 1 (mod 4), i' = −i iff u ≡ 3 (mod 4) (2:D5)189    ok = True; det = []190    for p, k in SHELLS:191        g = generators(p)[0]; i = quarter_turn(p, g)192        for u in units(p - 1):193            ip = quarter_turn(p, pow(g, u, p))194            ok &= (ip == i) == (u % 4 == 1) and (ip == (-i) % p) == (u % 4 == 3)195        same = sum(1 for u in units(p - 1) if u % 4 == 1)196        det.append(f"p={p}: {same} of {len(generators(p))} generators keep i")197    # 2:D5 (p02017)198    check("A3", "orientation classes of the quarter-turn under g -> g^u: kept for u = 1, flipped for u = 3 (mod 4)", ok, "; ".join(det))199200    # A4 the Euler identity e^{iπ} = g^{2κ i²} = (−1)^i, −1 iff the residue i is odd (2:D6, 00:C14)201    ok = True; det = []202    for p, k in SHELLS:203        for g in generators(p):204            i = quarter_turn(p, g); e = pow(g, i, p)205            ok &= pow(e, i * 2 * k, p) == (p - 1 if i % 2 else 1)206        odd = [g for g in generators(p) if quarter_turn(p, g) % 2 == 1]207        det.append(f"p={p}: i odd for g in {odd[:6]}{'...' if len(odd) > 6 else ''} ({len(odd)}/{len(generators(p))})")208    ok &= quarter_turn(13, 2) == 5 and quarter_turn(17, 3) == 4 and quarter_turn(17, 6) == 13209    # 2:D6 (p02018)210    check("A4", "Euler identity e^{i pi} = (-1)^i with i the residue representative; F13 g=2 odd, F17 g=3 even", ok, "; ".join(det[:3]))211212    # A5 negation/inversion orbits: F_p^x \ Q_p splits into κ−1 orbits of size four (2:D7, 1:B2)213    ok = True214    for p, k in SHELLS:215        Q = {x for x in range(1, p) if pow(x, 4, p) == 1}216        seen, orbits = set(), 0217        for x in range(1, p):218            if x in Q or x in seen: continue219            orb = {x, (-x) % p, pow(x, -1, p), (-pow(x, -1, p)) % p}220            ok &= len(orb) == 4; seen |= orb; orbits += 1221        ok &= len(Q) == 4 and orbits == k - 1222        R = lambda x: (-x) % p; I = lambda x: pow(x, -1, p)223        ok &= all(R(R(x)) == x and I(I(x)) == x and R(I(x)) == I(R(x)) for x in range(1, p))224    # 2:D7 (p02019)225    check("A5", "R, I commuting involutions; <R,I>-orbits off Q_p have four elements, k-1 of them", ok)226227    # A6 Euclidean conjugation: an involution of pairs; the pair map (a, b) -> a + b i is p-to-one (2:D8)228    ok = True229    for p, k in SHELLS:230        g = generators(p)[0]; i = quarter_turn(p, g)231        fibres = Counter((a + b * i) % p for a in range(p) for b in range(p))232        ok &= set(fibres.values()) == {p} and len(fibres) == p233        C = lambda a, b: (a, (-b) % p)234        ok &= all(C(*C(a, b)) == (a, b) for a in range(p) for b in range(p))235        a, b = 0, 1; a2, b2 = i, 0        # 0 + 1·i = i + 0·i, but the conjugates differ: not a map of the field236        ok &= (a + b * i) % p == (a2 + b2 * i) % p and (a - b * i) % p != (a2 - b2 * i) % p237    # 2:D8 (p02020)238    check("A6", "conjugation is an involution of the pairs, not of the field (the pair map is p-to-one)", ok)239240# ------------------------------------------------------------------------------------------------------------241# block B: the orbital shell and its spherical completion (2:B4, C2, C3, C4)242# Section 3 of the paper: the complex S_p of def:orbital-shell coded as vertices, edges and faces; the243# completion (the terminal latitude collapsed to S); the counts of rem:cell-counts (B1); closedness — every edge244# in two faces, every vertex link one cycle — of thm:combinatorial-sphere, exhaustive on p ∈ {5, 13, 17, 29} (B2);245# the cellular automorphisms: the reindexing ρ_u is cellular only for u = ±1, the dihedral maps are, the meridian246# reversal is a map of the completion only (prop:phase-frame-change, prop:shell-covariance; B3).247B_SHELLS = (5, 13, 17, 29)                    # the shells of block B (the package's SHELLS carry κ)248249def shell(p):250    """The orbital shell S_p of Def. 3.1: vertices, edges, faces; and its completion."""251    k = (p - 1) // 4; pi = 2 * k; n = p - 1252    V = ["N"] + [(a, m) for a in range(1, pi + 1) for m in range(n)]253    E = set()254    for m in range(n):255        E.add(frozenset(["N", (1, m)]))256        for a in range(1, pi): E.add(frozenset([(a, m), (a + 1, m)]))257        for a in range(1, pi + 1): E.add(frozenset([(a, m), (a, (m + 1) % n)]))258    F = []259    for m in range(n):260        F.append(("N", (1, m), (1, (m + 1) % n)))261        for a in range(1, pi): F.append(((a, m), (a + 1, m), (a + 1, (m + 1) % n), (a, (m + 1) % n)))262    col = lambda v: "S" if (v != "N" and v[0] == pi) else v      # the completion: L_pi collapsed to S263    Vc = sorted({col(v) for v in V}, key=str)264    Ec = {frozenset(map(col, e)) for e in E if len({col(v) for v in e}) == 2}265    Fc = []266    for f in F:267        cf = []268        for v in map(col, f):269            if v not in cf: cf.append(v)270        Fc.append(tuple(cf))271    return V, E, F, Vc, Ec, Fc272273def closed_surface(V, E, F):274    """Every edge in exactly two faces; every vertex link a single cycle."""275    edge_faces = defaultdict(list)276    for fi, f in enumerate(F):277        for j in range(len(f)): edge_faces[frozenset([f[j], f[(j + 1) % len(f)]])].append(fi)278    if set(edge_faces) != set(E) or any(len(v) != 2 for v in edge_faces.values()): return False279    for v in V:280        around = [fi for fi, f in enumerate(F) if v in f]281        adj = defaultdict(set)282        for fi in around:283            f = F[fi]; j = f.index(v)284            for w in (f[(j - 1) % len(f)], f[(j + 1) % len(f)]):285                for fj in edge_faces[frozenset([v, w])]:286                    if fj != fi: adj[fi].add(fj)287        if any(len(s) != 2 for s in adj.values()): return False288        seen, stack = set(), [around[0]]289        while stack:290            x = stack.pop()291            if x in seen: continue292            seen.add(x); stack.extend(adj[x])293        if seen != set(around): return False294    return True295296def block_B():297    """Block B — the orbital shell and its spherical completion (EXACT): B1–B3."""298    # B1 the counts and the Euler characteristics (2:C2)299    ok = True; det = []300    for p in B_SHELLS:301        k = (p - 1) // 4; pi = 2 * k; n = p - 1302        V, E, F, Vc, Ec, Fc = shell(p)303        ok &= (len(V), len(E), len(F)) == (pi * n + 1, 2 * pi * n, pi * n) and len(V) - len(E) + len(F) == 1304        ok &= (len(Vc), len(Ec), len(Fc)) == ((pi - 1) * n + 2, (2 * pi - 1) * n, pi * n) and len(Vc) - len(Ec) + len(Fc) == 2305        det.append(f"p={p}: |V|,|E|,|F| = {len(V)},{len(E)},{len(F)}, chi=1; completion {len(Vc)},{len(Ec)},{len(Fc)}, chi=2")306    # 2:C2 (p02010)307    check("B1", "cell counts of S_p and of its completion, chi = 1 and chi = 2 (Remark 3.4)", ok, "; ".join(det))308309    # B2 the completion is a closed surface (chi = 2, a sphere); S_p itself is not closed (2:C3)310    ok = True; det = []311    for p in B_SHELLS:312        V, E, F, Vc, Ec, Fc = shell(p)313        closed = closed_surface(Vc, Ec, Fc)314        ok &= closed and not closed_surface(V, E, F)315        det.append(f"p={p}: completion closed={closed}, S_p closed=False")316    # 2:C3 (p02011)317    check("B2", "the completion is a closed surface with chi = 2 (a sphere) by exhaustive incidence; S_p has a boundary", ok, "; ".join(det))318319    # B3 cellular automorphisms: rho_u iff u = ±1; the dihedral maps; meridian reversal on the completion only (2:B4, 2:C4)320    ok = True; det = []321    for p in B_SHELLS:322        k = (p - 1) // 4; pi = 2 * k; n = p - 1323        V, E, F, Vc, Ec, Fc = shell(p)324        faces = lambda FF, f: sorted(Counter(tuple(sorted(map(str, map(f, x)))) for x in FF).items())325        cellular = lambda f, EE, FF: {frozenset(map(f, e)) for e in EE} == EE and faces(FF, f) == faces(FF, lambda v: v)326        good = []327        for u in units(n):328            rho = lambda v: v if v in ("N", "S") else (v[0], v[1] * u % n)329            if cellular(rho, E, F): good.append(u)330            ok &= cellular(rho, E, F) == (u in (1, n - 1)) and cellular(rho, Ec, Fc) == (u in (1, n - 1))331        for c in range(n):332            for s_ in (1, -1):333                dih = lambda v: v if v in ("N", "S") else (v[0], (s_ * v[1] + c) % n)334                ok &= cellular(dih, E, F)335        sig = lambda v: "S" if v == "N" else "N" if v == "S" else (pi - v[0], v[1])336        ok &= cellular(sig, Ec, Fc)337        ok &= sum(1 for v in V if v == "N") == 1 and sum(1 for v in V if v != "N" and v[0] == pi) == n338        det.append(f"p={p}: rho_u cellular only for u in {good} of {len(units(n))} units")339    # 2:B4 (p02008), 2:C4 (p02012)340    check("B3", "rho_u (m -> um) is cellular iff u = ±1; the dihedral maps are; meridian reversal is a map of the completion only", ok, "; ".join(det))341342# ------------------------------------------------------------------------------------------------------------343# block C: the external spherical comparison (2:E2, E3, E4)344# Section 5 of the paper.  C1 (CHART): the base-grid covering radius of the comparison map Xi_{13,g} against the345# bound sqrt2·pi/(p−1) of prop:grid-density.  C2 (EXACT, rationals): the fixed-shell scale grid {x/g^n : |x| ≤ 2κ}346# has a covering radius in [0, 1] bounded below by ½ min(g^−m, 1 − 2κ g^−(m+1)) at every depth — the bounded347# precision of a fixed shell, prop:fixed-shell-density and thm:operational-precision (C2b: the covering radius348# exceeds ε = 1/20 on every tested shell).  C3 (CHART): across the tower of shells the grids resolve every target (1:D7).349def fib_sphere(N):350    pts = []351    ga = math.pi * (3 - math.sqrt(5))352    for j in range(N):353        z = 1 - 2 * (j + .5) / N; r = math.sqrt(1 - z * z); t = ga * j354        pts.append((r * math.cos(t), r * math.sin(t), z))355    return pts356357def geod(u, v): return math.acos(max(-1., min(1., u[0] * v[0] + u[1] * v[1] + u[2] * v[2])))358359def fixed_shell_radius(p, g, N, H=None):360    """Covering radius in [0,1] of {x/g^n : 0 <= x <= H, n <= N}; H = 2κ (the balanced window) by default."""361    k = (p - 1) // 4; H = 2 * k if H is None else H362    pts = sorted({Fraction(x, g ** n) for n in range(N + 1) for x in range(0, H + 1) if x <= g ** n})363    return max(b - a for a, b in zip(pts, pts[1:])) / 2364365def lower_bound(p, g, H=None):366    """½ min(g^−m, 1 − H g^−(m+1)) with m = floor(log_g(H+1)): the cut of lean fixed_shell_bound."""367    k = (p - 1) // 4; H = 2 * k if H is None else H; m = 0368    while g ** (m + 1) <= H + 1: m += 1369    return min(Fraction(1, g ** m), 1 - Fraction(H, g ** (m + 1))) / 2370371def tower_best(t, shells, N=12):372    best = 1.373    for p, g in shells:374        k = (p - 1) // 4375        for n in range(N + 1):376            for x in range(0, 2 * k + 1):377                best = min(best, abs(t - x / g ** n))378    return best379380def block_C():381    """Block C — the external spherical comparison (C1, C3 CHART; C2 EXACT rationals): C1–C3."""382    # C1 the base-grid covering radius at p = 13 within sqrt2·pi/(p−1) (2:E2) — chart383    p = 13; pi = 6; n = 12384    grid = [(0., 0., 1.), (0., 0., -1.)]385    for a in range(1, pi + 1):386        th = math.pi * a / pi387        for m in range(n):388            ph = 2 * math.pi * m / n389            grid.append((math.sin(th) * math.cos(ph), math.sin(th) * math.sin(ph), math.cos(th)))390    cov = max(min(geod(x, y) for y in grid) for x in fib_sphere(60000))391    bound = math.sqrt(2) * math.pi / (p - 1)392    # 2:E2 (p02022)393    check("C1", "base-grid covering radius at p = 13 within the bound sqrt2 pi/(p-1)", cov <= bound,394             f"measured {cov:.4f} rad, bound {bound:.4f} rad", kind="CHART")395396    # C2 the fixed-shell refinement is bounded (2:E3) — exact rationals397    ok = True; det = []398    for p, g in [(13, 2), (13, 11), (13, 6), (17, 3), (29, 2), (173, 2)]:399        radii = [fixed_shell_radius(p, g, N) for N in (3, 6, 12, 24)]400        lb = lower_bound(p, g)401        radii_p = [fixed_shell_radius(p, g, N, p - 1) for N in (12, 24)]; lb_p = lower_bound(p, g, p - 1)402        ok &= all(r >= lb for r in radii) and radii[-1] == radii[-2] and all(r >= lb_p for r in radii_p) and radii_p[0] == radii_p[1]403        det.append(f"({p},{g}): radius {radii[-1]} >= {lb}")404    # 2:E3 (p02023)405    check("C2", "fixed-shell covering radius stabilises at >= 1/2 min(g^-m, 1 - H g^-(m+1)) for every depth (H = 2k and H = p-1)", ok, "; ".join(det[:4]))406    eps = Fraction(1, 20)407    viol = [(p, g) for p, g in [(13, 2), (13, 11), (17, 3), (29, 2)] if fixed_shell_radius(p, g, 40) > eps]408    check("C2b", "fixed-shell density at eps = 1/20: the covering radius exceeds 1/20 on every tested shell", viol == [(13, 2), (13, 11), (17, 3), (29, 2)],409             f"shells with covering radius > 1/20 at N = 40: {viol}")410411    # C3 the tower of shells resolves every target (2:E4, 1:D7) — chart412    targets = [0.9, 1 / 3, math.pi / 4, math.sqrt(2) - 1]413    tower = [(p, 2) for p in (13, 29, 173, 1013, 4093)]414    errs = [tower_best(t, tower) for t in targets]415    single = [tower_best(t, [(13, 2)]) for t in targets]416    # 2:E4 (p02024)417    check("C3", "the tower of shells (p = 13..4093, g = 2) brings every target within 1/2048; the single shell (13, 2) does not",418             all(e <= 1 / 2048 for e in errs) and max(single) > 1 / 16,419             "tower errors " + ", ".join(f"{e:.2e}" for e in errs) + "; single shell " + ", ".join(f"{e:.3f}" for e in single), kind="CHART")420421# ------------------------------------------------------------------------------------------------------------422# block D: Fourier duality on the phase cycle (2:F1, F3, F4, F5, F6)423# Section 6 of the paper, on (13, 2), (13, 11), (17, 3), (29, 2): the principal root (prop:principal-root, D1);424# the inversion W^−1 = −(g^−jk) (prop:ring-dft-inverse, D2); the polynomial reading F(v)_k = P_v(g^k)425# (prop:polynomial-form, D3); the covariance under g → g^u (prop:ring-dft-covariance, D4); and the external426# transport chi(g^m) = exp(−2 pi i m/n) with its step, half-period and quarter-turn identities427# (prop:datum-role-transport, D5; CHART).428FRAMES = [(13, 2), (13, 11), (17, 3), (29, 2)]429430def dft_checks(p, g):431    n = p - 1; k = (p - 1) // 4432    W = [[pow(g, j * kk, p) for j in range(n)] for kk in range(n)]433    Winv = [[(-pow(g, -j * kk, p)) % p for j in range(n)] for kk in range(n)]434    mul = lambda A, B: [[sum(A[r][t] * B[t][c] for t in range(n)) % p for c in range(n)] for r in range(n)]435    I = [[int(r == c) for c in range(n)] for r in range(n)]436    d1 = pow(g, n, p) == 1 and all(sum(pow(g, j * kk, p) for j in range(n)) % p == 0 for kk in range(1, n)) and (n * (p - 1)) % p == 1437    d2 = mul(W, Winv) == I and mul(Winv, W) == I438    rnd = random.Random(p)439    v = [rnd.randrange(p) for _ in range(n)]440    Fv = [sum(v[j] * pow(g, j * kk, p) for j in range(n)) % p for kk in range(n)]441    Pv = lambda x: sum(v[j] * pow(x, j, p) for j in range(n)) % p442    d3 = all(Fv[kk] == Pv(pow(g, kk, p)) for kk in range(n))443    d4 = True444    for u in units(n):445        gp = pow(g, u, p); vp = [v[u * j % n] for j in range(n)]446        Fvp = [sum(vp[j] * pow(gp, j * kk, p) for j in range(n)) % p for kk in range(n)]447        d4 &= Fvp == Fv448    chi = lambda m: complex(math.cos(-2 * math.pi * m / n), math.sin(-2 * math.pi * m / n))449    i = quarter_turn(p, g); li = next(m for m in range(n) if pow(g, m, p) == i)450    d5 = all(abs(chi(m + 1) - chi(1) * chi(m)) < 1e-12 and abs(chi(m + 2 * k) + chi(m)) < 1e-12451             and abs(chi(m + li) - 1j * chi(m)) < 1e-12 for m in range(n))452    d5 &= len({(round(chi(m).real, 9), round(chi(m).imag, 9)) for m in range(n)}) == n453    return d1, d2, d3, d4, d5454455def block_D():456    """Block D — Fourier duality on the phase cycle (EXACT; D5 CHART): D1–D5."""457    ok = [True] * 5458    for p, g in FRAMES:459        d = dft_checks(p, g); ok = [a and b for a, b in zip(ok, d)]460    # 2:F1 (p02025)461    check("D1", "g a principal root: g^n = 1, sum_j g^{jk} = 0 for 0 < k < n, n^-1 = -1", ok[0])462    # 2:F3 (p02027)463    check("D2", "inversion W^-1 = -(g^{-jk}) on (13, 2), (13, 11), (17, 3), (29, 2)", ok[1])464    # 2:F4 (p02028)465    check("D3", "polynomial reading F(v)_k = P_v(g^k)", ok[2])466    # 2:F5 (p02029)467    check("D4", "covariance F_{g^u}(v') = F_g(v) with v'_j = v_{uj}", ok[3])468    # 2:F6 (p02030)469    check("D5", "external transport chi(g^m) = exp(-2 pi i m/n), injective: step, half-period, quarter-turn", ok[4], kind="CHART")470471if __name__ == "__main__":472    import time473    want = [a.upper() for a in sys.argv[1:]] or sorted(BLOCK)474    bad = [b for b in want if b not in BLOCK]475    if bad: sys.exit(f"no block {', '.join(bad)}: the blocks are {', '.join(sorted(BLOCK))}")476    t0 = time.time()477    for b in want:478        t = time.time(); _run_block(b); print(f"    [block {b}: {time.time() - t:.1f} s]")479    ok = summary(write=(want == sorted(BLOCK)))480    kinds = {}481    for r in RESULTS: kinds[r["kind"]] = kinds.get(r["kind"], 0) + 1482    print("by kind: " + ", ".join(f"{k} {v}" for k, v in sorted(kinds.items())) + f"; {len(RESULTS)} checks in {time.time() - t0:.1f} s" + ("; results.json written" if want == sorted(BLOCK) else ""))483    sys.exit(0 if ok else 1)484