"""
Test 6: Nested-dragging modification of the Euclidean constraint.

Jay's intuition: if the brane advances at different w4 rates at different 3D
positions (gravitational differential dragging), this might modify the
effective velocity-addition law at a given point, possibly recovering SR
content that the pure Euclidean constraint missed.

Modified constraint:
  |v - v_drag(r)|^2 + u^2 = c^2

where v_drag(r) is a position-dependent velocity shift caused by nearby mass.

This is analogous to:
  - Kerr frame-dragging in standard GR (omega shifts the effective velocity)
  - Lattice dispersion relations producing emergent effective metrics
  - Bjorken-Drell style vacuum-modified propagation

APPROACH:
  1. Set up a 1D radial geometry with a point mass at origin.
  2. Choose a physically motivated drag profile v_drag(r).
  3. Compute the local effective velocity-addition law at a point r.
  4. Compare to SR to see if drag closes the gap.

KEY TEST: under drag, is the speed of light invariant? Does 3-velocity
addition approach the SR formula?

Note: this is a first-pass numerical experiment. It does NOT constitute a
proof of anything. It tests whether the general idea is viable enough to
justify further investigation.
"""

import numpy as np
import matplotlib.pyplot as plt

c = 1.0

# =============================================================================
# STEP 1: Define the drag profile
# =============================================================================
# What SHOULD v_drag(r) look like?
# 
# In your framework, near a mass the brane advances FASTER in w4. So the 
# *rate of w4 advance* is higher near the mass. A static observer near the 
# mass has a 4-velocity (0, u(r)) with u(r) > u(infinity). But this by itself 
# doesn't violate |v|^2 + u^2 = c^2 -- it just says the unit-4-speed 
# normalization shifts.
#
# For the "drag" to mix space and time, it must be a 3D velocity component:
# a preferred direction in 3D space that the brane's w4-advance is correlated 
# with.
#
# This is actually a significant design choice. In spherical symmetry (point 
# mass), there IS no preferred 3D direction for drag -- the situation is 
# rotationally symmetric. So a spherically symmetric static mass cannot 
# produce the kind of space-time-mixing drag we'd need.
#
# What CAN produce space-time mixing is:
#   - Rotational sources (Kerr-like frame dragging: azimuthal drag)
#   - Moving sources (boost-like drag along the direction of motion)
#   - Gradients in the w4-advance rate (tidal drag, non-static)
#
# The simplest tractable case: linear gradient in u(x). Imagine the brane's 
# w4-advance rate u varies linearly with x across some region. This is the 
# 1D analog of being near a large mass distribution.

def u_of_x(x, u_0=1.0, alpha=0.1):
    """w4-advance rate at position x. u_0 far from mass, reduced elsewhere."""
    # For c=1, u=1 means "at rest in ambient frame, full 4-speed in w4"
    # Near a mass, u < 1 means some 4-speed is diverted to... what?
    # In your framework, near a mass u is HIGHER per unit something.
    # Let me be explicit: use the Schwarzschild-like profile.
    # g_tt in Schwarzschild: (1 - 2GM/rc^2). Near mass, g_tt -> 0.
    # So "u" (rate of proper time advance) decreases near a mass.
    # This is OPPOSITE to saying "brane advances faster in w4 near mass"
    # -- depending on what we mean by "faster".
    # Let's use Schwarzschild convention: u(x) = sqrt(1 - alpha/|x|) for x>alpha.
    # This represents a point mass at x=0 with gravitational factor alpha.
    # At large |x|: u -> 1. At x = alpha: u -> 0 (horizon).
    r = np.abs(x) + 1e-10
    factor = 1 - alpha/r if r > alpha else 0.01
    return u_0 * np.sqrt(max(factor, 0.01))

