Trinary Prime Logics




for every tested prime pp.

That means the prime signal wasn’t added to the construction. It appeared at the end of an orbit that began with the closure itself.

One primitive quadratic closure simultaneously generates the φ orbit, Fibonacci coefficients, alternating norm, ± translation symmetry, Lucas sequence, and finally a prime-sensitive congruence.

Version 23

#!/usr/bin/env python3
# =============================================================================
# HDGL / Z[phi] — VERSION 23
# =============================================================================
#
# EXACT ORBIT
# TRANSLATION SYMMETRY
# LUCAS COORDINATE
# LARGE-PRIME TEST
# CANDIDATE INVARIANT SEARCH
#
# VERSION 23 FIXES:
#
#   * No float arithmetic.
#   * No decimal conversion of giant integers.
#   * No Python 4300-digit string-conversion failure.
#   * Exact Z[phi] coefficient-pair arithmetic.
#   * Exact fast-doubling Fibonacci.
#   * Exact Lucas coordinate.
#   * Exact norm.
#   * Exact translation identities.
#   * Large n = 100003 works without converting 20,000+ digit
#     integers to decimal strings.
#   * Candidate invariant search.
#   * Held-out validation.
#   * Explicit counterexample showing that the zero-sum identity
#     is an oddness invariant rather than, by itself, a primality test.
#
# CORE:
#
#     phi^2 = phi + 1
#
#     X_n = phi^n
#         = F_(n-1) + F_n phi
#
#     N(a+b phi) = a^2 + ab - b^2
#
#     N(phi^n) = (-1)^n
#
# TRANSLATIONS:
#
#     Delta_0(X)  = X + 1
#     Delta_-0(X) = X - 1
#
#     N(X+1) + N(X-1)
#         = 2(N(X)+1)
#         = 2(1+(-1)^n)
#
#     N(X+1) - N(X-1)
#         = 2 L_n
#
# THEREFORE:
#
#     N(X+1) = 1 + (-1)^n + L_n
#     N(X-1) = 1 + (-1)^n - L_n
#
# IMPORTANT:
#
#     The zero translation sum detects parity.
#     It does NOT by itself prove primality.
#
# VERSION 23 searches for an additional structural invariant rather
# than silently equating oddness with primality.
# =============================================================================

import time
from math import gcd


# =============================================================================
# CONFIGURATION
# =============================================================================

BIG_N = 100003

# Representative small values.
SMALL_CASES = [
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
    10, 11, 12, 13, 15, 17, 19, 21, 23,
    25, 27, 29, 31
]

# Training primes.
TRAIN_PRIMES = [
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
    31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
    73, 79, 83, 89, 97
]

# Training composites.
TRAIN_COMPOSITES = [
    4, 6, 8, 9, 10, 12, 14, 15, 16, 18,
    20, 21, 22, 24, 25, 26, 27, 28, 30, 32,
    33, 34, 35, 36, 38, 39, 40, 42, 44, 45,
    46, 48, 49, 50, 51, 52, 54, 55, 56, 57,
    58, 60, 62, 63, 64, 65, 66, 68, 69, 70,
    72, 74, 75, 76, 77, 78, 80, 81, 82, 84,
    85, 86, 87, 88, 90, 91, 92, 93, 94, 95,
    96, 98, 99, 100
]

# Held-out primes.
VALIDATE_PRIMES = [
    101, 103, 107, 109, 113, 127, 131, 137,
    139, 149, 151, 157, 163, 167, 173, 179,
    181, 191, 193, 197, 199
]

# Held-out composites.
VALIDATE_COMPOSITES = [
    102, 104, 105, 106, 108, 110, 111, 112,
    114, 115, 116, 117, 118, 119, 120, 121,
    122, 123, 124, 125, 126, 128, 129, 130,
    132, 133, 134, 135, 136, 138, 140, 141,
    142, 143, 144, 145, 146, 147, 148, 150
]


# =============================================================================
# EXACT Z[phi]
#
# Every element is represented as:
#
#     (a,b) == a + b phi
#
# with:
#
#     phi^2 = phi + 1
#
# No floating-point phi exists anywhere in the calculation.
# =============================================================================

ZERO = (0, 0)
ONE = (1, 0)
PHI = (0, 1)


def zadd(x, y):
    return (
        x[0] + y[0],
        x[1] + y[1]
    )


def zsub(x, y):
    return (
        x[0] - y[0],
        x[1] - y[1]
    )


def zneg(x):
    return (
        -x[0],
        -x[1]
    )


def zscale(k, x):
    return (
        k * x[0],
        k * x[1]
    )


def zmul(x, y):
    """
    (a+b phi)(c+d phi)

    phi^2 = phi + 1

    Therefore:

        ac + bd + (ad+bc+bd)phi
    """

    a, b = x
    c, d = y

    return (
        a * c + b * d,
        a * d + b * c + b * d
    )


def zsquare(x):
    return zmul(x, x)


def zpow(x, n):
    """
    Exact integer-exponentiation in Z[phi].
    """

    result = ONE
    base = x

    while n > 0:
        if n & 1:
            result = zmul(result, base)

        base = zmul(base, base)
        n >>= 1

    return result


def znorm(x):
    """
    Exact norm:

        N(a+b phi) = a^2 + ab - b^2
    """

    a, b = x

    return (
        a * a
        + a * b
        - b * b
    )


def zfmt(x):
    """
    Human-readable exact representation.
    """

    a, b = x

    if a == 0 and b == 0:
        return "0"

    if b == 0:
        return str(a)

    if a == 0:
        if b == 1:
            return "phi"

        if b == -1:
            return "-phi"

        return f"{b}phi"

    if b == 1:
        return f"{a}+phi"

    if b == -1:
        return f"{a}-phi"

    if b > 0:
        return f"{a}+{b}phi"

    return f"{a}{b}phi"


# =============================================================================
# SAFE INTEGER MAGNITUDE
#
# NEVER converts a giant integer to a decimal string.
#
# Python 3.11+ intentionally limits:
#
#     str(huge_integer)
#
# to 4300 digits by default.
#
# This routine computes the decimal digit count without that conversion.
#
# No floating point is used.
# =============================================================================

def integer_digits(n):
    """
    Exact decimal digit count without str(n).

    The initial estimate uses the rational lower-resolution approximation
    to log10(2):

        log10(2) ~= 30103 / 100000

    The final result is corrected using exact powers of ten.
    """

    n = abs(n)

    if n == 0:
        return 1

    # Initial estimate.
    k = (n.bit_length() * 30103) // 100000

    if k < 1:
        k = 1

    # Exact correction.
    p = 10 ** (k - 1)

    while n < p:
        k -= 1
        p //= 10

    while n >= p * 10:
        p *= 10
        k += 1

    return k


def integer_bit_length(n):
    return abs(n).bit_length()


# =============================================================================
# PRIMITIVE OPERATORS
# =============================================================================

def delta_0(x):
    """
    Primitive positive translation:

        X -> X + 1
    """

    return zadd(x, ONE)


def delta_minus_0(x):
    """
    Primitive negative translation:

        X -> X - 1
    """

    return zsub(x, ONE)


def fixed_closure(x):
    """
    Quadratic closure:

        X^2 - X - 1
    """

    return zsub(
        zsub(
            zsquare(x),
            x
        ),
        ONE
    )


# =============================================================================
# FIBONACCI FAST DOUBLING
#
# Returns:
#
#     (F_n, F_(n+1))
#
# exactly.
# =============================================================================

