#!/usr/bin/env python3
"""
Two-point running test for the cube-geometry weak-mixing prediction.

The panel-greenlit second-hit test. Two anchors, with different standings:

  IR anchor :  sin^2 theta_W(Q=0) = vertexDegree / (4 pi) = 3/(4 pi) = 0.238732
       The one cube-forced weak-mixing number. Lean (Masses/WeakMixingFromCube.lean,
       weakMixingFromCube_consistent) certifies it lies inside the measured 1-sigma
       band [0.23851, 0.23883] of the Q=0 Thomson-limit value, ~0.4 sigma from center;
       identifying the cube ratio with sin^2 theta_W(0) is HYPOTHESIS tier
       (falsifier: weakMixingFromCube_falsifier). Not a claim about the M_Z value.
  UV anchor :  sin^2 theta_W(M_GUT) = 3/8 = 0.375
       NOT cube-forced. 3/8 is the textbook SU(5) tree-level value of sin^2 theta_W
       at unification (Georgi-Quinn-Weinberg); in Lean, sin2ThetaW_GUT := 3/8 is a
       definition importing that standard value. It is reached, by construction, at
       the scale where the GUT-normalized U(1) coupling g1 equals the SU(2) coupling g2.

The test (no free parameters): run the SM gauge couplings from M_Z upward with the
standard beta functions and find the scale mu* where g1 = g2 (equivalently
sin^2 theta_W(mu*) = 3/8). GREEN if mu* lands in the GUT window 1e15 - 1e17 GeV.

Inputs: PDG/MS-bar electroweak values at M_Z. Nothing here is fit to the cube.
"""

import numpy as np
from scipy.integrate import solve_ivp

# ---- experimental inputs at M_Z (MS-bar, PDG 2024) ----
M_Z      = 91.1876          # GeV
alpha_em_inv_MZ = 127.951   # alpha_em(M_Z)^-1  (MS-bar)
sin2_MZ  = 0.23121          # sin^2 theta_W(M_Z) (MS-bar)
alpha_s_MZ = 0.1179         # for completeness / g3

cos2_MZ = 1.0 - sin2_MZ

# couplings at M_Z. alpha2 = alpha_em / sin2 ; alpha' = alpha_em / cos2 ; alpha1 = (5/3) alpha'
alpha_em_MZ = 1.0 / alpha_em_inv_MZ
alpha2_MZ = alpha_em_MZ / sin2_MZ
alphaP_MZ = alpha_em_MZ / cos2_MZ          # U(1)_Y in SM normalization
alpha1_MZ = (5.0/3.0) * alphaP_MZ          # GUT-normalized U(1)
alpha3_MZ = alpha_s_MZ

print("== couplings at M_Z ==")
print(f"  alpha_em^-1 = {alpha_em_inv_MZ},  sin^2 = {sin2_MZ}")
print(f"  alpha1^-1(GUT-norm) = {1/alpha1_MZ:.4f}")
print(f"  alpha2^-1           = {1/alpha2_MZ:.4f}")
print(f"  alpha3^-1           = {1/alpha3_MZ:.4f}")

# ---- one-loop SM beta coefficients (GUT-normalized U(1)) ----
# d alpha_i^-1 / dt = - b_i / (2 pi),  t = ln(mu)
b1 = 41.0/10.0     #  +4.1
b2 = -19.0/6.0     #  -3.1667
b3 = -7.0

def running_inv(alpha_inv_MZ, b, mu):
    t = np.log(mu / M_Z)
    return alpha_inv_MZ - (b / (2*np.pi)) * t

# ---- one-loop crossing g1 = g2 (where sin^2 = 3/8) ----
# alpha1^-1(mu) = alpha2^-1(mu)
#   1/alpha1_MZ - b1/(2pi) t = 1/alpha2_MZ - b2/(2pi) t
#   (1/alpha1 - 1/alpha2) = (b1 - b2)/(2pi) t
t_cross = (1/alpha1_MZ - 1/alpha2_MZ) / ((b1 - b2) / (2*np.pi))
mu_cross_1loop = M_Z * np.exp(t_cross)
print("\n== one-loop g1=g2 crossing (sin^2 = 3/8 by construction) ==")
print(f"  t* = ln(mu/M_Z) = {t_cross:.3f}")
print(f"  mu* = {mu_cross_1loop:.3e} GeV")

def sin2_at(mu, two_loop=False):
    """sin^2 theta_W(mu) = alpha' / (alpha2 + alpha') with alpha' = (3/5) alpha1."""
    if not two_loop:
        a1i = running_inv(1.0/alpha1_MZ, b1, mu)
        a2i = running_inv(1.0/alpha2_MZ, b2, mu)
    else:
        a1i, a2i, _ = two_loop_inv(mu)
    a1 = 1.0/a1i
    a2 = 1.0/a2i
    aP = (3.0/5.0) * a1                 # back to SM hypercharge
    return aP / (a2 + aP)

