"""RUN-046 - UESFCM Cycles 07-09: the F3 runtime frontier, its body skeletons, the reflection quotient and the Taylor diagnostic.

Cycle 07 runs the first erosion branch-and-bound on the F3 branch and saves what it could not close: 22,516 root
boxes (centres and half-widths), with a runtime probe, the membership fractions and the exact centre areas; it
rejects a global Hausdorff-Lipschitz area bound as too loose and proposes an active-envelope compiler. Cycle 08
classifies the frontier centres by the cyclic order of the bodies on the hull (24 skeletons against 1,332 full
piece signatures) and samples corner stability. Cycle 09 quotients the skeletons by reflection (12 classes, one of
15,210 roots), and runs a finite-difference Taylor diagnostic on the 649 dominant-class roots whose centre lies
within 1e-3 of A3+ and on the 27 within 5e-4. Here the frontier is replayed centre by centre with this tree's exact
hull, the classifications rebuilt from its own envelopes, and the derivatives taken again.

Checks
  A0 anchors      the numbers below occur in the three documents as written
  W1 frontier     Cycle 07: the saved boxes; branch membership; the centre areas, their least and quantiles
  W2 lipschitz    Cycle 07 sec. 12: the perimeter bound A(c) - P(c) tau against the frontier
  W3 skeletons    Cycle 08: body skeletons and piece signatures at every centre, the coverage, the catalog; the
                  corner-stability fraction on a sample
  W4 reflection   Cycle 09 sec. 1-5: the reflection classes and the dominant class
  W5 taylor       Cycle 09 sec. 11-16: the near-ceiling sets, the derivatives, the first- and second-order margins
  W6 crystals     the three state crystals against the numbers rebuilt here
  W7 code         the three scaffolds, read after the rebuild and never run
  P1 package      the VALIDATION records; the frontier's dependency copies

Usage: python code/run046_uesfcm_cycles_07_09.py [--drill] [--blind CID]
"""
import argparse
import collections
import copy
import hashlib
import inspect
import io
import json
import math
import re

import numpy as np

import gate
import run044_uesfcm_cycles_01_03 as r44
from corpus import SOURCES, TREE, source_bytes, walk_sources
from run008_round05_saturation_gate import envelope, segments, verify_segments, wrap
from run009_round06_witness_exchange import hull_pieces

FOLDER = "main.zip!/UESFCM/"
PKG = {i: FOLDER + f"UESFCM_Lebesgue_Cycle_{i:02d}_Package_v0.1.zip!/" for i in (7, 8, 9)}
DOCS = {7: PKG[7] + "UESFCM_Lebesgue_Cycle_07_F3_Frontier_and_Envelope_Compiler_v0.1.md",
        8: PKG[8] + "UESFCM_Lebesgue_Cycle_08_Two_Level_Skeleton_Compiler_v0.1.md",
        9: PKG[9] + "UESFCM_Lebesgue_Cycle_09_Reflection_Taylor_Closure_v0.1.md"}
DATA = {"frontier": PKG[7] + "UESFCM_Lebesgue_Cycle_07_F3_Unresolved_Frontier.npz", "probe": PKG[7] + "UESFCM_Lebesgue_Cycle_07_F3_Runtime_Probe.json",
        "catalog": PKG[8] + "UESFCM_Lebesgue_Cycle_08_Body_Skeleton_Catalog.json",
        "reflection": PKG[9] + "UESFCM_Lebesgue_Cycle_09_Reflection_Quotient_Catalog.json",
        "taylor": PKG[9] + "UESFCM_Lebesgue_Cycle_09_Taylor_Diagnostic.npz", "summary": PKG[9] + "UESFCM_Lebesgue_Cycle_09_Taylor_Diagnostic_Summary.json"}
CODE = {7: PKG[7] + "UESFCM_Lebesgue_Cycle_07_Active_Envelope_Compiler_Scaffold.py",
        8: PKG[8] + "UESFCM_Lebesgue_Cycle_08_Body_Skeleton_Compiler_Scaffold.py",
        9: PKG[9] + "UESFCM_Lebesgue_Cycle_09_Interval_Taylor_Compiler_Scaffold.py"}
COPIES = [PKG[8] + "dependencies/UESFCM_Lebesgue_Cycle_07_F3_Unresolved_Frontier.npz",
          PKG[9] + "dependencies/UESFCM_Lebesgue_Cycle_07_F3_Unresolved_Frontier.npz"]