def fibonacci_pair(n):
    if n == 0:
        return (0, 1)

    a, b = fibonacci_pair(n >> 1)

    c = a * ((b << 1) - a)
    d = a * a + b * b

    if n & 1:
        return (d, c + d)

    return (c, d)


def fibonacci(n):
    if n < 0:
        raise ValueError("negative Fibonacci index")

    return fibonacci_pair(n)[0]


# =============================================================================
# LUCAS
#
#     L_n = F_(n-1) + F_(n+1)
#
# with:
#
#     L_0 = 2
# =============================================================================

def lucas(n):
    if n == 0:
        return 2

    fn, fn1 = fibonacci_pair(n)

    fn_minus_1 = fn1 - fn

    return fn_minus_1 + fn1


# =============================================================================
# PHI ORBIT
#
#     phi^n = F_(n-1) + F_n phi
#
# n=0:
#
#     phi^0 = 1
# =============================================================================

def phi_orbit(n):
    if n < 0:
        raise ValueError("negative orbit index")

    if n == 0:
        return ONE

    fn, fn1 = fibonacci_pair(n)

    fn_minus_1 = fn1 - fn

    return (
        fn_minus_1,
        fn
    )


# =============================================================================
# ORBIT DATA
# =============================================================================

def orbit_data(n):
    x = phi_orbit(n)

    xp = delta_0(x)
    xm = delta_minus_0(x)

    fc = fixed_closure(x)

    nx = znorm(x)
    nxp = znorm(xp)
    nxm = znorm(xm)
    nfc = znorm(fc)

    s = nxp + nxm
    d = nxp - nxm

    L = lucas(n)

    expected_n = -1 if (n & 1) else 1
    expected_sum = 0 if (n & 1) else 4
    expected_difference = 2 * L

    return {
        "n": n,

        "x": x,
        "xp": xp,
        "xm": xm,
        "fixed": fc,

        "N": nx,
        "N_plus": nxp,
        "N_minus": nxm,
        "N_fixed": nfc,

        "sum": s,
        "difference": d,
        "lucas": L,

        "expected_N": expected_n,
        "expected_sum": expected_sum,
        "expected_difference": expected_difference,

        "norm_identity": nx == expected_n,
        "sum_identity": s == expected_sum,
        "difference_identity": d == expected_difference,
    }


# =============================================================================
# EXACT PRIMALITY
#
# Used ONLY as ground truth while searching for a new invariant.
#
# This is not claimed as the HDGL mechanism.
# =============================================================================

def is_prime(n):
    if n < 2:
        return False

    if n == 2 or n == 3:
        return True

    if n % 2 == 0:
        return False

    if n % 3 == 0:
        return False

    d = 5
    step = 2

    while d * d <= n:
        if n % d == 0:
            return False

        d += step
        step = 6 - step

    return True


# =============================================================================
# EXACT IDENTITY CHECKS
# =============================================================================

def check_norm_identity(n):
    d = orbit_data(n)
    return d["norm_identity"]


def check_sum_identity(n):
    d = orbit_data(n)
    return d["sum_identity"]


def check_difference_identity(n):
    d = orbit_data(n)
    return d["difference_identity"]


def check_all_identities(n):
    d = orbit_data(n)

    return (
        d["norm_identity"]
        and d["sum_identity"]
        and d["difference_identity"]
    )


# =============================================================================
# NORM MULTIPLICATIVITY
# =============================================================================

def check_norm_multiplication(x, y):
    lhs = znorm(zmul(x, y))
    rhs = znorm(x) * znorm(y)

    return lhs == rhs


# =============================================================================
# STRUCTURAL INVARIANTS
#
# These are all generated from the primitive translations and closure.
# =============================================================================

def invariants(n):
    d = orbit_data(n)

    x = d["x"]
    xp = d["xp"]
    xm = d["xm"]
    fc = d["fixed"]

    nx = d["N"]
    np = d["N_plus"]
    nm = d["N_minus"]
    nf = d["N_fixed"]

    s = np + nm
    diff = np - nm

    L = d["lucas"]

    abs_np = abs(np)
    abs_nm = abs(nm)

    translation_gcd = gcd(
        abs_np,
        abs_nm
    )

    return {
        # Direct norms.
        "N(X)": nx,
        "N(X+1)": np,
        "N(X-1)": nm,

        # Translation geometry.
        "SUM": s,
        "DIFF": diff,
        "ABS_SUM": abs(s),
        "ABS_DIFF": abs(diff),

        # GCD structures.
        "GCD_TRANSLATIONS": translation_gcd,
        "GCD_N_X_PLUS": gcd(abs(nx), abs_np),
        "GCD_N_X_MINUS": gcd(abs(nx), abs_nm),

        # Closure.
        "N(FIXED)": nf,
        "ABS_N(FIXED)": abs(nf),

        # Norm powers.
        "N(X)^2": nx * nx,

        # Exact identity residuals.
        "R_NORM": nx - d["expected_N"],
        "R_SUM": s - d["expected_sum"],
        "R_DIFF": diff - 2 * L,

        # Modular structural quantities.
        "SUM_MOD_N": s % n if n else 0,
        "DIFF_MOD_N": diff % n if n else 0,
        "ABS_DIFF_MOD_N": abs(diff) % n if n else 0,

        "LUCAS": L,
        "LUCAS_MOD_N": L % n if n else 0,

        "N_DIVIDES_DIFF": (
            n != 0 and diff % n == 0
        ),

        "N_DIVIDES_ABS_DIFF": (
            n != 0 and abs(diff) % n == 0
        ),

        "N_DIVIDES_LUCAS": (
            n != 0 and L % n == 0
        ),

        "N_DIVIDES_SUM": (
            n != 0 and s % n == 0
        ),

        # GCD against Lucas.
        "GCD_N_LUCAS": gcd(abs(nx), abs(L)),
        "GCD_TRANSLATIONS_LUCAS": gcd(
            translation_gcd,
            abs(L)
        ),
    }


# =============================================================================
# CANDIDATE SEARCH
# =============================================================================

def invariant_names():
    return list(invariants(7).keys())


def candidate_zero_separates(name):
    prime_states = set()
    composite_states = set()

    for n in TRAIN_PRIMES:
        prime_states.add(
            invariants(n)[name] == 0
        )

    for n in TRAIN_COMPOSITES:
        composite_states.add(
            invariants(n)[name] == 0
        )

    if len(prime_states) != 1:
        return False

    if len(composite_states) != 1:
        return False

    return (
        next(iter(prime_states))
        != next(iter(composite_states))
    )


def candidate_divisibility_separates(name):
    prime_states = set()
    composite_states = set()

    for n in TRAIN_PRIMES:
        v = invariants(n)[name]
        prime_states.add(v % n == 0)

    for n in TRAIN_COMPOSITES:
        v = invariants(n)[name]
        composite_states.add(v % n == 0)

    if len(prime_states) != 1:
        return False

    if len(composite_states) != 1:
        return False

    return (
        next(iter(prime_states))
        != next(iter(composite_states))
    )


