"""RUN-059 - LESR Paper 09A v0.2: the multi-scale topology wall and the one-sided blow-up critical cone.

LESR Paper 09A v0.2 (2026-09-20) withdraws Paper 07's static-pressure argument for m'(0+) = 0 and the assumption of
one ordinary chart for all small rho > 0. It puts the positive-rotation branch of the root-6217 barrier in a one-sided
blow-up chart - B3 on the circle d3 = s at alpha3 = pi - rho u, y5 = rho v, x5 = x5* + rho w - and reports, at Cycle
12's first profile sample (rho = 0.0010069...), 18 merged open winner intervals against the barrier's 13: two primary
newborn arcs of width about 0.1480 rho (B3, near pi/2) and 1.1480 rho (B5, near 3 pi/2), and three thinner slivers (the
disc near pi/2, B5's corners near pi -/+ theta*) whose O(rho^2) scaling it calls a working hypothesis. It defines the
first and second epi-derivatives Phi1, Phi2, the critical set C1 and the reduced coefficient c* = inf inf Phi2, reads
u ~ 5.16, v ~ -0.1892, w ~ 0 and a ratio ~ 0.22 off the numerics, proposes a primary velocity box, and splits the local
gate into four certificates. It ships a diagnostic, a certificate schema, a state record and VALIDATION. It is
evidence, not a canonical document. Here the signature is rebuilt on this tree's verified hull, the branch is
re-minimized on the exact hull at three rho, and Phi1 is measured on a grid, at its minimizer and along the ray there.

Checks
  A0 anchors      the statements below occur in the document as written
  W1 chart        sec. 4, 12, 16: the stored positive samples on the circle d3 = s; the branch re-minimized here at rho =
                  0.002, 0.001, 0.0005 - its (u, v, w), their limit, the section-16 box
  W2 signature    sec. 5: the merged winner intervals at the barrier and at the anchor sample, against the diagnostic's
  W3 newborn      sec. 6-7: the five newborn intervals - at the anchor sample as the diagnostic lists them; at the
                  re-minimized branch the primary ones' coefficients, and the secondary ones' widths
  W4 first order  sec. 10-11, 14, 16: Phi1 on a grid around the box, its minimizer and slopes there, and the area along
                  the ray through it
  W5 growth       sec. 13, 18-19: the ratio (m(rho) - m(0)) / rho^2 on the re-minimized branch; the tail gate and the
                  200 -> 195 ledger as Paper 08 left them (RUN-058)
  W6 records      the diagnostic, the schema and the state against the text, Cycle 12's profile and Paper 09B's title
  P1 package      the VALIDATION record against the bytes and the archive

Usage: python code/run059_lesr_paper09a.py [--drill] [--blind CID]
"""
import argparse
import copy
import hashlib
import inspect
import itertools
import json
import math
import re
from multiprocessing import Pool

import numpy as np
from scipy.optimize import minimize

import gate
import run044_uesfcm_cycles_01_03 as r44
import run046_uesfcm_cycles_07_09 as r46
import run048_uesfcm_cycles_12_14 as r48
from corpus import SOURCES, TREE, source_bytes, walk_sources
from run052_lesr_paper02 import segs_of

FOLDER = "main.zip!/LESR_Skew_Field_Variational/"
PKG = FOLDER + "LESR_Paper09A_v0.2_Package.zip!/"
DOC = PKG + "LESR_Paper09A_Multiscale_Topology_Wall_and_Blowup_Critical_Cone_v0.2.md"
DIAG = PKG + "LESR_Paper09A_Multiscale_Topology_Wall_Diagnostic_v0.2.json"
SCHEMA = PKG + "LESR_Paper09A_Multiscale_Certificate_Schema_v0.2.json"
STATE = PKG + "LESR_State_Paper09A_v0.2.json"
VALIDATION = PKG + "LESR_Paper09A_v0.2_VALIDATION.json"
PAPER09B = FOLDER + "LESR_Paper09B_Package_v0.1.zip!/LESR_Paper09B_Analytic_Primary_Blowup_Equations_v0.1.md"
LOG = TREE / "data" / "gate-logs" / "run059-lesr-paper09a.json"
LOG58 = TREE / "data" / "gate-logs" / "run058-lesr-paper08.json"
MANIFEST = TREE / "data" / "SOURCE_MANIFEST.v1.json"
CACHE = TREE / "data" / "cache" / "run059-searches.json"     # data/cache is not committed
X30, S = -0.01215883209899, 0.00882
X5 = 0.019770946347947549                                    # x5* (RUN-057)
VB = [X30 - S, 0.0, 0.0, X5, 0.0]
RHO0 = 0.0010069207223044208                                 # Cycle 12's first positive sample, the paper's anchor
RHOS = (0.002, RHO0, 0.0005, 0.00025)
BOX = ((4.5, 6.0), (-0.23, -0.15), (-0.01, 0.01))
TIE = 1e-10                                                  # a winner interval narrower than this is a tie, not an interval


