"""RUN-064 - BCODR LUC 02: the atomic cut of LUC 01's frontier and its strict recomposition.

BCODR-LUC-02 (2026-09-21) cuts LUC 01's nexus (131) and its eight most balanced finite-radius cells in two along every
dimension, 2^5 = 32 children each and 288 in all, and signs every child by the fields that reach it at its parent's
meeting radius t_B = max(T+, T-): BOTH, PLUS_ONLY, MINUS_ONLY or UNREACHED. It reports 187 / 88 / 4 / 9; root 131 with no
BOTH child (a "coarse bicontact container"); 17 classes after a strict merge (same signature, rho class and prune
provenance, adjacent closures), 7 of them BOTH; a provenance flip at 127 (144 -> 155) and at 101 (28 -> 144); and, with
the nine BOTH children cut again, one BOTH grandchild each except 127's nine, "a thick / branched interaction corridor".
Here the cut is put on the exact dyadic grid, T- is exact (rational) and T+ is computed to 50 digits, so that a tie is
a tie.

Checks
  A0 anchors        the statements below occur in the document as written
  W1 cut            the nine parents are LUC 01's nexus and top eight, with LUC 01's radii; each child is its parent halved
                    in every dimension
  W2 signatures     sec. 4-7: every child's T+, T-, nearest prune box and signature, and how far each is from a tie
  W3 ties           at the parent's radius the field that arrives last only touches the parent: a child it reaches holds
                    the parent's nearest point(s) on that side
  W4 provenance     sec. 11, 17-19: 127 against 144 and 155; 101 against 28 and 144
  W5 recomposition  sec. 13-16, 24: the 17 classes and the seven BOTH classes, and under which reading of the rule
  W6 persistence    sec. 20-23, 29: the second cut, and what makes 127's broader
  W7 records        the ledger, CSV, recomposition, persistence, state and graph against each other and the rebuild
  W8 verifier       the verifier, read after this rebuild and never run
  P1 package        the VALIDATION record against the bytes and the archive

Usage: python code/run064_bcodr_luc02.py [--drill] [--blind CID]
"""
import argparse
import collections
import copy
import csv
import hashlib
import inspect
import io
import itertools
import json
import re
from fractions import Fraction

import mpmath as mp
import numpy as np

import gate
import run048_uesfcm_cycles_12_14 as r48
import run063_bcodr_luc01 as r63
from corpus import SOURCES, TREE, source_bytes, walk_sources

PKG = "main.zip!/BCODR/BCODR_LUC_02_Package_v0.1.zip!/"
DOC = PKG + "BCODR_LUC_02_Atomic_Cut_and_Structural_Recomposition_v0.1.md"
CUT = PKG + "BCODR_LUC_02_Atomic_Cut_Ledger_v0.1.json"
CSV = PKG + "BCODR_LUC_02_Atomic_Children_v0.1.csv"
RECOMP = PKG + "BCODR_LUC_02_Structural_Recomposition_v0.1.json"
PERSIST = PKG + "BCODR_LUC_02_Overlap_Persistence_v0.1.json"
GRAPH = PKG + "BCODR_LUC_02_First_Surviving_Spine_v0.1.dot"
PNG = PKG + "BCODR_LUC_02_First_Surviving_Spine_v0.1.png"
VERIFIER = PKG + "BCODR_LUC_02_Verify_Cut_Recompose.py"
STATE = PKG + "BCODR_LUC_02_STATE_v0.1.json"
VALIDATION = PKG + "BCODR_LUC_02_VALIDATION.json"
LOG = TREE / "data" / "gate-logs" / "run064-bcodr-luc02.json"
LOG63 = TREE / "data" / "gate-logs" / "run063-bcodr-luc01.json"
MANIFEST = TREE / "data" / "SOURCE_MANIFEST.v1.json"
CACHE = TREE / "data" / "cache" / "run064-searches.json"     # data/cache is not committed
X30, S, X5 = -0.01215883209899, 0.00882, 0.019770946347947549
U, V = 5.157126245268359, -0.18922818990664067               # Paper 09B's critical velocity, as LUC 01's ledger carries it
PRUNE = [28, 36, 68, 144, 155]
PARENTS = [131, 166, 167, 164, 165, 127, 101, 104, 103]
K = 2 ** 22                                                  # units a side of root 6217's box: two halvings below the finest subroot
DPS = 50
TIE = "1e-30"                                                # below this a difference of two 50-digit distances is a tie


def load_inputs():
    blobs = {n: source_bytes(n) for n in SOURCES}
    want = (DOC, CUT, CSV, RECOMP, PERSIST, GRAPH, PNG, VERIFIER, STATE, VALIDATION, r63.LEDGER)
    got = {leaf.id: d for leaf, d in walk_sources(blobs, keep=lambda l: l.id in want) if d is not None}
    rows = {r[0]: r for r in json.loads(MANIFEST.read_text(encoding="utf-8"))["leaves"] if r[0] in want}
    c11 = r48.load_inputs()
    own = want[:-1]
    return {"text": got[DOC].decode("utf-8"), "cut": json.loads(got[CUT]), "csv": got[CSV].decode("utf-8"), "recomp": json.loads(got[RECOMP]),
            "persist": json.loads(got[PERSIST]), "graph": got[GRAPH].decode("utf-8"), "verifier": got[VERIFIER].decode("utf-8"),
            "state": json.loads(got[STATE]), "validation": json.loads(got[VALIDATION]), "bytes": {k: got[k] for k in own},
            "manifest_rows": {k: v for k, v in rows.items() if k in own}, "luc01": json.loads(got[r63.LEDGER]), "subroots": c11["subroots"],
            "root": c11["root"], "log63": json.loads(LOG63.read_text(encoding="utf-8"))}


