# -*- coding: utf-8 -*-
"""
AUDIT 3d — DECISIVE: compute the tangent space of the FULL graded gauge orbit
at Alpoge's map, intersected with the ansatz class, by the Lie algebra action.

Infinitesimal gauge element xi (11 params):
  source vector field  delta_S = (alpha*x, beta*y + q1*x*z, gamma*z + (pp0 + pp1*x*y)*y^2)
  target vector field  delta_T = (d*Y1 + w1*Y2^2, e*Y2 + v1*Y1*Y3, f*Y3)
First-order variation of F under the gauge action:
  V(xi) = delta_T(F) + J_F . delta_S
In-class conditions: V's monomials confined to the ansatz slots AND V2 has no
bare-y term (class normalizes that coefficient to 1). The c-variation is
  delta_c = c * (alpha+beta+gamma+d+e+f)   (shears are traceless).
Rank of the resulting theta-tangent image vs dim ker M decides rigidity.
"""
import sympy as sp
import json, time

t0 = time.time()
x, y, z = sp.symbols('x y z')
a = x*y
al, be, ga, d_, e_, f_ = sp.symbols('alpha beta gamma d e f')
q1, pp0, pp1, w1, v1 = sp.symbols('q1 pp0 pp1 w1 v1')
XI = [al, be, ga, d_, e_, f_, q1, pp0, pp1, w1, v1]

F1 = (1+a)**3*z + y**2*(1+a)*(4+3*a)
F2 = y + 3*x*(1+a)**2*z + 3*x*y**2*(4+3*a)
F3 = 2*x - 3*x**2*y - x**3*z
F = sp.Matrix([F1, F2, F3])
J = F.jacobian([x, y, z])

dS = sp.Matrix([al*x, be*y + q1*x*z, ga*z + (pp0 + pp1*a)*y**2])
dT = sp.Matrix([d_*F1 + w1*F2**2, e_*F2 + v1*F1*F3, f_*F3])
V = sp.Matrix([sp.expand(dT[i] + sum(J[i, k]*dS[k] for k in range(3))) for i in range(3)])

# class slots (theta layout identical to audits 3/3b/3c)
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]

specs = [(V[0], [((j, j+2, 0), ps[j]) for j in range(4)] + [((j, j, 1), s1s[j]) for j in range(4)]),
         (V[1], [((j+1, j+2, 0), qs[j]) for j in range(3)] + [((j+1, j, 1), s2s[j]) for j in range(3)]),
         (V[2], [((j+1, j, 0), rs[j]) for j in range(3)] + [((j+3, j, 1), s3s[j]) for j in range(2)])]

coefmap, constraints = {}, []
for ci, (comp, slots) in enumerate(specs):
    p = sp.Poly(comp, x, y, z)
    table = {mo: co for mo, co in zip(p.monoms(), p.coeffs())}
    for mo, sym in slots:
        coefmap[sym] = table.pop(mo, sp.Integer(0))
    if ci == 1:
        constraints.append(table.pop((0, 1, 0), sp.Integer(0)))  # bare y in V2 must vanish
    for mo, co in table.items():
        constraints.append(co)  # any residual monomial must vanish

# solve the in-class linear conditions on xi
sol = sp.linsolve(constraints, XI)
assert sol != sp.EmptySet
solt = list(sol)[0]
subxi = dict(zip(XI, solt))
freexi = sorted({s for ee in solt for s in ee.free_symbols if s in XI}, key=str)
print(f"in-class gauge subalgebra: dimension {len(freexi)}  (free: {freexi})", flush=True)

# theta-tangent image
dc = sp.expand(-2*(al + be + ga + d_ + e_ + f_)).subs(subxi)
vecs = []
for s_ in freexi:
    vec = [sp.diff(sp.expand(coefmap[v_].subs(subxi)), s_) if v_ != c else sp.diff(dc, s_)
           for v_ in TH]
    vecs.append([sp.simplify(t_) for t_ in vec])
Gspan = sp.Matrix(vecs).T
rankG = Gspan.rank()
print(f"rank of gauge-orbit tangent in theta-space: {rankG}", flush=True)

# compare with ker M
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()
th = dict(zip(ps, [4, 7, 3, 0])); th.update(zip(s1s, [1, 3, 3, 1]))
th.update(zip(qs, [12, 9, 0]));   th.update(zip(s2s, [3, 6, 3]))
th.update(zip(rs, [2, -3, 0]));   th.update(zip(s3s, [-1, 0])); th[c] = -2
M = sp.Matrix([[sp.diff(k, v_).subs(th) for v_ in TH] for k in K])
ns = M.nullspace()
okin = all(all(sp.simplify(val) == 0 for val in M*sp.Matrix(v_)) for v_ in vecs)
Kmat = sp.Matrix([list(n) for n in ns]).T
joint = Kmat.row_join(Gspan)
closed = okin and (joint.rank() == rankG == len(ns))
print(f"dim ker M = {len(ns)};  gauge tangent rank = {rankG};  gauge in ker: {okin};  "
      f"ker == gauge tangent: {closed}", flush=True)
verdict = "RIGID-MOD-GRADED-GAUGE (PROVEN INFINITESIMALLY)" if closed else "DEFORMATION-BEYOND-GAUGE"
print(f"\n[{verdict}]", flush=True)
json.dump({"in_class_gauge_dim": len(freexi), "gauge_tangent_rank": rankG,
           "dim_ker_M": len(ns), "gauge_in_kernel": bool(okin),
           "ker_equals_gauge_tangent": bool(closed), "verdict": verdict,
           "elapsed": round(time.time()-t0, 2)},
          open('post-jc-program/audit/audit3d.json', 'w'), indent=2)
print(f"artifact: audit3d.json  ({round(time.time()-t0, 2)}s)", flush=True)