def load_inputs():
    blobs = {n: source_bytes(n) for n in SOURCES}
    want = (DOC, DIAG, SCHEMA, STATE, VALIDATION)
    got = {leaf.id: d for leaf, d in walk_sources(blobs, keep=lambda l: l.id in want + (PAPER09B,)) if d is not None}
    rows = {row[0]: row for row in json.loads(MANIFEST.read_text(encoding="utf-8"))["leaves"]}
    c12 = r48.load_inputs()
    return {"text": got[DOC].decode("utf-8"), "diag": json.loads(got[DIAG]), "schema": json.loads(got[SCHEMA]), "state": json.loads(got[STATE]),
            "validation": json.loads(got[VALIDATION]), "bytes": {k: got[k] for k in want}, "manifest_rows": {k: rows[k] for k in want},
            "paper09b": got[PAPER09B].decode("utf-8"), "profile": c12["profile"], "log58": json.loads(LOG58.read_text(encoding="utf-8"))}


# ---------------------------------------------------------------- the hull's winner intervals

def merged(v):
    """the verified hull's winner intervals, in angular order from normal 0, with consecutive runs of one piece merged:
    [start, width, piece]; a run across normal 0 is one interval here"""
    _, _, segs = segs_of(r44.base_pieces(v))
    out = []
    for a, w, k in sorted(((a % (2 * math.pi), b - a, k) for a, b, k in segs), key=lambda x: x[0]):
        if out and out[-1][2] == k and abs(out[-1][0] + out[-1][1] - a) < 1e-9:
            out[-1][1] += w
        else:
            out.append([a, w, k])
    if len(out) > 1 and out[0][2] == out[-1][2] and abs((out[-1][0] + out[-1][1]) % (2 * math.pi) - out[0][0]) < 1e-9:
        last = out.pop()
        out[0] = [last[0], last[1] + out[0][1], last[2]]
    return out


def signature(v):
    """the open winner intervals (ties dropped, neighbours of one piece merged again) as [start, end, piece name], cut at
    normal 0 as the diagnostic lists them"""
    runs = []
    for a, w, k in merged(v):
        if w <= TIE:
            continue
        if runs and runs[-1][2] == k and abs(runs[-1][0] + runs[-1][1] - a) < 1e-9:
            runs[-1][1] += w
        else:
            runs.append([a, w, k])
    out = []
    for a, w, k in runs:
        b = a + w
        if b > 2 * math.pi + 1e-12:
            out += [[a, 2 * math.pi, r46.PIECE[k]], [0.0, b - 2 * math.pi, r46.PIECE[k]]]
        else:
            out.append([a, b, r46.PIECE[k]])
    return sorted(out)


def newborn(v):
    """the intervals narrower than 0.01 rad at v: [piece, start, width], ties included"""
    return [[r46.PIECE[k], a, w] for a, w, k in merged(v) if w < 0.01]


# ---------------------------------------------------------------- the blow-up chart

def point(rho, u, v, w):
    """sec. 4: B3 on the circle d3 = s at alpha3 = pi - rho u, B5 turned by rho and moved to (x5* + rho w, rho v)"""
    return [X30 + S * math.cos(math.pi - rho * u), S * math.sin(math.pi - rho * u), rho, X5 + rho * w, rho * v]


