#!/usr/bin/env python3
"""
verify_P054.py -- Addendum 54: Dirac Yukawa trilinear form.

This verifier evaluates the cubic-norm polarization for representative
J3(O) basis configurations. It checks the paper's corrected body result:
different off-diagonal blocks with one diagonal slot give zero, while the
self-coupling opposite the diagonal gives -Re(q hbar)/3.

The flagged issues are internal contradictions left in the TeX: the abstract
and theorem statement still claim a compatible different-block value 1/6,
and the abstract Higgs-coupling summary claims nonzero 1/6 couplings that
the proof and later propositions correctly replace by zero/self-coupling
terms. The Cabibbo sine numerics themselves reproduce, but the angle
assignment remains explicitly open.
"""

from __future__ import annotations

import math
import sys
from dataclasses import dataclass
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from verify_common import CheckResult, Verifier


class ModernVerifier(Verifier):
    """Local output adapter: tolerance logic byte-identical to
    verify_common.Verifier.check; emits the modern corpus line format
    ("  [PASS] {n:>2}. {desc}") with computed/claimed/tolerance values
    kept as indented info lines, and the modern RESULT footer."""

    def __init__(self, name: str):
        self.name = name
        self.results = []
        self._n = 0
        print(name)

    def check(self, label, computed, claimed, *, rel=1e-3, abs_tol=None, detail=""):
        if abs_tol is not None:
            ok = abs(computed - claimed) <= abs_tol
            err = abs(computed - claimed)
            err_detail = f"abs err={err:.6g}, tol={abs_tol:.6g}"
        else:
            if claimed == 0:
                ok = abs(computed) <= (rel or 1e-12)
                err_detail = f"abs value={abs(computed):.6g}, tol={rel:.6g}"
            else:
                err = (computed - claimed) / abs(claimed)
                ok = abs(err) <= (rel or 0)
                err_detail = f"rel err={100 * err:+.6g}%, tol={100 * (rel or 0):.6g}%"
        return self._emit(label, ok, computed, claimed, err_detail, detail)

    def record(self, label, ok, computed="", claimed="", detail=""):
        return self._emit(label, ok, computed, claimed, "", detail)

    def _emit(self, label, ok, computed, claimed, info, ann):
        full_detail = (info + (f"; {ann}" if ann else "")) if info else ann
        self.results.append(CheckResult(label, ok, computed, claimed, full_detail))
        self._n += 1
        desc = f"{label} -- {ann}" if ann else label
        print(f"  [{'PASS' if ok else 'FAIL'}] {self._n:>2}. {desc}")
        if computed != "" or claimed != "":
            print(f"        computed: {computed}")
            print(f"        claimed : {claimed}")
        if info:
            print(f"        {info}")
        return ok

    def summary(self):
        passed = sum(bool(r.ok) for r in self.results)
        failed = len(self.results) - passed
        print(f"\n{'='*60}\nRESULT: {passed} PASS / {failed} FAIL")
        return 1 if failed else 0


v = ModernVerifier("P054 -- Dirac Yukawa Trilinear")

ROOT = Path(__file__).resolve().parents[1]
TEX = (ROOT / "54_Addendum_DiracYukawa.tex").read_text()


Vec = tuple[float, float]
ZERO: Vec = (0.0, 0.0)
E0: Vec = (1.0, 0.0)
E7: Vec = (0.0, 1.0)


def add_vec(a: Vec, b: Vec) -> Vec:
    return (a[0] + b[0], a[1] + b[1])


def norm2(a: Vec) -> float:
    return a[0] * a[0] + a[1] * a[1]


def inner(a: Vec, b: Vec) -> float:
    return a[0] * b[0] + a[1] * b[1]


@dataclass(frozen=True)
class J:
    alpha: tuple[float, float, float] = (0.0, 0.0, 0.0)
    x12: Vec = ZERO
    x13: Vec = ZERO
    x23: Vec = ZERO

    def __add__(self, other: "J") -> "J":
        return J(
            tuple(self.alpha[i] + other.alpha[i] for i in range(3)),
            add_vec(self.x12, other.x12),
            add_vec(self.x13, other.x13),
            add_vec(self.x23, other.x23),
        )


def diag(i: int) -> J:
    alpha = [0.0, 0.0, 0.0]
    alpha[i - 1] = 1.0
    return J(tuple(alpha), ZERO, ZERO, ZERO)


def off(pair: str, q: Vec) -> J:
    if pair == "12":
        return J((0.0, 0.0, 0.0), q, ZERO, ZERO)
    if pair == "13":
        return J((0.0, 0.0, 0.0), ZERO, q, ZERO)
    if pair == "23":
        return J((0.0, 0.0, 0.0), ZERO, ZERO, q)
    raise ValueError(pair)


