use super::super::super::distance::CpuDistance;
use super::*;
use crate::distance::DistanceMetric;
fn empty_hnsw() -> NativeHnsw<CpuDistance> {
let dist = CpuDistance::new(DistanceMetric::Euclidean);
NativeHnsw::new(dist, 16, 200, 0)
}
#[test]
fn single_probe_at_exactly_10k() {
let hnsw = empty_hnsw();
assert_eq!(hnsw.adaptive_num_probes(10_000, 160, 10), 1);
}
#[test]
fn single_probe_below_10k() {
let hnsw = empty_hnsw();
assert_eq!(hnsw.adaptive_num_probes(9_999, 160, 10), 1);
}
#[test]
fn two_probes_above_10k_balanced() {
let hnsw = empty_hnsw();
assert_eq!(hnsw.adaptive_num_probes(10_001, 160, 10), 2);
}
#[test]
fn single_probe_for_small_ef() {
let hnsw = empty_hnsw();
assert_eq!(hnsw.adaptive_num_probes(100_000, 40, 10), 1);
}
#[test]
fn four_probes_for_large_ef_at_scale() {
let hnsw = empty_hnsw();
assert_eq!(hnsw.adaptive_num_probes(50_000, 1024, 10), 4);
}
#[test]
fn probe_rng_no_zero_and_no_short_cycle() {
let mut seen = std::collections::HashSet::new();
for _ in 0..64 {
let v = NativeHnsw::<CpuDistance>::next_probe_rng();
assert_ne!(v, 0, "XORshift64 must never produce 0");
seen.insert(v);
}
assert_eq!(
seen.len(),
64,
"64 consecutive calls should all be distinct"
);
}
#[test]
fn probe_rng_threads_diverge() {
use std::sync::{Arc, Barrier};
use std::thread;
let barrier = Arc::new(Barrier::new(2));
let b1 = Arc::clone(&barrier);
let b2 = Arc::clone(&barrier);
let t1 = thread::spawn(move || {
b1.wait();
NativeHnsw::<CpuDistance>::next_probe_rng()
});
let t2 = thread::spawn(move || {
b2.wait();
NativeHnsw::<CpuDistance>::next_probe_rng()
});
let v1 = t1.join().expect("thread 1 panicked");
let v2 = t2.join().expect("thread 2 panicked");
assert_ne!(
v1, v2,
"different threads must start with different RNG values"
);
}