def chart(p):
    """(u, v, w) of a placement on the circle, and its distance from the circle"""
    rho = p[2]
    return [(math.pi - math.atan2(p[1], p[0] - X30)) / rho, p[4] / rho, (p[3] - X5) / rho], math.hypot(p[0] - X30, p[1]) - S


def polish(f, z, scales=(1.0, 0.1, 0.01, 1e-3, 1e-4, 1e-5), rounds=3):
    """Nelder-Mead restarted from shrinking simplices (steps 0.02, 0.002, 0.002 times the scale) until a round gains
    less than 1e-12: the area is kinked at the branch's minimum, where one simplex stalls"""
    best = (f(z), list(z))
    base = np.array([0.02, 0.002, 0.002])
    for _ in range(rounds):
        before = best[0]
        for s in scales:
            z0 = np.array(best[1])
            simplex = [z0] + [z0 + s * base[i] * np.eye(3)[i] for i in range(3)]
            r = minimize(f, z0, method="Nelder-Mead",
                         options={"initial_simplex": np.array(simplex), "xatol": 1e-11, "fatol": 1e-14, "maxfev": 600, "adaptive": True})
            if r.fun < best[0]:
                best = (float(r.fun), [float(x) for x in r.x])
        if before - best[0] < 1e-12:
            break
    return best


def branch_one(rho):
    """the positive branch at rho: (A - A0) / rho^2 minimized over (u, v, w) on the exact hull; the minimizer's area,
    ratio, chart and newborn intervals"""
    a0 = r46.area(VB)
    val, z = polish(lambda q: (r46.area(point(rho, *q)) - a0) / rho ** 2, [5.16 + 36 * rho, -0.1892, -0.18 * rho])
    p = point(rho, *z)
    return {"rho": rho, "ratio": val, "z": z, "area": r46.area(p), "newborn": newborn(p)}


def phi1(u, v, w, rho=1e-6):
    """Phi1(u, v, w) = lim (A(rho, u, v, w) - A0) / rho: Richardson from rho and 2 rho (2 f(rho) - f(2 rho))"""
    a0 = r46.area(VB)
    f = lambda r: (r46.area(point(r, u, v, w)) - a0) / r
    return 2 * f(rho) - f(2 * rho)


def phi1_min(_):
    """Phi1's minimizer by the same restarted Nelder-Mead, and its one-sided slopes along each axis there"""
    f = lambda z: phi1(*z)
    val, z = polish(f, [5.16, -0.1892, 0.0], rounds=4)
    slopes = []
    for i, h in enumerate((1e-3, 1e-4, 1e-4)):
        e = np.eye(3)[i] * h
        slopes.append([(f(np.array(z) + e) - val) / h, (f(np.array(z) - e) - val) / h])
    return {"value": val, "z": z, "slopes": slopes}


def task(job):
    return branch_one(job[1]) if job[0] == "branch" else phi1_min(None)


# ---------------------------------------------------------------- the searches

def anchor_scan(profile):
    s = profile["samples"][1]
    v = [s["x3"], s["y3"], s["rho"], s["x5"], s["y5"]]
    return {"barrier": signature(VB), "barrier_merged": len(merged(VB)), "anchor": signature(v), "newborn": newborn(v), "area": r46.area(v),
            "samples": [[t["rho"], *chart([t["x3"], t["y3"], t["rho"], t["x5"], t["y5"]])] for t in profile["samples"][1:]]}


def branch_scan():
    with Pool(len(RHOS) + 1) as pool:
        out = pool.map(task, [("branch", r) for r in RHOS] + [("phi1", None)], chunksize=1)
    return {"branch": out[:-1], "phi1_min": out[-1]}


def grid_scan(zmin):
    """Phi1 on a 280-point grid around the box, and the area along the ray through Phi1's minimizer"""
    U = (3.0, 4.0, 4.5, 5.0, 5.16, 5.5, 6.0, 7.0)
    V = (-0.3, -0.23, -0.2, -0.1892, -0.17, -0.15, -0.1)
    W = (-0.05, -0.01, 0.0, 0.01, 0.05)
    grid = [[phi1(u, v, w), u, v, w] for u, v, w in itertools.product(U, V, W)]
    a0 = r46.area(VB)
    ray = [[rho, r46.area(point(rho, *zmin)) - a0] for rho in (3e-8, 1e-7, 3e-7, 1e-6, 3e-6, 1e-5, 3e-5, 1e-4, 3e-4, 1e-3)]
    fits = {str(r): phi1(*zmin, rho=r) for r in (1e-5, 1e-6)}
    return {"grid": grid, "ray": ray, "richardson": fits}