def cubic_norm(x: J) -> float:
    a1, a2, a3 = x.alpha
    # The tests below use at most two off-diagonal blocks, so the Freudenthal
    # TO(x12,x23,x13) term is zero by trilinearity.
    return (
        a1 * a2 * a3
        - a1 * norm2(x.x23)
        - a2 * norm2(x.x13)
        - a3 * norm2(x.x12)
    )


def trilinear(x: J, y: J, z: J) -> float:
    return (
        cubic_norm(x + y + z)
        - cubic_norm(x + y)
        - cubic_norm(x + z)
        - cubic_norm(y + z)
        + cubic_norm(x)
        + cubic_norm(y)
        + cubic_norm(z)
    ) / 6.0


v.check("different blocks E12/E13 with E22", trilinear(off("12", E0), off("13", E0), diag(2)), 0.0, abs_tol=1e-12)
v.check("different blocks E23/E13 with E22", trilinear(off("23", E0), off("13", E0), diag(2)), 0.0, abs_tol=1e-12)
v.check("different blocks E12/E23 with E33", trilinear(off("12", E0), off("23", E0), diag(3)), 0.0, abs_tol=1e-12)
v.check("self-coupling E13/E13 with E22", trilinear(off("13", E0), off("13", E0), diag(2)), -1.0 / 3.0, rel=1e-12)
v.check("self-coupling E23/E23 with E11", trilinear(off("23", E0), off("23", E0), diag(1)), -1.0 / 3.0, rel=1e-12)
v.check("self-coupling orthogonal directions", trilinear(off("13", E0), off("13", E7), diag(2)), 0.0, abs_tol=1e-12)
v.check("same block but diagonal index inside block", trilinear(off("13", E0), off("13", E0), diag(1)), 0.0, abs_tol=1e-12)
v.check("same block but other inside-block diagonal", trilinear(off("13", E0), off("13", E0), diag(3)), 0.0, abs_tol=1e-12)

v.check(
    "Theorem statement compatible-pair value",
    trilinear(off("12", E0), off("13", E0), diag(2)),
    1.0 / 6.0,
    rel=1e-12,
    detail="Expected fail: the theorem statement/abstract says 1/6, but the proof later correctly recomputes this as zero.",
)
v.check(
    "abstract Higgs coupling E12/E13/E22",
    trilinear(off("12", E0), off("13", E0), diag(2)),
    1.0 / 6.0,
    rel=1e-12,
    detail="Expected fail: the abstract's Proposition 2 summary lists this as nonzero 1/6; the body proposition gives zero.",
)
v.check(
    "abstract Higgs coupling E23/E13/E22",
    trilinear(off("23", E0), off("13", E0), diag(2)),
    1.0 / 6.0,
    rel=1e-12,
    detail="Expected fail: different-block Higgs couplings cancel under the cubic-norm polarization.",
)

h = (0.6, 0.8)
v.check("VEV overlap q=e0", trilinear(off("13", E0), off("13", h), diag(2)), -h[0] / 3.0, rel=1e-12)
v.check("VEV overlap q=e7", trilinear(off("13", E7), off("13", h), diag(2)), -h[1] / 3.0, rel=1e-12)

theta_c = math.pi / 14.0
y1 = math.sin(theta_c)
y2 = math.sin(2.0 * theta_c)
y3 = math.sin(7.0 * theta_c)
v.check("sin(pi/14)", y1, 0.2225, rel=2e-4)
v.check("sin(2pi/14)", y2, 0.4339, rel=2e-4)
v.check("sin(7pi/14)", y3, 1.0, rel=1e-12)
v.check("y2/y1", y2 / y1, 1.9499, rel=3e-5)

v.record(
    "cubic form directly derives Cabibbo-step hierarchy",
    "Step 2 (open" not in TEX and "Phase~5a-ii" not in TEX,
    computed="paper leaves angle assignment <u_k,e7>=sin(k*pi/14) open",
    claimed="Cabibbo-step Yukawa hierarchy from trilinear form",
    detail="Expected status fail: the trilinear form gives the overlap mechanism, not the angle values.",
)
v.record(
    "generation-3 y3=1 follows from the standard Higgs doublet cubic coupling",
    trilinear(off("12", E0), off("13", E0), diag(3)) != 0.0 or trilinear(off("12", E0), off("23", E0), diag(3)) != 0.0,
    computed="all cubic couplings T(., E13/E23, E33) vanish in the model",
    claimed="generation-3 y3=1 from cubic trilinear with SM Higgs doublet",
    detail="Expected status fail: P54 itself correctly identifies this as the gen-3 gap.",
)
v.record(
    "status table democracy wording matches corrected theorem",
    "Democracy: cubic form gives equal coupling for all compatible configs" not in TEX,
    computed="corrected theorem has zero for different blocks and -Re(q hbar)/3 for self-couplings",
    claimed="equal coupling for all compatible configs",
    detail="Expected wording fail: the final status-table row preserves the older democracy wording.",
)

sys.exit(v.summary())
