#!/usr/bin/env python3
"""
verify_P105.py -- Addendum 105: golden-ratio Weinberg angle.

This checks the OP4 numerics after P94/P95/P101/P102:
  * exact NLO Weinberg angle and 3/(8 phi) near-miss
  * Peirce k1-doubling / 3/13 arithmetic
  * NNLO c4 elimination and the required c6 values
  * basic 600-cell/H4 counting claims

The verifier intentionally distinguishes exact formula values from rounded
paper values, because several P105 "gap" percentages are artifacts of mixing
rounded NLO values with exact symbolic formulas.
"""

import math
import sys
from pathlib import Path

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


class ModernVerifier(Verifier):
    """Local adapter: modern check-line output format. Tolerance logic is
    inherited unchanged from verify_common.Verifier; only printing differs.
    Computed/claimed values stay as indented info lines."""

    def __init__(self, name: str) -> None:
        self.name = name
        self.results = []
        print(name)

    def record(self, label, ok, computed="", claimed="", detail=""):
        self.results.append(CheckResult(label, ok, computed, claimed, detail))
        status = "PASS" if ok else "FAIL"
        desc = f"{label} -- {detail}" if detail else label
        print(f"  [{status}] {len(self.results):>2}. {desc}")
        if computed != "" or claimed != "":
            print(f"          computed: {computed}")
            print(f"          claimed : {claimed}")
        return ok

    def summary(self) -> int:
        passed = sum(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("P105 -- Golden-Ratio Weinberg Angle")

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

PI = math.pi
PHI = (1 + math.sqrt(5)) / 2
INV_PHI = 1 / PHI
LAM = math.sin(PI / 14)
PDG_SW = 0.23122

F_NLO = 1 - 4 * LAM**2 / 5
SW_LO = 1 / (1 + PI)
SW_NLO = SW_LO * F_NLO
SW_SU5 = 3 / 8
SW_GOLDEN = 3 / (8 * PHI)


def rel_pct(value: float, target: float) -> float:
    return 100 * (value - target) / target


v.check("phi", PHI, 1.61803, rel=5e-6)
v.check("1/phi", INV_PHI, 0.61803, rel=1e-5)
v.check("lambda = sin(pi/14)", LAM, 0.22252, rel=5e-6)
v.check("lambda^2", LAM**2, 0.049516, rel=1e-5)
v.check("lambda^4", LAM**4, 2.4518e-3, rel=2e-5)
v.check("lambda^6", LAM**6, 1.2142e-4, rel=2e-4)
v.check("NLO factor 1 - 4 lambda^2/5", F_NLO, 0.960387, rel=1e-5)

ratio_form = SW_NLO / SW_SU5
ratio_gap_pct = rel_pct(ratio_form, INV_PHI)
identity_lhs = 8 * PHI * F_NLO
identity_rhs = 3 * (1 + PI)
identity_gap_pct = rel_pct(identity_lhs, identity_rhs)

v.check("LO Weinberg angle 1/(1+pi)", SW_LO, 0.24145, rel=5e-5)
v.check("NLO Weinberg angle exact formula", SW_NLO, 0.23192, abs_tol=4e-5)
v.check("3/(8 phi)", SW_GOLDEN, 0.23178, abs_tol=2e-5)
v.check("NLO absolute gap to 3/(8 phi)", SW_NLO - SW_GOLDEN, 0.00014, abs_tol=2e-5)
v.check(
    "NLO gap fraction from exact formulas",
    rel_pct(SW_NLO, SW_GOLDEN),
    0.060,
    rel=5e-2,
    detail="Expected fail: exact formulas give about 0.054%; 0.060% comes from rounded 0.23192/0.23178 values.",
)
v.check(
    "identity LHS exact",
    identity_lhs,
    12.4311,
    rel=5e-5,
)
v.check("identity RHS 3(1+pi)", identity_rhs, 12.4248, rel=5e-5)
v.check(
    "identity gap fraction from exact formulas",
    identity_gap_pct,
    0.048,
    rel=5e-2,
    detail="Expected fail: exact formulas give the same near-miss gap as the ratio form, about 0.054%, not 0.048%.",
)
v.check(
    "proposition's 0.0507% gap",
    identity_gap_pct,
    0.0507,
    rel=5e-2,
    detail="Expected fail: exact arithmetic gives about 0.05424%; 0.0507% follows from the paper's rounded LHS.",
)
v.record(
    "Form 1 and Form 2 have the same exact fractional gap",
    abs(ratio_gap_pct - identity_gap_pct) < 1e-12,
    f"ratio gap={ratio_gap_pct:.6f}%, identity gap={identity_gap_pct:.6f}%",
    "same",
)

k1_j3 = 2 * 5 / 3
sw_3_13 = 1 / (1 + k1_j3)
sw_3_13_nlo = sw_3_13 * F_NLO
v.check("Peirce k1 doubling", k1_j3, 10 / 3, rel=1e-12)
v.check("Peirce tree value 3/13", sw_3_13, 0.23077, rel=2e-5)
v.check(
    "printed numerator for 3/13 PDG gap",
    abs(sw_3_13 - PDG_SW),
    0.045,
    rel=1e-2,
    detail="Expected fail: the absolute difference is about 0.000451; the percent 0.19% is right, but the numerator is off by factor 100.",
)
v.check("3/13 PDG gap percent", abs(rel_pct(sw_3_13, PDG_SW)), 0.19, rel=5e-2)
v.check("3/13 NLO value", sw_3_13_nlo, 0.22163, rel=2e-5)
v.check(
    "3/13 NLO below PDG percent",
    abs(rel_pct(sw_3_13_nlo, PDG_SW)),
    0.41,
    rel=5e-2,
    detail="Expected fail: 0.22163 is about 4.15% below 0.23122, not 0.41%.",
)
v.check("3/13 gap to 3/(8 phi)", abs(rel_pct(sw_3_13, SW_GOLDEN)), 0.44, rel=5e-2)
v.check("LO requires k1 = pi", (1 / SW_LO) - 1, PI, rel=1e-12)

c2 = 4 / 5
c4_p95 = -INV_PHI * c2**2
c4_actual = -74 / 25
c4_topology_c_needed = c4_p95 - 16 / 25
sw_nnlo = (1 - 4 * LAM**2 / 5 - 74 * LAM**4 / 25) / (1 + PI)
v.check("P95 c4 candidate", c4_p95, -0.396, rel=2e-3)
v.check("P101 c4 actual", c4_actual, -2.960, rel=1e-12)
v.check("c4 candidate / actual", c4_p95 / c4_actual, 0.134, rel=5e-3)
v.check("topology c needed by P95 candidate", c4_topology_c_needed, -1.036, rel=5e-3)
v.check("NNLO Weinberg angle", sw_nnlo, 0.23013, rel=5e-5)
v.check("NNLO residual vs PDG percent", rel_pct(sw_nnlo, PDG_SW), -0.47, rel=1e-2)

c6_close_nnlo = (SW_GOLDEN - sw_nnlo) * (1 + PI) / LAM**6
c6_close_nlo_exact = -(SW_NLO - SW_GOLDEN) * (1 + PI) / LAM**6
c6_close_nlo_rounded = -(0.23192 - 0.23178) * (1 + PI) / LAM**6
v.check("c6 required after NNLO", c6_close_nnlo, 56.3, rel=2e-2)
v.check(
    "abstract c6 about 1.25",
    c6_close_nnlo,
    1.25,
    rel=1e-1,
    detail="Expected fail: closing the NNLO gap needs c6 about 55-56, while closing the rounded NLO gap needs about -4.8; 1.25 is not produced.",
)
v.check(
    "c6 required to close exact NLO gap",
    c6_close_nlo_exact,
    -4.77,
    rel=5e-2,
    detail="Expected fail: exact formulas give about -4.29; -4.77 comes from rounded 0.23192 and 0.23178.",
)
v.check("c6 required to close rounded NLO gap", c6_close_nlo_rounded, -4.77, rel=1e-2)
v.check("next natural scale |c4|/lambda^2", abs(c4_actual) / LAM**2, 60.0, rel=5e-3)

det_h4 = (7 - 3 * math.sqrt(5)) / 2
v.check("H4 Cartan determinant", det_h4, 0.146, rel=5e-3)
v.check("H4 order / W(G2) order", 14400 / 12, 1200, rel=1e-12)
v.check("|H4| / |2I|", 14400 / 120, 120, rel=1e-12)
v.check("600-cell plus dual volume ratio phi^10", PHI**10, 122.99, rel=5e-4)
v.record(
    "Hopf projection target is not described as 60 vertices of an icosahedron",
    "60\nvertices of the icosahedron" not in TEX and "60 vertices of the icosahedron" not in TEX,
    "TeX says 60 vertices of the icosahedron",
    "icosahedron has 12 vertices; 60 is the rotation group order",
    "Expected fail: the 2I -> I projection maps group elements/rotations, not to 60 vertices of a single icosahedron.",
)
v.record(
    "header/body c6 statements are consistent",
    "c_6 \\approx 1.25" not in TEX,
    "abstract/header says c6 approx 1.25; body computes +56.3 and -4.77",
    "single c6 target",
    "Expected fail: the abstract's c6 approx 1.25 is stale relative to the paper's own NNNLO section.",
)

sys.exit(v.summary())