def search_candidates():
    names = invariant_names()

    zero_candidates = []
    divisibility_candidates = []

    for name in names:
        if candidate_zero_separates(name):
            zero_candidates.append(name)

        if candidate_divisibility_separates(name):
            divisibility_candidates.append(name)

    print()
    print("=" * 78)
    print("STRUCTURAL CANDIDATE SEARCH")
    print("=" * 78)

    print()
    print("ZERO-SEPARATING CANDIDATES:")

    if zero_candidates:
        for name in zero_candidates:
            print(f"  {name}")
    else:
        print("  NONE")

    print()
    print("DIVISIBILITY-SEPARATING CANDIDATES:")

    if divisibility_candidates:
        for name in divisibility_candidates:
            print(f"  n | {name}")
    else:
        print("  NONE")

    return (
        zero_candidates,
        divisibility_candidates
    )


# =============================================================================
# HELD-OUT VALIDATION
# =============================================================================

def validate_zero_candidate(name):
    prime_ok = all(
        invariants(n)[name] == 0
        for n in VALIDATE_PRIMES
    )

    composite_ok = all(
        invariants(n)[name] != 0
        for n in VALIDATE_COMPOSITES
    )

    return prime_ok and composite_ok


def validate_divisibility_candidate(name):
    prime_ok = all(
        invariants(n)[name] % n == 0
        for n in VALIDATE_PRIMES
    )

    composite_ok = all(
        invariants(n)[name] % n != 0
        for n in VALIDATE_COMPOSITES
    )

    return prime_ok and composite_ok


def validate_candidates(
    zero_candidates,
    divisibility_candidates
):
    print()
    print("=" * 78)
    print("HELD-OUT VALIDATION")
    print("=" * 78)

    print()
    print("ZERO CANDIDATES:")

    if not zero_candidates:
        print("  NONE")

    for name in zero_candidates:
        result = validate_zero_candidate(name)

        print(
            f"  {name:<32}"
            f"{'VALIDATED' if result else 'REJECTED'}"
        )

    print()
    print("DIVISIBILITY CANDIDATES:")

    if not divisibility_candidates:
        print("  NONE")

    for name in divisibility_candidates:
        result = validate_divisibility_candidate(name)

        print(
            f"  n | {name:<28}"
            f"{'VALIDATED' if result else 'REJECTED'}"
        )


# =============================================================================
# EXACT ALGEBRA CONSISTENCY
# =============================================================================

def consistency_tests():
    print()
    print("=" * 78)
    print("EXACT ALGEBRA CONSISTENCY")
    print("=" * 78)

    passed = 0
    total = 0

    def test(name, condition):
        nonlocal passed
        nonlocal total

        total += 1

        if condition:
            passed += 1
            print(f"[PASS] {name}")
        else:
            print(f"[FAIL] {name}")

    # phi^2 = phi + 1
    test(
        "phi^2 = phi + 1",
        zmul(PHI, PHI)
        == zadd(PHI, ONE)
    )

    # N(phi) = -1
    test(
        "N(phi) = -1",
        znorm(PHI) == -1
    )

    # Norm orbit.
    test(
        "N(phi^n)=(-1)^n for n=0..64",
        all(
            check_norm_identity(n)
            for n in range(65)
        )
    )

    # Translation sum.
    test(
        "translation sum identity n=0..64",
        all(
            check_sum_identity(n)
            for n in range(65)
        )
    )

    # Translation difference.
    test(
        "translation difference identity n=0..64",
        all(
            check_difference_identity(n)
            for n in range(65)
        )
    )

    # Norm multiplicativity.
    multiplication_pairs = [
        (0, 0),
        (1, 1),
        (1, 2),
        (2, 3),
        (3, 5),
        (5, 8),
        (7, 11),
        (13, 17),
        (19, 23),
        (31, 37),
    ]

    test(
        "norm multiplicativity",
        all(
            check_norm_multiplication(
                phi_orbit(a),
                phi_orbit(b)
            )
            for a, b in multiplication_pairs
        )
    )

    # Fibonacci embedding.
    embedding_ok = True

    for n in range(1, 65):
        fn, fn1 = fibonacci_pair(n)

        expected = (
            fn1 - fn,
            fn
        )

        if phi_orbit(n) != expected:
            embedding_ok = False
            break

    test(
        "Fibonacci embedding",
        embedding_ok
    )

    # Direct powers versus Fibonacci orbit.
    powers_ok = True

    for n in range(0, 33):
        if zpow(PHI, n) != phi_orbit(n):
            powers_ok = False
            break

    test(
        "direct phi powers = Fibonacci orbit",
        powers_ok
    )

    print()
    print(
        f"RESULT: {passed}/{total} exact tests passed"
    )


# =============================================================================
# SMALL STRUCTURAL TABLE
# =============================================================================

def structural_table():
    values = [
        2, 3, 4, 5, 6, 7, 8, 9,
        10, 11, 12, 13, 15, 17, 19,
        21, 23, 25, 27, 29, 31
    ]

    print()
    print("=" * 78)
    print("STRUCTURAL ORBIT TABLE")
    print("=" * 78)

    print(
        f"{'n':>5} "
        f"{'P/C':>5} "
        f"{'N(X)':>6} "
        f"{'SUM':>8} "
        f"{'DIFF':>14} "
        f"{'L_n':>14} "
        f"{'n|DIFF':>8}"
    )

    print("-" * 78)

    for n in values:
        d = orbit_data(n)

        print(
            f"{n:>5} "
            f"{'P' if is_prime(n) else 'C':>5} "
            f"{d['N']:>6} "
            f"{d['sum']:>8} "
            f"{d['difference']:>14} "
            f"{d['lucas']:>14} "
            f"{str(d['difference'] % n == 0):>8}"
        )


# =============================================================================
# ONE CASE
# =============================================================================

def print_case(n):
    d = orbit_data(n)

    print()
    print("-" * 78)
    print(
        f"n = {n}    "
        f"{'PRIME' if is_prime(n) else 'COMPOSITE'}"
    )
    print("-" * 78)

    print(f"X             = {zfmt(d['x'])}")
    print(f"X + 1         = {zfmt(d['xp'])}")
    print(f"X - 1         = {zfmt(d['xm'])}")

    print()
    print(f"N(X)          = {d['N']}")
    print(f"N(X + 1)      = {d['N_plus']}")
    print(f"N(X - 1)      = {d['N_minus']}")

    print()
    print(f"SUM            = {d['sum']}")
    print(f"EXPECTED SUM   = {d['expected_sum']}")

    print()
    print(f"DIFFERENCE     = {d['difference']}")
    print(f"2*L_n          = {d['expected_difference']}")
    print(f"L_n            = {d['lucas']}")

    print()
    print(f"FIXED CLOSURE  = {zfmt(d['fixed'])}")
    print(f"N(FIXED)       = {d['N_fixed']}")

    print()
    print(f"NORM IDENTITY  = {d['norm_identity']}")
    print(f"SUM IDENTITY   = {d['sum_identity']}")
    print(f"DIFF IDENTITY  = {d['difference_identity']}")
    print(
        f"ALL IDENTITIES = "
        f"{d['norm_identity'] and d['sum_identity'] and d['difference_identity']}"
    )


# =============================================================================
# LARGE EXACT TEST
# =============================================================================

