#!/usr/bin/env python3
"""
SCOPE NOTE, 2026-10-11 (comp 202).  c = 0.9978 +- 0.0054 below is on comp 123's 2013
inputs; on PDG 2024 inputs with the pole-mass ambiguity comp 202 re-solves the same equation
at c = 0.9927 +- 0.0044, 1.6 sigma from c = 1 and 148 sigma from sqrt(e).  The exclusion of
reading B and the rule against spending the agreement are unchanged.

PROVENANCE: PROOF

Computation 176 -- the field-normalisation open item is the value of one number
==============================================================================
WHAT THIS DOES.  sec:ch-normalisation carries the book's sharpest open item: the
one-loop seagull eq:ch-exact presupposes a canonically normalised scalar, while
eq:field_norm_Z2 asserts Z^2 = e^{-1}, i.e. that the substrate field and the
emergent doublet are NOT mutually canonical.  The section states that at most one
of the two can be canonical, that the two readings give M_* = 1.285 TeV and
M_* ~ 2.2 TeV, and that "the ratio between the two is 1/Z identically".  It does
not say what would settle the question.

This file reduces the whole item to a single undetermined number, derives the
1/Z ratio the section asserts, and measures the number.

THE OBSERVATION.  Canonical normalisation is not a matter of interpretation.  It
is a statement about the kinetic coefficient, and P3 carries one explicitly:

    F[psi, eps] = int[ -eps <psi,psi> + (1/4)<psi,psi>^2 + c |grad_C psi|^2 ] dmu

The book constrains c ONLY by c > 0 (sec:postulates, and the gradient-regularisation
discussion).  It is never given a value anywhere.  And c is not absorbable: the
Boolean Dirichlet form |grad_C psi|^2 is defined with a fixed normalisation, a bare
sum over edges of the hypercube, so c is a genuine independent coefficient.
Rescaling psi to canonical form does not remove c, it MOVES c into the quartic:

    phi = sqrt(c) psi   =>   c|grad psi|^2 = |grad phi|^2      (canonical)
                             (1/4)<psi,psi>^2 = (1/(4c^2)) (phi^dag phi)^2

    so    lambda_canon = 1 / (4 c^2),    NOT 1/4.

EVERYTHING FOLLOWS FROM THAT ONE LINE.

    C_H  = 6 lambda_canon = 3 / (2 c^2)
    M_*  = 4 pi m_h / sqrt(C_H) = 4 pi m_h c sqrt(2/3)          LINEAR in c

so the book's C_H = 3/2 and M_* = 1285 GeV are the c = 1 values, and the c = 1
assumption has never been stated, let alone derived.

THE TWO READINGS ARE TWO VALUES OF c.

    reading A, lambda_PST = 1/4 is the canonical quartic   <=>   c = 1
    reading B, lambda_SM(M_*) is the canonical quartic     <=>   c^2 = e

and their M_* ratio is exactly sqrt(e) = 1/Z, with Z^2 = e^{-1}.  That is the
"1/Z identically" sec:ch-normalisation asserts, obtained here as a consequence of
lambda_canon = 1/(4c^2) rather than as an observation.

THE BRIDGE PREMISE MEASURES c, AND KILLS READING B.  Read as what it says it is,
a field-strength ratio, Z^2 compares lambda_SM(M_*) against the CANONICAL quartic:

    Z^2 = lambda_SM(M_*) / lambda_canon = 4 c^2 lambda_SM(M_*) = e^{-1}

and since M_* = 1285 c GeV, that is ONE equation in ONE unknown:

    lambda_SM(1285 c GeV)  =  e^{-1} / (4 c^2)

It is not circular.  The e^{-1} is the substrate spectral action's f(1), exact and
D-independent (eq:S_sub_ratio), with the Gaussian f itself DERIVED in comp 174
rather than assumed; the 1/4 is P3's quartic; lambda_SM runs from measured
(m_t, alpha_s, m_h, v).  Nothing on either side assumes c = 1.  The residual is
monotone across [0.5, 3.0], so the root is unique:

    c = 0.9978 +/- 0.0054      0.4 sigma from c = 1
                             121 sigma from reading B's sqrt(e) = 1.6487

CORRECTED ON A REFEREE PASS OF THIS FILE.  The first version evaluated lambda_SM at
the FIXED anchor M_*(c=1) = 1285 GeV while varying c, and quoted c = 0.9961 +/-
0.0038 and 174 sigma.  That holds M_* still while moving the very parameter M_*
is proportional to, which is exactly the shortcut comp 173 established the
discipline against ("propagated by re-solving for M_* at each input variation
rather than holding it fixed").  Re-solving widens the budget by 1.4x and shrinks
the exclusion by 1.5x.  Neither correction changes a verdict, but the arithmetic
was wrong and the file it was wrong in was one hour old.

READING B IS EXCLUDED THREE WAYS.  This equation puts it 121 sigma out; comp 173
puts it 9.5 sigma out by a different route, as a conflict with P3's postulated
b = 1/4; and comp 174 closed the regulator-shape escape that was its only defence.
sec:ch-normalisation should therefore stop presenting a live dichotomy: only
lambda_PST = 1/4 as the canonical quartic survives.

WHAT THIS DOES NOT DO, AND THE FILE IS USELESS IF THIS IS MISREAD.  It does NOT
derive c = 1 from P1-P3.  And the 0.4 sigma must not be read as a measurement that
closes the item, because THE COINCIDENCE CANNOT BE SPENT TWICE:

  - if c = 1 is an ASSUMPTION, then lambda_SM(M_*) = e^{-1}/4 is a rigid,
    parameter-free prediction and comp 123's 1.0 sigma agreement confirms it;
  - if c is a FREE PARAMETER fixed by that agreement, the agreement is a fit.
    One equation, one unknown, always solvable.  comp 123's headline result would
    have to be withdrawn as a confirmation.

The first is worth far more to PST than the second, so c = 1 is carried as a
STATED ASSUMPTION (P3's gradient term is written in canonical normalisation)
and the 0.4 sigma is a consistency check on it.  Naming that assumption is what
replaces the old open dichotomy: out of a continuum of candidate values, the data
picks the natural one, which is worth saying and is not a derivation.

WHERE A DERIVATION WOULD COME FROM.  c multiplies the Boolean Dirichlet form, and
sec:mosco-convergence takes that form to the Laplace-Beltrami form on
M = R x S^3, which has a canonical normalisation of its own.  Either the Mosco
limit fixes c against that normalisation, or c is absorbed into the a_n -> 0
scaling and is not an independent parameter at all.  Either would promote the
assumption to a theorem and hand the 1.0 sigma back as a free-standing
prediction.  That is a well-posed analysis question and the natural next piece of
work.  This file does not attempt it.
"""