def minimum_signatures(branch):
    """the signature (cut at normal 0) at each re-minimized branch point"""
    return [[x["rho"], [p for _, _, p in signature(point(x["rho"], *x["z"]))]] for x in branch]


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


HELPERS = {"anchor": (merged, signature, newborn, chart), "branch": (branch_one, phi1_min, phi1, polish, point, newborn, merged, task),
           "grid": (phi1, point), "minsig": (signature, merged, point)}


def searches(inputs):
    cached = json.loads(CACHE.read_text(encoding="utf-8")) if CACHE.exists() else {}
    out = {}
    tag = lambda *xs: hashlib.sha256(json.dumps(xs, sort_keys=True).encode()).hexdigest()
    jobs = (("anchor", anchor_scan, lambda: anchor_scan(inputs["profile"]), lambda: tag(inputs["profile"])),
            ("branch", branch_scan, lambda: branch_scan(), lambda: tag(RHOS)),
            ("grid", grid_scan, lambda: grid_scan(out["branch"]["phi1_min"]["z"]), lambda: tag(out["branch"]["phi1_min"]["z"])),
            ("minsig", minimum_signatures, lambda: minimum_signatures(out["branch"]["branch"]), lambda: tag([x["z"] for x in out["branch"]["branch"]])))
    for name, fn, call, extra in jobs:
        key = _key(name, fn, extra())
        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"\text{staticzero-chartpressurebalance}\Rightarrowm'(0^+)=0.", r"\text{thepositive-}\rho\text{branchisitselfmulti-scalenearthewall.}",
                  r"\rho=0.0010069207223044208\ldots", r"$$\boxed{18}$$mergedopenwinnerintervals.", "Hencefiveadditionalopenintervalsappearonthispositivebranch.",
                  r"0.1480\,\rho.", r"1.1480\,\rho.", "iscurrentlyonlyaworkingasymptotichypothesis.", r"u\approx5.16,", r"v\approx-0.1892,",
                  r"w\approx0.", r"\frac{m(\rho)-m(0)}{\rho^2}\approx0.22", r"u\in[4.5,6.0],", r"v\in[-0.23,-0.15],", r"w\in[-0.01,0.01].",
                  r"\Phi_2\ge0.1.", r"200\to195."]}