print("\n== one-loop sin^2 theta_W at canonical scales ==")
for mu in [M_Z, 1e3, 1e6, 1e9, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, mu_cross_1loop]:
    print(f"  mu = {mu:.3e} GeV :  sin^2 = {sin2_at(mu):.5f}")

# ---- two-loop SM RGEs (GUT-normalized), standard B matrix ----
# d a_i / dt = (1/2pi) b_i a_i^2 + (1/8 pi^2) sum_j B_ij a_i^2 a_j  ... use alpha = g^2/4pi
# Use the standard two-loop gauge B-matrix (SM, one Higgs doublet):
B = np.array([
    [199.0/50.0, 27.0/10.0, 44.0/5.0],
    [9.0/10.0,   35.0/6.0,  12.0],
    [11.0/10.0,  9.0/2.0,  -26.0],
])
b_vec = np.array([b1, b2, b3])

def rge(t, y):
    # y = alpha_i (not inverse). t = ln mu.
    a = y
    dadt = np.zeros(3)
    for i in range(3):
        gauge2 = sum(B[i, j] * a[j] for j in range(3))
        dadt[i] = (b_vec[i] / (2*np.pi)) * a[i]**2 + (1.0/(8*np.pi**2)) * a[i]**2 * gauge2
    return dadt

y0 = np.array([alpha1_MZ, alpha2_MZ, alpha3_MZ])
t_span = (0.0, np.log(1e18 / M_Z))
sol = solve_ivp(rge, t_span, y0, dense_output=True, rtol=1e-9, atol=1e-12, max_step=0.05)

def two_loop_inv(mu):
    t = np.log(mu / M_Z)
    a = sol.sol(t)
    return 1.0/a[0], 1.0/a[1], 1.0/a[2]

# find two-loop g1=g2 crossing
from scipy.optimize import brentq
def diff12(logmu):
    mu = np.exp(logmu)
    a1i, a2i, _ = two_loop_inv(mu)
    return a1i - a2i
lo, hi = np.log(1e10), np.log(1e16)
try:
    logmu_star = brentq(diff12, lo, hi)
    mu_cross_2loop = np.exp(logmu_star)
except Exception as e:
    mu_cross_2loop = None
    print("two-loop crossing solve failed:", e)

print("\n== two-loop g1=g2 crossing (sin^2 = 3/8) ==")
if mu_cross_2loop:
    print(f"  mu* = {mu_cross_2loop:.3e} GeV")
    print(f"  sin^2 at mu*  = {sin2_at(mu_cross_2loop, two_loop=True):.5f}  (should be 0.375)")

print("\n== two-loop sin^2 theta_W at canonical scales ==")
for mu in [M_Z, 1e3, 1e9, 1e13, 1e14, 1e15, 1e16, 1e17]:
    print(f"  mu = {mu:.3e} GeV :  sin^2 = {sin2_at(mu, two_loop=True):.5f}")

# ---- VERDICT ----
print("\n" + "="*64)
print("VERDICT (panel criterion: mu* in GUT window 1e15 - 1e17 GeV)")
print("="*64)
window = (1e15, 1e17)
for name, mu in [("1-loop", mu_cross_1loop), ("2-loop", mu_cross_2loop)]:
    if mu is None:
        continue
    inside = window[0] <= mu <= window[1]
    print(f"  {name}: sin^2=3/8 reached at {mu:.3e} GeV  -> "
          f"{'INSIDE' if inside else 'OUTSIDE'} [1e15,1e17]")
print("\nIR anchor 3/(4pi) =", 3/(4*np.pi))
print("UV anchor 3/8     =", 3/8)

# ---- the decisive caveat: the crossing scale is set by the BSM spectrum, not the cube ----
# Repeat the one-loop g1=g2 crossing with MSSM coefficients to show the verdict flips.
b1_mssm, b2_mssm = 33.0/5.0, 1.0
t_mssm = (1/alpha1_MZ - 1/alpha2_MZ) / ((b1_mssm - b2_mssm) / (2*np.pi))
mu_mssm = M_Z * np.exp(t_mssm)
print("\n== same test, MSSM beta functions (b1=33/5, b2=1) ==")
print(f"  g1=g2 (sin^2=3/8) at mu* = {mu_mssm:.3e} GeV  -> "
      f"{'INSIDE' if 1e15 <= mu_mssm <= 1e17 else 'OUTSIDE'} [1e15,1e17]")
print("  => the crossing scale is dominated by the (unknown) BSM content, not by cube geometry.")