# ---------------------------------------------------------------- the cut, rebuilt on the exact grid

def grid_boxes(sub, root):
    """the 200 subroots as integer boxes on the 2^22 grid of root 6217's box; the frame (root corner, unit, span of the
    200 boxes' union) in which normalized distances are taken"""
    rc, rh = root
    rlo = [mp.mpf(rc[k]) - mp.mpf(rh[k]) for k in range(5)]
    unit = [2 * mp.mpf(rh[k]) / K for k in range(5)]
    LO, HI, err = [], [], 0.0
    for c, h in zip(sub["centers"], sub["halfwidths"]):
        lo = [(mp.mpf(c[k]) - mp.mpf(h[k]) - rlo[k]) / unit[k] for k in range(5)]
        hi = [(mp.mpf(c[k]) + mp.mpf(h[k]) - rlo[k]) / unit[k] for k in range(5)]
        LO.append([int(mp.nint(x)) for x in lo])
        HI.append([int(mp.nint(x)) for x in hi])
        err = max(err, max(float(abs(a - b)) for a, b in zip(lo + hi, LO[-1] + HI[-1])))
    span = [max(h[k] for h in HI) - min(l[k] for l in LO) for k in range(5)]
    return {"LO": LO, "HI": HI, "rlo": rlo, "unit": unit, "span": span, "low": [min(l[k] for l in LO) for k in range(5)], "rounding": err}


def ray_units(rho, g):
    """the critical ray in grid units; sin and cos of rho U, so that its start (rho = 0) is exact"""
    p = [X30 - S * mp.cos(rho * U), S * mp.sin(rho * U), rho, X5, rho * V]
    return [(mp.mpf(p[k]) - g["rlo"][k]) / g["unit"][k] for k in range(5)]


def t_minus(lo, hi, g):
    """the squared normalized distance to the nearest prune box, as a fraction, with every prune box at that distance"""
    best, near = None, []
    for j in PRUNE:
        d2 = sum((Fraction(max(0, g["LO"][j][k] - hi[k], lo[k] - g["HI"][j][k]), g["span"][k]) ** 2 for k in range(5)), Fraction(0))
        if best is None or d2 < best:
            best, near = d2, [j]
        elif d2 == best:
            near.append(j)
    return best, near


def t_plus(lo, hi, g):
    """the normalized distance to the ray for 0 <= rho <= 0.003: a 300-step scan, golden section in the best step's
    neighbourhood, and both ends"""
    def f(r):
        p = ray_units(r, g)
        return sum((max(mp.mpf(0), lo[k] - p[k], p[k] - hi[k]) / g["span"][k]) ** 2 for k in range(5))
    rmax = mp.mpf(r63.RHO_MAX)
    grid = [rmax * i / 300 for i in range(301)]
    vals = [f(r) for r in grid]
    k = min(range(301), key=lambda i: vals[i])
    a, b = grid[max(k - 1, 0)], grid[min(k + 1, 300)]
    q = (mp.sqrt(5) - 1) / 2
    x1, x2 = b - q * (b - a), a + q * (b - a)
    f1, f2 = f(x1), f(x2)
    for _ in range(200):
        if f1 < f2:
            b, x2, f2 = x2, x1, f1
            x1 = b - q * (b - a)
            f1 = f(x1)
        else:
            a, x1, f1 = x1, x2, f2
            x2 = a + q * (b - a)
            f2 = f(x2)
    d2, r = min([(vals[0], grid[0]), (vals[-1], grid[-1]), (f1, x1), (f2, x2)])
    return mp.sqrt(d2), r