import math

# comp 123 owns the two-loop SM running.  It is imported rather than duplicated
# because part 4 needs lambda_SM at a scale that MOVES with c, not the single
# anchored number at M_*(c=1).  The first version of this file used that anchored
# number and held M_* fixed while varying c, which is exactly the shortcut comp
# 173 established the discipline against; see part 4.
# A plain sibling import, not importlib on a path built from __file__.  The website
# runs this in the browser, where __file__ is not defined at all, and the mechanism
# comps 61 and 63 to 65 already use works in both places: run from the command line,
# this directory is sys.path[0]; run from the page, the preamble puts it there and the
# page mounts the helper beside it.  Keep the page's HELPER_DEPS in step.
import computation_123_se as c123

MH = 125.25
E = math.e

# M_*(c) = 4 pi m_h c sqrt(2/3).  BASE is the c = 1 value, 1285.1 GeV.
BASE = 4 * math.pi * MH * math.sqrt(2.0 / 3.0)

# comp 123's parametric budget: the three inputs that move lambda_SM(M_*).
BUDGET = (("m_t    +/- 0.76 GeV", dict(dmt=0.76)),
          ("alpha_s +/- 0.0006", dict(das=0.0006)),
          ("m_h    +/- 0.17 GeV", dict(dmh=0.17)))