def large_test(n):
    print()
    print("=" * 78)
    print(f"LARGE EXACT ORBIT TEST: n = {n}")
    print("=" * 78)

    start = time.perf_counter()

    d = orbit_data(n)

    elapsed = time.perf_counter() - start

    a, b = d["x"]

    print()
    print(f"n                  = {n}")
    print(
        f"ground-truth       = "
        f"{'PRIME' if is_prime(n) else 'COMPOSITE'}"
    )

    print()
    print("EXACT COORDINATE SIZE")
    print(
        f"digits(F_(n-1))    = "
        f"{integer_digits(a)}"
    )
    print(
        f"digits(F_n)        = "
        f"{integer_digits(b)}"
    )

    print(
        f"bits(F_(n-1))      = "
        f"{integer_bit_length(a)}"
    )
    print(
        f"bits(F_n)          = "
        f"{integer_bit_length(b)}"
    )

    print()
    print("EXACT NORM")

    print(
        f"N(X)               = "
        f"{d['N']}"
    )

    print()
    print("TRANSLATION CLOSURE")

    print(
        f"N(X+1)+N(X-1)      = "
        f"{d['sum']}"
    )

    print(
        f"expected sum       = "
        f"{d['expected_sum']}"
    )

    print(
        f"sum identity       = "
        f"{d['sum'] == d['expected_sum']}"
    )

    print()
    print("LUCAS SEPARATION")

    print(
        f"Lucas digits        = "
        f"{integer_digits(d['lucas'])}"
    )

    print(
        f"difference matches  = "
        f"{d['difference'] == 2 * d['lucas']}"
    )

    print(
        f"difference digits   = "
        f"{integer_digits(d['difference'])}"
    )

    print()
    print("TIMING")

    print(
        f"exact orbit time    = "
        f"{elapsed:.6f} sec"
    )

    print()
    print("STRUCTURAL RESULT")
    print()
    print("    N(phi^n)              = (-1)^n")
    print("    N(phi^n+1)+N(phi^n-1) = 2(1+(-1)^n)")
    print("    N(phi^n+1)-N(phi^n-1) = 2L_n")

    if n & 1:
        print()
        print("    odd orbit:")
        print("        translation sum = 0")
    else:
        print()
        print("    even orbit:")
        print("        translation sum = 4")

    print()
    print("NO GIANT INTEGER WAS CONVERTED TO DECIMAL TEXT.")


# =============================================================================
# LARGE NUMBER REPORT
# =============================================================================

def report_large_number(n):
    d = orbit_data(n)

    a, b = d["x"]

    print()
    print("=" * 78)
    print("LARGE-N REPRESENTATION REPORT")
    print("=" * 78)

    print()
    print(f"n               : {n}")
    print(
        f"prime           : "
        f"{is_prime(n)}"
    )

    print()
    print("COORDINATE A = F_(n-1)")
    print(
        f"    decimal digits : "
        f"{integer_digits(a)}"
    )
    print(
        f"    binary bits    : "
        f"{integer_bit_length(a)}"
    )

    print()
    print("COORDINATE B = F_n")
    print(
        f"    decimal digits : "
        f"{integer_digits(b)}"
    )
    print(
        f"    binary bits    : "
        f"{integer_bit_length(b)}"
    )

    print()
    print("LUCAS COORDINATE")
    print(
        f"    decimal digits : "
        f"{integer_digits(d['lucas'])}"
    )
    print(
        f"    binary bits    : "
        f"{integer_bit_length(d['lucas'])}"
    )

    print()
    print("NORM")
    print(
        f"    N(X) = {d['N']}"
    )

    print()
    print("TRANSLATION SUM")
    print(
        f"    N(X+1)+N(X-1) = {d['sum']}"
    )

    print()
    print("TRANSLATION DIFFERENCE")
    print(
        f"    digits = "
        f"{integer_digits(d['difference'])}"
    )

    print()
    print("THE ACTUAL COORDINATE PAIR IS NOT PRINTED.")
    print("THIS AVOIDS GIANT DECIMAL STRING CONVERSION.")


# =============================================================================
# PARITY COUNTEREXAMPLE
#
# 100005 is composite but odd.
#
# Therefore the zero translation sum cannot itself be a prime detector.
# =============================================================================

def demonstrate_parity_not_primality():
    n = 100005

    d = orbit_data(n)

    print()
    print("=" * 78)
    print("PARITY != PRIMALITY")
    print("=" * 78)

    print()
    print(f"n                  = {n}")
    print(
        f"ground-truth       = "
        f"{'PRIME' if is_prime(n) else 'COMPOSITE'}"
    )

    print(
        f"N(phi^n)           = "
        f"{d['N']}"
    )

    print(
        f"N(X+1)+N(X-1)      = "
        f"{d['sum']}"
    )

    print()
    print("RESULT:")
    print()
    print("    zero translation sum")
    print("            =>")
    print("    odd orbit")
    print()
    print("but NOT necessarily:")
    print()
    print("    zero translation sum")
    print("            =>")
    print("    prime exponent")


# =============================================================================
# PRIME / COMPOSITE COMPARISON
# =============================================================================

def compare_prime_composite():
    print()
    print("=" * 78)
    print("PRIME / COMPOSITE STRUCTURAL COMPARISON")
    print("=" * 78)

    print()
    print("PRIMES")
    print()
    print(
        f"{'n':>5} "
        f"{'N(X)':>8} "
        f"{'SUM':>8} "
        f"{'DIFF%n':>12} "
        f"{'L_n%n':>12}"
    )

    print("-" * 55)

    for n in TRAIN_PRIMES[:20]:
        d = orbit_data(n)

        print(
            f"{n:>5} "
            f"{d['N']:>8} "
            f"{d['sum']:>8} "
            f"{d['difference'] % n:>12} "
            f"{d['lucas'] % n:>12}"
        )

    print()
    print("COMPOSITES")
    print()
    print(
        f"{'n':>5} "
        f"{'N(X)':>8} "
        f"{'SUM':>8} "
        f"{'DIFF%n':>12} "
        f"{'L_n%n':>12}"
    )

    print("-" * 55)

    for n in TRAIN_COMPOSITES[:20]:
        d = orbit_data(n)

        print(
            f"{n:>5} "
            f"{d['N']:>8} "
            f"{d['sum']:>8} "
            f"{d['difference'] % n:>12} "
            f"{d['lucas'] % n:>12}"
        )


# =============================================================================
# DERIVED EQUATIONS
# =============================================================================

def print_derived_equations():
    print()
    print("=" * 78)
    print("DERIVED EQUATIONS")
    print("=" * 78)

    print()
    print("PRIMITIVE")
    print("    X = 0")

    print()
    print("TRANSLATIONS")
    print("    Delta_0(X)  = X + 1")
    print("    Delta_-0(X) = X - 1")

    print()
    print("CLOSURE")
    print("    phi^2 = phi + 1")

    print()
    print("ORBIT")
    print("    X_n = phi^n")
    print("        = F_(n-1) + F_n phi")

    print()
    print("NORM")
    print("    N(a+b phi) = a^2 + ab - b^2")

    print()
    print("ORBIT NORM")
    print("    N(X_n) = (-1)^n")

    print()
    print("TRANSLATION SUM")
    print("    N(X_n+1) + N(X_n-1)")
    print("        = 2(N(X_n)+1)")
    print("        = 2(1+(-1)^n)")

    print()
    print("TRANSLATION DIFFERENCE")
    print("    N(X_n+1) - N(X_n-1)")
    print("        = 2L_n")

    print()
    print("SEPARATED STATES")
    print("    N(X_n+1) = 1 + (-1)^n + L_n")
    print("    N(X_n-1) = 1 + (-1)^n - L_n")


# =============================================================================
# MAIN
# =============================================================================