CLAIMS = {
    # the stored samples within 2.4e-8 of the circle; the anchor's (u, v, w); the re-minimized branch at four rho, linear in
    # rho, its limit (5.157, -0.1892, 0) - Paper 09B's closed form (5.157126..., -0.189228..., 0)
    "chart_claim": {"on_circle": 2.4e-08, "anchor_uvw": [5.1967, -0.1891, -0.0002],
                    "branch": [[0.002, 5.2312, -0.18896, -0.00035], [RHO0, 5.1944, -0.18909, -0.00018], [0.0005, 5.1756, -0.18916, -9e-05],
                               [0.00025, 5.1664, -0.18919, -4e-05]],
                    "limit": [5.157, -0.1892, 0.0], "in_box": [True, True, True], "stated": [True, True, True]},
    # 13 at the barrier when B3's corner v0 is cut at normal 0 (12 around the circle); 18 at the anchor, the diagnostic's
    "signature_claim": {"barrier": [13, 12], "anchor": 18, "diagnostic": [18, 18], "same_pieces": True, "ends": 2e-08},
    # the anchor's five newborn intervals (width / rho) are the diagnostic's to 6e-9; on the branch B3's arc is 0.149 ->
    # 0.147 rho, B5's is that plus rho, and the three secondary slivers are ties; the anchor is 1.5e-9 above the branch
    "newborn_claim": {"anchor": [["D", 8.4595e-06], ["B3a2", 0.14804], ["B5v2", 0.0003696], ["B5v3", 0.00069135], ["B5a1", 1.148]],
                      "diagnostic": [True, 6e-09],
                      "primary": [[0.002, 0.14903], [RHO0, 0.14797], [0.0005, 0.14741], [0.00025, 0.14714]], "b5_is_b3_plus_rho": True,
                      "secondary_below_1e-11": [True, True, True, True], "pieces_at_minimum": [15, 15, 15, 15], "same_at_every_rho": True,
                      "anchor_excess": 1.5e-09},
    # Phi1 >= 0 on the grid (least 8.6e-6 next to the critical point), >= 5.4e-4 outside the box; one minimizer, with slopes
    # in every direction; the area along the ray through it positive at every rho, though rho >= 1e-6 extrapolates to -1.2e-7
    "first_order_claim": {"grid": [280, 8.6e-06, [5.16, -0.1892, 0.0]], "outside_least": 0.00054, "minimizer": [5.1572, -0.18923, 0.0],
                          "min_value": -1e-07, "slopes": [[0.0013, 0.0012], [0.26, 0.26], [0.4, 0.036]], "ray_positive": True,
                          "ray_ratio": [[1e-05, 0.336], [3e-05, 0.344], [0.0001, 0.347], [0.0003, 0.348], [0.001, 0.348]],
                          "richardson": [-1.2e-07, -1.2e-07]},
    "growth_claim": {"ratios": [0.225236, 0.225413, 0.225501, 0.225544], "limit": 0.2256, "target_slack": 2.26, "ledger_195": 195, "missing_share": 0.146},
    "records_claim": {"status": "NUMERICAL_STRUCTURAL_DIAGNOSTIC_NOT_PROOF", "corrections": ["WITHDRAWN", "WITHDRAWN"], "anchor_is_sample1": True,
                      "critical_branch": [5.16, -0.1892, 0.0, 0.22, 0.1],
                      "schema": [["PRIMARY_BLOWUP_CHART", "SECONDARY_SLIVER_CLASSIFICATION", "FIRST_EPI_DERIVATIVE_NONNEGATIVITY",
                                  "SECOND_EPI_DERIVATIVE_LOWER_0p1"], {"u": 5.16, "v": -0.1892, "w": 0.0, "c2_approx": 0.22},
                                 "Do not freeze an 18-piece signature globally; interval classification may yield several positive-rho subcharts."],
                      "state": ["MULTISCALE_TOPOLOGY_WALL_CORRECTION",
                                ["Paper07 zero-rotation barrier", "Paper08 local/tail two-gate decomposition", "barrier shape ledger 200->195"],
                                ["fixed zero-chart rotation derivative", "single smooth positive-rho chart assumption"],
                                "Paper 09B — Analytic Primary Blow-Up Equations"],
                      "next": [True, True]},
    "package_claim": {"validation": {"status": "PASS", "paper_sha256": "4e6589e4e8735088c234ed3738377dbdfef1a87a64e541e1448b3693d4350a0e",
                                     "diagnostic_sha256": "f80a2f7ba4c2ea0eb1da8c1370a89ae85f782ec1db515bfe94844d3b335a7e9f",
                                     "schema_sha256": "141fe9bfb94e3733567898acac73f1ebd97d901535b61bedb34add9061e13c10",
                                     "state_sha256": "2cb22302e416c6a984be2fbfff9e9d198a25573a678f2ab28e773d36e2f05324", "utf8": True,
                                     "math_delimiters": "PASS"},
                      "digests": {"LESR_Paper09A_Multiscale_Topology_Wall_and_Blowup_Critical_Cone_v0.2.md": "4e6589e4e8735088c234ed3738377dbdfef1a87a64e541e1448b3693d4350a0e",
                                  "LESR_Paper09A_Multiscale_Topology_Wall_Diagnostic_v0.2.json": "f80a2f7ba4c2ea0eb1da8c1370a89ae85f782ec1db515bfe94844d3b335a7e9f",
                                  "LESR_Paper09A_Multiscale_Certificate_Schema_v0.2.json": "141fe9bfb94e3733567898acac73f1ebd97d901535b61bedb34add9061e13c10",
                                  "LESR_State_Paper09A_v0.2.json": "2cb22302e416c6a984be2fbfff9e9d198a25573a678f2ab28e773d36e2f05324"},
                      "delimiters": [], "manifest": True},
}


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


