#!/usr/bin/env python3
"""Cosmetic scrub of a bound-arc Frobenius value file: rewrite the two comment header lines and
replace the arbitrary-precision float gamma label of each POINT line by the exact value of gamma.
Data lines (z_re, z_im, W[i,k]) are copied byte for byte.  Prints a payload-identity check and a
check that each exact label reproduces the removed float label.
usage: scrub_values.py IN OUT"""
import sys, re
from fractions import Fraction
import mpmath as mp
from common import sha256_file

EXACT = {  # tag -> (exact label, mpmath value)
    'g995': ('199/200', lambda: mp.mpf(199)/200), 'g990': ('99/100', lambda: mp.mpf(99)/100),
    'g980': ('49/50', lambda: mp.mpf(49)/50),     'g970': ('97/100', lambda: mp.mpf(97)/100),
    'gisco': ('sqrt(8/9)', lambda: mp.sqrt(mp.mpf(8)/9)), 'g920': ('23/25', lambda: mp.mpf(23)/25),
    'g900': ('9/10', lambda: mp.mpf(9)/10),       'g866': ('sqrt(3)/2', lambda: mp.sqrt(3)/2),
    'g800': ('4/5', lambda: mp.mpf(4)/5),         'g707': ('sqrt(2)/2', lambda: mp.sqrt(2)/2),
    'g600': ('3/5', lambda: mp.mpf(3)/5),         'g500': ('1/2', lambda: mp.mpf(1)/2),
}
GEOM = {'K30': "K3 (Legendre symmetric square, 4PM): operator (1/8) z^(-1/2) L_K3 z^(1/2), whose power-series solution at z = 0 is W[1,1] = varpi_0 = (2/pi)^2 K(z)^2 = 1 + z/2 + ...",
        'K3P': "K3' (Apery / Beukers-Peters surface, topology 40): operator L_A, varpi_0 = sum_n A_n z^n",
        'F43': "CY3 (4F3 hypergeometric, topology 3): operator L_CY3, varpi_0 = 4F3(1/2,1/2,1/2,1/2;1,1,1;2^8 z)",
        'HAD': "CY3' (Hadamard, chi = 80, topology 37): operator L_CY3', varpi_0 = 1 + 112 z + 47376 z^2 + ..."}
PATH = {'K30': "z = x^2 with x = exp(-i theta), gamma = cos(theta): transported from the MUM point z = 0 through the region Im z < 0 (the class of z - i0) to the arc point |z| = 1",
        'K3P': "z = x^2 with x = exp(-i theta), gamma = cos(theta): transported from the MUM point z = 0 through the region Im z < 0 (the class of z - i0) to the arc point |z| = 1; the path encloses no singular point",
        'F43': "z = x^4/2^8 with x = exp(-i theta): transported from the MUM point z = 0 clockwise inside the disk |z| < 2^-8, never crossing arg z = 0, to the point on the singular circle |z| = 2^-8 at arg z = -4 theta (the class of z - i0 continued past z = 2^-8)",
        'HAD': "z = (1 - gamma^2)/2^10, real, in (0, 2^-10): transported from the MUM point z = 0 along the real segment (all entries real)"}
src, out = sys.argv[1], sys.argv[2]
mp.mp.dps = 200
lines = open(src).read().split('\n')
outl, worst = [], mp.mpf(0)
tagfam = None
for l in lines:
    if l.startswith('#'):
        continue
    m = re.match(r'^POINT (\S+) (\S+) gamma=(\S+)$', l)
    if m:
        fam, tag, gfloat = m.groups(); tagfam = fam
        lab, val = EXACT[tag]
        rel = abs(mp.mpf(gfloat) - val())/val()
        worst = max(worst, rel)
        outl.append(f'POINT {fam} {tag} gamma={lab}')
    else:
        outl.append(l)
hdr = [f"# {GEOM[tagfam]}.",
       f"# Full Frobenius (Wronskian) matrix W[i,k] = (d/dz)^(i-1) varpi_(k-1)(z) on the bound arc, {PATH[tagfam]}.",
       "# 600-bit ball arithmetic, 350 series terms per expansion step.  Per POINT: exact gamma; z_re, z_im; then each entry as",
       "# 're  im' (ball midpoints, about 180 significant digits).  Frobenius normalization, enclosure radii and the",
       "# two-path agreement per point are given in README_bound.md."]
open(out, 'w').write('\n'.join(hdr + outl))
def payload(ls):
    return [l for l in ls if l.startswith('  ')]   # z_re, z_im, W lines
def tags(ls):
    return [' '.join(l.split()[:3]) for l in ls if l.startswith('POINT')]
a, b = payload(lines), payload(open(out).read().split('\n'))
same = (a == b) and (tags(lines) == tags(open(out).read().split('\n')))
nW = sum(1 for l in b if l.lstrip().startswith('W['))
print(f'PAYLOAD-IDENTITY {out.split("/")[-1]}: {"IDENTICAL" if same else "DIFFERENT"} '
      f'({len(b)} data lines, {nW} matrix entries, {len(tags(lines))} points); '
      f'exact-gamma labels reproduce the removed float labels to rel {mp.nstr(worst, 3)}; '
      f'in sha256 {sha256_file(src)[:16]} out sha256 {sha256_file(out)[:16]}')
sys.exit(0 if same and worst < mp.mpf('1e-170') else 1)
