#!/usr/bin/env python3
"""retroDE_ps2 — Ch351 step 1: isolate reciprocal quantization from S1 under-interpolation banding.

Compares the RTL FB dump (sh3_real_fb_out.mem, written by tb_top_psmct32_sh3_real_draw_demo) against TWO host
references over the identical crop geometry:
  FLOAT      — ideal perspective (the Codex pixel-diff oracle).
  RECIP-8b   — RTL-FAITHFUL: fixed-point vertex attrs + the 8-bit gs_reciprocal_stub (gs_make_sh3_real_draw_fixture).
Verdict:
  - RTL ≈ RECIP-8b (tight) AND RECIP-8b ≠ FLOAT (loose)  -> the residual is RECIPROCAL QUANTIZATION. Widening
    gs_reciprocal_stub IDX_BITS should fix it (Ch351 step 2).
  - RTL ≠ RECIP-8b (still loose)                          -> S1 attribute UNDER-INTERPOLATION beyond the reciprocal.

Usage: gs_ch351_oracle.py   (run gs_make_sh3_real_draw_fixture.py + the TB first to produce the .mem files)
"""
import sys, os
ROOT=os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)),".."))
DATA=os.path.join(ROOT,"sim","data","top_psmct32_raster_demo")
FBPXW, CH, TW, TH = 256, 120, 512, 512

def loadmem(name):
    return [int(l,16) for l in open(os.path.join(DATA,name)) if l.strip() and not l.strip().startswith("//")]

def main():
    fb   = loadmem("sh3_real_fb_out.mem")
    rmF  = loadmem("sh3_real_refmap.mem")
    rmR  = loadmem("sh3_real_refmap_recip.mem")
    idxw = loadmem("sh3_real_idx.mem")
    pal  = loadmem("sh3_real_pal.mem")
    def sh3_idx(u,v):
        lin=v*TW+u; return (idxw[lin>>2] >> (8*(lin&3))) & 0xFF
    def exp_cell(u,v):
        return pal[sh3_idx(u,v)] & 0xFFFFFF
    palset = set(p & 0xFFFFFF for p in pal)

    def histo(refmap, label):
        hist=[0]*5; ihist=[0]*5; tot=0; itot=0; exact_color=0; clut_bad=0
        sdu=sdv=0.0; nd=0
        for o in range(FBPXW*CH):
            rm=refmap[o]
            if not (rm>>31): continue
            interior=(rm>>30)&1; tu=(rm>>9)&0x1FF; tv=rm&0x1FF
            col = fb[o] & 0xFFFFFF
            tot+=1
            if col not in palset: clut_bad+=1
            if col == exp_cell(tu,tv): exact_color+=1
            D=99; mdu=mdv=0
            for rad in range(0,4):
                for du in range(-rad,rad+1):
                    for dv in range(-rad,rad+1):
                        if max(abs(du),abs(dv))!=rad or D!=99: continue
                        ttu=tu+du; ttv=tv+dv
                        if 0<=ttu<TW and 0<=ttv<TH and col==exp_cell(ttu,ttv):
                            D=rad; mdu=du; mdv=dv
            hist[D if D<=3 else 4]+=1
            if D<=3: sdu+=mdu; sdv+=mdv; nd+=1
            if interior: itot+=1; ihist[D if D<=3 else 4]+=1
        print(f"== {label} ==  covered={tot} interior={itot}")
        print(f"   ALL  hist D0={hist[0]} D1={hist[1]} D2={hist[2]} D3={hist[3]} none={hist[4]}  "
              f"<=1tex={100.0*(hist[0]+hist[1])/max(1,tot):.1f}%")
        print(f"   INT  hist D0={ihist[0]} D1={ihist[1]} D2={ihist[2]} D3={ihist[3]} none={ihist[4]}  "
              f"<=1tex={100.0*(ihist[0]+ihist[1])/max(1,itot):.1f}%")
        print(f"   exact-color-match={100.0*exact_color/max(1,tot):.1f}%  clut_bad={clut_bad}  "
              f"mean-delta=({sdu/max(1,nd):.3f},{sdv/max(1,nd):.3f})")
        return ihist

    rmA = loadmem("sh3_real_refmap_affine.mem")
    print("RTL FB dump vs the references (same geometry):\n")
    histo(rmF, "RTL-vs-FLOAT (ideal perspective)")
    print()
    ih = histo(rmR, "RTL-vs-RECIP8b (perspective + 8-bit reciprocal)")
    print()
    iha = histo(rmA, "RTL-vs-AFFINE (per-vertex divide + linear u,v)")
    print()
    # also: how different are the two references themselves (FLOAT vs RECIP8b) — the reciprocal's own error
    diff=sum(1 for o in range(FBPXW*CH) if (rmF[o]>>31) and (rmF[o]&0x3FFFF)!=(rmR[o]&0x3FFFF))
    covered=sum(1 for o in range(FBPXW*CH) if rmF[o]>>31)
    print(f"FLOAT vs RECIP8b reference texel disagreement: {diff}/{covered} ({100.0*diff/max(1,covered):.1f}%) "
          f"— the reciprocal LUT's intrinsic error on this geometry")
    print()
    rtl_tight = (ih[2]+ih[3]+ih[4]) < 0.05*max(1,sum(ih))
    if rtl_tight:
        print("VERDICT: RTL matches the RTL-faithful 8-bit-reciprocal reference -> residual is RECIPROCAL")
        print("         QUANTIZATION. Ch351 step 2: widen gs_reciprocal_stub IDX_BITS (10/11/12).")
    else:
        print("VERDICT: RTL does NOT match even the 8-bit-reciprocal reference -> S1 attribute UNDER-")
        print("         INTERPOLATION banding beyond the reciprocal. Ch351: fix S1 interpolation.")
    return 0

if __name__ == "__main__":
    sys.exit(main())