# The "drag velocity" in this picture: the effective 3D velocity that a 
# static observer at position x has in the ambient frame, due to the fact 
# that their w4-advance differs from the asymptotic rate.
#
# If locally the unit-4-speed is (v_local, u_local) with v_local^2 + u_local^2 = c^2,
# and u_local < c at this location, then the 4-velocity already has a
# nonzero spatial component in the ambient frame -- that's the drag velocity.

def v_drag(x, alpha=0.1):
    """Effective 3D drag velocity at position x, in ambient frame.
    
    A static observer at position x has 4-velocity (v_drag, u_local) with
    v_drag^2 + u_local^2 = c^2. The direction of v_drag is radial (toward mass).
    """
    u_local = u_of_x(x, alpha=alpha)
    v = np.sqrt(max(c**2 - u_local**2, 0))
    # Direction: toward the mass (at origin). For x > 0, drag is in -x direction.
    return -np.sign(x) * v

# =============================================================================
# STEP 2: What velocity does a local observer at x measure for a moving test 
# particle?
# =============================================================================

def local_velocity_measurement(v_ambient, x, alpha=0.1):
    """A static observer at x measures the velocity of a test particle with 
    ambient-frame velocity v_ambient.
    
    In the pure Euclidean model with drag, the local observer's rest frame 
    is boosted by v_drag(x) relative to ambient. The local-frame velocity of 
    the test particle is:
      v_local = v_ambient - v_drag(x)   (Galilean drag subtraction)
    or using Euclidean composition:
      v_local = eucl_compose(v_ambient, -v_drag(x))
    
    Let's try both and compare to SR subtraction.
    """
    v_d = v_drag(x, alpha=alpha)
    # Galilean
    v_galilean = v_ambient - v_d
    # Euclidean rotation composition
    if abs(v_ambient) < c and abs(v_d) < c:
        theta_v = np.arcsin(v_ambient/c)
        theta_d = np.arcsin(-v_d/c)
        theta_sum = theta_v + theta_d
        if abs(theta_sum) < np.pi/2:
            v_eucl = c * np.sin(theta_sum)
        else:
            v_eucl = np.nan
    else:
        v_eucl = np.nan
    # SR subtraction (what SR predicts)
    v_sr = (v_ambient - v_d) / (1 - v_ambient*v_d/c**2)
    return v_galilean, v_eucl, v_sr, v_d

# =============================================================================
# STEP 3: Run the test
# =============================================================================
print("="*78)
print("TEST 6: Does Schwarzschild-like drag produce SR-like kinematics?")
print("="*78)
print()
print("Setup: point mass at origin with coupling alpha.")
print("Static observer at position x has drag velocity v_drag(x).")
print("Test particle moves at ambient velocity v_ambient.")
print("Local observer measures v_local by subtracting drag.")
print()

alpha = 0.1
x_test = 0.2  # 2x the Schwarzschild radius
print(f"alpha = {alpha}, test position x = {x_test} (= {x_test/alpha:.1f} * r_s)")
print(f"u(x) = {u_of_x(x_test, alpha=alpha):.4f}")
print(f"v_drag(x) = {v_drag(x_test, alpha=alpha):.4f}")
print()

print(f"{'v_ambient':>12} {'v_galilean':>12} {'v_euclidean':>12} {'v_SR':>12}")
for v_amb in [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 0.99, -0.5]:
    vg, ve, vs, vd = local_velocity_measurement(v_amb, x_test, alpha=alpha)
    ve_str = f"{ve:>12.4f}" if not np.isnan(ve) else f"{'NaN':>12}"
    print(f"{v_amb:>12.3f} {vg:>12.4f} {ve_str} {vs:>12.4f}")

print()
print("Observation: even with drag, Galilean and Euclidean do not match SR.")
print("Drag just SHIFTS the reference velocity -- it doesn't change the")
print("addition LAW. SR requires a specific nonlinear composition which")
print("the drag term doesn't produce.")
print()

