"""
Scope-boundary test 4: PPN parameter gamma

Claim: The parametrised post-Newtonian (PPN) parameter gamma, which
characterises how much spatial curvature is produced by a unit mass
(relative to the time-time curvature measured by Newtonian gravity),
equals 1 in standard GR. This reflects the no-slip condition Phi = Psi
in Newtonian-gauge scalar perturbation theory: the time-time and
space-space scalar potentials are equal for matter without anisotropic
stress.

Framework scope analysis:
  - Psi (time-time scalar potential) -> lives in g_tt -> IN SCOPE
  - Phi (space-space scalar potential) -> lives in g_ij -> OUT OF SCOPE
    (not directly generated by u; inherited from GR)
  - The EQUALITY Psi = Phi (i.e. gamma = 1) -> inherited from GR's
    ij-traceless Einstein equation, NOT independently predicted by
    the framework

This test clarifies that the framework's claim about scalar-static
content is specifically about the Psi-sector, and that gamma = 1
is part of what the framework INHERITS (correctly) from GR rather
than derives.

Setup (geometrised units G = c = 1):

Isotropic PPN line element for a static, spherically symmetric
mass M at the origin, to order 1/r:

    ds^2 = -(1 - 2 Phi_N + 2 beta Phi_N^2) dt^2
           + (1 + 2 gamma Phi_N) (dx^2 + dy^2 + dz^2) + ...

where Phi_N = M/r is the Newtonian potential magnitude (positive
by convention).

GR values: gamma = 1, beta = 1.

Experimental bounds (current):
  - Shapiro delay / Cassini: |gamma - 1| < 2.3e-5
  - Lunar Laser Ranging:     |beta  - 1| < ~1e-4

The framework's u-reading:
  u(r) = u_0 sqrt(1 - 2M/r + O(1/r^2))  [from -g_tt]
       = u_0 (1 - M/r + ...)  to leading order

This does NOT tell us gamma -- gamma lives in the spatial (ij) part,
not in g_tt.
"""

import numpy as np
import sympy as sp

print("=" * 72)
print("SCOPE-BOUNDARY TEST 4: PPN PARAMETER gamma")
print("=" * 72)
print()

# ------------------------------------------------------------------
# Symbolic setup
# ------------------------------------------------------------------
r, M, u0 = sp.symbols('r M u_0', positive=True, real=True)
Psi, Phi, gamma, beta_ppn = sp.symbols('Psi Phi gamma beta', real=True)

print("Isotropic PPN metric (static, spherical, post-Newtonian):")
print()
print("  ds^2 = -(1 - 2 Phi_N + 2 beta Phi_N^2) dt^2")
print("         + (1 + 2 gamma Phi_N) (dx^2 + dy^2 + dz^2) + ...")
print()
print("where Phi_N = M/r (magnitude of Newtonian potential)")
print()

# ------------------------------------------------------------------
# Check 1: What u(x) sees from g_tt
# ------------------------------------------------------------------
print("-" * 72)
print("Check 1: Framework's u-reading of the PPN g_tt")
print("-" * 72)
Phi_N = M/r
g_tt_PPN = -(1 - 2*Phi_N + 2*beta_ppn*Phi_N**2)
u_PPN = u0 * sp.sqrt(-g_tt_PPN)
u_expanded = sp.series(u_PPN, M, 0, 3).removeO()
u_expanded = sp.simplify(u_expanded)
print(f"-g_tt        = 1 - 2 M/r + 2 beta (M/r)^2")
print(f"u(r)         = u_0 * sqrt(-g_tt)")
print(f"u(r) series  = {u_expanded}")
print()
print("Leading: u_0 (1 - M/r)")
print("Second:  additional (M/r)^2 correction involving beta.")
print()
print("The framework's u ONLY sees g_tt. Parameter beta enters at")
print("second order in M/r (post-Newtonian). Parameter gamma does NOT")
print("enter u at all -- gamma lives in the spatial part of the metric.")
print()

# ------------------------------------------------------------------
# Check 2: Where gamma lives
# ------------------------------------------------------------------
print("-" * 72)
print("Check 2: Where gamma lives in the metric")
print("-" * 72)
print()
print("Spatial (ij) metric: g_ij = (1 + 2 gamma Phi_N) delta_ij + ...")
print()
print("gamma controls the spatial curvature produced by unit mass. It is")
print("determined by the ij-traceless part of the Einstein equations,")
print("which in the perfect-fluid case (no anisotropic stress) forces")
print("gamma = 1.")
print()
print("The framework does NOT independently predict gamma. The framework's")
print("u(x) = u_0 * sqrt(-g_tt) only has access to g_tt. The spatial")
print("metric g_ij is brane dynamics -- how the brane's induced intrinsic")
print("spatial geometry responds to the 3D density distribution -- and")
print("is inherited from Einstein's equations unchanged.")
print()

# ------------------------------------------------------------------
# Check 3: Observational consequences -- light bending and Shapiro delay
# ------------------------------------------------------------------
print("-" * 72)
print("Check 3: Observational consequences of gamma")
print("-" * 72)
print()
print("Two classical tests:")
print()
print("(a) Light deflection by the Sun:")
print("       angle = (1 + gamma) * 2 M_sun / (impact parameter)")
print("       GR (gamma = 1): angle = 4 M_sun / b = 1.75 arcsec at solar limb")
print("       Newtonian only: angle = 2 M_sun / b = 0.87 arcsec")
print()
print("    Physical reading: half the bending comes from g_tt (the Psi")
print("    potential -- framework's u), half from the spatial g_ij (Phi)")
print("    curvature -- framework inherits this from GR).")
print()
print("(b) Shapiro time delay (radar ranging past a mass):")
print("       delta_t = 2 (1 + gamma) M ln(4 r_1 r_2 / b^2)")
print("       GR: factor (1 + gamma) = 2.")
print()
print("    Physical reading: same half-and-half decomposition.")
print()