CRYSTALS = {i: PKG[i] + f"UESFCM_Lebesgue_State_Crystal_Cycle_{i:02d}_v0.1.json" for i in (7, 8, 9)}
VALIDATIONS = {i: PKG[i] + f"UESFCM_Lebesgue_Cycle_{i:02d}_VALIDATION.json" for i in (7, 8, 9)}
LOG = TREE / "data" / "gate-logs" / "run046-uesfcm-cycles-07-09.json"
MANIFEST = TREE / "data" / "SOURCE_MANIFEST.v1.json"
CACHE = TREE / "data" / "cache" / "run046-searches.json"     # data/cache is not committed

A3, S_SYM, X30 = r44.A3, 0.00882, -0.01215883209899
QUANTILES = (0, 0.001, 0.01, 0.05, 0.1, 0.5, 0.9, 1)
LABEL = ["D"] + ["B3"] * 6 + ["B5"] * 10          # the disc; B3's 3 corners and 3 arcs; B5's 5 corners and 5 arcs
PIECE = ["D"] + [f"B3v{k}" for k in range(3)] + [f"B3a{k}" for k in range(3)] + [f"B5v{k}" for k in range(5)] + [f"B5a{k}" for k in range(5)]


def load_inputs():
    blobs = {n: source_bytes(n) for n in SOURCES}
    want = set(DOCS.values()) | set(DATA.values()) | set(CODE.values()) | set(COPIES) | set(CRYSTALS.values()) | set(VALIDATIONS.values())
    got = {leaf.id: d for leaf, d in walk_sources(blobs, keep=lambda l: l.id in want) if d is not None}
    rows = {row[0]: row for row in json.loads(MANIFEST.read_text(encoding="utf-8"))["leaves"]}
    npz = lambda k: {name: a for name, a in np.load(io.BytesIO(got[DATA[k]])).items()}
    return {"texts": {i: got[DOCS[i]].decode("utf-8") for i in DOCS}, "code": {i: got[CODE[i]].decode("utf-8") for i in CODE},
            "frontier": npz("frontier"), "taylor": {k: v.tolist() for k, v in npz("taylor").items()},     # lists: the drill compares plants by ==
            **{k: json.loads(got[DATA[k]]) for k in ("probe", "catalog", "reflection", "summary")},
            "crystals": {i: json.loads(got[CRYSTALS[i]]) for i in CRYSTALS},
            "validations": {i: json.loads(got[VALIDATIONS[i]]) for i in VALIDATIONS},
            "files": {k: got[k] for k in want}, "manifest_rows": {k: rows[k] for k in want}}


# ---------------------------------------------------------------- the hull at a centre, robust to the grid

def hull(v):
    """the verified hull's area and perimeter (RUN-009 with RUN-031's segments); where the hand-over chain breaks
    on one grid (the kernel raises rather than guess), the next grid is used - the segments are checked in closed
    form either way. Returns (area, perimeter, whether another grid was needed)"""
    for n in (16384, 16381, 12289, 8191, 4096):
        try:
            a, p = hull_pieces(r44.base_pieces(v), n)
            return a, p, n != 16384
        except RuntimeError:
            continue
    raise RuntimeError(f"no grid for {list(v)}")


def area(v):
    return hull(v)[0]


def owners(v):
    """the pieces that hold the hull's support, in angular order, from the verified segments"""
    P = r44.base_pieces(v)
    C = np.array([p[0] for p in P], dtype=float)
    rho = np.array([p[1] for p in P], dtype=float)
    ranged = [(k, p[2]) for k, p in enumerate(P) if p[2] is not None]

    def owner(t):
        V = C @ np.stack([np.cos(t), np.sin(t)]) + rho[:, None]
        for k, (mid, half) in ranged:
            V[k, np.abs(wrap(t - mid)) > half] = -np.inf
        return np.argmax(V, axis=0)
    for n in (2048, 2039, 4096, 4093, 8191):
        try:
            cuts, own = envelope(owner, n, len(P))
            return [k for _, _, k in sorted(verify_segments(C, rho, [p[2] for p in P], list(segments(cuts, own))))], n != 2048
        except RuntimeError:
            continue
    raise RuntimeError(f"no grid for {list(v)}")


def runs(seq):
    out = []
    for s in seq:
        if not out or out[-1] != s:
            out.append(s)
    if len(out) > 1 and out[0] == out[-1]:
        out.pop()
    return out


def canon(seq):
    return min(tuple(seq[i:] + seq[:i]) for i in range(len(seq)))


def unoriented(seq):
    return min(canon(list(seq)), canon(list(reversed(seq))))


