#!/usr/bin/env python3
"""
verify_P091.py -- Addendum 91: Jarlskog invariant at NLO.

This verifier distinguishes two paths in 91_Addendum_JarskogNLO.tex:

1. the rounded numerical path used in the propositions, which mostly
   reproduces the printed J_LO and J_NLO values; and
2. the exact trigonometric formula path stated for Phi, rho_bar, and eta_bar,
   which gives different eta and therefore different J values/residuals.
"""

import math
import sys
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: inherits Verifier's tolerance logic unchanged,
    emits the corpus's modern check-line format (numbered [PASS]/[FAIL]
    lines, computed/claimed as indented info lines)."""

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

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

    def summary(self):
        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("P091 -- Jarlskog CP Invariant at NLO")

PI = math.pi
LAM = math.sin(PI / 14)
LAM2 = LAM**2
LAM3 = LAM**3
LAM6 = LAM**6
C_NLO = 1 - 4 * LAM2 / 5
A_LO = math.cos(PI / 14) ** 2 * math.cos(2 * PI / 14)
A_NLO = A_LO * C_NLO
DELTA = PI / 3 + PI / 28
PHI = math.cos(PI / 14) * math.cos(2 * PI / 14) * math.cos(5 * PI / 28)
RHO = 0.5 * PHI * math.cos(DELTA)
ETA = 0.5 * PHI * math.sin(DELTA)

PDG_A = 0.823
PDG_LAMBDA = 0.2245
PDG_VCB = 0.04110
PDG_VUB = 0.003517
PDG_J = 3.08e-5
PDG_ETA = 0.3441


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


v.check("lambda = sin(pi/14)", LAM, 0.22252, rel=5e-6)
v.check("lambda^2", LAM2, 0.049515, rel=2e-5)
v.check("lambda^6", LAM6, 1.2148e-4, rel=8e-4)
v.check("A_LO", A_LO, 0.8563, rel=1e-4)
v.check("A_NLO", A_NLO, 0.8224, rel=5e-5)
v.check("A_NLO/A_LO reduction percent", residual(A_NLO, A_LO), -3.96, rel=2e-3)

v.check(
    "cos(5pi/28) precision note",
    math.cos(5 * PI / 28),
    0.84565,
    rel=2e-4,
    detail="Expected fail: cos(5pi/28) is about 0.846724, not 0.84565.",
)
v.check(
    "Phi exact formula",
    PHI,
    0.74289,
    rel=5e-4,
    detail="Expected fail: the stated exact formula gives about 0.743745.",
)
v.check(
    "rho from exact Phi formula",
    RHO,
    0.1484,
    rel=5e-4,
    detail="Expected fail: exact Phi and delta give about 0.148709.",
)
v.check(
    "eta from exact Phi formula",
    ETA,
    0.3439,
    rel=5e-4,
    detail="Expected fail: exact Phi and delta give about 0.340844, not the older P82 eta.",
)

j_lo_exact = A_LO**2 * LAM6 * ETA
j_nlo_exact = A_NLO**2 * LAM6 * ETA
j_lo_rounded_path = 0.8563**2 * 1.2148e-4 * 0.3439
j_nlo_rounded_path = 0.8224**2 * 1.2148e-4 * 0.3439

v.check("J_LO rounded proposition path", j_lo_rounded_path, 3.064e-5, rel=5e-4)
v.check("J_NLO rounded proposition path", j_nlo_rounded_path, 2.826e-5, rel=5e-4)
v.check(
    "J_LO from exact stated formulas",
    j_lo_exact,
    3.064e-5,
    rel=5e-4,
    detail="Expected fail: exact Phi-derived eta gives about 3.035e-5.",
)
v.check(
    "J_NLO from exact stated formulas",
    j_nlo_exact,
    2.826e-5,
    rel=5e-4,
    detail="Expected fail: exact Phi-derived eta gives about 2.799e-5.",
)
v.check("J_LO rounded residual vs PDG 3.08e-5", residual(j_lo_rounded_path, PDG_J), -0.5, rel=1e-1)
v.check("J_NLO rounded residual vs PDG 3.08e-5", residual(j_nlo_rounded_path, PDG_J), -8.2, rel=2e-2)
v.check(
    "J_NLO exact-formula residual vs PDG 3.08e-5",
    residual(j_nlo_exact, PDG_J),
    -8.2,
    rel=2e-2,
    detail="Expected fail: exact formulas give about -9.13%.",
)

p82_j = 3.037e-5
v.check("P82 J residual vs 3.18e-5", residual(p82_j, 3.18e-5), -4.4, rel=3e-2)
v.check(
    "P82 J residual vs 3.08e-5",
    residual(p82_j, PDG_J),
    -0.5,
    rel=5e-2,
    detail="Expected fail: changing only the PDG comparator gives about -1.40%, so the P82/P91 difference is not entirely the PDG central value.",
)

v.check("A_LO residual percent", residual(A_LO, PDG_A), 4.0, rel=2e-2)
v.check(
    "A_NLO residual percent sign",
    residual(A_NLO, PDG_A),
    0.07,
    rel=1e-1,
    detail="Expected fail under the paper's residual convention: A_NLO is about -0.07% vs PDG 0.823, not +0.07%.",
)
vcb_lo = A_LO * LAM2
vcb_nlo = A_NLO * LAM2
vub_lo = A_LO * LAM3 * 0.5 * PHI
vub_nlo = A_NLO * LAM3 * 0.5 * PHI
v.check("Vcb LO", vcb_lo, 0.04241, rel=2e-4)
v.check("Vcb LO residual percent", residual(vcb_lo, PDG_VCB), 3.2, rel=2e-2)
v.check("Vcb NLO", vcb_nlo, 0.04073, rel=2e-4)
v.check(
    "Vcb NLO residual percent sign",
    residual(vcb_nlo, PDG_VCB),
    0.9,
    rel=5e-2,
    detail="Expected fail under the paper's residual convention: the residual is about -0.92%, not +0.9%.",
)
v.check(
    "Vub LO exact Phi residual percent",
    residual(vub_lo, PDG_VUB),
    0.4,
    rel=5e-2,
    detail="Expected fail: exact Phi gives about -0.23%; +0.4% used the larger P82 Phi.",
)
v.check(
    "Vub NLO exact Phi residual percent",
    residual(vub_nlo, PDG_VUB),
    -3.7,
    rel=5e-2,
    detail="Expected fail: exact Phi gives about -4.18%.",
)

pdg_product = PDG_VCB**2 * 0.22452**2 * PDG_ETA
v.check(
    "PDG factor product equals PDG J used in residual decomposition",
    pdg_product,
    PDG_J,
    rel=5e-3,
    detail="Expected fail: the listed PDG factors multiply to about 2.93e-5, not 3.08e-5.",
)
term_vcb = 2 * residual(vcb_nlo, PDG_VCB)
term_lam2 = residual(LAM2, 0.22452**2)
term_eta = residual(ETA, PDG_ETA)
linear_sum = term_vcb + term_lam2 + term_eta
v.check(
    "residual decomposition term sum",
    linear_sum,
    -3.7,
    rel=5e-2,
    detail="Expected fail with exact eta: term sum is about -4.55%; the remaining gap is due to inconsistent PDG inputs, not ordinary cross terms.",
)
v.check("lambda^6 residual approximation", 6 * residual(LAM, PDG_LAMBDA), -5.3, rel=2e-2)

eta_required_95 = 0.95 * PDG_J / (A_NLO**2 * LAM6)
v.check("eta required for J_NLO within 5 percent", eta_required_95, 0.3561, rel=1e-3)
v.check("eta required shift vs rounded eta", residual(eta_required_95, 0.3439), 3.6, rel=2e-2)
v.check(
    "eta required shift vs exact eta",
    residual(eta_required_95, ETA),
    3.6,
    rel=2e-2,
    detail="Expected fail: relative to exact eta from the stated formulas, the required shift is about +4.54%.",
)

sys.exit(v.summary())