def _bc(dmt=0.0, das=0.0, dmh=0.0):
    """comp 123's boundary conditions at m_t, with its input shifts applied."""
    bc = dict(c123.BC0)
    bc["yt"] = c123.BC0["yt"] * (1 + dmt / c123.MT_REF)
    as0 = c123.BC0["g3"] ** 2 / (4 * math.pi)
    bc["g3"] = math.sqrt(4 * math.pi * (as0 + das))
    bc["lam"] = c123.BC0["lam"] + 0.00206 * dmh
    return bc


def lam_sm(mu, bc=None):
    """Two-loop lambda_SM at an ARBITRARY scale, not just at M_*(c=1)."""
    return c123.run(c123.MT_REF, mu, bc or c123.BC0)[4]


def residual(c, bc=None):
    """lambda_SM(M_*(c)) - e^-1/(4c^2).  Zero at the self-consistent point."""
    return lam_sm(BASE * c, bc) - (1 / E) / (4 * c * c)


def solve_c(bc=None, lo=0.5, hi=3.0):
    """Bisect for the root.  residual is monotone increasing across this range."""
    for _ in range(60):
        mid = 0.5 * (lo + hi)
        if residual(lo, bc) * residual(mid, bc) <= 0:
            hi = mid
        else:
            lo = mid
    return 0.5 * (lo + hi)


def rule():
    print("=" * 100)


# ---------------------------------------------------------------------------
def part1():
    rule()
    print("  1.  P3 CARRIES A KINETIC COEFFICIENT, AND IT IS NEVER GIVEN A VALUE")
    rule()
    print("      F[psi,eps] = int[ -eps <psi,psi> + (1/4)<psi,psi>^2 + c |grad_C psi|^2 ] dmu")
    print()
    print("      The book constrains c only by c > 0.  It is not assigned a value")
    print("      anywhere, and canonical normalisation is a statement about exactly")
    print("      this coefficient.")
    print()
    print("      Nor is c absorbable.  |grad_C psi|^2 is the Boolean Dirichlet form,")
    print("      a bare sum over hypercube edges with a fixed normalisation, so c is")
    print("      an independent coefficient rather than a convention.")
    print()
    return True


def part2():
    rule()
    print("  2.  RESCALING TO CANONICAL FORM MOVES c INTO THE QUARTIC")
    rule()
    print("      Put phi = sqrt(c) psi, i.e. psi = phi / sqrt(c):")
    print()
    cs = [0.5, 1.0, 1.6487, 2.0]
    print(f"      {'c':>10}{'kinetic coeff':>16}{'lambda_canon':>16}{'= 1/(4c^2)?':>14}")
    ok = True
    for c in cs:
        kin = c * (1 / math.sqrt(c)) ** 2          # c * (1/sqrt(c))^2
        quart = 0.25 * (1 / math.sqrt(c)) ** 4     # (1/4) * (1/sqrt(c))^4
        pred = 1.0 / (4 * c * c)
        good = abs(kin - 1) < 1e-12 and abs(quart - pred) < 1e-12
        ok = ok and good
        print(f"      {c:>10.4f}{kin:>16.6f}{quart:>16.6f}{str(good):>14}")
    print()
    print("      kinetic coefficient is 1 for every c, so phi IS the canonical field,")
    print("      and the quartic it carries is lambda_canon = 1/(4 c^2), not 1/4.")
    print(f"      -> lambda_canon = 1/(4c^2) verified                    : {ok}")
    print()
    return ok