def frontier_scan(C):
    """every centre: its exact area and perimeter, body skeleton and piece signature, and how many needed another grid"""
    out = {"area": [], "perimeter": [], "body": [], "piece": [], "retries": [0, 0]}
    for c in C:
        a, p, ra = hull(c)
        o, ro = owners(c)
        out["area"].append(float(a))
        out["perimeter"].append(float(p))
        out["body"].append(list(canon(runs([LABEL[k] for k in o]))))
        out["piece"].append(list(canon(runs([PIECE[k] for k in o]))))
        out["retries"][0] += ra
        out["retries"][1] += ro
    return out


def corner_stability(C, H, sample=2000, seed=46):
    """Cycle 08 sec. 17 on a sample of roots: the body skeleton at the centre and at all 32 box corners agree"""
    rng = np.random.default_rng(seed)
    idx = rng.choice(len(C), sample, replace=False)
    signs = np.array([[1 if (k >> j) & 1 else -1 for j in range(5)] for k in range(32)])
    stable = 0
    for i in idx:
        centre = canon(runs([LABEL[k] for k in owners(C[i])[0]]))
        stable += all(canon(runs([LABEL[k] for k in owners(C[i] + s * H[i])[0]])) == centre for s in signs)
    return {"sample": sample, "stable": stable}


def derivatives(C, idx, step=1e-7, second=False):
    """central finite differences of the exact area at the centres idx: gradients, and for second=True Hessians"""
    out = {}
    for i in idx:
        c = np.asarray(C[i], dtype=float)
        f = area
        g = []
        for j in range(5):
            e = np.zeros(5)
            e[j] = step
            g.append((f(c + e) - f(c - e)) / (2 * step))
        rec = {"g": g}
        if second:
            Hs = np.zeros((5, 5))
            f0 = f(c)
            hstep = 1e-5
            for j in range(5):
                for k in range(j, 5):
                    ej, ek = np.zeros(5), np.zeros(5)
                    ej[j], ek[k] = hstep, hstep
                    if j == k:
                        Hs[j, j] = (f(c + ej) - 2 * f0 + f(c - ej)) / hstep ** 2
                    else:
                        Hs[j, k] = Hs[k, j] = (f(c + ej + ek) - f(c + ej - ek) - f(c - ej + ek) + f(c - ej - ek)) / (4 * hstep ** 2)
            rec["H"] = Hs.tolist()
        out[str(int(i))] = rec
    return out


def gradient_scan(C, idx):
    return derivatives(C, idx)


def hessian_scan(C, idx):
    return derivatives(C, idx, second=True)


HELPERS = (hull, area, owners, runs, canon, derivatives, r44.base_pieces, r44.rn)


def _key(fn):
    import run008_round05_saturation_gate as r8
    import run009_round06_witness_exchange as r9
    digest = hashlib.sha256(inspect.getsource(fn).encode())
    for helper in HELPERS:
        digest.update(inspect.getsource(helper).encode())
    for path in (r8.__file__, r9.__file__):
        digest.update(open(path, "rb").read())
    return digest.hexdigest()


def searches(inputs):
    """each search cached under its own key; the Taylor sets come from the scan"""
    C, H = inputs["frontier"]["centers"], inputs["frontier"]["halfwidths"]
    cached = json.loads(CACHE.read_text(encoding="utf-8")) if CACHE.exists() else {}
    out, changed = {}, False

    def get(name, fn, call, extra=""):
        nonlocal changed
        key = _key(fn) + extra
        if cached.get(name, {}).get("key") != key:
            cached[name] = {"key": key, "value": call()}
            changed = True
        out[name] = cached[name]["value"]

    get("scan", frontier_scan, lambda: frontier_scan(C))
    get("stability", corner_stability, lambda: corner_stability(C, H))
    near1, near5 = near_sets(out["scan"])
    tag = lambda idx: "|" + hashlib.sha256(json.dumps(idx).encode()).hexdigest()[:16]
    get("gradients", gradient_scan, lambda: gradient_scan(C, near1), tag(near1))
    get("hessians", hessian_scan, lambda: hessian_scan(C, near5), tag(near5))
    if changed:
        CACHE.parent.mkdir(parents=True, exist_ok=True)
        CACHE.write_text(json.dumps(cached, indent=1) + "\n", encoding="utf-8")
    return out