def w1_chart(w):
    """sec. 4, 12, 16: the stored positive samples lie on the circle d3 = s; the re-minimized branch's (u, v, w) at four
    rho and their linear limit at rho -> 0; the section-16 box holds all of them"""
    s, b = w["search"]["anchor"], w["search"]["branch"]["branch"]
    zs = [x["z"] for x in b]
    lim = [2 * zs[3][i] - zs[2][i] for i in range(3)]          # u, v, w linear in rho: extrapolate from 0.0005 and 0.00025
    inbox = lambda z: all(lo <= x <= hi for x, (lo, hi) in zip(z, BOX))
    got = {"on_circle": sig(max(abs(t[2]) for t in s["samples"]), 2), "anchor_uvw": [round(x, 4) for x in s["samples"][0][1]],
           "branch": [[x["rho"], round(z[0], 4), round(z[1], 5), round(z[2], 5)] for x, z in zip(b, zs)],
           "limit": [round(lim[0], 3), round(lim[1], 4), round(lim[2], 4) + 0.0],          # + 0.0: no negative zero in the log
           "in_box": [inbox(s["samples"][0][1]), all(inbox(z) for z in zs), inbox(lim)],
           "stated": [abs(lim[0] - 5.16) < 0.01, abs(lim[1] + 0.1892) < 1e-4, abs(lim[2]) < 1e-3]}
    w["w1"] = got
    return [] if got == w["chart_claim"] else [f"the chart: {got}"]


def w2_signature(w):
    """sec. 5: at the barrier the open intervals are 12 around the circle, 13 cut at normal 0; at the anchor sample 18,
    each the diagnostic's - piece and ends"""
    s, d = w["search"]["anchor"], w["diag"]["cycle12_profile_anchor"]
    ds = d["merged_signature"]
    name = lambda p: {"D": "Disk arc"}.get(p) or f"{p[:2]} {'corner' if p[2] == 'v' else 'arc'} v{p[3]}"
    got = {"barrier": [len(s["barrier"]), sum(1 for x in s["barrier"] if x[0] > 0)], "anchor": len(s["anchor"]), "diagnostic": [d["merged_signature_count"], len(ds)],
           "same_pieces": [name(x[2]) for x in s["anchor"]] == [e["winner_piece"] for e in ds],
           "ends": sig(max(max(abs(x[0] - e["start"]), abs(x[1] - e["end"])) for x, e in zip(s["anchor"], ds)), 1)}
    w["w2"] = got
    return [] if got == w["signature_claim"] else [f"the signature: {got}"]


def w3_newborn(w):
    """sec. 6-7: at the anchor sample the five newborn intervals are the diagnostic's; on the re-minimized branch the
    primary ones are O(rho) - B3's arc near pi/2 with a coefficient that drifts, B5's arc near 3 pi/2 exactly rho wider -
    and the secondary ones are ties: the minimizer keeps the contacts they break"""
    s, b, d = w["search"]["anchor"], w["search"]["branch"]["branch"], w["diag"]
    rho0 = w["profile"]["samples"][1]["rho"]
    anchor = [[p, a, x] for p, a, x in s["newborn"] if x > TIE]
    thin = d["thin_intervals"]
    name = lambda p: {"D": "Disk arc"}.get(p) or f"{p[:2]} {'corner' if p[2] == 'v' else 'arc'} v{p[3]}"
    width = lambda nb, p: sum(x for q, _, x in nb if q == p)
    got = {"anchor": [[p, sig(x / rho0, 5)] for p, _, x in anchor],
           # the pieces, and the largest width difference: hand-overs between tangent pieces are located to ~1e-8 (W2)
           "diagnostic": [[name(p) for p, _, _ in anchor] == [t["winner_piece"] for t in thin], sig(max(abs(x - t["width"]) for (_, _, x), t in zip(anchor, thin)), 1)],
           "primary": [[x["rho"], round(width(x["newborn"], "B3a2") / x["rho"], 5)] for x in b],
           # B5's arc near 3 pi/2 is B3's near pi/2 plus rho (both bodies have constant width 1; the rho is B5's own turn)
           "b5_is_b3_plus_rho": all(abs(width(x["newborn"], "B5a1") - width(x["newborn"], "B3a2") - x["rho"]) < 1e-4 * x["rho"] for x in b),
           # the three secondary slivers at the minimizers: ties (the hull's own resolution is ~1e-13), not intervals
           "secondary_below_1e-11": [max([xx for p, _, xx in x["newborn"] if p in ("D", "B5v2", "B5v3")] + [0.0]) < 1e-11 for x in b],
           # so the signature at the minimum: 15 pieces, the same at every rho (Paper 09B's "primary 15-piece structure")
           "pieces_at_minimum": [len(p) for _, p in w["search"]["minsig"]],
           "same_at_every_rho": all(p == w["search"]["minsig"][0][1] for _, p in w["search"]["minsig"]),
           "anchor_excess": sig(s["area"] - b[1]["area"], 2) if b[1]["rho"] == rho0 else None}
    w["w3"] = got
    return [] if got == w["newborn_claim"] else [f"the newborn intervals: {got}"]