# =============================================================================
# STEP 4: The deeper test -- does drag preserve c-invariance?
# =============================================================================
print("="*78)
print("STEP 4: c-invariance test with drag")
print("="*78)
print()
print("Send a photon from ambient frame. What speed does local observer at x")
print("measure?")
print()
print(f"{'x':>8} {'alpha':>8} {'v_drag':>10} {'v_phot (Gal)':>14} {'v_phot (Eucl)':>14} {'v_phot (SR)':>14}")
for x in [0.15, 0.2, 0.5, 1.0, 10.0]:
    vd = v_drag(x, alpha=alpha)
    # Photon moves at c in ambient frame (take +c)
    v_amb = c
    vg = v_amb - vd
    # Euclidean: photon is at edge of sphere (theta = pi/2), composition breaks
    # For a photon, only SR says c is invariant
    vs = (v_amb - vd) / (1 - v_amb*vd/c**2)
    print(f"{x:>8.2f} {alpha:>8.2f} {vd:>10.4f} {vg:>14.4f} {'undef':>14} {vs:>14.4f}")

print()
print("SR: photon speed stays at c for any drag -- c invariant.")
print("Galilean with drag: photon speed is c - v_drag, NOT invariant.")
print("Euclidean composition undefined at photon boundary.")
print()
print("Drag alone does NOT produce c-invariance. You still need the SR")
print("composition LAW on top of the drag.")
print()

# =============================================================================
# STEP 5: Could drag produce emergent Lorentz symmetry at LOW velocities?
# =============================================================================
print("="*78)
print("STEP 5: Low-velocity effective description with drag")
print("="*78)
print()
print("Compare Galilean-with-drag, Euclidean-with-drag, and SR at low velocities.")
print("Expansion in small v:")
print()
print("  Galilean w/ drag:   v_local = v_amb - v_drag")
print("  Euclidean w/ drag:  v_local = v_amb - v_drag + O(v^3)")
print("  SR w/ drag:         v_local = (v_amb - v_drag)(1 + (v_amb*v_drag)/c^2 + ...)")
print()
print("At O(v) they all agree. SR differs at O(v^2)*v_drag.")
print()
print("Numerical comparison:")
print(f"{'v_amb':>8} {'v_drag':>8} {'Galilean':>12} {'SR':>12} {'Gal-SR diff':>14}")
for v_amb, vd in [(0.01, 0.01), (0.01, 0.1), (0.1, 0.01), (0.1, 0.1), (0.3, 0.3)]:
    vg = v_amb - vd
    vs = (v_amb - vd) / (1 - v_amb*vd/c**2)
    diff = vg - vs
    print(f"{v_amb:>8.3f} {vd:>8.3f} {vg:>12.6f} {vs:>12.6f} {diff:>+14.2e}")

print()
print("At low velocities AND low drag, Galilean-with-drag ≈ SR.")
print("The SR correction is O(v^2 * v_drag / c^2) -- Newton's regime.")
print("This is consistent with standard Newtonian gravity as SR's weak-field limit.")
print("But it does NOT tell us anything new; it's just saying 'at low v, SR ≈ Newton.'")
print()

# =============================================================================
# STEP 6: The key question -- can drag INDUCE the nonlinear composition law?
# =============================================================================
print("="*78)
print("STEP 6: Can drag somehow produce the 1/(1 - v*w/c^2) denominator?")
print("="*78)
print()
print("SR velocity addition: v_compose = (v + w) / (1 + v*w/c^2)")
print("The denominator is the thing a Euclidean framework can't produce.")
print()
print("Could the DRAG FIELD provide this denominator by being velocity-dependent?")
print("E.g., if v_drag itself depends on the relative motion of sources,")
print("then composing boosts would pick up cross-terms.")
print()
print("Let's parametrize: v_drag = v_drag(x, v_source) where v_source is the")
print("velocity of the source producing the drag. For a static source v_source=0.")
print("For a moving source, v_drag would pick up a v_source-dependent correction.")
print()
print("If v_drag(x, v_source) = v_drag_static(x) + k * v_source * (v_drag_static(x)/c^2)")
print("with k to be determined, can we match SR?")
print()

