use super::*;
use nebu::Goldilocks as F;
use nox::{Order, Reduction, data::Data};

const N: usize = 2048;

fn memory(max: u32) -> Memory {
    Memory::new(max, max as usize * size_of::<VerifiedNode>()).unwrap()
}

fn native(ar: &Reduction<N>, index: Order) -> Definition {
    let node = ar.get(index).unwrap();
    Definition {
        value: match node.inner {
            Data::Atom { value } => Value::Atom(value.as_u64()),
            Data::Pair { left, right } => Value::Pair { left, right },
        },
        particle: node.hash.map(F::as_u64),
        cost: if node.bound.is_dynamic() {
            Cost::Dynamic(node.bound.value())
        } else {
            Cost::Exact(node.bound.value())
        },
    }
}

fn import(ar: &Reduction<N>) -> Memory {
    let mut table = memory(ar.count());
    let mut compact = memory(ar.count());
    for i in 0..ar.count() {
        assert_eq!(table.append(native(ar, i)), Ok(i));
        assert_eq!(compact.append_value(native(ar, i).value), Ok(i));
        assert_eq!(table.node(i), compact.node(i));
    }
    table
}

#[test]
fn compact_values_preserve_canonicality_prior_order_limits_and_root_binding() {
    let mut table = memory(2);
    for invalid in [
        Value::Atom(nebu::field::P),
        Value::Atom(u64::MAX),
        Value::Pair { left: 0, right: 0 },
    ] {
        assert!(table.append_value(invalid).is_err());
        assert!(table.is_empty());
    }
    let zero = table.append_value(Value::Atom(0)).unwrap();
    assert_eq!(
        table.append_value(Value::Pair {
            left: zero,
            right: 1
        }),
        Err(Error::Reference)
    );
    let pair = table
        .append_value(Value::Pair {
            left: zero,
            right: zero,
        })
        .unwrap();
    assert_eq!(table.append_value(Value::Atom(1)), Err(Error::Limit));
    let view = table.view(2).unwrap();
    let expected = nox::data::hash::hash_pair(
        &nox::data::hash::hash_atom(F::ZERO),
        &nox::data::hash::hash_atom(F::ZERO),
    )
    .map(F::as_u64);
    assert!(view.bind(pair, expected).is_ok());
    assert_eq!(
        view.bind(pair, view.get(zero).unwrap().particle()),
        Err(Error::Particle)
    );
}

fn atom(ar: &mut Reduction<N>, value: u64) -> Order {
    ar.atom(F::new(value)).unwrap()
}

fn op(ar: &mut Reduction<N>, tag: u64, body: Order) -> Order {
    let tag = atom(ar, tag);
    ar.pair(tag, body).unwrap()
}

fn binary(ar: &mut Reduction<N>, tag: u64, a: Order, b: Order) -> Order {
    let body = ar.pair(a, b).unwrap();
    op(ar, tag, body)
}

#[test]
fn canonical_atom_and_particle_domains_reject_aliases() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    for value in [0, 1, nebu::field::P - 1] {
        atom(&mut ar, value);
    }
    let table = import(&ar);
    assert_eq!(table.len(), 3);
    let mut table = memory(16);
    for value in [nebu::field::P, u64::MAX] {
        let invalid = Definition {
            value: Value::Atom(value),
            particle: [0; 4],
            cost: Cost::Exact(0),
        };
        assert_eq!(table.append(invalid), Err(Error::NonCanonical));
        assert!(table.is_empty());
    }
    for i in 0..4 {
        let mut invalid = native(&ar, 0);
        invalid.particle[i] = nebu::field::P;
        assert_eq!(table.append(invalid), Err(Error::NonCanonical));
        invalid.particle = native(&ar, 0).particle;
        invalid.particle[i] ^= 1;
        assert_eq!(table.append(invalid), Err(Error::Particle));
        assert!(table.is_empty());
    }
}

#[test]
fn native_headers_and_every_cost_case_are_derived_from_prior_records() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let data = atom(&mut ar, 42);
    let quote = op(&mut ar, 1, data);
    let dyn_f = binary(&mut ar, 2, quote, quote);
    for tag in (0..20).chain([nebu::field::P - 1]) {
        op(&mut ar, tag, data); // malformed binary/branch bodies
        op(&mut ar, tag, quote);
        binary(&mut ar, tag, quote, quote);
        binary(&mut ar, tag, dyn_f, quote);
        binary(&mut ar, tag, quote, dyn_f);
        let arms = ar.pair(quote, dyn_f).unwrap();
        binary(&mut ar, tag, quote, arms);
        let arms = ar.pair(dyn_f, quote).unwrap();
        binary(&mut ar, tag, dyn_f, arms);
    }
    ar.pair(quote, data).unwrap(); // non-atom head
    let table = import(&ar);
    for index in 0..ar.count() {
        assert_eq!(
            table.view(table.len()).unwrap().get(index).unwrap().0,
            native(&ar, index)
        );
    }
}