def part3():
    rule()
    print("  3.  THE TWO READINGS ARE c = 1 AND c^2 = e, AND THEIR RATIO IS 1/Z")
    rule()
    print("      C_H = 6 lambda_canon = 3/(2c^2);  M_* = 4 pi m_h / sqrt(C_H)")
    print()
    rows = [("A: lambda_PST = 1/4 canonical", 1.0),
            ("B: lambda_SM(M_*) canonical", math.sqrt(E))]
    print(f"      {'reading':34}{'c':>9}{'C_H':>9}{'M_* (GeV)':>12}")
    ms = []
    for name, c in rows:
        CH = 3.0 / (2 * c * c)
        M = 4 * math.pi * MH / math.sqrt(CH)
        ms.append(M)
        print(f"      {name:34}{c:>9.4f}{CH:>9.4f}{M:>12.1f}")
    ratio = ms[1] / ms[0]
    inv_Z = 1.0 / math.sqrt(1.0 / E)
    print()
    print(f"      ratio of the two M_*            : {ratio:.6f}")
    print(f"      1/Z with Z^2 = e^-1, i.e. sqrt(e): {inv_Z:.6f}")
    ok = abs(ratio - inv_Z) < 1e-9
    print(f"      -> the ratio IS 1/Z, identically                       : {ok}")
    print()
    print("      sec:ch-normalisation asserts this ratio.  Here it is a consequence")
    print("      of lambda_canon = 1/(4c^2): M_* is linear in c, reading A is c = 1,")
    print("      reading B is c = sqrt(e), so the ratio is sqrt(e) = 1/Z exactly.")
    print()
    return ok


def part4():
    rule()
    print("  4.  THE BRIDGE PREMISE MEASURES c, AND EXCLUDES READING B")
    rule()
    print("      Z^2 is a field-strength ratio, so it compares lambda_SM(M_*) against")
    print("      the CANONICAL quartic:  Z^2 = lambda_SM/lambda_canon = 4 c^2 lambda_SM.")
    print("      Setting Z^2 = e^-1 and remembering M_* = 1285 c GeV gives ONE equation")
    print("      in ONE unknown:")
    print()
    print("          lambda_SM(1285 c GeV)  =  e^-1 / (4 c^2)")
    print()
    print("      Both sides are supplied from outside this equation.  The e^-1 is the")
    print("      substrate spectral action's f(1), exact and D-independent (eq:S_sub_ratio),")
    print("      with the Gaussian f itself derived in comp 174 rather than assumed.  The")
    print("      1/4 is P3's quartic.  lambda_SM runs from measured (m_t, alpha_s, m_h, v).")
    print("      NOTHING here assumes c = 1, so the root is not put in by hand.")
    print()
    print(f"      {'c':>9}{'M_*(c) GeV':>13}{'lam_SM(M_*)':>14}{'e^-1/(4c^2)':>14}{'residual':>12}")
    for c in (0.6, 0.8, 1.0, 1.2, 1.5, math.sqrt(E), 2.0):
        tag = "   <- reading B" if abs(c - math.sqrt(E)) < 1e-9 else ""
        print(f"      {c:>9.4f}{BASE*c:>13.1f}{lam_sm(BASE*c):>14.5f}"
              f"{(1/E)/(4*c*c):>14.5f}{residual(c):>12.5f}{tag}")
    print()
    root = solve_c()
    print(f"      unique root in [0.5, 3.0]  :  c = {root:.5f}   (M_* = {BASE*root:.1f} GeV)")
    print()
    print("      THE UNCERTAINTY IS PROPAGATED BY RE-SOLVING, not by holding M_* fixed.")
    print("      comp 173 set that discipline because the solution point MOVES with the")
    print("      inputs; the first version of this file ignored it and quoted a sigma")
    print("      that was too small by 1.4x and an exclusion too large by 1.5x.")
    print()
    parts = []
    for name, kw in BUDGET:
        d = abs(solve_c(_bc(**kw)) - root)
        parts.append(d)
        print(f"      {name:>22}  ->  dc = {d:.5f}")
    sig = math.sqrt(sum(d * d for d in parts))
    print(f"      {'combined in quadrature':>22}  ->  dc = {sig:.5f}")
    print()
    print(f"      c = {root:.4f} +/- {sig:.4f}")
    print(f"      distance from the canonical c = 1        : {abs(root-1)/sig:7.1f} sigma")
    print(f"      distance from reading B's c = sqrt(e)    : {abs(math.sqrt(E)-root)/sig:7.0f} sigma")
    print()
    print("      READING B IS EXCLUDED, and not only here.  comp 173 excludes it at")
    print("      9.5 sigma by a different route, as a conflict with P3's postulated")
    print("      b = 1/4, and comp 174 closed the regulator-shape escape that was its")
    print("      only defence.  Three independent routes, one verdict: lambda_PST = 1/4")
    print("      is the canonical quartic, and c^2 = e is not tenable.")
    print()
    ok = abs(root - 1) / sig < 3.0 and abs(math.sqrt(E) - root) / sig > 20.0
    print(f"      -> c is consistent with 1 and reading B is excluded      : {ok}")
    print()
    return ok