def drag_composition_test(v1, v2, alpha=0.1, x=0.2, k=1.0):
    """Test: boost observer A (at x) into B's frame (moving at v1 in x direction),
    then B measures a particle moving at v2 in B's frame. What does A see?
    
    With drag: v_drag modifies the subtraction.
    With velocity-dependent drag: v_drag(x) depends on v1 too.
    """
    v_d_static = v_drag(x, alpha=alpha)
    # Velocity-dependent drag
    v_d_effective = v_d_static + k * v1 * v_d_static / c**2
    # A's frame: v_d_static
    # B's frame: v_d_static + v1 contribution = v_d_effective
    # Now A sees B's measurement. The particle at v2 in B's frame is at 
    # v2 + v1 in A's frame IF drag just shifts. With velocity-dep drag:
    v_composed = v1 + v2  # Galilean (no drag)
    v_composed_drag = v1 + v2 - (v_d_effective - v_d_static)  # drag correction
    v_composed_sr = (v1 + v2) / (1 + v1*v2/c**2)
    return v_composed_drag, v_composed_sr

print(f"{'v1':>6} {'v2':>6} {'Gal+drag(k=1)':>14} {'SR':>10} {'match?':>8}")
for v1, v2 in [(0.3, 0.3), (0.5, 0.5), (0.7, 0.3), (0.8, 0.8), (0.9, 0.5)]:
    vd_test, vs = drag_composition_test(v1, v2, k=1.0)
    print(f"{v1:>6.2f} {v2:>6.2f} {vd_test:>14.4f} {vs:>10.4f} {'close' if abs(vd_test-vs)<0.05 else 'NO':>8}")

print()
print("This doesn't work either. The cross-term a velocity-dependent drag")
print("introduces is LINEAR in v1, but SR needs a MULTIPLICATIVE correction")
print("1/(1 + v1*v2/c^2) that depends on BOTH v1 and v2.")
print()
print("No linear modification of the Euclidean framework reproduces SR.")
print()

# =============================================================================
# VISUALIZATION
# =============================================================================
print("="*78)
print("Generating comparison plot...")
print("="*78)

fig, axes = plt.subplots(1, 2, figsize=(13, 5.5), dpi=150)

# Panel 1: Velocity composition with and without drag
ax = axes[0]
v_range = np.linspace(0.01, 0.95, 200)
vd_test = 0.3  # fixed drag velocity

galilean = v_range - vd_test
sr_drag = (v_range - vd_test) / (1 - v_range*vd_test/c**2)
# Euclidean with drag (careful with domain)
eucl_drag = []
for v in v_range:
    theta_v = np.arcsin(v/c) if abs(v) < 1 else np.pi/2 * np.sign(v)
    theta_d = np.arcsin(-vd_test/c)
    theta_sum = theta_v + theta_d
    if abs(theta_sum) < np.pi/2:
        eucl_drag.append(c * np.sin(theta_sum))
    else:
        eucl_drag.append(np.nan)
eucl_drag = np.array(eucl_drag)

ax.plot(v_range, galilean, 'k--', label='Galilean with drag', linewidth=1.5)
ax.plot(v_range, eucl_drag, 'b-', label='Euclidean with drag', linewidth=2)
ax.plot(v_range, sr_drag, 'r-', label='SR with drag', linewidth=2)
ax.axhline(c, color='gray', linestyle=':', alpha=0.5, label='$c$')
ax.set_xlabel('$v_{ambient}/c$', fontsize=11)
ax.set_ylabel('$v_{local}/c$ (measured by observer with drag)', fontsize=11)
ax.set_title(f'Local velocity with drag $v_{{drag}} = {vd_test}c$', fontsize=11)
ax.legend(loc='lower right', fontsize=9)
ax.grid(True, alpha=0.3)

