# -*- coding: utf-8 -*-
"""
AUDIT 3b — resolve the '1 mystery direction' from audit 3.

Hypothesis: it is the COMBINED automorphism  pre-scale y by t, post-scale F2
by 1/t  — each factor individually leaves the z-affine class (breaks the unit
y-coefficient of F2), but the composition stays in-class. If adding it closes
the gap (trivial rank 5 -> 6 == dim ker M), Alpoge's map IS infinitesimally
rigid modulo automorphisms in the FULL equivariant z-affine class.
Otherwise a genuine deformation family exists.
"""
import sympy as sp
import json, time

t0 = time.time()
x, y, z = sp.symbols('x y z')
a = x*y
ps  = sp.symbols('p0:4');  s1s = sp.symbols('s10:14')
qs  = sp.symbols('q0:3');  s2s = sp.symbols('s20:23')
rs  = sp.symbols('r0:3');  s3s = sp.symbols('s30:32')
c   = sp.Symbol('c')
TH  = list(ps)+list(s1s)+list(qs)+list(s2s)+list(rs)+list(s3s)+[c]

def poly(cs): return sum(co*a**i for i, co in enumerate(cs))
F1s = y**2*poly(ps) + z*poly(s1s)
F2s = y + x*y**2*poly(qs) + x*z*poly(s2s)
F3s = x*poly(rs) + x**3*z*poly(s3s)
D  = sp.expand(sp.Matrix([F1s,F2s,F3s]).jacobian([x,y,z]).det() - c)
K  = sp.Poly(D, x, y, z).coeffs()

theta_star = dict(zip(ps,[4,7,3,0])); theta_star.update(zip(s1s,[1,3,3,1]))
theta_star.update(zip(qs,[12,9,0]));  theta_star.update(zip(s2s,[3,6,3]))
theta_star.update(zip(rs,[2,-3,0]));  theta_star.update(zip(s3s,[-1,0]))
theta_star[c] = -2

M = sp.Matrix([[sp.diff(k, v).subs(theta_star) for v in TH] for k in K])
ns = M.nullspace()
print(f"dim ker M = {len(ns)}", flush=True)

def extract(Fs):
    out = {}
    specs = [ (Fs[0], [((j, j+2, 0), ps[j]) for j in range(4)] + [((j, j, 1), s1s[j]) for j in range(4)]),
              (sp.expand(Fs[1]-y), [((j+1, j+2, 0), qs[j]) for j in range(3)] + [((j+1, j, 1), s2s[j]) for j in range(3)]),
              (Fs[2], [((j+1, j, 0), rs[j]) for j in range(3)] + [((j+3, j, 1), s3s[j]) for j in range(2)]) ]
    for comp, slots in specs:
        p = sp.Poly(sp.expand(comp), x, y, z)
        table = {mono: co for mono, co in zip(p.monoms(), p.coeffs())}
        for mono, sym in slots:
            out[sym] = table.pop(mono, 0)
        if table: return None
    return out

e = sp.Symbol('epsilon')
F0 = [F1s.subs(theta_star), sp.expand(F2s.subs(theta_star)), F3s.subs(theta_star)]
dirs, names = [], []
def add_dir(Fe, cfun, name):
    ex = extract([sp.expand(f) for f in Fe])
    if ex is None:
        print(f"        [{name}] leaves class", flush=True); return
    vec = [sp.diff(ex.get(v_, 0), e).subs(e, 0) if v_ != c else sp.diff(cfun, e).subs(e, 0) for v_ in TH]
    dirs.append(vec); names.append(name)

lam = 1 + e
add_dir([f.subs({x: lam*x}, simultaneous=True) for f in F0], -2*lam, "pre-x")
add_dir([f.subs({z: lam*z}, simultaneous=True) for f in F0], -2*lam, "pre-z")
add_dir([lam*F0[0], F0[1], F0[2]], -2*lam, "post-F1")
add_dir([F0[0], F0[1], lam*F0[2]], -2*lam, "post-F3")
add_dir([f.subs({z: z + e*y**2}, simultaneous=True) for f in F0], sp.Integer(-2)+0*e, "z-shear y^2")
# THE COMBINED DIRECTION: pre-scale y by lam, post-scale F2 by 1/lam
Fy = [f.subs({y: lam*y}, simultaneous=True) for f in F0]
add_dir([Fy[0], sp.expand(Fy[1]/lam), Fy[2]], sp.Integer(-2)+0*e, "pre-y (+) post-F2^{-1} [COMBINED]")

Tspan = sp.Matrix(dirs).T
rankT = Tspan.rank()
okT = all(all(sp.simplify(val) == 0 for val in M*sp.Matrix(d)) for d in dirs)
print(f"trivial directions: {len(dirs)}, rank = {rankT}; all in ker M: {okT}", flush=True)

if ns:
    Kmat = sp.Matrix([list(n) for n in ns]).T
    joint = Kmat.row_join(Tspan)
    closed = (joint.rank() == rankT == len(ns))
else:
    closed = (rankT == 0)
print(f"dim ker M = {len(ns)}  vs  trivial rank = {rankT}  ; spans coincide: {closed}", flush=True)
verdict = "RIGID-MOD-AUTOS" if closed else "GENUINE-DEFORMATION-EXISTS"
print(f"[{verdict}] " + ("The audit-3 'mystery direction' was the combined pre-y/post-F2 "
      "automorphism. Alpoge's map is INFINITESIMALLY RIGID modulo automorphisms in the "
      "full torus-equivariant z-affine class with slope free. Rigidity UPGRADED, not broken."
      if closed else
      "a deformation direction beyond all constructed automorphisms persists — "
      "investigate for new specimen families."), flush=True)

json.dump({"dim_ker": len(ns), "trivial_rank": rankT, "trivial_in_ker": bool(okT),
           "spans_coincide": bool(closed), "verdict": verdict,
           "trivial_names": names, "elapsed": round(time.time()-t0,2)},
          open('post-jc-program/audit/audit3b.json','w'), indent=2)
print(f"artifact: audit3b.json ({round(time.time()-t0,2)}s)", flush=True)
