"""Independent coverage check: the leaves and empty boxes of a search part form a guillotine
partition of its root box (exact float comparisons). Usage: python cover.py par_j0_p1.json"""
import sys, json
sys.setrecursionlimit(10000)
def check(boxes, rlo, rhi):
    # iterative: stack of (indices, region)
    stack=[(boxes, rlo, rhi)]; nleaf=0
    while stack:
        bs, lo, hi = stack.pop()
        if not bs: return False, ("gap", lo, hi)
        if len(bs)==1:
            b=bs[0]
            if list(b[0])!=list(lo) or list(b[1])!=list(hi): return False, ("mismatch", b, lo, hi)
            nleaf+=1; continue
        for b in bs:
            if any(b[0][m]<lo[m] or b[1][m]>hi[m] or b[0][m]>=b[1][m] for m in range(len(lo))):
                return False, ("outside", b, lo, hi)
        found=False
        for m in range(len(lo)):
            srt=sorted(bs, key=lambda b: b[0][m]); mx=srt[0][1][m]
            for k in range(1,len(srt)):
                if srt[k][0][m]>=mx and lo[m]<srt[k][0][m]<hi[m]:
                    t=srt[k][0][m]; left=srt[:k]; right=srt[k:]
                    h1=list(hi); h1[m]=t; l2=list(lo); l2[m]=t
                    stack.append((left,lo,h1)); stack.append((right,l2,hi)); found=True; break
                mx=max(mx,srt[k][1][m])
            if found: break
        if not found: return False, ("nosplit", len(bs), lo, hi)
    return True, nleaf
if __name__=="__main__":
    fn=sys.argv[1]; d=json.load(open(fn)); assert d["final"] and not d["stack"]
    part=int(fn.split("_p")[1].split(".")[0]); cuts=[-1.0,-1/3,1/3,1.0]
    lo=[-1.0]*6; hi=[1.0]*6; lo[0]=cuts[part]; hi[0]=cuts[part+1]
    boxes=[(tuple(b["lo"]),tuple(b["hi"])) for b in d["leaves"]+d["empties"]]
    ok,info=check(boxes,lo,hi)
    print(fn, "COVER-OK" if ok else "COVER-FAIL", len(boxes), info if not ok else f"{info} boxes")