# Panel 2: Difference from SR
ax = axes[1]
ax.plot(v_range, galilean - sr_drag, 'k--', label='Galilean - SR', linewidth=1.5)
eucl_drag_clean = np.where(np.isnan(eucl_drag), 0, eucl_drag - sr_drag)
mask = ~np.isnan(eucl_drag)
ax.plot(v_range[mask], (eucl_drag - sr_drag)[mask], 'b-', label='Euclidean - SR', linewidth=2)
ax.axhline(0, color='r', linestyle='-', alpha=0.5, label='SR (reference)')
ax.set_xlabel('$v_{ambient}/c$', fontsize=11)
ax.set_ylabel('Residual from SR / $c$', fontsize=11)
ax.set_title(f'Deviation from SR (drag $= {vd_test}c$)', fontsize=11)
ax.legend(loc='best', fontsize=9)
ax.grid(True, alpha=0.3)

plt.suptitle('Drag does not close the gap: Galilean, Euclidean, and SR remain distinct',
             fontsize=12, y=1.0)
plt.tight_layout()
plt.savefig('/home/claude/draft8/drag_test.png', dpi=180, bbox_inches='tight')
plt.savefig('/mnt/user-data/outputs/drag_test.png', dpi=180, bbox_inches='tight')
print("Saved plot.")

# =============================================================================
# FINAL SUMMARY
# =============================================================================
print()
print("="*78)
print("FINAL SUMMARY OF TEST 6")
print("="*78)
print("""
Adding position-dependent drag to the Euclidean constraint does NOT close
the gap to SR. Here's why, as concretely as I can state it:

1. DRAG IS A SHIFT, NOT A LAW.
   v_drag(x) changes WHICH velocity is "at rest" at position x, but it
   doesn't change the RULE for how velocities compose. You still need
   a composition rule, and the Euclidean rule (angle addition) and the
   Galilean rule (simple subtraction) both give the wrong answer at
   high velocities. Adding drag to either one just shifts the reference.

2. C-INVARIANCE IS THE DEEP PROBLEM.
   Even with drag, a photon in the ambient frame is measured by a local
   drag-observer as moving at c - v_drag (in Galilean) or something
   less than c (in Euclidean). SR says it must be c in any frame.
   No shift operation can fix this -- you need the denominator
   1/(1 + v*w/c^2) that mixes the two velocities multiplicatively.

3. VELOCITY-DEPENDENT DRAG DOESN'T FIX IT EITHER.
   If v_drag itself depends on v_source (a "dragged drag"), it introduces
   LINEAR cross-terms in v1, but SR needs MULTIPLICATIVE mixing of v1
   and v2 -- they're qualitatively different corrections.

4. THIS IS THE SAME OBSTRUCTION AS BEFORE.
   The Lorentz group structure lives in the MULTIPLICATIVE composition
   law, not in any additive or positional shift. Drag shifts positions
   and frames; it doesn't change groups.

WHAT THIS DOESN'T RULE OUT.
The test here is a kinematic test -- I added a drag field to the
kinematics and asked what happens to velocity addition. The harder
question is whether a DYNAMICAL system (brane action, field equations)
can produce an EFFECTIVE description at long wavelengths that has the
SR composition law baked in. That's the "emergent Lorentz invariance"
route I mentioned. Kinematic drag doesn't do it, but there might be a
dynamical route that does -- and that's what Jacobson's thermodynamic
gravity and Horava-Lifshitz-style emergent-symmetry arguments point at.

Answer to your question: your intuition about nested dragging is a real
physical effect (it's present in Kerr metrics and in frame-dragging
phenomena), but it's a KINEMATIC effect and not strong enough to fix
the group-theoretic obstruction we identified. The obstruction is
structural, and structural problems need structural solutions.

What this means: the reading list I gave you is the right next step.
Kinematic tweaks on top of the Euclidean framework won't close the gap.
The closure, if it exists, has to come from an emergent-symmetry
mechanism -- a dynamical system whose long-wavelength effective
description happens to be Lorentz-invariant even though the underlying
structure is Euclidean.
""")