#[test]
fn ordered_topology_and_strict_prior_references_are_checked_atomically() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let a = atom(&mut ar, 42);
    let b = atom(&mut ar, 43);
    let pair = ar.pair(a, b).unwrap();
    let mut table = memory(16);
    table.append(native(&ar, a)).unwrap();
    table.append(native(&ar, b)).unwrap();
    let prior: Vec<_> = table.nodes.clone();
    let original = native(&ar, pair);
    for value in [
        Value::Pair { left: b, right: a },
        Value::Atom(42),
        Value::Pair {
            left: pair,
            right: a,
        },
        Value::Pair {
            left: a,
            right: pair + 1,
        },
        Value::Pair {
            left: u32::MAX,
            right: a,
        },
        Value::Pair {
            left: a,
            right: u32::MAX,
        },
    ] {
        assert!(table.append(Definition { value, ..original }).is_err());
        assert_eq!(table.nodes, prior);
    }
    assert_eq!(table.append(original), Ok(pair));
}

#[test]
fn all_cost_bits_and_dynamic_selector_are_authenticated_even_for_quoted_data() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let data = atom(&mut ar, 42);
    let q = op(&mut ar, 1, data);
    let mut table = memory(256);
    for i in 0..q {
        table.append(native(&ar, i)).unwrap();
    }
    let correct = native(&ar, q);
    for cost in [
        Cost::Exact(0),
        Cost::Exact(2),
        Cost::Exact(1u64 << 32 | 1),
        Cost::Exact(u64::MAX),
        Cost::Dynamic(1),
    ] {
        assert_eq!(
            table.append(Definition { cost, ..correct }),
            Err(Error::Cost)
        );
        assert_eq!(table.len(), q);
    }
    table.append(correct).unwrap();
    let dynamic = binary(&mut ar, 2, q, q);
    for i in table.len()..dynamic {
        table.append(native(&ar, i)).unwrap();
    }
    let correct = native(&ar, dynamic);
    assert_eq!(
        table.append(Definition {
            cost: Cost::Exact(correct.cost.value()),
            ..correct
        }),
        Err(Error::Cost)
    );
    assert_eq!(table.append(correct), Ok(dynamic));
}

#[test]
fn saturated_u64_costs_do_not_wrap_or_reduce_in_the_field() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let data = atom(&mut ar, 42);
    let mut f = op(&mut ar, 1, data);
    let mut crossed_field = false;
    let mut crossed_limb = false;
    for _ in 0..66 {
        f = binary(&mut ar, 3, f, f);
        let value = ar.get(f).unwrap().bound.value();
        crossed_limb |= value > u32::MAX as u64;
        crossed_field |= value >= nebu::field::P;
    }
    assert!(crossed_limb && crossed_field);
    assert_eq!(ar.get(f).unwrap().bound, nox::data::Cost::Exact(u64::MAX));
    let table = import(&ar);
    assert_eq!(table.node(f).unwrap().cost(), Cost::Exact(u64::MAX));
}

#[test]
fn duplicate_occurrences_preserve_content_identity_and_cost() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let a = atom(&mut ar, 1);
    let b = atom(&mut ar, 42);
    let p = ar.pair(a, b).unwrap();
    let mut table = memory(16);
    for i in 0..ar.count() {
        table.append(native(&ar, i)).unwrap();
    }
    let a2 = table.append(native(&ar, a)).unwrap();
    let b2 = table.append(native(&ar, b)).unwrap();
    let p2 = table
        .append(Definition {
            value: Value::Pair {
                left: a2,
                right: b2,
            },
            ..native(&ar, p)
        })
        .unwrap();
    assert_ne!(p, p2);
    assert_eq!(
        table.node(p).unwrap().particle(),
        table.node(p2).unwrap().particle()
    );
    assert_eq!(
        table.node(p).unwrap().cost(),
        table.node(p2).unwrap().cost()
    );
    let forged = Definition {
        cost: Cost::Dynamic(1),
        ..native(&ar, p)
    };
    assert_eq!(table.append(forged), Err(Error::Cost));
}