def w4_first_order(w):
    """sec. 10-11, 14, 16: Phi1 on the grid is non-negative to the resolution, least at the grid point next to the branch's
    limit, larger outside the box; its minimizer is one point, with a slope in every direction; along the ray through it
    the area rises at every rho down to the double-precision floor, where a straight extrapolation from rho >= 1e-6 would
    give -1.2e-7"""
    g, m = w["search"]["grid"], w["search"]["branch"]["phi1_min"]
    grid = sorted(g["grid"])
    inside = [r for r in grid if all(lo <= x <= hi for x, (lo, hi) in zip(r[1:], BOX))]
    outside = [r for r in grid if r not in inside]
    got = {"grid": [len(grid), sig(grid[0][0], 2), grid[0][1:]], "outside_least": sig(outside[0][0], 2),
           "minimizer": [round(m["z"][0], 4), round(m["z"][1], 5), round(m["z"][2], 5) + 0.0], "min_value": sig(m["value"], 1),
           "slopes": [[sig(a, 2), sig(b, 2)] for a, b in m["slopes"]],
           "ray_positive": all(d > 0 for _, d in g["ray"]), "ray_ratio": [[r, round(d / r ** 2, 3)] for r, d in g["ray"] if r >= 1e-5],
           "richardson": [sig(v, 2) for v in g["richardson"].values()]}
    w["w4"] = got
    return [] if got == w["first_order_claim"] else [f"the first order: {got}"]


def w5_growth(w):
    """sec. 13, 18-19: the ratio on the re-minimized branch and its limit; the tail gate and the ledger, which this paper
    leaves as Paper 08 had them (RUN-058: 14.6 % of root 6217's box closed for A3+ only is not in the 195)"""
    b, l58 = w["search"]["branch"]["branch"], w["log58"]
    r = [x["ratio"] for x in b]
    got = {"ratios": [round(x, 6) for x in r], "limit": round(2 * r[3] - r[2], 4), "target_slack": round((2 * r[3] - r[2]) / 0.1, 2),
           "ledger_195": l58["w4"]["file_counts"][0], "missing_share": l58["w4"]["shares"]["outside_f3"]}
    w["w5"] = got
    return [] if got == w["growth_claim"] else [f"the growth: {got}"]


def w6_records(w):
    d, sc, st = w["diag"], w["schema"], w["state"]
    prof = w["profile"]["samples"][1]
    a = d["cycle12_profile_anchor"]
    got = {"status": d["status"], "corrections": [d["correction"]["fixed_zero_chart_rotation_derivative"], d["correction"]["single_generic_chart_assumption"]],
           "anchor_is_sample1": all(a[k] == prof[k] for k in ("rho", "x3", "y3", "x5", "y5")) and a["area"] == prof["area_min_search"],
           "critical_branch": [d["critical_branch"][k] for k in ("u_approx", "v_approx", "w_approx", "reduced_quadratic_coefficient_approx", "certificate_target")],
           "schema": [sc["certificates"], sc["numerical_center"], sc["warning"]], "state": [st["status"], st["retained"], st["withdrawn"], st["next"]],
           "next": ["Paper09B" in gate.squash(json.dumps(st)), "AnalyticPrimaryBlow-UpEquations" in gate.squash(w["paper09b"][:400])]}
    w["w6"] = got
    return [] if got == w["records_claim"] else [f"the records: {got}"]