def part5():
    rule()
    print("  5.  WHAT THIS BUYS, AND WHAT IT WOULD COST TO CLAIM MORE")
    rule()
    print("      ESTABLISHED, and this is a real narrowing: reading B is excluded.")
    print("      The item was a live dichotomy between two readings; it is not one now.")
    print("      Only lambda_PST = 1/4 as the canonical quartic survives part 4, comp")
    print("      173 and comp 174.")
    print()
    print("      NOT ESTABLISHED: c = 1 is still not DERIVED from P1-P3.  What part 4")
    print("      does is show that the value the data picks out of a continuum is the")
    print("      natural one.  That is a consistency check on c = 1, not a derivation.")
    print()
    print("      AND THE COINCIDENCE CANNOT BE SPENT TWICE.  This is the trap, and it")
    print("      is the reason this file does not simply declare the item closed.")
    print()
    print("      comp 123's headline result is that PST predicts lambda_SM(M_*) =")
    print("      e^-1/4 RIGIDLY, with no free parameter, and that two-loop running")
    print("      confirms it at 1.0 sigma.  That prediction exists only because M_* is")
    print("      pinned at 1285 GeV, which is C_H = 3/2, which is c = 1.  So:")
    print()
    print("        - if c = 1 is an ASSUMPTION, lambda_SM(M_*) = e^-1/4 is a")
    print("          parameter-free prediction, and its 1.0 sigma agreement is a")
    print("          genuine confirmation;")
    print("        - if c is a FREE PARAMETER fixed by that same agreement, then the")
    print("          agreement is a fit.  One equation, one unknown, always solvable.")
    print("          comp 123's headline would have to be withdrawn as a confirmation.")
    print()
    print("      The book cannot have both, and the first is worth far more to PST than")
    print("      the second.  So c = 1 is carried as a STATED ASSUMPTION rather than a")
    print("      measurement: P3's gradient term is written in canonical normalisation.")
    print("      That is a mild and nameable assumption, and naming it is what replaces")
    print("      the old open dichotomy.")
    print()
    print("      WHERE A DERIVATION WOULD COME FROM: c multiplies the Boolean Dirichlet")
    print("      form, and sec:mosco-convergence takes that form to the Laplace-Beltrami")
    print("      form on M = R x S^3, which carries a canonical normalisation of its")
    print("      own.  Either the Mosco limit fixes c against it, or c is absorbed into")
    print("      the a_n -> 0 scaling and is not an independent parameter.  Either would")
    print("      turn the assumption into a theorem and hand the 1.0 sigma back as a")
    print("      free-standing prediction.  Not attempted here.")
    print()
    return True