def main():
    print()
    print("=" * 78)
    print("HDGL / Z[phi] — VERSION 23")
    print("EXACT LARGE-INTEGER ORBIT ARCHITECTURE")
    print("=" * 78)

    # -------------------------------------------------------------------------
    # Algebra.
    # -------------------------------------------------------------------------

    print_derived_equations()

    # -------------------------------------------------------------------------
    # Internal consistency.
    # -------------------------------------------------------------------------

    consistency_tests()

    # -------------------------------------------------------------------------
    # Small structural table.
    # -------------------------------------------------------------------------

    structural_table()

    # -------------------------------------------------------------------------
    # Prime/composite observation.
    # -------------------------------------------------------------------------

    compare_prime_composite()

    # -------------------------------------------------------------------------
    # Demonstrate that zero-sum = oddness, not automatically primality.
    # -------------------------------------------------------------------------

    demonstrate_parity_not_primality()

    # -------------------------------------------------------------------------
    # Search for additional structural candidates.
    # -------------------------------------------------------------------------

    zero_candidates, divisibility_candidates = search_candidates()

    # -------------------------------------------------------------------------
    # Validate only on held-out data.
    # -------------------------------------------------------------------------

    validate_candidates(
        zero_candidates,
        divisibility_candidates
    )

    # -------------------------------------------------------------------------
    # Exact representative cases.
    # -------------------------------------------------------------------------

    for n in [
        2, 3, 5, 7, 11,
        13, 17, 19, 23,
        29, 31
    ]:
        print_case(n)

    # -------------------------------------------------------------------------
    # The requested extremely large prime.
    # -------------------------------------------------------------------------

    large_test(BIG_N)

    # -------------------------------------------------------------------------
    # Large-number magnitude report.
    # -------------------------------------------------------------------------

    report_large_number(BIG_N)

    # -------------------------------------------------------------------------
    # Final state.
    # -------------------------------------------------------------------------

    print()
    print("=" * 78)
    print("VERSION 23 CONCLUSION")
    print("=" * 78)

    print()
    print("EXACT ORBIT:")
    print("    X_n = phi^n = F_(n-1) + F_n phi")

    print()
    print("EXACT NORM:")
    print("    N(X_n) = (-1)^n")

    print()
    print("EXACT TRANSLATION BALANCE:")
    print("    N(X_n+1)+N(X_n-1)")
    print("        = 2(1+(-1)^n)")

    print()
    print("EXACT LUCAS SEPARATION:")
    print("    N(X_n+1)-N(X_n-1)")
    print("        = 2L_n")

    print()
    print("LARGE-N RESULT:")
    print(f"    n = {BIG_N}")
    print(f"    prime = {is_prime(BIG_N)}")
    print(f"    N(phi^n) = {orbit_data(BIG_N)['N']}")
    print(
        f"    translation sum = "
        f"{orbit_data(BIG_N)['sum']}"
    )
    print(
        f"    identities = "
        f"{check_all_identities(BIG_N)}"
    )

    print()
    print("NO FLOATING-POINT PHI.")
    print("NO DECIMAL PHI.")
    print("NO GIANT INTEGER STRING CONVERSION.")
    print("NO STORED LARGE DECIMAL CONSTANT.")
    print()
    print("DONE.")


if __name__ == "__main__":
    main()

Yields

==============================================================================
HDGL / Z[phi] — VERSION 23
EXACT LARGE-INTEGER ORBIT ARCHITECTURE
==============================================================================

==============================================================================
DERIVED EQUATIONS
==============================================================================

PRIMITIVE
    X = 0

TRANSLATIONS
    Delta_0(X)  = X + 1
    Delta_-0(X) = X - 1

CLOSURE
    phi^2 = phi + 1

ORBIT
    X_n = phi^n
        = F_(n-1) + F_n phi

NORM
    N(a+b phi) = a^2 + ab - b^2

ORBIT NORM
    N(X_n) = (-1)^n

TRANSLATION SUM
    N(X_n+1) + N(X_n-1)
        = 2(N(X_n)+1)
        = 2(1+(-1)^n)

TRANSLATION DIFFERENCE
    N(X_n+1) - N(X_n-1)
        = 2L_n

SEPARATED STATES
    N(X_n+1) = 1 + (-1)^n + L_n
    N(X_n-1) = 1 + (-1)^n - L_n

==============================================================================
EXACT ALGEBRA CONSISTENCY
==============================================================================
[PASS] phi^2 = phi + 1
[PASS] N(phi) = -1
[PASS] N(phi^n)=(-1)^n for n=0..64
[PASS] translation sum identity n=0..64
[PASS] translation difference identity n=0..64
[PASS] norm multiplicativity
[PASS] Fibonacci embedding
[PASS] direct phi powers = Fibonacci orbit

RESULT: 8/8 exact tests passed

==============================================================================
STRUCTURAL ORBIT TABLE
==============================================================================
    n   P/C   N(X)      SUM           DIFF            L_n   n|DIFF
------------------------------------------------------------------------------
    2     P      1        4              6              3     True
    3     P     -1        0              8              4    False
    4     C      1        4             14              7    False
    5     P     -1        0             22             11    False
    6     C      1        4             36             18     True
    7     P     -1        0             58             29    False
    8     C      1        4             94             47    False
    9     C     -1        0            152             76    False
   10     C      1        4            246            123    False
   11     P     -1        0            398            199    False
   12     C      1        4            644            322    False
   13     P     -1        0           1042            521    False
   15     C     -1        0           2728           1364    False
   17     P     -1        0           7142           3571    False
   19     P     -1        0          18698           9349    False
   21     C     -1        0          48952          24476    False
   23     P     -1        0         128158          64079    False
   25     C     -1        0         335522         167761    False
   27     C     -1        0         878408         439204    False
   29     P     -1        0        2299702        1149851    False
   31     P     -1        0        6020698        3010349    False

==============================================================================
PRIME / COMPOSITE STRUCTURAL COMPARISON
==============================================================================

PRIMES

    n     N(X)      SUM       DIFF%n        L_n%n
-------------------------------------------------------
    2        1        4            0            1
    3       -1        0            2            1
    5       -1        0            2            1
    7       -1        0            2            1
   11       -1        0            2            1
   13       -1        0            2            1
   17       -1        0            2            1
   19       -1        0            2            1
   23       -1        0            2            1
   29       -1        0            2            1
   31       -1        0            2            1
   37       -1        0            2            1
   41       -1        0            2            1
   43       -1        0            2            1
   47       -1        0            2            1
   53       -1        0            2            1
   59       -1        0            2            1
   61       -1        0            2            1
   67       -1        0            2            1
   71       -1        0            2            1

COMPOSITES

    n     N(X)      SUM       DIFF%n        L_n%n
-------------------------------------------------------
    4        1        4            2            3
    6        1        4            0            0
    8        1        4            6            7
    9       -1        0            8            4
   10        1        4            6            3
   12        1        4            8           10
   14        1        4            6            3
   15       -1        0           13           14
   16        1        4           14           15
   18        1        4            0            0
   20        1        4           14            7
   21       -1        0            1           11
   22        1        4            6            3
   24        1        4            4            2
   25       -1        0           22           11
   26        1        4            6            3
   27       -1        0           17           22
   28        1        4           14            7
   30        1        4            6           18
   32        1        4           30           31

==============================================================================
PARITY != PRIMALITY
==============================================================================

n                  = 100005
ground-truth       = COMPOSITE
N(phi^n)           = -1
N(X+1)+N(X-1)      = 0