#[test]
fn views_reject_future_reads_and_bind_every_expected_root_limb() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let a = atom(&mut ar, 42);
    let b = atom(&mut ar, 43);
    let table = import(&ar);
    assert!(matches!(table.view(3), Err(Error::Reference)));
    let view = table.view(1).unwrap();
    assert_eq!(view.get(b), Err(Error::Reference));
    assert_eq!(view.get(u32::MAX), Err(Error::Reference));
    let expected = native(&ar, a).particle;
    assert!(view.bind(a, expected).is_ok());
    for i in 0..4 {
        let mut wrong = expected;
        wrong[i] ^= 1;
        assert_eq!(view.bind(a, wrong), Err(Error::Particle));
    }
    assert_eq!(table.view(0).unwrap().get(0), Err(Error::Reference));
}

#[test]
fn record_and_buffer_limits_accept_exact_capacity_and_fail_closed_below() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    for value in 0..65 {
        atom(&mut ar, value);
    }
    let bytes = 65 * size_of::<VerifiedNode>();
    assert!(matches!(Memory::new(65, bytes - 1), Err(Error::Limit)));
    let mut table = Memory::new(65, bytes).unwrap();
    for i in 0..65 {
        table.append(native(&ar, i)).unwrap();
        assert!(table.buffer_bytes() <= bytes);
    }
    let before = table.nodes.clone();
    assert_eq!(table.append(native(&ar, 0)), Err(Error::Limit));
    assert_eq!(table.nodes, before);
    let mut zero = Memory::new(0, 0).unwrap();
    assert_eq!(zero.append(native(&ar, 0)), Err(Error::Limit));
    assert_eq!(zero.buffer_bytes(), 0);
}

fn exact_bound(
    ar: &mut Reduction<N>,
    value: u64,
    cache: &mut std::collections::BTreeMap<u64, Order>,
) -> Order {
    if let Some(&id) = cache.get(&value) {
        return id;
    }
    let id = if value == 0 {
        atom(ar, 42)
    } else {
        let a = exact_bound(ar, value / 2, cache);
        let b = exact_bound(ar, (value - 1) / 2, cache);
        binary(ar, 3, a, b)
    };
    assert_eq!(ar.get(id).unwrap().bound, nox::data::Cost::Exact(value));
    cache.insert(value, id);
    id
}

#[test]
fn non_saturated_costs_above_the_field_keep_both_limbs_authenticated() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let mut cache = std::collections::BTreeMap::new();
    for value in [
        1u64 << 32,
        nebu::field::P - 1,
        nebu::field::P,
        nebu::field::P + 17,
        u64::MAX - 1,
    ] {
        let id = exact_bound(&mut ar, value, &mut cache);
        let mut table = memory(ar.count() + 1);
        for i in 0..ar.count() {
            table.append(native(&ar, i)).unwrap();
        }
        let good = native(&ar, id);
        for cost in [
            Cost::Exact(value ^ 1),
            Cost::Exact(value ^ (1 << 32)),
            Cost::Exact(value % nebu::field::P),
            Cost::Dynamic(value),
        ] {
            // Reduction modulo p equals the original below p.
            if cost == good.cost {
                continue;
            }
            assert_eq!(table.append(Definition { cost, ..good }), Err(Error::Cost));
            assert_eq!(table.len(), ar.count());
        }
        assert_eq!(table.append(good), Ok(ar.count()));
    }
}

#[test]
fn dynamic_metadata_survives_smaller_branch_arms_and_saturation() {
    let mut ar = Reduction::<N>::try_new_boxed().unwrap();
    let mut cache = std::collections::BTreeMap::new();
    let zero = exact_bound(&mut ar, 0, &mut cache);
    let small = binary(&mut ar, 2, zero, zero);
    let large = exact_bound(&mut ar, 1000, &mut cache);
    for (yes, no) in [(small, large), (large, small)] {
        let arms = ar.pair(yes, no).unwrap();
        let branch = binary(&mut ar, 4, zero, arms);
        assert_eq!(
            ar.get(branch).unwrap().bound,
            nox::data::Cost::Dynamic(1001)
        );
    }
    let max = exact_bound(&mut ar, u64::MAX, &mut cache);
    let saturated = binary(&mut ar, 2, max, max);
    assert_eq!(
        ar.get(saturated).unwrap().bound,
        nox::data::Cost::Dynamic(u64::MAX)
    );
    let table = import(&ar);
    assert_eq!(
        table.node(saturated).unwrap().cost(),
        Cost::Dynamic(u64::MAX)
    );
}

Homonyms