def part6():
    rule()
    print("  6.  TAMPER TEST")
    rule()
    # If M_* were not linear in c the whole reduction would be wrong.
    base = 4 * math.pi * MH * math.sqrt(2.0 / 3.0)
    print(f"      {'c':>10}{'M_*(c)':>12}{'M_*(c)/c':>12}")
    vals = []
    for c in (0.5, 1.0, 1.5, 2.0, 3.0):
        CH = 3.0 / (2 * c * c)
        M = 4 * math.pi * MH / math.sqrt(CH)
        vals.append(M / c)
        print(f"      {c:>10.2f}{M:>12.1f}{M/c:>12.4f}")
    spread = max(vals) - min(vals)
    linear = spread < 1e-9
    print()
    print(f"      spread of M_*/c across a factor 6 in c : {spread:.2e}")
    print(f"      constant = M_* is exactly linear in c  : {linear}")
    print()
    # and a wrong exponent must break it
    bad = [4 * math.pi * MH / math.sqrt(3.0 / (2 * c)) / c for c in (0.5, 1.0, 2.0)]
    breaks = (max(bad) - min(bad)) > 1e-6
    print(f"      control: with C_H = 3/(2c) instead of 3/(2c^2), M_*/c is NOT")
    print(f"      constant (spread {max(bad)-min(bad):.3f}), so the test discriminates : {breaks}")
    print()
    return linear and breaks


def main():
    print()
    rule()
    print("  COMPUTATION 176.  The field-normalisation item is the value of one number")
    rule()
    print("""
  sec:ch-normalisation asks which scalar field is canonically normalised and
  leaves it open.  Canonical normalisation is a statement about a kinetic
  coefficient, and P3 carries one, called c, which the book never evaluates.
  Everything in the item follows from lambda_canon = 1/(4c^2).
""")
    r1, r2, r3, r4, r5, r6 = part1(), part2(), part3(), part4(), part5(), part6()

    rule()
    print("  ASSESSMENT")
    rule()
    print(f"  1. P3 carries c, constrained only by c > 0                : {r1}")
    print(f"  2. rescaling gives lambda_canon = 1/(4c^2)                : {r2}")
    print(f"  3. the two readings are c = 1 and c^2 = e, ratio 1/Z      : {r3}")
    print(f"  4. c = 0.998 +/- 0.005, and reading B is excluded         : {r4}")
    print(f"  5. scope stated: c = 1 is a named assumption, not derived  : {r5}")
    print(f"  6. tamper test: M_* linear in c, control breaks it        : {r6}")
    print()
    print("""  RESULT.  The item is NARROWED, not closed, and the distinction is the
  whole point of part 5.

  What is settled: reading B is gone.  Solved self-consistently, with M_* moving
  with c and the budget propagated by re-solving, the bridge premise gives
  c = 0.9978 +/- 0.0054: 0.4 sigma from the canonical c = 1 and 121 sigma from
  reading B's sqrt(e).  comp 173 excludes the same reading at 9.5 sigma by a
  different route, as a conflict with P3's postulated b = 1/4, and comp 174 shut
  the regulator-shape escape that was its only defence.  Three routes, one verdict.
  The book should stop presenting this as a live dichotomy.

  What is NOT settled: c = 1 is not derived from P1-P3.  And the 0.4 sigma must
  not be read as a measurement that closes the item, because it is comp 123's
  lambda_SM(M_*) = e^-1/4 in another variable, the SAME single coincidence.
  Spending it to fix c would turn comp 123's parameter-free prediction into a
  one-parameter fit and cost the book its strongest number.  So c = 1 is carried
  as a stated assumption: P3's gradient term is written canonically.  The 0.4
  sigma is then a consistency check on that assumption, which is what it is.

  A derivation would come from the Mosco limit, where c multiplies the Boolean
  Dirichlet form that goes over to the Laplace-Beltrami form.  Either that fixes
  c, or c is absorbed into the a_n -> 0 scaling and c is not independent.  Either
  would promote the assumption to a theorem and hand the 1.0 sigma back as a
  free-standing prediction.  Neither is attempted here.""")
    print()
    return all([r1, r2, r3, r4, r5, r6])


if __name__ == "__main__":
    import sys
    # Exit only on FAILURE.  The website runs these in the browser under Pyodide,
    # where SystemExit is fatal even at status 0: it tears the runtime down, and the
    # page prints ERROR after output that was in fact correct.  Falling off the end
    # hands the build gates the same status 0, and a failure still exits 1.
    if not main():
        sys.exit(1)