def near_sets(scan):
    """Cycle 09 sec. 13, 15: dominant-class roots whose centre area is within 1e-3, and within 5e-4, of A3+"""
    counts = collections.Counter(unoriented(b) for b in scan["body"])
    dominant = counts.most_common(1)[0][0]
    dom = [i for i, b in enumerate(scan["body"]) if unoriented(b) == dominant]
    near1 = [i for i in dom if scan["area"][i] < A3 + 1e-3]
    near5 = [i for i in near1 if scan["area"][i] < A3 + 5e-4]
    return near1, near5


def world(inputs):
    return {**inputs, "search": searches(inputs)}


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

ANCHORS = {
    7: {"W1": ["273{,}305", "156{,}423", "2{,}133", "396{,}975", "6{,}509", "490{,}166", "254{,}189", "11{,}680", "22{,}516", r"\boxed{92.04\%}",
               r"0.24\%", r"\boxed{0.8351883751674862.}", r"\boxed{0.0004071841674862.}", "`NUMERICAL-STRUCTURAL-DIAGNOSTIC`"],
        "W2": [r"A(v)\geA(c)-P(c)\tau-O(\tau^2).", r"10^{-3}\text{--}10^{-2}.", r"4\times10^{-4}", "`REJECTED-AS-TOO-LOOSE`"]},
    8: {"W3": [r"\boxed{1{,}332}", r"\boxed{24}", r"\boxed{67.551963\%}", r"\boxed{79.992006\%}", r"\boxed{91.845799\%}", r"\boxed{1{,}493}",
               r"\boxed{29.86\%.}", r"\boxed{55.5\times}", "`EXACTCOMPUTATIONALCLASSIFICATIONOFSAVEDFRONTIERCENTERS`"]},
    9: {"W4": [r"\boxed{12}", "8{,}231", "6{,}979", r"\boxed{15{,}210}", r"h_{\sigmaK}(\theta)=h_K(-\theta)."],
        "W5": [r"|\mathcalN_{10^{-3}}|=649.", r"\boxed{646/649}", r"\boxed{3766,\quad6217,\quad8001.}", r"\boxed{27.}", r"\boxed{27/27}",
               r"\boxed{1.65544\times10^{-4}.}", r"3.95\times10^{-4}.", r"D_1=A(c)-A_3^+-\sum_i|g_i|h_i.",
               r"D_2=A(c)-A_3^+-\sum_i|g_i|h_i-\frac12\sum_{ij}|H_{ij}^{\mathrm{num}}|h_ih_j."]},
}

CLAIMS = {
    "frontier_claim": {"roots": [22516, 5, 5], "membership_equal": True, "least": [0.835188375167486, 21409], "least_ulps": 2, "quantiles_agree": True,
                       "retries": [85, 76]},
    "lipschitz_claim": {"closes": 0, "perimeter": [3.33, 3.4], "tau": [0.00024, 0.031], "median_penalty_over_slack": 3.21},
    "skeleton_claim": {"body": 24, "piece": 1332, "coverage_equal": True, "catalog_equal": True, "stability": [549, 2000, 0.275], "against_theirs": -0.024},
    "reflection_claim": {"classes": 12, "catalog_equal": True, "members_are_reversals": True, "dominant": [15210, [6979, 8231]]},
    "taylor_claim": {"near": [649, True, 27, True], "stored_areas": 1e-15, "stored_halfwidths": True, "gradient_diff": 4e-08,
                     "d1": [646, [3766, 6217, 8001], 3e-11], "d2": [27, 0.0001655, 3e-12], "hessian_diff": 2e-05, "stored_d2_from_parts": True,
                     "summary": [True, True, True, True, True]},
    "crystal_claim": {"C07": [True, True, True, True, False], "C08": [True, True, True, True, 0.2986], "C09": [True, True, True, True]},
    "code_facts": {"lines": {7: 66, 8: 92, 9: 61}, "executable": {7: 26, 8: 39, 9: 39},
                   "defs": {7: ["certify_signature_stability", "interval_area_of_signature", "classify_box"],
                            8: ["cyclic_compress", "body_level_from_piece_labels", "interval_newton_root", "certify_winner_gap", "interval_area_two_level"],
                            9: ["first_order_lower", "second_order_lower", "verify_multichart"]},
                   "not_implemented": {7: 2, 8: 3, 9: 0}, "imports": {7: [], 8: ["math", "numpy"], 9: ["dataclasses", "typing"]}},
}


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


def a0_anchors(w):
    out = []
    for i, groups in ANCHORS.items():
        text = gate.squash(w["texts"][i])
        out += [f"Cycle {i:02d} {cid}: anchor not found: {a[:50]}" for cid, frags in groups.items() for a in frags if a not in text]
    return out