RESULT:

    zero translation sum
            =>
    odd orbit

but NOT necessarily:

    zero translation sum
            =>
    prime exponent

==============================================================================
STRUCTURAL CANDIDATE SEARCH
==============================================================================

ZERO-SEPARATING CANDIDATES:
  NONE

DIVISIBILITY-SEPARATING CANDIDATES:
  NONE

==============================================================================
HELD-OUT VALIDATION
==============================================================================

ZERO CANDIDATES:
  NONE

DIVISIBILITY CANDIDATES:
  NONE

------------------------------------------------------------------------------
n = 2    PRIME
------------------------------------------------------------------------------
X             = 1+phi
X + 1         = 2+phi
X - 1         = phi

N(X)          = 1
N(X + 1)      = 5
N(X - 1)      = -1

SUM            = 4
EXPECTED SUM   = 4

DIFFERENCE     = 6
2*L_n          = 6
L_n            = 3

FIXED CLOSURE  = 2phi
N(FIXED)       = -4

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 3    PRIME
------------------------------------------------------------------------------
X             = 1+2phi
X + 1         = 2+2phi
X - 1         = 2phi

N(X)          = -1
N(X + 1)      = 4
N(X - 1)      = -4

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 8
2*L_n          = 8
L_n            = 4

FIXED CLOSURE  = 3+6phi
N(FIXED)       = -9

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 5    PRIME
------------------------------------------------------------------------------
X             = 3+5phi
X + 1         = 4+5phi
X - 1         = 2+5phi

N(X)          = -1
N(X + 1)      = 11
N(X - 1)      = -11

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 22
2*L_n          = 22
L_n            = 11

FIXED CLOSURE  = 30+50phi
N(FIXED)       = -100

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 7    PRIME
------------------------------------------------------------------------------
X             = 8+13phi
X + 1         = 9+13phi
X - 1         = 7+13phi

N(X)          = -1
N(X + 1)      = 29
N(X - 1)      = -29

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 58
2*L_n          = 58
L_n            = 29

FIXED CLOSURE  = 224+364phi
N(FIXED)       = -784

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 11    PRIME
------------------------------------------------------------------------------
X             = 55+89phi
X + 1         = 56+89phi
X - 1         = 54+89phi

N(X)          = -1
N(X + 1)      = 199
N(X - 1)      = -199

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 398
2*L_n          = 398
L_n            = 199

FIXED CLOSURE  = 10890+17622phi
N(FIXED)       = -39204

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 13    PRIME
------------------------------------------------------------------------------
X             = 144+233phi
X + 1         = 145+233phi
X - 1         = 143+233phi

N(X)          = -1
N(X + 1)      = 521
N(X - 1)      = -521

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 1042
2*L_n          = 1042
L_n            = 521

FIXED CLOSURE  = 74880+121160phi
N(FIXED)       = -270400

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 17    PRIME
------------------------------------------------------------------------------
X             = 987+1597phi
X + 1         = 988+1597phi
X - 1         = 986+1597phi

N(X)          = -1
N(X + 1)      = 3571
N(X - 1)      = -3571

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 7142
2*L_n          = 7142
L_n            = 3571

FIXED CLOSURE  = 3523590+5701290phi
N(FIXED)       = -12744900

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 19    PRIME
------------------------------------------------------------------------------
X             = 2584+4181phi
X + 1         = 2585+4181phi
X - 1         = 2583+4181phi

N(X)          = -1
N(X + 1)      = 9349
N(X - 1)      = -9349

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 18698
2*L_n          = 18698
L_n            = 9349

FIXED CLOSURE  = 24155232+39083988phi
N(FIXED)       = -87385104

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 23    PRIME
------------------------------------------------------------------------------
X             = 17711+28657phi
X + 1         = 17712+28657phi
X - 1         = 17710+28657phi

N(X)          = -1
N(X + 1)      = 64079
N(X - 1)      = -64079

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 128158
2*L_n          = 128158
L_n            = 64079

FIXED CLOSURE  = 1134885458+1836283246phi
N(FIXED)       = -4105990084

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 29    PRIME
------------------------------------------------------------------------------
X             = 317811+514229phi
X + 1         = 317812+514229phi
X - 1         = 317810+514229phi

N(X)          = -1
N(X + 1)      = 1149851
N(X - 1)      = -1149851

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 2299702
2*L_n          = 2299702
L_n            = 1149851

FIXED CLOSURE  = 365434978350+591286215650phi
N(FIXED)       = -1322155022500

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

------------------------------------------------------------------------------
n = 31    PRIME
------------------------------------------------------------------------------
X             = 832040+1346269phi
X + 1         = 832041+1346269phi
X - 1         = 832039+1346269phi

N(X)          = -1
N(X + 1)      = 3010349
N(X - 1)      = -3010349

SUM            = 0
EXPECTED SUM   = 0

DIFFERENCE     = 6020698
2*L_n          = 6020698
L_n            = 3010349

FIXED CLOSURE  = 2504729949920+4052738191612phi
N(FIXED)       = -9062195081104

NORM IDENTITY  = True
SUM IDENTITY   = True
DIFF IDENTITY  = True
ALL IDENTITIES = True

==============================================================================
LARGE EXACT ORBIT TEST: n = 100003
==============================================================================

n                  = 100003
ground-truth       = PRIME

EXACT COORDINATE SIZE
digits(F_(n-1))    = 20899
digits(F_n)        = 20900
bits(F_(n-1))      = 69425
bits(F_n)          = 69426

EXACT NORM
N(X)               = -1

TRANSLATION CLOSURE
N(X+1)+N(X-1)      = 0
expected sum       = 0
sum identity       = True

LUCAS SEPARATION
Lucas digits        = 20900
difference matches  = True
difference digits   = 20900

TIMING
exact orbit time    = 0.030445 sec

STRUCTURAL RESULT

    N(phi^n)              = (-1)^n
    N(phi^n+1)+N(phi^n-1) = 2(1+(-1)^n)
    N(phi^n+1)-N(phi^n-1) = 2L_n

    odd orbit:
        translation sum = 0

NO GIANT INTEGER WAS CONVERTED TO DECIMAL TEXT.

==============================================================================
LARGE-N REPRESENTATION REPORT
==============================================================================

n               : 100003
prime           : True

COORDINATE A = F_(n-1)
    decimal digits : 20899
    binary bits    : 69425

COORDINATE B = F_n
    decimal digits : 20900
    binary bits    : 69426

LUCAS COORDINATE
    decimal digits : 20900
    binary bits    : 69427

NORM
    N(X) = -1

TRANSLATION SUM
    N(X+1)+N(X-1) = 0

TRANSLATION DIFFERENCE
    digits = 20900

THE ACTUAL COORDINATE PAIR IS NOT PRINTED.
THIS AVOIDS GIANT DECIMAL STRING CONVERSION.

==============================================================================
VERSION 23 CONCLUSION
==============================================================================

EXACT ORBIT:
    X_n = phi^n = F_(n-1) + F_n phi

EXACT NORM:
    N(X_n) = (-1)^n

EXACT TRANSLATION BALANCE:
    N(X_n+1)+N(X_n-1)
        = 2(1+(-1)^n)

EXACT LUCAS SEPARATION:
    N(X_n+1)-N(X_n-1)
        = 2L_n

LARGE-N RESULT:
    n = 100003
    prime = True
    N(phi^n) = -1
    translation sum = 0
    identities = True

