"""Tests for A_n BCC Golden Discriminant analysis (Theorem Z).

Validates the discriminant Δ(n) = n⁴−6n³+19n²−22n+9, its golden-ratio
eigenvalue forms at n = φ+1, norm theorems, perfect-square classification,
mod-5 structure, and the resolvent cubic / Q(√−2) factorization.
"""

import math
import sys

import numpy as np
import pytest

sys.path.insert(0, "src")

from watkins_nn.cascade import (
    an_bcc_charpoly,
    an_bcc_discriminant,
    an_bcc_discriminant_at_fib,
    an_bcc_discriminant_at_lucas,
    an_bcc_discriminant_is_square,
    an_bcc_discriminant_mod5,
    an_bcc_eigenvalue_product_norm,
    an_bcc_eigenvalues,
    an_bcc_golden_eigenvalues,
    an_bcc_golden_intermediate_norm,
    an_bcc_golden_norm,
    an_bcc_resolvent_cubic_roots,
    an_bcc_shifted_discriminant_factorization,
)


# ---------------------------------------------------------------------------
# Discriminant formula consistency
# ---------------------------------------------------------------------------


@pytest.mark.parametrize("n", range(2, 20))
def test_discriminant_matches_charpoly(n):
    """Δ(n) equals the discriminant from an_bcc_charpoly."""
    _, _, disc = an_bcc_charpoly(n)
    assert an_bcc_discriminant(n) == disc


def test_discriminant_explicit_values():
    """Check Δ at small n against hand computation."""
    assert an_bcc_discriminant(0) == 9
    assert an_bcc_discriminant(1) == 1
    assert an_bcc_discriminant(2) == 9
    assert an_bcc_discriminant(3) == 33
    assert an_bcc_discriminant(5) == 249


def test_discriminant_equals_s_squared_plus_4n_n1():
    """Verify Δ = s² + 4n(n−1) where s = n²−3n+3."""
    for n in range(0, 30):
        s = n * n - 3 * n + 3
        expected = s * s + 4 * n * (n - 1)
        assert an_bcc_discriminant(n) == expected


# ---------------------------------------------------------------------------
# Perfect square classification
# ---------------------------------------------------------------------------


def test_perfect_squares_only_at_012():
    """Only n ∈ {0, 1, 2} give perfect-square Δ (up to n=500)."""
    squares = [n for n in range(-10, 501) if an_bcc_discriminant_is_square(n)]
    assert squares == [0, 1, 2]


def test_perfect_square_values():
    """Δ(0) = 9 = 3², Δ(1) = 1 = 1², Δ(2) = 9 = 3²."""
    assert an_bcc_discriminant(0) == 9
    assert an_bcc_discriminant(1) == 1
    assert an_bcc_discriminant(2) == 9


# ---------------------------------------------------------------------------
# Golden eigenvalue forms
# ---------------------------------------------------------------------------


def test_golden_eigenvalues():
    """λ± = 1 ± √2·φ at the trace-zero point."""
    phi = (1 + math.sqrt(5)) / 2
    sqrt2 = math.sqrt(2)
    lp, lm = an_bcc_golden_eigenvalues()
    assert abs(lp - (1 + sqrt2 * phi)) < 1e-14
    assert abs(lm - (1 - sqrt2 * phi)) < 1e-14


def test_golden_eigenvalue_sum_is_2():
    """λ₊ + λ₋ = 2 (since s = 2 at n = φ+1)."""
    lp, lm = an_bcc_golden_eigenvalues()
    assert abs(lp + lm - 2.0) < 1e-14


def test_golden_eigenvalue_product():
    """λ₊ · λ₋ = 1 − 2φ² = −(1+2φ) = −(2+√5)."""
    lp, lm = an_bcc_golden_eigenvalues()
    expected = -(2 + math.sqrt(5))
    assert abs(lp * lm - expected) < 1e-13