def p1_package(w):
    v, B = w["validation"], w["bytes"]
    sha = lambda b: hashlib.sha256(b).hexdigest()
    body = B[DOC].decode("utf-8")
    got = {"validation": v, "digests": {k.rsplit("/", 1)[-1]: sha(b) for k, b in B.items() if k != VALIDATION},
           "delimiters": sorted(set(re.findall(r"\\\(|\\\)|\\\[|\\\]", body))), "manifest": all(w["manifest_rows"][k][2] == sha(b) for k, b in B.items())}
    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_chart, "W2": w2_signature, "W3": w3_newborn, "W4": w4_first_order, "W5": w5_growth, "W6": w6_records, "P1": p1_package}
DEPENDS = {"A0": {"text"}, "W1": {"search", "chart_claim"}, "W2": {"search", "diag", "signature_claim"}, "W3": {"search", "diag", "profile", "newborn_claim"},
           "W4": {"search", "first_order_claim"}, "W5": {"search", "log58", "growth_claim"}, "W6": {"diag", "schema", "state", "profile", "paper09b", "records_claim"},
           "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 = base["text"], base["search"]
    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()),
        ("M1-primary-width", "mutation", {"text": text.replace(r"0.1480\,\rho.", r"0.1490\,\rho.")}, {"A0"}),
        ("M2-off-circle", "mutation", {"search": edit(s, lambda x: x["anchor"]["samples"][3].__setitem__(2, 1e-6))}, {"W1"}),
        ("M3-branch-u", "mutation", {"search": edit(s, lambda x: x["branch"]["branch"][3]["z"].__setitem__(0, 5.3))}, {"W1"}),
        ("M4-diagnostic-piece", "mutation",
         {"diag": edit(base["diag"], lambda x: x["cycle12_profile_anchor"]["merged_signature"][4].update(winner_piece="B5 corner v1"))}, {"W2"}),
        ("M5-signature-dropped", "mutation", {"search": edit(s, lambda x: x["anchor"]["anchor"].pop(8))}, {"W2"}),
        ("M6-diagnostic-width", "mutation", {"diag": edit(base["diag"], lambda x: x["thin_intervals"][2].update(width=x["thin_intervals"][2]["width"] + 1e-7))},
         {"W3"}),
        ("M7-branch-sliver", "mutation", {"search": edit(s, lambda x: x["branch"]["branch"][0]["newborn"].append(["B5v2", 2.6877, 1e-7]))}, {"W3"}),
        ("M8-grid-negative", "mutation", {"search": edit(s, lambda x: x["grid"]["grid"][17].__setitem__(0, -1e-3))}, {"W4"}),
        ("M9-ray-negative", "mutation", {"search": edit(s, lambda x: x["grid"]["ray"][4].__setitem__(1, -1e-14))}, {"W4"}),
        ("M10-branch-ratio", "mutation", {"search": edit(s, lambda x: x["branch"]["branch"][2].update(ratio=0.2301))}, {"W5"}),
        ("M11-ledger-share", "mutation", {"log58": edit(base["log58"], lambda x: x["w4"]["shares"].update(outside_f3=0.0))}, {"W5"}),
        ("M12-diagnostic-status", "mutation", {"diag": edit(base["diag"], lambda x: x.update(status="PROVED"))}, {"W6"}),
        ("M13-state-next", "mutation", {"state": edit(base["state"], lambda x: x.update(next="Paper 10"))}, {"W6"}),
        ("M14-validation-digest", "mutation", {"validation": {**base["validation"], "diagnostic_sha256": "0" * 64}}, {"P1"}),
        ("M15-schema-bytes", "mutation", {"bytes": {**base["bytes"], SCHEMA: base["bytes"][SCHEMA] + 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", "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-059", "subject": "LESR Paper 09A v0.2: the multi-scale topology wall and the one-sided blow-up critical cone",
           "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", "p1")}, "drill": summary}
    LOG.write_text(json.dumps(log, ensure_ascii=False, indent=1, default=float) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
