#!/usr/bin/env python3
"""
XOR8 numerical invariance tests (2025-11-01)

Proves that XOR8 enforces iterator discipline only; it does NOT alter the
numeric dot-product result in the CWC kernel.
"""

import struct
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from kernels.dot_product_code import dot_product_code
from sdk.code_iter import FILL8, COPY, XOR8, ANCHOR, END

def build_bytes(segments):
    """Build bytecode from segment list."""
    b = bytearray()
    for seg in segments:
        op = seg[0].upper()
        if op == "FILL8":
            _, ln, val = seg
            b.append(FILL8)
            b += struct.pack("<Q", int(ln))
            b.append(int(val) & 0xFF)
        elif op == "COPY":
            _, src_off, ln = seg
            b.append(COPY)
            b += struct.pack("<Q", int(src_off))
            b += struct.pack("<Q", int(ln))
        elif op == "XOR8":
            _, ln, key = seg
            b.append(XOR8)
            b += struct.pack("<Q", int(ln))
            b.append(int(key) & 0xFF)
        elif op == "ANCHOR":
            _, aid, gain = seg
            b.append(ANCHOR)
            b += struct.pack("<I", int(aid))
            b += struct.pack("<f", float(gain))
        elif op == "END":
            b.append(END)
        else:
            raise ValueError(f"unknown op {op}")
    if not b or b[-1] != END:
        b.append(END)
    return bytes(b)

def v_ones(anchor_id: int, anchor_gain: float, virt_off: int, length: int) -> float:
    """Return contribution when every element = 1.0."""
    return float(length)

def test_xor_key_variance_invariant():
    """XOR8 key should not affect dot result (discipline only, not numeric)."""
    # Program 1: XOR8(100, 0x00) → COPY(0, 100)
    prog1 = build_bytes([
        ("FILL8", 100, 7),
        ("XOR8", 100, 0x00),
        ("COPY", 0, 100),
        ("END",),
    ])

    # Program 2: XOR8(100, 0xFF) → COPY(0, 100)
    prog2 = build_bytes([
        ("FILL8", 100, 7),
        ("XOR8", 100, 0xFF),
        ("COPY", 0, 100),
        ("END",),
    ])

    # Program 3: XOR8(100, 0xAA) → COPY(0, 100)
    prog3 = build_bytes([
        ("FILL8", 100, 7),
        ("XOR8", 100, 0xAA),
        ("COPY", 0, 100),
        ("END",),
    ])

    # All should yield same dot result (200.0 = 100 FILL8 + 100 COPY)
    r1 = dot_product_code(prog1, v_ones)
    r2 = dot_product_code(prog2, v_ones)
    r3 = dot_product_code(prog3, v_ones)

    assert r1 == 200.0
    assert r2 == 200.0
    assert r3 == 200.0
    assert r1 == r2 == r3

def test_xor_key_variance_fill8():
    """XOR8 key should not affect FILL8 dot result."""
    # FILL8 with XOR8 before it (different keys)
    prog1 = build_bytes([
        ("XOR8", 50, 0x00),
        ("FILL8", 50, 9),
        ("END",),
    ])

    prog2 = build_bytes([
        ("XOR8", 50, 0xFF),
        ("FILL8", 50, 9),
        ("END",),
    ])

    r1 = dot_product_code(prog1, v_ones)
    r2 = dot_product_code(prog2, v_ones)

    # Both should yield 50.0 (FILL8 contribution only)
    assert r1 == 50.0
    assert r2 == 50.0
    assert r1 == r2

def test_xor_multiple_keys_same_length():
    """Multiple programs with same structure but different XOR keys → same dot."""
    keys = [0x00, 0x11, 0x22, 0x33, 0x55, 0xAA, 0xFF]
    results = []

    for key in keys:
        prog = build_bytes([
            ("FILL8", 100, 3),
            ("XOR8", 100, key),
            ("COPY", 0, 100),
            ("END",),
        ])
        results.append(dot_product_code(prog, v_ones))

    # All should be identical
    assert all(r == 200.0 for r in results)
    assert len(set(results)) == 1  # All unique values should collapse to 1