def membership(C, H):
    lo = np.hypot(np.maximum(np.abs(C[:, 0] - X30) - H[:, 0], 0), np.maximum(np.abs(C[:, 1]) - H[:, 1], 0))
    hi = np.hypot(np.abs(C[:, 0] - X30) + H[:, 0], np.abs(C[:, 1]) + H[:, 1])
    return [float(np.mean(lo >= S_SYM)), float(np.mean((lo < S_SYM) & (hi >= S_SYM))), float(np.mean(hi < S_SYM))]


def w1_frontier(w):
    """Cycle 07: the saved roots; their membership in F3 (a box is in when its least d3 reaches 0.00882, out when its
    greatest does not); the exact centre areas - the least, where, and the quantiles the probe records"""
    C, H, p = w["frontier"]["centers"], w["frontier"]["halfwidths"], w["probe"]["current_frontier"]
    a = np.array(w["search"]["scan"]["area"])
    m = membership(C, H)
    got = {"roots": [int(C.shape[0]), int(C.shape[1]), int(H.shape[1])],
           "membership_equal": m == [p["fully_F3_fraction"], p["straddles_F3_boundary_fraction"], p["already_not_F3_fraction"]],
           "least": [sig(float(a.min()), 16), int(a.argmin())],
           "least_ulps": round(abs(float(a.min()) - p["exact_center_area_min"]) / math.ulp(p["exact_center_area_min"])),
           "quantiles_agree": float(np.max(np.abs(np.quantile(a, QUANTILES) - p["exact_center_area_quantiles"]))) < 5e-9,
           "retries": w["search"]["scan"]["retries"]}
    w["w1"] = got
    return [] if got["membership_equal"] and got["quantiles_agree"] and got == w["frontier_claim"] else [f"Cycle 07's frontier: {got}"]


def w2_lipschitz(w):
    """Cycle 07 sec. 12: A(v) >= A(c) - P(c) tau(B) on each box; how many frontier roots it would close, and how
    the penalty compares with each centre's slack over A3+"""
    C, H = w["frontier"]["centers"], w["frontier"]["halfwidths"]
    a, P = np.array(w["search"]["scan"]["area"]), np.array(w["search"]["scan"]["perimeter"])
    tau = np.maximum(np.hypot(H[:, 0], H[:, 1]), np.hypot(H[:, 3], H[:, 4]) + 2 * r44.rn(5) * np.sin(H[:, 2] / 2))
    got = {"closes": int(np.sum(a - P * tau > A3)), "perimeter": [sig(float(P.min()), 3), sig(float(P.max()), 3)],
           "tau": [sig(float(tau.min()), 2), sig(float(tau.max()), 2)], "median_penalty_over_slack": sig(float(np.median(P * tau / (a - A3))), 3)}
    w["w2"] = got
    return [] if got == w["lipschitz_claim"] else [f"Cycle 07's Lipschitz bound: {got}"]


def w3_skeletons(w):
    """Cycle 08: the body skeleton (the bodies in the hull's cyclic order) and the full piece signature at every
    centre; the coverage of the commonest; the catalog; corner stability on a sample of 2,000 roots"""
    scan, cat, st = w["search"]["scan"], w["catalog"], w["search"]["stability"]
    N = len(scan["body"])
    bc = collections.Counter(tuple(b) for b in scan["body"])
    top = [c for _, c in bc.most_common()]
    cov = [sum(top[:2]) / N, sum(top[:5]) / N, sum(top[:10]) / N]
    theirs = collections.Counter({canon(t["sequence"]): t["count"] for t in cat["topologies"]})
    frac = st["stable"] / st["sample"]
    got = {"body": len(bc), "piece": len({tuple(p) for p in scan["piece"]}), "coverage_equal": cov == [cat["coverage"][k] for k in ("top_2", "top_5", "top_10")],
           "catalog_equal": bc == theirs and cat["observed_center_body_skeleton_count"] == len(bc) and cat["observed_center_full_piece_signature_count"] == len({tuple(p) for p in scan["piece"]}),
           "stability": [st["stable"], st["sample"], round(frac, 3)], "against_theirs": round(frac - cat["corner_stability_probe"]["stable_fraction"], 3)}
    w["w3"] = got
    return [] if got["coverage_equal"] and got["catalog_equal"] and got == w["skeleton_claim"] else [f"Cycle 08's skeletons: {got}"]