def halves(lo, hi):
    """the 32 children, keyed as the ledger keys them: bit k of the index is the upper half in dimension k"""
    mid = [(lo[k] + hi[k]) // 2 for k in range(5)]
    assert all((hi[k] - lo[k]) % 2 == 0 for k in range(5))
    out = []
    for bits in itertools.product((0, 1), repeat=5):
        out.append((sum(b << k for k, b in enumerate(bits)), [mid[k] if bits[k] else lo[k] for k in range(5)], [hi[k] if bits[k] else mid[k] for k in range(5)]))
    return out


def rho_role(lo, hi, g):
    cut = (mp.mpf(0.003) - g["rlo"][2]) / g["unit"][2]
    return "LOCAL_ONLY" if hi[2] <= cut else "TAIL_ONLY" if lo[2] >= cut else "SPLIT_AT_RHO_CUT"


def arrive(lo, hi, g):
    tp, rr = t_plus(lo, hi, g)
    d2, near = t_minus(lo, hi, g)
    return {"tp": tp, "rho": rr, "tm": mp.sqrt(mp.mpf(d2.numerator) / d2.denominator), "near": near}


def sign(a, t):
    tie = mp.mpf(TIE)
    plus, minus = a["tp"] <= t + tie, a["tm"] <= t + tie
    return "BOTH" if plus and minus else "PLUS_ONLY" if plus else "MINUS_ONLY" if minus else "UNREACHED"


def cut(sub, root):
    """both levels of the cut: per parent its arrival times, radius and later side; per child its arrival times,
    signature, nearest prune boxes, rho classes and whether it attains the parent's later arrival time; each BOTH child
    cut again at its own radius; the least distance of any non-tie from its radius"""
    with mp.workdps(DPS):
        g = grid_boxes(sub, root)
        tie, s = mp.mpf(TIE), lambda x: mp.nstr(x, 30)
        margins = {"level1": [mp.inf, mp.inf], "level2": [mp.inf, mp.inf]}

        def level(lo, hi, a, lvl):
            t = max(a["tp"], a["tm"])
            later = "tp" if a["tp"] >= a["tm"] else "tm"
            kids = {}
            for bits, clo, chi in halves(lo, hi):
                b = arrive(clo, chi, g)
                on = abs(b[later] - t) < tie
                if t > 0:
                    other = "tm" if later == "tp" else "tp"
                    if not on:
                        margins[lvl][0] = min(margins[lvl][0], abs(b[later] - t))
                    if abs(b[other] - t) >= tie:
                        margins[lvl][1] = min(margins[lvl][1], abs(b[other] - t))
                kids[bits] = {"lo": clo, "hi": chi, "b": b, "sig": sign(b, t), "attains": bool(on)}
            return t, later, kids

        parents, children, level2 = {}, {}, {}
        for P in PARENTS:
            a = arrive(g["LO"][P], g["HI"][P], g)
            t, later, kids = level(g["LO"][P], g["HI"][P], a, "level1")
            parents[str(P)] = {"tp": s(a["tp"]), "rho": s(a["rho"]), "tm": s(a["tm"]), "near": a["near"], "t": s(t), "later": "T+" if later == "tp" else "T-",
                               "role": rho_role(g["LO"][P], g["HI"][P], g), "widths": [g["HI"][P][k] - g["LO"][P][k] for k in range(5)]}
            for bits, kid in kids.items():
                cid = f"{P}:{bits:02d}"
                b = kid["b"]
                children[cid] = {"lo": kid["lo"], "hi": kid["hi"], "tp": s(b["tp"]), "rho": s(b["rho"]), "tm": s(b["tm"]), "near": b["near"], "sig": kid["sig"],
                                 "attains": kid["attains"], "own_role": rho_role(kid["lo"], kid["hi"], g), "parent_role": parents[str(P)]["role"],
                                 "zero": [b["tp"] == 0, b["tm"] == 0]}
                if kid["sig"] == "BOTH":
                    t2, later2, grand = level(kid["lo"], kid["hi"], b, "level2")
                    level2[cid] = {"t": s(t2), "later": "T+" if later2 == "tp" else "T-",
                                   "grand": {str(gb): {"sig": x["sig"], "near": x["b"]["near"], "attains": x["attains"], "tp": s(x["b"]["tp"]), "tm": s(x["b"]["tm"])}
                                             for gb, x in grand.items()}}
        return {"rounding": g["rounding"], "span": g["span"], "low": g["low"], "parents": parents, "children": children, "level2": level2,
                "margins": {k: [float(x) for x in v] for k, v in margins.items()}}


def meet(a, b):
    """closed boxes meet: the number of dimensions in which they only touch, or None"""
    zero = 0
    for k in range(5):
        c = min(a["hi"][k], b["hi"][k]) - max(a["lo"][k], b["lo"][k])
        if c < 0:
            return None
        zero += c == 0
    return zero


def recompose(children, role, adjacency):
    """the strict merge: same signature, same rho class, the same nearest prune boxes for BOTH and MINUS_ONLY, and
    closures that meet - across a face only (adjacency 1) or anywhere (adjacency 4) - closed under chains"""
    ids = sorted(children)
    up = {i: i for i in ids}

    def find(i):
        while up[i] != i:
            up[i] = up[up[i]]
            i = up[i]
        return i
    for x, y in itertools.combinations(ids, 2):
        a, b = children[x], children[y]
        if a["sig"] != b["sig"] or a[role] != b[role] or (a["sig"] in ("BOTH", "MINUS_ONLY") and a["near"] != b["near"]):
            continue
        z = meet(a, b)
        if z is not None and z <= adjacency:
            up[find(x)] = find(y)
    groups = collections.defaultdict(list)
    for i in ids:
        groups[find(i)].append(i)
    return sorted(groups.values())


def classes(children):
    out = {f"{an}/{rn}": recompose(children, role, adj) for an, adj in (("contact", 4), ("face", 1)) for rn, role in (("parent", "parent_role"), ("own", "own_role"))}
    both = [c for c in out["contact/parent"] if children[c[0]]["sig"] == "BOTH"]
    meeting = [[i, j] for i, j in itertools.combinations(range(len(both)), 2) if any(meet(children[x], children[y]) is not None for x in both[i] for y in both[j])]
    return {"partitions": out, "both_meeting": meeting}


def _key(fn, extra=""):
    digest = hashlib.sha256(inspect.getsource(fn).encode())
    for helper in (grid_boxes, ray_units, t_minus, t_plus, halves, rho_role, arrive, sign, meet, recompose):
        digest.update(inspect.getsource(helper).encode())
    digest.update(f"{K} {DPS} {TIE} {X30} {S} {X5} {U} {V} {PRUNE} {PARENTS}".encode())
    digest.update(extra.encode())
    return digest.hexdigest()


def searches(inputs):
    cached = json.loads(CACHE.read_text(encoding="utf-8")) if CACHE.exists() else {}
    out = {}
    tag = json.dumps([inputs["subroots"], inputs["root"]], sort_keys=True)
    jobs = (("cut", cut, lambda: cut(inputs["subroots"], inputs["root"])), ("classes", classes, lambda: classes(out["cut"]["children"])))
    for name, fn, call in jobs:
        key = _key(fn, tag)
        if cached.get(name, {}).get("key") != key:
            cached[name] = {"key": key, "value": json.loads(json.dumps(call()))}
            CACHE.parent.mkdir(parents=True, exist_ok=True)
            CACHE.write_text(json.dumps(cached, indent=1) + "\n", encoding="utf-8")
        out[name] = cached[name]["value"]
    return out


# ---------------------------------------------------------------- checks

ANCHORS = {"A0": [r"\boxed{9\times32=288.}", r"187\\texttt{MINUS\_ONLY}", r"88\\texttt{UNREACHED}", r"4\\texttt{PLUS\_ONLY}", r"9\\texttt{BOTH}.",
                  r"\frac9{288}=3.125\%.", "PLUS_ONLY=2childrenMINUS_ONLY=8childrenUNREACHED=22childrenBOTH=0children", r"288\longrightarrow17",
                  r"7\\text{BOTHclasses}", r"6\\text{MINUSclasses}", r"2\\text{PLUSclasses}", r"2\\text{UNREACHEDclasses}.",
                  "-`R05`:parents`166,167`,`TAIL_ONLY`,pruneprovenance`155`,2child(s).",
                  "-`R10`:parents`127`,`SPLIT_AT_RHO_CUT`,pruneprovenance`155`,2child(s).",
                  "root`127`coarseparent的nearestinwardseed是：$$144.$$", r"\text{coarsenearestprovenance}\neq\text{localoverlapprovenance}.",
                  "root`101`coarseparent的nearestinwardseed是：$$28.$$", r"\frac92=4.5.", r"\frac9{64}=0.140625.", r"\frac1{32}=0.03125.",
                  r"P_{\rmrefine}=\frac{N_{\rmBOTH}^{(k+1)}}{32N_{\rmBOTH}^{(k)}}.", "THICK/BRANCHEDINTERACTIONCORRIDOR",
                  "PROVENANCE_SWITCHINGTHICK_INTERACTION_CORRIDOR", "3.對BOTH/MINUS_ONLY而言certificateprovenance相同；4.normalized5Dclosures相鄰；"]}
FAMILIES = {"SPLIT_155": ["R10"], "TAIL_144": ["R14", "R15", "R16"], "TAIL_155": ["R05", "R06", "R07"]}
CLAIMS = {
    "cut_claim": {"parents": PARENTS, "luc01_parents": True, "radii_luc01": [9, 9], "radii_rebuilt": 1e-09, "grid": [True, True, True], "widths_even": True,
                  "children": [288, 288], "ledger_boxes": True},
    # all 288 signatures are the ledger's, and no comparison that is not a tie comes within 1e-5 of its radius; two
    # UNREACHED children of 165 sit at equal distance from 68 and 155
    "signature_claim": {"same": [288, 288], "counts": {"BOTH": 9, "MINUS_ONLY": 187, "PLUS_ONLY": 4, "UNREACHED": 88}, "aggregate": True, "per_parent": True,
                        "T_plus": 1e-09, "T_minus": 4e-16, "nearest": [288, 288], "tied": [["165:18", "165:19"], ["UNREACHED"]],
                        "margins": {"level1": [1.9e-05, 0.0034], "level2": [1.3e-05, 2.5e-05]}, "root131": {"ray": 2, "prune": 8, "both": 0}},
    # the ray arrives last at seven parents and touches each at one point, its end: one child attains it; at 127 the
    # prune boxes arrive last, across a face: twelve children attain it
    "tie_claim": {"later": {"166": "T+", "167": "T+", "164": "T+", "165": "T+", "127": "T-", "101": "T+", "104": "T+", "103": "T+"},
                  "attaining": {"166": 1, "167": 1, "164": 1, "165": 1, "127": 12, "101": 1, "104": 1, "103": 1}, "both_within": True, "ray_end": 8,
                  "ledger_on_radius": [19, 19]},
    # 127 is exactly 1/8 from 144 and from 155 (the same float), so its "flip" is a tie; 101's is real
    "provenance_claim": {"127": {"near": [144, 155], "T_minus": "0.125", "float": [0.12499999999999978, 0.12499999999999978], "ledger": [144, 0.12499999999999978],
                                 "both_children": [[155], [155]], "children": {"144": 8, "155": 24}, "attaining": {"144": 4, "155": 8}},
                         "101": {"near": [28], "both_child": [[144]]}, "ledger_flips": [101, 127], "flips": [101]},
    # 17 with closures meeting anywhere and the parent's rho class; 22 with each child's own; 19 with faces only
    "recomposition_claim": {"classes": {"contact/parent": 17, "contact/own": 22, "face/parent": 19, "face/own": 23},
                            "by_signature": {"BOTH": 7, "MINUS_ONLY": 6, "PLUS_ONLY": 2, "UNREACHED": 2}, "ledger_partition": True, "file_partition": True,
                            "own_role_differs": {"127": 16, "131": 16},
                            "both_classes": [[[101], [144], 1], [[103], [144], 1], [[104], [144], 1], [[127], [155], 2], [[164], [155], 1], [[165], [155], 1],
                                             [[166, 167], [155], 2]], "cross_parent": [[166, 167]], "both_meeting": []},
    "persistence_claim": {"records": True, "counts": True, "bits": True, "radii": 1e-09,
                          "both": {"101:25": 1, "103:25": 1, "104:25": 1, "127:16": 6, "127:24": 3, "164:24": 1, "165:24": 1, "166:24": 1, "167:25": 1},
                          "attaining": {"101:25": 1, "103:25": 1, "104:25": 1, "127:16": 8, "127:24": 4, "164:24": 1, "165:24": 1, "166:24": 1, "167:25": 1},
                          "later": ["T+", "T-/127"], "formula": True, "127": [9, 0.140625]},
    "records_claim": {"csv": [288, True, True], "state": [9, 288, 17, 7, 3], "state_matches": [True, True, True, True, True],
                      "graph_nodes": {"box": 9, "diamond": 3, "ellipse": 3, "oval": 7}, "graph_families": FAMILIES, "families": FAMILIES,
                      "graph_classes_are_both": True, "highest_priority": [127, "PROVENANCE_SWITCHING_THICK_INTERACTION_CORRIDOR"],
                      "next": "BCODR-LUC-03 — Deep Cut of Root 127 Provenance-Switching Corridor", "persistence_classes": True},
    "verifier_facts": {"lines": 40, "loads": ["1", "2", "3"], "asserts": 15, "recomputes": False, "counts_from_children": False},
    "package_claim": {"digests": {"paper_sha256": True, "cut_sha256": True, "recomposition_sha256": True, "persistence_sha256": True, "csv_sha256": True,
                                  "graph_sha256": True, "verifier_sha256": True, "state_sha256": True}, "dot_digested": False, "manifest": True, "utf8": True,
                      "delimiters": ["PASS", []],
                      "replay": ["PASS", "children = 288", "signature counts = {'MINUS_ONLY': 187, 'UNREACHED': 88, 'PLUS_ONLY': 4, 'BOTH': 9}",
                                 "recomposed classes = 17", "BOTH geometric classes = 7", "root131 BOTH children = 0", "root127 BOTH grandchildren = 9"]},
}


def sig(x, n=2):
    return float(f"{x:.{n - 1}e}")


def w1_cut(w):
    """the nine parents are LUC 01's nexus and its most balanced eight (RUN-063), with LUC 01's radii; every parent's
    width is even on the grid, so each child is exactly its parent halved; the ledger's children are these"""
    c, L, L1 = w["search"]["cut"], w["cut"], {x["local_index"]: x for x in w["luc01"]["cells"]}
    f4 = w["log63"]["w4"]
    radii = [s["parent_meeting_radius"] == L1[s["parent"]]["meeting_radius"] and s["parent_T_plus"] == L1[s["parent"]]["T_plus"]
             and s["parent_T_minus"] == L1[s["parent"]]["T_minus"] for s in L["parent_summaries"]]
    rebuilt = max(abs(float(c["parents"][str(s["parent"])]["t"]) - s["parent_meeting_radius"]) for s in L["parent_summaries"])
    half, box = 2 ** 21, 0.0
    for x in L["children"]:
        k = c["children"][x["child_id"]]
        cen = [((k["lo"][d] + k["hi"][d]) / 2 - half) / K for d in range(5)]
        hw = [(k["hi"][d] - k["lo"][d]) / 2 / K for d in range(5)]
        box = max(box, max(abs(a - b) for a, b in zip(cen + hw, x["center_normalized"] + x["halfwidth_normalized"])))
    got = {"parents": L["selected_parents"], "luc01_parents": L["selected_parents"] == f4["nexus"] + f4["top8"], "radii_luc01": [sum(radii), len(radii)],
           "radii_rebuilt": sig(rebuilt, 1), "grid": [c["rounding"] < 1e-6, c["span"] == [K] * 5, c["low"] == [0] * 5],
           "widths_even": all(x % 2 == 0 for p in c["parents"].values() for x in p["widths"]), "children": [len(c["children"]), len(L["children"])],
           "ledger_boxes": box < 1e-12}
    w["w1"] = got
    return [] if got == w["cut_claim"] else [f"the cut: {got}"]


def w2_signatures(w):
    """every child's signature is the ledger's; T+ to the ledger's ray grid, T- to rounding; the nearest prune box is the
    ledger's or tied with it; no comparison is near a tie that is not one; 131's children touch the ray or a prune box
    exactly, never both"""
    c, L = w["search"]["cut"], w["cut"]
    kids = c["children"]
    same = sum(kids[x["child_id"]]["sig"] == x["signature"] for x in L["children"])
    counts = collections.Counter(k["sig"] for k in kids.values())
    per = {p: dict(sorted(collections.Counter(k["sig"] for i, k in kids.items() if i.startswith(f"{p}:")).items())) for p in PARENTS}
    led = {s["parent"]: dict(sorted(s["child_counts"].items())) for s in L["parent_summaries"]}
    near = sum(x["nearest_prune_root"] in kids[x["child_id"]]["near"] for x in L["children"])
    tied = sorted(i for i, k in kids.items() if len(k["near"]) > 1)
    z = [k["zero"] for i, k in kids.items() if i.startswith("131:")]
    got = {"same": [same, len(L["children"])], "counts": dict(sorted(counts.items())), "aggregate": dict(sorted(counts.items())) == dict(sorted(L["aggregate_child_counts"].items())),
           "per_parent": per == led, "T_plus": sig(max(abs(float(kids[x["child_id"]]["tp"]) - x["T_plus"]) for x in L["children"]), 1),
           "T_minus": sig(max(abs(float(kids[x["child_id"]]["tm"]) - x["T_minus"]) for x in L["children"]), 1),
           "nearest": [near, len(L["children"])], "tied": [tied, sorted({kids[i]["sig"] for i in tied})],
           "margins": {k: [sig(v[0]), sig(v[1])] for k, v in c["margins"].items()},
           "root131": {"ray": sum(a for a, b in z), "prune": sum(b for a, b in z), "both": sum(a and b for a, b in z)}}
    w["w2"] = got
    return [] if got == w["signature_claim"] else [f"the signatures: {got}"]


def w3_ties(w):
    """at t_B the field that arrives last has only just reached the parent: it touches it at its nearest point (the ray)
    or along part of a face (a prune box). A child cannot be nearer than its parent, so that field reaches a child only
    if the child holds such a point: the BOTH children are among these. The ledger's floats sit on the radius at every
    one of these ties"""
    c, L = w["search"]["cut"], w["cut"]
    kids, par = c["children"], c["parents"]
    later = {p: par[str(p)]["later"] for p in PARENTS[1:]}
    att = {p: sum(k["attains"] for i, k in kids.items() if i.startswith(f"{p}:")) for p in PARENTS[1:]}
    within = all(k["attains"] for i, k in kids.items() if k["sig"] == "BOTH")
    ends = sum(abs(float(par[str(p)]["rho"]) - r63.RHO_MAX) < 1e-25 for p in PARENTS[1:])
    on = [x["T_plus" if later[x["parent"]] == "T+" else "T_minus"] - x["parent_meeting_radius"] for x in L["children"]
          if x["parent"] != 131 and kids[x["child_id"]]["attains"]]
    got = {"later": {str(p): v for p, v in later.items()}, "attaining": {str(p): v for p, v in att.items()}, "both_within": within, "ray_end": ends,
           "ledger_on_radius": [sum(d == 0.0 for d in on), len(on)]}
    w["w3"] = got
    return [] if got == w["tie_claim"] else [f"the ties: {got}"]


def w4_provenance(w):
    """127 is 1/8 of the span from 144 and from 155 alike, across x3; in floating point the two distances are the same
    number, and the ledger names the first; the BOTH children are nearest to 155 alone. 101 is nearest to 28 alone and its
    BOTH child to 144 alone"""
    c, L = w["search"]["cut"], w["cut"]
    kids, par = c["children"], c["parents"]
    C, H, lo, hi, mid, scale = r63.domain(w["subroots"])
    Cn, Hn = (C - mid) / scale, H / scale
    flt = [float(np.linalg.norm(np.maximum(0.0, np.abs(Cn[127] - Cn[j]) - (Hn[127] + Hn[j])))) for j in (144, 155)]
    s127 = next(s for s in L["parent_summaries"] if s["parent"] == 127)
    k127 = {i: k for i, k in kids.items() if i.startswith("127:")}
    by = lambda ks: dict(sorted(collections.Counter(str(k["near"][0]) if len(k["near"]) == 1 else "tie" for k in ks).items()))
    b101 = [k["near"] for i, k in kids.items() if i.startswith("101:") and k["sig"] == "BOTH"]
    got = {"127": {"near": par["127"]["near"], "T_minus": par["127"]["tm"][:6], "float": flt, "ledger": [s127["parent_nearest_prune"], s127["parent_T_minus"]],
                   "both_children": [k["near"] for k in k127.values() if k["sig"] == "BOTH"], "children": by(k127.values()),
                   "attaining": by(k for k in k127.values() if k["attains"])},
           "101": {"near": par["101"]["near"], "both_child": b101},
           "ledger_flips": sorted(s["parent"] for s in L["parent_summaries"] if s["provenance_flip_in_overlap"]),
           "flips": sorted(int(p) for p, v in par.items() if len(v["near"]) == 1 and any(k["sig"] == "BOTH" and k["near"] != v["near"] for i, k in kids.items() if i.startswith(p + ":")))}
    w["w4"] = got
    return [] if got == w["provenance_claim"] else [f"the provenance: {got}"]


def w5_recomposition(w):
    """the 17 classes are the ledger's, child for child, when closures meeting anywhere count as adjacent and every child
    keeps its parent's rho class; with each child's own rho class there are 22, with faces only 19; the seven BOTH
    classes as sec. 16 lists them"""
    c, cl, L, R = w["search"]["cut"], w["search"]["classes"], w["cut"], w["recomp"]
    kids, parts = c["children"], cl["partitions"]
    ledger = collections.defaultdict(list)
    for x in L["children"]:
        ledger[x["recomposed_class"]].append(x["child_id"])
    mine = parts["contact/parent"]
    both = sorted([sorted({int(i.split(":")[0]) for i in g}), kids[g[0]]["near"], len(g)] for g in mine if kids[g[0]]["sig"] == "BOTH")
    got = {"classes": {k: len(v) for k, v in parts.items()},
           "by_signature": dict(sorted(collections.Counter(kids[g[0]]["sig"] for g in mine).items())),
           "ledger_partition": sorted(sorted(v) for v in ledger.values()) == mine, "file_partition": sorted(sorted(x["children"]) for x in R["classes"]) == mine,
           "own_role_differs": dict(sorted(collections.Counter(i.split(":")[0] for i, k in kids.items() if k["own_role"] != k["parent_role"]).items())),
           "both_classes": both, "cross_parent": [b[0] for b in both if len(b[0]) > 1], "both_meeting": cl["both_meeting"]}
    w["w5"] = got
    return [] if got == w["recomposition_claim"] else [f"the recomposition: {got}"]


def w6_persistence(w):
    """the second cut, per BOTH child: its counts and BOTH grandchildren are the persistence file's; the grandchildren
    that attain the later arrival time are one for each of the seven and 8 and 4 for 127's two; the multiplicities and
    the normalized persistence follow sec. 29"""
    c, P = w["search"]["cut"], w["persist"]
    lv2 = c["level2"]
    recs = {r["level1_child_id"]: r for r in P["level2_records"]}
    counts = all(dict(sorted(collections.Counter(g["sig"] for g in lv2[i]["grand"].values()).items())) == dict(sorted(recs[i]["level2_counts"].items())) for i in lv2)
    bits = all(sorted(int(b) for b, g in lv2[i]["grand"].items() if g["sig"] == "BOTH") == sorted(x["bits"] for x in recs[i]["both_grandchildren"]) for i in lv2)
    radius = max(abs(float(lv2[i]["t"]) - recs[i]["level1_child_meeting_radius"]) for i in lv2)
    formula = all(abs(s["normalized_persistence"] - s["level2_both_grandchildren"] / (32 * s["level1_both_children"])) < 1e-15
                  and abs(s["refinement_multiplicity"] - s["level2_both_grandchildren"] / s["level1_both_children"]) < 1e-15 for s in P["class_summary"])
    got = {"records": sorted(lv2) == sorted(recs), "counts": counts, "bits": bits, "radii": sig(radius, 1),
           "both": {i: sum(g["sig"] == "BOTH" for g in lv2[i]["grand"].values()) for i in sorted(lv2)},
           "attaining": {i: sum(g["attains"] for g in lv2[i]["grand"].values()) for i in sorted(lv2)},
           "later": sorted({lv2[i]["later"] + ("/127" if i.startswith("127:") else "") for i in lv2}),
           "formula": formula, "127": [P["root127_result"]["level2_both_grandchildren"], P["root127_result"]["normalized_persistence"]]}
    w["w6"] = got
    return [] if got == w["persistence_claim"] else [f"the persistence: {got}"]


def w7_records(w):
    L, R, P, st = w["cut"], w["recomp"], w["persist"], w["state"]
    text = w["csv"]
    rows = list(csv.DictReader(io.StringIO(text.lstrip("﻿"))))
    conv = {"parent": int, "bits": int, "nearest_prune_root": int, "T_plus": float, "T_minus": float, "child_meeting_radius": float, "nearest_critical_ray_rho": float}
    same = len(rows) == len(L["children"]) and all(conv.get(k, str)(r[k]) == x[k] for r, x in zip(rows, L["children"]) for k in r)
    nodes = dict(re.findall(r'^"([^"]+)" \[shape=(\w+)', w["graph"], re.M))
    edges = re.findall(r'^"([^"]+)" -> "([^"]+)" \[label="([^"]+)"\]', w["graph"], re.M)
    fam = {f["family_id"]: f["geometric_classes"] for f in R["semantic_spine_families"]}
    got = {"csv": [len(rows), same, text.startswith("﻿")],
           "state": [st["selected_parent_count"], st["atomic_child_count"], st["structural_class_count"], st["both_geometric_class_count"], st["semantic_spine_family_count"]],
           "state_matches": [st["selected_parent_count"] == len(L["selected_parents"]), st["atomic_child_count"] == len(L["children"]),
                             st["structural_class_count"] == R["recomposed_class_count"] == len(R["classes"]),
                             st["both_geometric_class_count"] == R["both_geometric_class_count"] == len(R["both_classes"]),
                             st["semantic_spine_family_count"] == len(R["semantic_spine_families"])],
           "graph_nodes": dict(sorted(collections.Counter(nodes.values()).items())),
           "graph_families": {f: sorted(a for a, b, l in edges if b == f) for f in sorted(fam)}, "families": dict(sorted(fam.items())),
           "graph_classes_are_both": sorted(n for n, s in nodes.items() if s == "oval") == sorted(x["class_id"] for x in R["both_classes"]),
           "highest_priority": [st["highest_priority"]["parent"], st["highest_priority"]["role"]], "next": st["next"],
           "persistence_classes": sorted(s["class_id"] for s in P["class_summary"]) == sorted(x["class_id"] for x in R["both_classes"])}
    w["w7"] = got
    return [] if got == w["records_claim"] else [f"the records: {got}"]


def w8_verifier(w):
    """read after the rebuild and never run: it loads the three JSON files, asserts the reported counts as constants and
    recomputes nothing - not even the signature counts from the children"""
    code = w["verifier"]
    got = {"lines": code.count("\n"), "loads": re.findall(r"json\.load\(open\(sys\.argv\[(\d)\]", code), "asserts": len(re.findall(r"^assert ", code, re.M)),
           "recomputes": bool(re.search(r"T_plus|T_minus|center|halfwidth|subroot", code)), "counts_from_children": bool(re.search(r"for [\w, ]+ in A\[.children.\]", code))}
    w["w8"] = got
    return [] if got == w["verifier_facts"] else [f"the verifier: {got}"]


def p1_package(w):
    v, B = w["validation"], w["bytes"]
    sha = lambda b: hashlib.sha256(b).hexdigest()
    keys = {"paper_sha256": DOC, "cut_sha256": CUT, "recomposition_sha256": RECOMP, "persistence_sha256": PERSIST, "csv_sha256": CSV, "graph_sha256": PNG,
            "verifier_sha256": VERIFIER, "state_sha256": STATE}
    body = B[DOC].decode("utf-8")
    # "graph_sha256" is the PNG's digest; the DOT the graph is drawn from is not digested
    got = {"digests": {k: v[k] == sha(B[f]) for k, f in keys.items()}, "dot_digested": any(sha(B[GRAPH]) == x for x in v.values()),
           "manifest": all(w["manifest_rows"][k][2] == sha(b) for k, b in B.items()), "utf8": v["utf8"] is True,
           "delimiters": [v["math_delimiters"], sorted(set(re.findall(r"\\\(|\\\)|\\\[|\\\]", body)))], "replay": v["fresh_replay_stdout"].split("\n")}
    w["p1"] = got
    return [] if got == w["package_claim"] else [f"the package: {got}"]


def a0_anchors(w):
    text = gate.squash(w["text"])
    return [f"anchor not found: {a[:60]}" for a in ANCHORS["A0"] if a not in text]


CHECKS = {"A0": a0_anchors, "W1": w1_cut, "W2": w2_signatures, "W3": w3_ties, "W4": w4_provenance, "W5": w5_recomposition, "W6": w6_persistence,
          "W7": w7_records, "W8": w8_verifier, "P1": p1_package}
DEPENDS = {"A0": {"text"}, "W1": {"search", "cut", "luc01", "log63", "cut_claim"}, "W2": {"search", "cut", "signature_claim"},
           "W3": {"search", "cut", "tie_claim"}, "W4": {"search", "cut", "subroots", "provenance_claim"},
           "W5": {"search", "cut", "recomp", "recomposition_claim"}, "W6": {"search", "persist", "persistence_claim"},
           "W7": {"cut", "recomp", "persist", "state", "csv", "graph", "records_claim"}, "W8": {"verifier", "verifier_facts"},
           "P1": {"validation", "bytes", "manifest_rows", "package_claim"}}


def drill_cases(base):
    def edit(obj, fn):
        x = copy.deepcopy(obj)
        fn(x)
        return x

    text, s, L = base["text"], base["search"], base["cut"]
    row = base["csv"].split("\n")
    kid = lambda x, i: next(c for c in x["children"] if c["child_id"] == i)
    return [
        ("K0-unmodified", "control", {}, set()),
        ("K1-crlf", "control", {"text": text.replace("\n", "\r\n")}, set()),
        ("K2-validation-keys-sorted", "control", {"validation": json.loads(json.dumps(base["validation"], sort_keys=True))}, set()),
        ("K3-cut-keys-sorted", "control", {"cut": json.loads(json.dumps(L, sort_keys=True))}, set()),
        ("M1-text-count", "mutation", {"text": text.replace(r"187\ \texttt{MINUS\_ONLY}", r"186\ \texttt{MINUS\_ONLY}")}, {"A0"}),
        ("M2-parents", "mutation", {"cut": edit(L, lambda x: x["selected_parents"].__setitem__(8, 160))}, {"W1"}),
        ("M3-luc01-radius", "mutation", {"luc01": edit(base["luc01"], lambda x: next(c for c in x["cells"] if c["local_index"] == 164).update(meeting_radius=0.355))}, {"W1"}),
        ("M4-T-minus", "mutation", {"search": edit(s, lambda x: x["cut"]["children"]["131:05"].update(tm="0.3"))}, {"W2"}),
        ("M5-signature", "mutation", {"cut": edit(L, lambda x: kid(x, "131:01").update(signature="MINUS_ONLY"))}, {"W2", "W7"}),
        ("M6-attains", "mutation", {"search": edit(s, lambda x: x["cut"]["children"]["166:24"].update(attains=False))}, {"W3"}),
        ("M7-ledger-off-radius", "mutation", {"cut": edit(L, lambda x: kid(x, "164:24").update(T_plus=kid(x, "164:24")["T_plus"] + 1e-15))}, {"W3", "W7"}),
        ("M8-127-near", "mutation", {"search": edit(s, lambda x: x["cut"]["parents"]["127"].update(near=[144]))}, {"W4"}),
        ("M9-ledger-flip", "mutation", {"cut": edit(L, lambda x: next(p for p in x["parent_summaries"] if p["parent"] == 104).update(provenance_flip_in_overlap=True))}, {"W4"}),
        ("M10-class-label", "mutation", {"cut": edit(L, lambda x: kid(x, "131:00").update(recomposed_class="R03"))}, {"W5", "W7"}),
        ("M11-own-role", "mutation", {"search": edit(s, lambda x: x["classes"]["partitions"]["contact/own"].pop())}, {"W5"}),
        ("M12-level2-count", "mutation", {"persist": edit(base["persist"], lambda x: x["level2_records"][5]["level2_counts"].update(BOTH=4))}, {"W6"}),
        ("M13-level2-attains", "mutation", {"search": edit(s, lambda x: x["cut"]["level2"]["127:24"]["grand"]["0"].update(attains=False))}, {"W6"}),
        ("M14-csv-value", "mutation", {"csv": "\n".join(row[:1] + [row[1].replace(",0.0,", ",0.01,", 1)] + row[2:])}, {"W7"}),
        ("M15-graph-node", "mutation", {"graph": base["graph"].replace("}", '"R08" [shape=oval, label="R08"];\n}')}, {"W7"}),
        ("M16-verifier-recount", "mutation", {"verifier": base["verifier"] + '\nfor c in A["children"]:\n    pass\n'}, {"W8"}),
        ("M17-validation-digest", "mutation", {"validation": {**base["validation"], "cut_sha256": "0" * 64}}, {"P1"}),
        ("M18-paper-bytes", "mutation", {"bytes": {**base["bytes"], DOC: base["bytes"][DOC] + b" "}}, {"P1"}),
    ]


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--drill", action="store_true")
    ap.add_argument("--blind", metavar="CID")
    args = ap.parse_args()
    inputs = load_inputs()
    base = {**inputs, "search": searches(inputs)}

    def build():
        return {**copy.deepcopy(base), **copy.deepcopy(CLAIMS)}

    if args.blind:
        CHECKS[args.blind] = lambda w: []
        args.drill = True
    w = build()
    results = {}
    for c, fn in CHECKS.items():
        try:
            results[c] = fn(w)
        except KeyError as e:
            results[c] = [f"claim missing: {e}"]
    for c, f in results.items():
        print(f"{c} {'PASS' if not f else 'FAIL'}", *(f"\n     {x}" for x in f[:6]))
    for key in ("w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "p1"):
        print(key, "=", json.dumps(w.get(key), default=str, ensure_ascii=False))
    if not args.drill:
        return
    cases = drill_cases(base)
    if args.blind:
        cases = [c for c in cases if args.blind in c[3]]
    d = gate.run_drill(build, CHECKS, DEPENDS, cases)
    summary = gate.summarize(d, CHECKS)
    print(gate.report_drill(summary))
    for x in d:
        if not x["ok"]:
            print("   DRILL FAIL", x)
    if args.blind:
        n = sum(1 for x in d if args.blind in x["expected"] and args.blind not in x["tripped"])
        print(f"blind {args.blind}: drill reported it blind in {n} cases (nothing written)")
        return
    log = {"run": "RUN-064", "subject": "BCODR LUC 02: the atomic cut of the frontier and its strict recomposition", "document": DOC,
           "checks": {c: {"pass": not f, "failures": f} for c, f in results.items()},
           **{k: w.get(k) for k in ("w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "p1")}, "drill": summary}
    LOG.write_text(json.dumps(log, ensure_ascii=False, indent=1, default=float) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