neural/trident/src/neural/tests.rs
cyb/honeycrisp/aruminium/src/tests.rs
soft3/radio/iroh-blobs/src/tests.rs
warriors/trisha/rs/lower/tests.rs
neural/trident/src/deploy/tests.rs
soft3/crate/src/wallet/tests.rs
soft3/lens/cli/src/tests.rs
soft3/crate/src/genesis/tests.rs
neural/trident/src/verify/solve/tests.rs
neural/trident/src/package/hash/tests.rs
cyb/wysm/crates/wasmi/src/tests.rs
soft3/zheng/rs/src/accumulate/tests.rs
soft3/zheng/rs/src/air/tests.rs
neural/trident/src/package/registry/tests.rs
neural/trident/src/verify/sym/tests.rs
cyb/honeycrisp/aruminium/src/render/tests.rs
neural/trident/src/syntax/format/tests.rs
neural/trident/src/verify/smt/tests.rs
neural/trident/src/syntax/grammar/tests.rs
neural/trident/src/config/scaffold/tests.rs
warriors/trisha/rs/cost/stack_verifier/tests.rs
soft3/radio/quinn/quinn-udp/tests/tests.rs
soft3/radio/cyber-bao/src/tree/tests.rs
cyb/honeycrisp/unimem/src/block/tests.rs
neural/trident/src/lsp/semantic/tests.rs
neural/trident/src/package/manifest/tests.rs
soft3/radio/cyber-bao/src/group/tests.rs
cyb/wysm/crates/cli/src/tests.rs
soft3/crate/src/chain/pool/tests.rs
soft3/cybernode/rs/src/genesis/tests.rs
soft3/zheng/rs/src/envelope/tests.rs
soft3/zheng/rs/src/machine/tests.rs
soft3/cybernode/rs/src/wallet/tests.rs
neural/trident/src/verify/synthesize/tests.rs
warriors/trisha/rs/lower/legalize/tests.rs
neural/trident/src/syntax/lexer/tests.rs
cyb/wysm/crates/collections/src/tests.rs
neural/trident/src/verify/equiv/tests.rs
neural/trident/src/lsp/util/tests.rs
neural/trident/src/verify/report/tests.rs
soft3/crate/src/chain/economy/tests.rs
soft3/radio/quinn/quinn/src/tests.rs
neural/trident/src/config/resolve/tests.rs
neural/trident/src/package/store/tests.rs
soft3/zheng/rs/src/execution/veil/tests.rs
soft3/zheng/rs/src/execution/zk/tests.rs
warriors/erga/rs/mine-bench/src/texture_ffi/tests.rs
neural/trident/src/ir/tir/stack/tests.rs
cyb/wysm/crates/core/src/memory/tests.rs
cyb/evy/forks/bevy_ecs/src/bundle/tests.rs
warriors/erga/rs/rtable-bench/src/probe/tests.rs
soft3/radio/quinn/quinn-proto/src/range_set/tests.rs
cyb/wysm/crates/core/src/table/tests.rs
warriors/erga/rs/blake-bench/src/batch/tests.rs
soft3/crate/src/chain/net/kernel/tests.rs
soft3/zheng/rs/src/recursion/circuit/tests.rs
soft3/zheng/rs/src/recursion/whir/tests.rs
soft3/zheng/rs/src/recursion/wrap/tests.rs
soft3/cybernode/rs/src/chain/economy/tests.rs
neural/trident/src/neural/data/tir_graph/tests.rs
neural/trident/src/ir/tir/optimize/tests.rs
soft3/zheng/rs/src/execution/tagged/tests.rs
warriors/erga/rs/rtable-bench/src/table/tests.rs
warriors/erga/rs/mine-bench/src/owner/tests.rs
soft3/cybernode/rs/src/chain/pool/tests.rs
soft3/zheng/rs/src/ccs/universal/tests.rs
cyb/wysm/crates/wasmi/src/instance/tests.rs
soft3/zheng/rs/src/execution/disclosed/stream/tests.rs
cyb/evy/forks/naga/src/front/wgsl/tests.rs
cyb/wysm/crates/wasmi/src/module/instantiate/tests.rs
soft3/bbg/rs/src/storage/application/transfer/tests.rs
soft3/zheng/rs/src/execution/disclosed/evaluation/tests.rs
warriors/erga/rs/mine-bench/src/cli/timed/tests.rs
cyb/wysm/crates/wasmi/src/engine/limits/tests.rs
soft3/cybernode/rs/src/chain/net/kernel/tests.rs
soft3/zheng/rs/src/execution/veil/hiding/tests.rs

Graph