def w4_reflection(w):
    """Cycle 09 sec. 1-5: reflection reverses the cyclic order, so the skeletons fall into classes under rotation and
    reversal; the classes and their counts against the catalog; the dominant class"""
    scan, ref = w["search"]["scan"], w["reflection"]
    un = collections.Counter(unoriented(b) for b in scan["body"])
    theirs = collections.Counter({unoriented(c["canonical_sequence"]): c["count"] for c in ref["classes"]})
    members = all(len({unoriented(m["sequence"]) for m in c["oriented_members"]}) == 1 and sum(m["count"] for m in c["oriented_members"]) == c["count"]
                  for c in ref["classes"])
    (dom, n), = un.most_common(1)
    got = {"classes": len(un), "catalog_equal": un == theirs, "members_are_reversals": members,
           "dominant": [n, sorted(c for s, c in collections.Counter(tuple(b) for b in scan["body"]).items() if unoriented(s) == dom)]}
    w["w4"] = got
    return [] if got["catalog_equal"] and got["members_are_reversals"] and got == w["reflection_claim"] else [f"Cycle 09's reflection: {got}"]


def w5_taylor(w):
    """Cycle 09 sec. 11-16: the dominant-class roots within 1e-3 and 5e-4 of A3+ rebuilt from the scan; the stored
    centre areas and half-widths; this tree's central differences against the stored gradients and Hessians; D1 and
    D2 from them; the summary against its own arrays"""
    T, scan, S = {k: np.asarray(v) for k, v in w["taylor"].items()}, w["search"]["scan"], w["summary"]
    C, H = w["frontier"]["centers"], w["frontier"]["halfwidths"]
    near1, near5 = near_sets(scan)
    idx = [int(i) for i in T["frontier_indices"]]
    G = w["search"]["gradients"]
    g = np.array([G[str(i)]["g"] for i in idx])
    d1 = np.array([scan["area"][i] for i in idx]) - A3 - np.sum(np.abs(g) * H[idx], axis=1)
    cl = [int(i) for i in T["closest_frontier_indices"]]
    Hs = np.array([w["search"]["hessians"][str(i)]["H"] for i in cl])
    k = [idx.index(i) for i in cl]
    d2 = d1[k] - 0.5 * np.einsum("kij,ki,kj->k", np.abs(Hs), H[cl], H[cl])
    theirs2 = T["first_order_margin"][k] - 0.5 * np.einsum("kij,ki,kj->k", np.abs(T["fd_hessian"]), H[cl], H[cl])
    got = {"near": [len(near1), sorted(near1) == sorted(idx), len(near5), sorted(near5) == sorted(cl)],
           "stored_areas": sig(float(np.max(np.abs(np.array([scan["area"][i] for i in idx]) - T["center_area"]))), 1),
           "stored_halfwidths": bool(np.array_equal(T["halfwidths"], H[idx])),
           "gradient_diff": sig(float(np.max(np.abs(g - T["fd_gradient"]))), 1),
           "d1": [int(np.sum(d1 > 0)), sorted(int(idx[j]) for j in np.nonzero(d1 <= 0)[0]), sig(float(np.max(np.abs(d1 - T["first_order_margin"]))), 1)],
           "d2": [int(np.sum(d2 > 0)), sig(float(d2.min()), 4), sig(float(np.max(np.abs(d2 - T["numerical_second_margin"]))), 1)],
           "hessian_diff": sig(float(np.max(np.abs(Hs - T["fd_hessian"]))), 1),
           "stored_d2_from_parts": float(np.max(np.abs(theirs2 - T["numerical_second_margin"]))) < 1e-15,
           "summary": [S["roots_with_center_area_within_1e-3_of_ceiling"] == len(idx), S["first_order_positive"] == int(np.sum(T["first_order_margin"] > 0)),
                       S["first_order_exception_frontier_indices"] == sorted(int(idx[j]) for j in np.nonzero(T["first_order_margin"] <= 0)[0]),
                       float(np.max(np.abs(np.quantile(T["first_order_margin"], (0, 0.01, 0.05, 0.25, 0.5, 0.75, 0.95, 1)) - S["first_order_margin_quantiles"]))) == 0.0,
                       float(np.max(np.abs(np.quantile(T["numerical_second_margin"], (0, 0.1, 0.5, 1)) - S["second_order_margin_quantiles"]))) == 0.0]}
    w["w5"] = got
    return [] if got["near"][1] and got["near"][3] and all(got["summary"]) and got == w["taylor_claim"] else [f"Cycle 09's Taylor diagnostic: {got}"]