def test_golden_sqrt_delta_identity():
    """√Δ = 2√2·φ at n = φ+1, equivalently √(3+√5) = √2·φ."""
    phi = (1 + math.sqrt(5)) / 2
    lhs = math.sqrt(3 + math.sqrt(5))
    rhs = math.sqrt(2) * phi
    assert abs(lhs - rhs) < 1e-14


def test_conjugate_sqrt_delta_identity():
    """√(3−√5) = √2/φ, equivalently 3−√5 = 2/φ²."""
    phi = (1 + math.sqrt(5)) / 2
    lhs = math.sqrt(3 - math.sqrt(5))
    rhs = math.sqrt(2) / phi
    assert abs(lhs - rhs) < 1e-14


# ---------------------------------------------------------------------------
# Norm theorems
# ---------------------------------------------------------------------------


def test_full_biquadratic_norm_is_minus_1():
    """N_{Q(√2,√5)/Q}(λ₊) = −1."""
    norm = an_bcc_golden_norm()
    assert abs(norm - (-1.0)) < 1e-13


def test_intermediate_norm_is_sqrt2_minus_1():
    """N_{Q(√5)/Q}(λ₊) = √2 − 1."""
    inorm = an_bcc_golden_intermediate_norm()
    assert abs(inorm - (math.sqrt(2) - 1)) < 1e-14


def test_eigenvalue_product_norm_is_minus_1():
    """N_{Q(√5)/Q}(λ₊λ₋) = (2+√5)(2−√5) = −1."""
    norm = an_bcc_eigenvalue_product_norm()
    assert abs(norm - (-1.0)) < 1e-13


def test_norm_algebraic_proof():
    """Verify the norm computation algebraically: 1+2(φ+ψ)+4φψ = −1."""
    phi = (1 + math.sqrt(5)) / 2
    psi = (1 - math.sqrt(5)) / 2
    result = 1 + 2 * (phi + psi) + 4 * phi * psi
    # phi + psi = 1, phi * psi = -1
    # So 1 + 2 + (-4) = -1
    assert abs(result - (-1.0)) < 1e-14


# ---------------------------------------------------------------------------
# Mod 5 structure
# ---------------------------------------------------------------------------


def test_discriminant_mod5_period():
    """Δ(n) mod 5 has period 5: [4, 1, 4, 3, 2]."""
    pattern = [4, 1, 4, 3, 2]
    for n in range(50):
        assert an_bcc_discriminant_mod5(n) == pattern[n % 5], (
            f"Failed at n={n}: got {an_bcc_discriminant_mod5(n)}, "
            f"expected {pattern[n % 5]}"
        )


def test_discriminant_never_0_mod5():
    """Δ(n) ≢ 0 mod 5 for any n — both factors in F₅ are irreducible."""
    for n in range(100):
        assert an_bcc_discriminant_mod5(n) != 0


# ---------------------------------------------------------------------------
# Fibonacci and Lucas evaluations
# ---------------------------------------------------------------------------


def test_discriminant_at_fibonacci():
    """Verify Δ(F_k) for small k."""
    expected = {
        1: (1, 1),
        2: (1, 1),
        3: (2, 9),
        4: (3, 33),
        5: (5, 249),
        6: (8, 2073),
    }
    for k, (fk, dk) in expected.items():
        result_fk, result_dk = an_bcc_discriminant_at_fib(k)
        assert result_fk == fk, f"F_{k} = {result_fk}, expected {fk}"
        assert result_dk == dk, f"Δ(F_{k}) = {result_dk}, expected {dk}"


def test_discriminant_at_lucas():
    """Verify Δ(L_k) for small k."""
    expected = {
        1: (1, 1),
        2: (3, 33),
        3: (4, 97),
        4: (7, 1129),
        5: (11, 8721),
    }
    for k, (lk, dk) in expected.items():
        result_lk, result_dk = an_bcc_discriminant_at_lucas(k)
        assert result_lk == lk, f"L_{k} = {result_lk}, expected {lk}"
        assert result_dk == dk, f"Δ(L_{k}) = {result_dk}, expected {dk}"


