use std::collections::BTreeMap;
use std::ops::Range;
use horon_engine::constants::SEMANTIC_INDEX_MIN_NODES;
use horon_engine::Store;
use g_math::fixed_point::FixedPoint;
fn coords(vals: &[i32]) -> Vec<u8> {
let mut out = vec![0u8; 16 * 16];
for &v in vals {
out.extend_from_slice(&FixedPoint::from_int(v).raw().to_le_bytes());
}
out
}
struct Lcg(u64);
impl Lcg {
fn next(&mut self) -> i32 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
((self.0 >> 33) % 201) as i32 - 100 }
}
fn build_store(n: usize, dims: usize, seed: u64) -> (Store, BTreeMap<String, Vec<u8>>) {
let store = Store::new();
store.put_data_only("/n", b"parent").unwrap();
let mut lcg = Lcg(seed);
let mut written = BTreeMap::new();
for i in 0..n {
let key = format!("/n/{:05}", i);
store.put_data_only(&key, b"x").unwrap();
let d = if i % 3 == 0 { dims - 1 } else { dims };
let vals: Vec<i32> = (0..d).map(|_| lcg.next()).collect();
let c = coords(&vals);
store.set_semantic(&key, c.clone()).unwrap();
written.insert(key, c);
}
(store, written)
}
fn reference_knn(
written: &BTreeMap<String, Vec<u8>>,
query: &[u8],
k: usize,
range: Range<usize>,
) -> Vec<(String, g_math::fixed_point::FixedPoint)> {
let mut all: Vec<(String, g_math::fixed_point::FixedPoint)> = written
.iter()
.map(|(key, c)| (key.clone(), Store::semantic_distance(query, c, range.clone())))
.collect();
all.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap().then_with(|| a.0.cmp(&b.0)));
all.truncate(k);
all
}
#[test]
fn nearest_semantic_matches_reference_on_both_paths() {
for &(n, seed) in &[
(60usize, 11u64), (SEMANTIC_INDEX_MIN_NODES + 60, 4242u64), ] {
let (store, written) = build_store(n, 4, seed);
let mut lcg = Lcg(seed ^ 0x5eed);
for ref_range in [16..20usize, 16..18, 17..19] {
for _ in 0..5 {
let qvals: Vec<i32> = (0..4).map(|_| lcg.next()).collect();
let query = coords(&qvals);
for &k in &[1usize, 7, 25] {
let got = store
.nearest_semantic(&query, k, ref_range.clone())
.unwrap();
let want = reference_knn(&written, &query, k, ref_range.clone());
assert_eq!(
got, want,
"mismatch: n={} slice={:?} k={}",
n, ref_range, k
);
}
}
}
}
}
#[test]
fn index_invalidates_on_write_and_delete() {
let n = SEMANTIC_INDEX_MIN_NODES + 20;
let (store, _) = build_store(n, 4, 99);
let query = coords(&[500, 500, 500, 500]); let range = 16..20usize;
let before = store.nearest_semantic(&query, 3, range.clone()).unwrap();
assert_eq!(before.len(), 3);
assert!(before[0].1.to_f64() > 0.0, "no node sits at the query point yet");
let moved = "/n/00007";
store.set_semantic(moved, query.clone()).unwrap();
let after = store.nearest_semantic(&query, 3, range.clone()).unwrap();
assert_eq!(after[0].0, moved, "stale index: write not visible");
assert_eq!(after[0].1, g_math::fixed_point::FixedPoint::from_int(0));
store.remove(moved).unwrap();
let gone = store.nearest_semantic(&query, 3, range.clone()).unwrap();
assert!(
gone.iter().all(|(key, _)| key != moved),
"deleted node still served by the index: {:?}",
gone
);
}
#[test]
fn ties_break_deterministically_by_key() {
let n = SEMANTIC_INDEX_MIN_NODES + 10;
let (store, _) = build_store(n, 4, 7);
let spot = coords(&[77, 77, 77, 77]);
let tied = ["/n/00050", "/n/00010", "/n/00030", "/n/00020", "/n/00040"];
for key in &tied {
store.set_semantic(key, spot.clone()).unwrap();
}
let r1 = store.nearest_semantic(&spot, 5, 16..20).unwrap();
let r2 = store.nearest_semantic(&spot, 5, 16..20).unwrap();
assert_eq!(r1, r2, "repeated identical queries must be byte-identical");
let keys: Vec<&str> = r1.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(
keys,
vec!["/n/00010", "/n/00020", "/n/00030", "/n/00040", "/n/00050"],
"distance-zero ties must be sorted by key"
);
assert!(r1.iter().all(|(_, d)| *d == g_math::fixed_point::FixedPoint::from_int(0)));
}