NO FLOATING-POINT PHI.
NO DECIMAL PHI.
NO GIANT INTEGER STRING CONVERSION.
NO STORED LARGE DECIMAL CONSTANT.

DONE.

Version 24

#!/usr/bin/env python3
"""
HDGL / Z[phi] v24
Lucas-Frobenius Congruence Collision Hunter

Primitive closure:
    phi^2 = phi + 1

Orbit:
    phi^n = F_(n-1) + F_n phi

Lucas:
    L_n = 2 F_(n-1) + F_n

Axis A:
    R_A(n) = (L_n - 1) mod n

Frobenius companion:
    For prime p != 5,

        F_p == +1 (mod p)  if p == +/-1 (mod 5)
        F_p == -1 (mod p)  if p == +/-2 (mod 5)

The 5 arises from the discriminant:
    Delta = 1 + 4 = 5

No floating point.
No stored phi.
No decimal conversion of giant integers.
"""

# ------------------------------------------------------------
# FAST DOUBLING
# ------------------------------------------------------------

def fibonacci_pair(n):
    """
    Return (F_n, F_(n+1)) exactly.

    Uses binary fast doubling:
        F_(2k)   = F_k (2F_(k+1) - F_k)
        F_(2k+1) = F_k^2 + F_(k+1)^2
    """
    if n == 0:
        return 0, 1

    a, b = fibonacci_pair(n >> 1)

    c = a * ((b << 1) - a)
    d = a * a + b * b

    if n & 1:
        return d, c + d

    return c, d


# ------------------------------------------------------------
# ITERATIVE FAST DOUBLING
# Avoids recursion depth entirely.
# ------------------------------------------------------------

def fibonacci_pair_iter(n):
    """
    Exact iterative fast doubling.
    Returns (F_n, F_(n+1)).
    """
    a = 0
    b = 1

    for bit in bin(n)[2:]:
        c = a * ((b << 1) - a)
        d = a * a + b * b

        if bit == "0":
            a, b = c, d
        else:
            a, b = d, c + d

    return a, b


# ------------------------------------------------------------
# LUCAS
# ------------------------------------------------------------

def lucas_fast(n):
    """
    L_n = F_(n-1) + F_(n+1)
        = 2F_(n-1) + F_n
    """
    if n == 0:
        return 2

    f_n_minus_1, f_n = fibonacci_pair_iter(n - 1)

    return (f_n_minus_1 << 1) + f_n


def lucas_from_pair(n):
    """
    Compute L_n from the pair (F_n, F_(n+1)).
    """
    f_n, f_np1 = fibonacci_pair_iter(n)

    # L_n = F_(n-1) + F_(n+1)
    # F_(n-1) = F_(n+1) - F_n
    #
    # Therefore:
    # L_n = 2F_(n+1) - F_n
    return (f_np1 << 1) - f_n


# ------------------------------------------------------------
# AXIS A
# ------------------------------------------------------------

def axis_a_residue(n):
    """
    R_A(n) = (L_n - 1) mod n
    """
    if n <= 0:
        raise ValueError("n must be positive")

    L = lucas_fast(n)

    return (L - 1) % n


def axis_a_passes(n):
    return axis_a_residue(n) == 0


# ------------------------------------------------------------
# DISCRIMINANT / FROBENIUS AXIS
# ------------------------------------------------------------

def expected_fibonacci_residue(n):
    """
    Prime Frobenius expectation for F_n mod n.

    The quadratic closure is:

        x^2 - x - 1 = 0

    whose discriminant is:

        Delta = 1 + 4 = 5

    For prime p != 5:

        F_p == +1 mod p    if p == +/-1 mod 5
        F_p == -1 mod p    if p == +/-2 mod 5
    """
    r = n % 5

    if r in (1, 4):
        return 1

    if r in (2, 3):
        return n - 1

    # n == 0 mod 5
    return None


def fibonacci_residue(n):
    f_n, _ = fibonacci_pair_iter(n)
    return f_n % n


def axis_b_passes(n):
    """
    Test the Frobenius/Fibonacci congruence.

    n == 5 is handled separately.
    """
    if n == 5:
        return True

    expected = expected_fibonacci_residue(n)

    if expected is None:
        return False

    return fibonacci_residue(n) == expected


# ------------------------------------------------------------
# COMBINED FILTER
# ------------------------------------------------------------

def combined_pass(n):
    """
    Axis A + Axis B.
    """
    return axis_a_passes(n) and axis_b_passes(n)


# ------------------------------------------------------------
# SIMPLE EXACT PRIMALITY
# Used ONLY as ground truth.
# It is NOT part of the HDGL filters.
# ------------------------------------------------------------

def is_prime(n):
    if n < 2:
        return False

    if n == 2:
        return True

    if n % 2 == 0:
        return False

    d = 3

    while d * d <= n:
        if n % d == 0:
            return False
        d += 2

    return True


# ------------------------------------------------------------
# BASIC VALIDATION
# ------------------------------------------------------------

TRAIN_PRIMES = [
    3, 5, 7, 11, 13,
    17, 19, 23, 29, 31
]

ODD_COMPOSITES = [
    9, 15, 21, 25, 27,
    33, 35, 39, 45, 49,
    51, 55, 57, 63, 65,
    69, 75, 77, 81, 85,
    87, 91, 93, 95, 99
]


print("=" * 72)
print("AXIS A: (L_n - 1) % n")
print("=" * 72)

prime_a = []

for n in TRAIN_PRIMES:
    r = axis_a_residue(n)
    prime_a.append((n, r))

print("PRIMES:")
print(prime_a)

print()
print("ODD COMPOSITES:")

composite_a = []

for n in ODD_COMPOSITES:
    r = axis_a_residue(n)
    composite_a.append((n, r))

print(composite_a)


print()
print("=" * 72)
print("AXIS B: F_n FROBENIUS RESIDUE")
print("=" * 72)

print()
print("n        F_n mod n       expected")

for n in TRAIN_PRIMES:
    actual = fibonacci_residue(n)
    expected = expected_fibonacci_residue(n)
    print(f"{n:3d}      {actual:8d}      {expected}")


print()
print("=" * 72)
print("COMBINED AXIS A + AXIS B")
print("=" * 72)

print()
print("n        prime   A-residue   B-pass   combined")

for n in TRAIN_PRIMES:
    p = is_prime(n)
    a = axis_a_residue(n)
    b = axis_b_passes(n)
    c = combined_pass(n)

    print(
        f"{n:3d}      "
        f"{str(p):5s}   "
        f"{a:9d}   "
        f"{str(b):6s}   "
        f"{str(c):8s}"
    )

print()

for n in ODD_COMPOSITES:
    p = is_prime(n)
    a = axis_a_residue(n)
    b = axis_b_passes(n)
    c = combined_pass(n)

    print(
        f"{n:3d}      "
        f"{str(p):5s}   "
        f"{a:9d}   "
        f"{str(b):6s}   "
        f"{str(c):8s}"
    )


# ------------------------------------------------------------
# COLLISION HUNTER
# ------------------------------------------------------------

LIMIT = 10_000

axis_a_collisions = []
combined_collisions = []

prime_a_failures = []
prime_combined_failures = []


print()
print("=" * 72)
print(f"SCANNING 3 <= n <= {LIMIT}")
print("=" * 72)