# ---------------------------------------------------------------------------
# Resolvent cubic and Q(√−2) factorization
# ---------------------------------------------------------------------------


def test_resolvent_cubic_roots():
    """Resolvent roots: −5/2 and 4 ± √41/2."""
    r1, r2, r3 = an_bcc_resolvent_cubic_roots()
    assert abs(r1 - (-2.5)) < 1e-14
    assert abs(r2 - (4.0 - math.sqrt(41) / 2)) < 1e-14
    assert abs(r3 - (4.0 + math.sqrt(41) / 2)) < 1e-14


def test_resolvent_cubic_satisfies_polynomial():
    """Each root satisfies y³ − (11/2)y² − (57/4)y + 115/8 = 0."""
    for y in an_bcc_resolvent_cubic_roots():
        val = y**3 - 5.5 * y**2 - 14.25 * y + 115 / 8
        assert abs(val) < 1e-10, f"y={y}, residual={val}"


def test_shifted_discriminant_factorization():
    """f(x) = (x²+a₁x+b₁)(x²+a₂x+b₂) where a_k = −1 ∓ 2√2·i, b_k = −1."""
    (a1, b1), (a2, b2) = an_bcc_shifted_discriminant_factorization()

    # b values
    assert abs(b1 - (-1)) < 1e-14
    assert abs(b2 - (-1)) < 1e-14

    # a values are complex conjugates
    assert abs(a1.real - (-1)) < 1e-14
    assert abs(a2.real - (-1)) < 1e-14
    assert abs(a1.imag + a2.imag) < 1e-14  # conjugate pair
    assert abs(abs(a1.imag) - 2 * math.sqrt(2)) < 1e-14

    # Verify the product recovers f(x) at several points
    for x_val in [-3, -2, -1, 0, 1, 2, 3, 5, 10]:
        z = complex(x_val)
        f_val = z**4 - 2 * z**3 + 7 * z**2 + 2 * z + 1
        prod = (z**2 + a1 * z + b1) * (z**2 + a2 * z + b2)
        assert abs(f_val - prod) < 1e-10, (
            f"x={x_val}: f={f_val}, prod={prod}"
        )


def test_polynomial_discriminant_is_2_10_times_41():
    """The polynomial discriminant of Δ as a quartic in n is 2¹⁰ · 41."""
    # Polynomial discriminant of x^4+px^2+qx+r (depressed) is
    # 256r^3 - 128p^2r^2 + 144pq^2r - 27q^4 + 16p^4r - 4p^3q^2
    # For the depressed form: p=11/2, q=8, r=57/16
    p, q, r = 11 / 2, 8, 57 / 16
    disc = (
        256 * r**3
        - 128 * p**2 * r**2
        + 144 * p * q**2 * r
        - 27 * q**4
        + 16 * p**4 * r
        - 4 * p**3 * q**2
    )
    expected = 2**10 * 41
    assert abs(disc - expected) < 1e-6, f"disc={disc}, expected={expected}"


# ---------------------------------------------------------------------------
# Consistency with existing eigenvalue functions
# ---------------------------------------------------------------------------


@pytest.mark.parametrize("n", range(2, 15))
def test_discriminant_consistent_with_eigenvalues(n):
    """Δ(n) = (λ₊ − λ₋)² from the numerical eigenvalues."""
    lp, _, lm = an_bcc_eigenvalues(n)
    gap_sq = (lp - lm) ** 2
    disc = an_bcc_discriminant(n)
    assert abs(gap_sq - disc) < 1e-8


@pytest.mark.parametrize("n", range(2, 15))
def test_eigenvalue_product_is_minus_n_n1(n):
    """λ₊ · λ₋ = −n(n−1) for integer n."""
    lp, _, lm = an_bcc_eigenvalues(n)
    expected = -n * (n - 1)
    assert abs(lp * lm - expected) < 1e-8