# Numerical: solar limb deflection
M_sun_geom = 1.4768  # km, solar mass in geometrised units (GM_sun/c^2)
R_sun_km = 6.957e5   # km
b = R_sun_km
deflection_Newtonian = 2 * M_sun_geom / b  # in radians
deflection_GR = 4 * M_sun_geom / b
arcsec_per_rad = 206265
print(f"Numerical solar-limb deflection:")
print(f"  Newtonian (g_tt only)        : {deflection_Newtonian*arcsec_per_rad:.3f} arcsec")
print(f"  GR (g_tt + g_ij, gamma = 1)  : {deflection_GR*arcsec_per_rad:.3f} arcsec")
print(f"  Measured (Eddington, subsequent): 1.751 arcsec")
print()

# ------------------------------------------------------------------
# Check 4: Scope reading of gamma = 1
# ------------------------------------------------------------------
print("-" * 72)
print("Check 4: Scope reading of the gamma = 1 prediction")
print("-" * 72)
print("""
Standard GR's gamma = 1 comes from the ij-traceless Einstein equation
applied to a perfect fluid:

    R_{ij} - (1/3) g_{ij} R = 8 pi G (T_{ij} - (1/3) g_{ij} T)

For a perfect fluid, the spatial traceless part of T_{ij} is zero
(no anisotropic stress). This forces the spatial traceless part of
the Ricci tensor to zero, which in the linearised static case gives
Psi = Phi, i.e. gamma = 1.

Framework scope reading:
  - The framework homes g_tt (scalar time-time) via u(x) = u_0 * sqrt(-g_tt).
  - The framework does NOT home g_ij (scalar space-space).
  - Therefore gamma = 1 is not independently predicted by the framework.
  - The framework INHERITS gamma = 1 from GR, because the framework
    reads u off the same metric GR provides.

This is analogous to:
  - frame-dragging (vector metric content) -- out of framework scope,
    inherited from GR
  - tensor GW modes -- out of framework scope, inherited from GR
  - spatial curvature coefficient gamma (scalar metric content in g_ij)
    -- out of framework scope, inherited from GR

The scope boundary is NOT "scalar = in, vector/tensor = out". The
sharper boundary is:
  - g_tt (time-time) -> IN (u-reading)
  - everything else (g_ij, g_ti) -> INHERITED

Light bending half-and-half:
  - The Shapiro-delay-like half (from g_tt) IS the framework's part.
  - The spatial-curvature half (from g_ij, gamma part) is inherited.
  - Total light bending = framework part + inherited part, giving the
    full GR answer.

This is exactly the scope structure we'd expect. Historical bonus: when
Eddington measured 1.75'' (twice the Newtonian value) in 1919, the factor
of 2 was the first observational confirmation that spatial curvature
matters -- i.e. that gamma = 1 is a real feature, not 0 (which would be
pure-potential Newtonian gravity). The framework agrees with this history:
the framework alone, generating only the g_tt contribution, would predict
half the Eddington bending (0.87''). It's only by inheriting gamma = 1
from GR that the framework's total prediction becomes the observed
1.75''. Neither half is redundant.
""")

# ------------------------------------------------------------------
# Check 5: What this implies about the scope boundary as worded
# ------------------------------------------------------------------
print("-" * 72)
print("Check 5: Refining the scope-boundary language")
print("-" * 72)
print("""
Sanitation v2 currently says the scope is "scalar/static content".
This test refines that:

  Old wording: "scalar/static content in scope"
  New wording: "the g_tt component (scalar time-time metric) is homed
               by u(x); everything else in the metric is inherited
               from Einstein's equations unchanged."

Under this refinement:
  - g_tt scalar perturbations (Psi in Newtonian gauge)   : IN
  - g_ij scalar perturbations (Phi in Newtonian gauge)   : INHERITED
  - g_ti vector perturbations (frame-dragging, Kerr)     : INHERITED
  - Tensor perturbations (GW h_+, h_x)                   : INHERITED

'In scope' means 'natively homed by u(x)'.
'Inherited' means 'the framework does not modify this GR content; it
reads u off whatever metric GR produces'.

No contradiction arises because the framework is not claiming to
derive all of GR; it's claiming to provide geometric homes for
specific scalar features (u_0 as Lambda, u(x) as time dilation,
horizon temperatures via Euclidean periodicity) while leaving the
dynamical structure of GR untouched.

This refinement makes the scope boundary sharper and more defensible.
""")

print("=" * 72)
print("TEST 4 RESULT: PASS, with scope refinement")
print("=" * 72)
print("Framework inherits gamma = 1 from GR -- it is not and cannot be")
print("derived from u(x) = u_0 * sqrt(-g_tt) alone, because gamma lives")
print("in the spatial part of the metric. This is a clean scope result:")
print("the framework correctly identifies what it homes (g_tt) and what")
print("it inherits (g_ij, g_ti). Light bending is half-and-half between")
print("the two; both halves are needed for the observed value.")