def w6_crystals(w):
    """the three state crystals against the numbers rebuilt here"""
    c7, c8, c9 = (w["crystals"][i] for i in (7, 8, 9))
    scan, C, H = w["search"]["scan"], w["frontier"]["centers"], w["frontier"]["halfwidths"]
    a = np.array(scan["area"])
    fr = c7["runtime_probe"]["current_frontier"]
    bc = collections.Counter(tuple(b) for b in scan["body"])
    top = [c for _, c in bc.most_common()]
    near1, near5 = near_sets(scan)
    got = {"C07": [c7["runtime_probe"] == w["probe"], fr["roots"] == len(C),
                   abs(fr["exact_center_area_min"] - float(a.min())) <= 4 * math.ulp(fr["exact_center_area_min"]),
                   membership(C, H) == [fr["fully_F3_fraction"], fr["straddles_F3_boundary_fraction"], fr["already_not_F3_fraction"]],
                   c7["diagnosis"]["second_low_area_basin_observed"]],
           "C08": [c8["frontier_roots"] == len(C), c8["topology_compression"]["observed_body_level_center_skeletons"] == len(bc),
                   c8["topology_compression"]["observed_full_piece_center_signatures"] == len({tuple(p) for p in scan["piece"]}),
                   c8["topology_compression"]["top10_coverage"] == sum(top[:10]) / len(C), c8["diagnostics"]["corner_stability_fraction"]],
           "C09": [c9["reflection_quotient"]["unoriented_reflection_classes"] == len({unoriented(b) for b in scan["body"]}),
                   c9["taylor_diagnostic"]["roots_with_center_area_within_1e-3_of_ceiling"] == len(near1),
                   c9["taylor_diagnostic"]["roots_with_center_area_within_5e-4_of_ceiling"] == len(near5),
                   c9["taylor_diagnostic"] == {k: v for k, v in w["summary"].items()}]}
    w["w6"] = got
    return [] if got == w["crystal_claim"] else [f"the crystals: {got}"]


def w7_code(w):
    """the three scaffolds, read after the rebuild and never run"""
    code = w["code"]
    body = lambda t: [l for l in re.sub(r'"""[\s\S]*?"""', "", t).splitlines() if l.strip() and not l.strip().startswith("#")]
    got = {"lines": {i: t.count("\n") for i, t in code.items()}, "executable": {i: len(body(t)) for i, t in code.items()},
           "defs": {i: re.findall(r"^def (\w+)", t, re.M) for i, t in code.items()},
           "not_implemented": {i: t.count("raise NotImplementedError") for i, t in code.items()},
           "imports": {i: sorted(set(re.findall(r"^(?:import|from) (\w+)", t, re.M))) for i, t in code.items()}}
    w["w7"] = got
    return [] if got == w["code_facts"] else [f"the scaffolds: {got}"]


def p1_package(w):
    """each VALIDATION digest against the bytes hashed here and the archive's manifest; the frontier's copies in the
    packages of Cycles 08 and 09 byte-identical to Cycle 07's"""
    files = w["files"]
    sha = lambda k: hashlib.sha256(files[k]).hexdigest()
    v7, v8, v9 = (w["validations"][i] for i in (7, 8, 9))
    checks = {"C07": [v7["paper_sha256"] == sha(DOCS[7]), v7["frontier_sha256"] == sha(DATA["frontier"]), v7["runtime_stats_sha256"] == sha(DATA["probe"]),
                      v7["scaffold_sha256"] == sha(CODE[7]), v7["state_sha256"] == sha(CRYSTALS[7])],
              "C08": [v8["paper_sha256"] == sha(DOCS[8]), v8["catalog_sha256"] == sha(DATA["catalog"]), v8["compiler_sha256"] == sha(CODE[8]),
                      v8["state_sha256"] == sha(CRYSTALS[8])],
              "C09": [v9["paper_sha256"] == sha(DOCS[9]), v9["reflection_catalog_sha256"] == sha(DATA["reflection"]),
                      v9["taylor_diag_npz_sha256"] == sha(DATA["taylor"]), v9["taylor_diag_summary_sha256"] == sha(DATA["summary"]),
                      v9["compiler_sha256"] == sha(CODE[9]), v9["state_sha256"] == sha(CRYSTALS[9])],
              "statuses": [v["status"] == "PASS" for v in (v7, v8, v9)],
              "manifest": [w["manifest_rows"][k][2] == sha(k) for k in sorted(files)],
              "copies": [files[c] == files[DATA["frontier"]] for c in COPIES]}
    w["p1"] = {k: all(v) for k, v in checks.items()}
    return [k for k, v in checks.items() if not all(v)]


