"""Tests for the Watkins Spectral Kaleidoscope sequences."""

from math import isqrt

from watkins_nn.kaleidoscope import (
    lucas, fibonacci, mersenne,
    seq1_cycle_lb_numerator, seq2_mersenne_numerator, seq3_mersenne_gcd,
    seq4_lucas_numerator, seq5_lucas_gcd, seq6_mersenne_lucas_gcd,
    seq7_binary_preservation, seq8_cross_gcd, seq9_weight_moments,
    generate_all_sequences, verify_pairings, find_seq8_spikes,
)


# ── Fundamental sequences ────────────────────────────────────────────────────

def test_lucas_first_terms():
    expected = [2, 1, 3, 4, 7, 11, 18, 29, 47, 76]
    assert [lucas(n) for n in range(10)] == expected

def test_fibonacci_first_terms():
    expected = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
    assert [fibonacci(n) for n in range(10)] == expected

def test_mersenne():
    assert mersenne(1) == 1
    assert mersenne(3) == 7
    assert mersenne(10) == 1023


# ── Kaleidoscope sequences: known values ─────────────────────────────────────

def test_seq1_first_terms():
    expected = [1, 3, 7, 5, 31, 7, 127, 17, 73, 341]
    assert [seq1_cycle_lb_numerator(n) for n in range(10)] == expected

def test_seq2_first_terms():
    expected = [3, 7, 5, 31, 21, 127, 17, 73, 341, 2047]
    assert [seq2_mersenne_numerator(n) for n in range(10)] == expected

def test_seq3_first_terms():
    expected = [1, 1, 3, 1, 3, 1, 15, 7, 3, 1]
    assert [seq3_mersenne_gcd(n) for n in range(10)] == expected

def test_seq4_first_terms():
    expected = [1, 3, 2, 7, 11, 1, 29, 47, 19, 41]
    assert [seq4_lucas_numerator(n) for n in range(10)] == expected

def test_seq5_first_terms():
    expected = [1, 1, 2, 1, 1, 18, 1, 1, 4, 3]
    assert [seq5_lucas_gcd(n) for n in range(10)] == expected

def test_seq6_known_values():
    assert seq6_mersenne_lucas_gcd(2) == 3
    assert seq6_mersenne_lucas_gcd(6) == 9
    assert seq6_mersenne_lucas_gcd(12) == 7
    assert seq6_mersenne_lucas_gcd(15) == 31


# ── Pairing verification ─────────────────────────────────────────────────────

def test_verify_pairings():
    assert verify_pairings(30) is True

def test_mersenne_pairing_identity():
    for n in range(20):
        assert seq2_mersenne_numerator(n) * seq3_mersenne_gcd(n) == mersenne(n + 2)

def test_lucas_pairing_identity():
    for n in range(20):
        assert seq4_lucas_numerator(n) * seq5_lucas_gcd(n) == lucas(n + 1)


# ── Seq6 divisibility rules (CRT-derived) ────────────────────────────────────

def test_seq6_div_by_3():
    for n in range(1, 101):
        divides = seq6_mersenne_lucas_gcd(n) % 3 == 0
        assert divides == (n % 4 == 2), f"3-rule failed at n={n}"

def test_seq6_div_by_7():
    for n in range(1, 101):
        divides = seq6_mersenne_lucas_gcd(n) % 7 == 0
        assert divides == (n % 24 == 12), f"7-rule failed at n={n}"

def test_seq6_div_by_31():
    for n in range(1, 101):
        divides = seq6_mersenne_lucas_gcd(n) % 31 == 0
        assert divides == (n % 30 == 15), f"31-rule failed at n={n}"


# ── Generator utility ────────────────────────────────────────────────────────

def test_generate_all_sequences_keys():
    seqs = generate_all_sequences(5)
    expected_keys = {
        'A393329', 'A394248', 'A394249', 'lucas_num', 'lucas_gcd',
        'ml_interference', 'binary_preservation', 'cross_gcd', 'A005248',
    }
    assert set(seqs.keys()) == expected_keys
    assert all(len(v) == 5 for v in seqs.values())

def test_generate_all_sequences_values():
    seqs = generate_all_sequences(10)
    assert seqs['A393329'] == [1, 3, 7, 5, 31, 7, 127, 17, 73, 341]
    assert seqs['A394248'] == [3, 7, 5, 31, 21, 127, 17, 73, 341, 2047]
    assert seqs['ml_interference'] == [seq6_mersenne_lucas_gcd(n) for n in range(1, 11)]


# ── New sequences (v2) ────────────────────────────────────────────────────────

def test_seq7_first_terms():
    expected = [0, 0, 2, 1, 2, 2, 3, 5, 4, 6]
    assert [seq7_binary_preservation(n) for n in range(10)] == expected

def test_seq8_first_terms():
    expected = [1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31]
    assert [seq8_cross_gcd(n) for n in range(15)] == expected

def test_seq8_spikes_are_prime():
    def is_prime(n):
        if n < 2: return False
        for d in range(2, isqrt(n) + 1):
            if n % d == 0: return False
        return True
    spikes = find_seq8_spikes(100)
    assert len(spikes) == 8
    for n, g in spikes:
        assert is_prime(g), f"spike at n={n} has non-prime value {g}"

def test_seq9_first_terms():
    expected = [2, 3, 7, 18, 47, 123, 322, 843, 2207, 5778]
    assert [seq9_weight_moments(k) for k in range(10)] == expected

def test_seq9_is_even_lucas():
    for k in range(21):
        assert seq9_weight_moments(k) == lucas(2 * k)