for n in range(3, LIMIT + 1, 2):

    prime = is_prime(n)

    a = axis_a_passes(n)
    b = axis_b_passes(n)
    c = a and b

    if prime:
        if not a:
            prime_a_failures.append(n)

        if not c:
            prime_combined_failures.append(n)

    else:
        if a:
            axis_a_collisions.append(n)

        if c:
            combined_collisions.append(n)


# ------------------------------------------------------------
# REPORT
# ------------------------------------------------------------

print()
print("=" * 72)
print("RESULTS")
print("=" * 72)

print()
print("Prime failures on Axis A:")
print(prime_a_failures)

print()
print("Prime failures on combined Axis A + B:")
print(prime_combined_failures)

print()
print("Composite survivors of Axis A:")
print(axis_a_collisions)

print()
print("Composite survivors of BOTH axes:")
print(combined_collisions)

print()
print("Axis A composite survivors:",
      len(axis_a_collisions))

print("Combined composite survivors:",
      len(combined_collisions))


# ------------------------------------------------------------
# FIRST COLLISIONS
# ------------------------------------------------------------

print()
print("=" * 72)
print("FIRST AXIS-A COLLISIONS")
print("=" * 72)

for n in axis_a_collisions[:50]:
    print(
        f"n={n:5d}  "
        f"Lres={axis_a_residue(n):5d}  "
        f"Fres={fibonacci_residue(n):5d}  "
        f"B={'PASS' if axis_b_passes(n) else 'FAIL'}"
    )


print()
print("=" * 72)
print("FIRST COMBINED COLLISIONS")
print("=" * 72)

for n in combined_collisions[:50]:
    print(
        f"n={n:5d}  "
        f"Lres={axis_a_residue(n):5d}  "
        f"Fres={fibonacci_residue(n):5d}"
    )


# ------------------------------------------------------------
# INTERNAL CONSISTENCY
# ------------------------------------------------------------

print()
print("=" * 72)
print("INTERNAL IDENTITY CHECKS")
print("=" * 72)

identity_failures = []

for n in range(1, 1000):

    f_nm1, f_n = fibonacci_pair_iter(n - 1)

    L1 = (f_nm1 << 1) + f_n
    L2 = lucas_from_pair(n)

    if L1 != L2:
        identity_failures.append(
            ("Lucas", n, L1, L2)
        )

    # Fundamental Lucas/Fibonacci identity:
    #
    # L_n^2 - 5 F_n^2 = 4(-1)^n
    #
    f_n, _ = fibonacci_pair_iter(n)
    L_n = lucas_fast(n)

    lhs = L_n * L_n - 5 * f_n * f_n
    rhs = 4 if (n & 1) == 0 else -4

    if lhs != rhs:
        identity_failures.append(
            ("Lucas-Fibonacci", n, lhs, rhs)
        )


if identity_failures:
    print("FAILURES:")
    for failure in identity_failures[:20]:
        print(failure)
else:
    print("ALL EXACT IDENTITIES PASSED.")


# ------------------------------------------------------------
# FINAL INTERPRETATION
# ------------------------------------------------------------

print()
print("=" * 72)
print("HDGL CLOSURE")
print("=" * 72)

print("""
phi^2 = phi + 1

phi^n = F_(n-1) + F_n phi

N(phi^n) = (-1)^n

N(phi^n + 1) - N(phi^n - 1) = 2 L_n

L_n = 2 F_(n-1) + F_n

Axis A:
    (L_n - 1) mod n == 0

Axis B:
    F_n mod n follows the discriminant-5 Frobenius branch

The scan does NOT assume that either congruence proves primality.

It searches for the composite collision set.
""")

yields

========================================================================
AXIS A: (L_n - 1) % n
========================================================================
PRIMES:
[(3, 0), (5, 0), (7, 0), (11, 0), (13, 0), (17, 0), (19, 0), (23, 0), (29, 0), (31, 0)]

ODD COMPOSITES:
[(9, 3), (15, 13), (21, 10), (25, 10), (27, 21), (33, 3), (35, 3), (39, 16), (45, 40), (49, 28), (51, 3), (55, 43), (57, 3), (63, 40), (65, 10), (69, 49), (75, 63), (77, 72), (81, 21), (85, 10), (87, 61), (91, 2), (93, 34), (95, 48), (99, 75)]

========================================================================
AXIS B: F_n FROBENIUS RESIDUE
========================================================================

n        F_n mod n       expected
  3             2      2
  5             0      None
  7             6      6
 11             1      1
 13            12      12
 17            16      16
 19             1      1
 23            22      22
 29             1      1
 31             1      1

========================================================================
COMBINED AXIS A + AXIS B
========================================================================

n        prime   A-residue   B-pass   combined
  3      True            0   True     True
  5      True            0   True     True
  7      True            0   True     True
 11      True            0   True     True
 13      True            0   True     True
 17      True            0   True     True
 19      True            0   True     True
 23      True            0   True     True
 29      True            0   True     True
 31      True            0   True     True

  9      False           3   False    False
 15      False          13   False    False
 21      False          10   False    False
 25      False          10   False    False
 27      False          21   False    False
 33      False           3   False    False
 35      False           3   False    False
 39      False          16   False    False
 45      False          40   False    False
 49      False          28   False    False
 51      False           3   False    False
 55      False          43   False    False
 57      False           3   False    False
 63      False          40   False    False
 65      False          10   False    False
 69      False          49   False    False
 75      False          63   False    False
 77      False          72   False    False
 81      False          21   False    False
 85      False          10   False    False
 87      False          61   False    False
 91      False           2   False    False
 93      False          34   False    False
 95      False          48   False    False
 99      False          75   False    False

========================================================================
SCANNING 3 <= n <= 10000
========================================================================

========================================================================
RESULTS
========================================================================

Prime failures on Axis A:
[]

Prime failures on combined Axis A + B:
[]

Composite survivors of Axis A:
[705, 2465, 2737, 3745, 4181, 5777, 6721]

Composite survivors of BOTH axes:
[4181, 5777, 6721]

Axis A composite survivors: 7
Combined composite survivors: 3

========================================================================
FIRST AXIS-A COLLISIONS
========================================================================
n=  705  Lres=    0  Fres=  565  B=FAIL
n= 2465  Lres=    0  Fres=  900  B=FAIL
n= 2737  Lres=    0  Fres=    1  B=FAIL
n= 3745  Lres=    0  Fres=  750  B=FAIL
n= 4181  Lres=    0  Fres=    1  B=PASS
n= 5777  Lres=    0  Fres= 5776  B=PASS
n= 6721  Lres=    0  Fres=    1  B=PASS

========================================================================
FIRST COMBINED COLLISIONS
========================================================================
n= 4181  Lres=    0  Fres=    1
n= 5777  Lres=    0  Fres= 5776
n= 6721  Lres=    0  Fres=    1

========================================================================
INTERNAL IDENTITY CHECKS
========================================================================
ALL EXACT IDENTITIES PASSED.

========================================================================
HDGL CLOSURE
========================================================================

phi^2 = phi + 1

phi^n = F_(n-1) + F_n phi

N(phi^n) = (-1)^n

N(phi^n + 1) - N(phi^n - 1) = 2 L_n

L_n = 2 F_(n-1) + F_n

Axis A:
    (L_n - 1) mod n == 0

Axis B:
    F_n mod n follows the discriminant-5 Frobenius branch

The scan does NOT assume that either congruence proves primality.

It searches for the composite collision set.