CHECKS = {"A0": a0_anchors, "W1": w1_frontier, "W2": w2_lipschitz, "W3": w3_skeletons, "W4": w4_reflection, "W5": w5_taylor, "W6": w6_crystals,
          "W7": w7_code, "P1": p1_package}
DEPENDS = {"A0": {"texts"}, "W1": {"frontier", "probe", "search", "frontier_claim"}, "W2": {"frontier", "search", "lipschitz_claim"},
           "W3": {"search", "catalog", "skeleton_claim"}, "W4": {"search", "reflection", "reflection_claim"},
           "W5": {"taylor", "search", "summary", "frontier", "taylor_claim"}, "W6": {"crystals", "search", "frontier", "probe", "summary", "crystal_claim"},
           "W7": {"code", "code_facts"}, "P1": {"validations", "files", "manifest_rows"}}


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

    texts, s = base["texts"], base["search"]
    least, most = int(np.argmin(s["scan"]["area"])), int(np.argmax(s["scan"]["area"]))    # the frontier's lowest and highest centres
    return [
        ("K0-unmodified", "control", {}, set()),
        ("K1-crlf", "control", {"texts": {i: t.replace("\n", "\r\n") for i, t in texts.items()}, "code": {i: t.replace("\n", "\r\n") for i, t in base["code"].items()}},
         set()),
        ("K2-validation-keys-sorted", "control", {"validations": {i: json.loads(json.dumps(v, sort_keys=True)) for i, v in base["validations"].items()}}, set()),
        ("M1-visited-nodes", "mutation", {"texts": {**texts, 7: texts[7].replace("490{,}166", "490{,}167")}}, {"A0"}),
        ("M2-probe-fraction", "mutation", {"probe": edit(base["probe"], lambda x: x["current_frontier"].update(fully_F3_fraction=0.9205))}, {"W1", "W6"}),
        ("M3-least-centre-area", "mutation", {"search": edit(s, lambda x: x["scan"]["area"].__setitem__(least, x["scan"]["area"][least] - 1e-7))},
         {"W1", "W5", "W6"}),
        ("M4-perimeter", "mutation", {"search": edit(s, lambda x: x["scan"]["perimeter"].__setitem__(0, 0.0))}, {"W2"}),
        ("M5-skeleton", "mutation", {"search": edit(s, lambda x: x["scan"]["body"].__setitem__(most, ["B3", "D"]))}, {"W3", "W4", "W6"}),
        ("M6-catalog-count", "mutation", {"catalog": edit(base["catalog"], lambda x: x["topologies"][0].update(count=8230))}, {"W3"}),
        ("M7-stability", "mutation", {"search": edit(s, lambda x: x["stability"].update(stable=x["stability"]["stable"] + 1))}, {"W3"}),
        ("M8-reflection-member", "mutation", {"reflection": edit(base["reflection"], lambda x: x["classes"][0]["oriented_members"][0].update(count=8230))}, {"W4"}),
        ("M9-taylor-gradient", "mutation", {"taylor": {**base["taylor"], "fd_gradient": (np.asarray(base["taylor"]["fd_gradient"]) * 1.01).tolist()}}, {"W5"}),
        ("M10-summary-exception", "mutation", {"summary": {**base["summary"], "first_order_exception_frontier_indices": [3766, 6217]}}, {"W5", "W6"}),
        ("M11-crystal-stability", "mutation", {"crystals": edit(base["crystals"], lambda x: x[8]["diagnostics"].update(corner_stability_fraction=0.3))}, {"W6"}),
        ("M12-scaffold-def", "mutation", {"code": {**base["code"], 9: base["code"][9] + "\ndef extra():\n    return 0\n"}}, {"W7"}),
        ("M13-validation-digest", "mutation", {"validations": edit(base["validations"], lambda x: x[7].update(frontier_sha256="0" * 64))}, {"P1"}),
        ("M14-copy-bytes", "mutation", {"files": {**base["files"], COPIES[0]: base["files"][COPIES[0]] + b" "}}, {"P1"}),
    ]


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--drill", action="store_true")
    ap.add_argument("--blind", metavar="CID")
    args = ap.parse_args()
    base = world(load_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", "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-046", "subject": "UESFCM Cycles 07-09: the F3 runtime frontier, its body skeletons, the reflection quotient and the Taylor diagnostic",
           "documents": list(DOCS.values()), "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", "p1")}, "drill": summary}
    LOG.write_text(json.dumps(log, ensure_ascii=False, indent=1, default=str) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
