use rand::{RngExt, SeedableRng};
use rand_chacha::ChaCha8Rng;
use ordvec::rank::rank_norm;
use ordvec::{search_asymmetric_byte_lut, Bitmap, Rank, RankQuant, SignBitmap};
const MAX_VECTORS_U32: u32 = 64 * 1024 * 1024;
const MAX_DIM_U32: u32 = u16::MAX as u32;
const MAX_SIGN_BITMAP_DIM_U32: u32 = 1 << 24;
fn make_corpus(seed: u64, n: usize, dim: usize) -> Vec<f32> {
let mut rng = ChaCha8Rng::seed_from_u64(seed);
(0..n * dim).map(|_| rng.random_range(-1.0..1.0)).collect()
}
struct TempFile(std::path::PathBuf);
impl std::ops::Deref for TempFile {
type Target = std::path::Path;
fn deref(&self) -> &std::path::Path {
&self.0
}
}
impl AsRef<std::path::Path> for TempFile {
fn as_ref(&self) -> &std::path::Path {
&self.0
}
}
impl Drop for TempFile {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.0);
}
}
fn forge(suffix: &str, bytes: &[u8]) -> TempFile {
let mut p = std::env::temp_dir();
let nonce = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
p.push(format!(
"ordvec_redteam_delta_{}_{}_{}",
std::process::id(),
nonce,
suffix
));
std::fs::write(&p, bytes).unwrap();
TempFile(p)
}
fn assert_all_loaders_reject(path: &std::path::Path, label: &str) {
let p = path.to_path_buf();
let r1 = std::panic::catch_unwind(|| Rank::load(&p));
assert!(r1.is_ok(), "Rank::load panicked on {label}");
assert!(r1.unwrap().is_err(), "Rank::load accepted {label}");
let r2 = std::panic::catch_unwind(|| RankQuant::load(&p));
assert!(r2.is_ok(), "RankQuant::load panicked on {label}");
assert!(r2.unwrap().is_err(), "RankQuant::load accepted {label}");
let r3 = std::panic::catch_unwind(|| Bitmap::load(&p));
assert!(r3.is_ok(), "Bitmap::load panicked on {label}");
assert!(r3.unwrap().is_err(), "Bitmap::load accepted {label}");
let r4 = std::panic::catch_unwind(|| SignBitmap::load(&p));
assert!(r4.is_ok(), "SignBitmap::load panicked on {label}");
assert!(r4.unwrap().is_err(), "SignBitmap::load accepted {label}");
}
#[test]
fn delta_a1_loader_rejects_huge_declared_nvectors_with_empty_payload() {
let mut v = Vec::new();
v.extend_from_slice(b"TVR1");
v.push(1); v.extend_from_slice(&1024u32.to_le_bytes()); v.extend_from_slice(&MAX_VECTORS_U32.to_le_bytes()); let p = forge("dos_huge_nvectors.tvr", &v);
let start = std::time::Instant::now();
let r = std::panic::catch_unwind(|| Rank::load(&p));
let elapsed = start.elapsed();
std::fs::remove_file(&p).ok();
assert!(r.is_ok(), "Rank::load panicked on the DoS header");
assert!(
r.unwrap().is_err(),
"Rank::load must reject a header declaring a gigabyte payload over an empty file"
);
assert!(
elapsed < std::time::Duration::from_secs(30),
"loader took {elapsed:?} to reject a tiny-file/huge-payload header — \
a size guard must precede allocation",
);
}
#[test]
fn delta_a2_loader_rejects_nvectors_one_past_max() {
let over = MAX_VECTORS_U32 + 1;
let mut v = Vec::new();
v.extend_from_slice(b"TVR1");
v.push(1);
v.extend_from_slice(&1024u32.to_le_bytes()); v.extend_from_slice(&over.to_le_bytes());
let p = forge("nvectors_over_max.tvr", &v);
let r = std::panic::catch_unwind(|| Rank::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok(), "Rank::load panicked on n_vectors = MAX+1");
assert!(
r.unwrap().is_err(),
"Rank::load must reject n_vectors = MAX_VECTORS + 1"
);
}
#[test]
fn delta_a3_loader_rejects_dim_one() {
let mut v = Vec::new();
v.extend_from_slice(b"TVR1");
v.push(1);
v.extend_from_slice(&1u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("dim_one.tvr", &v);
let r = std::panic::catch_unwind(|| Rank::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok(), "Rank::load panicked on dim = 1");
assert!(r.unwrap().is_err(), "Rank::load must reject dim = 1");
}
#[test]
fn delta_a4_all_loaders_reject_dim_zero() {
{
let mut v = Vec::new();
v.extend_from_slice(b"TVR1");
v.push(1);
v.extend_from_slice(&0u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("dim0.tvr", &v);
let r = std::panic::catch_unwind(|| Rank::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok() && r.unwrap().is_err(), "TVR1 dim=0 must Err");
}
{
let mut v = Vec::new();
v.extend_from_slice(b"TVRQ");
v.push(1);
v.push(2); v.extend_from_slice(&0u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("dim0.tvrq", &v);
let r = std::panic::catch_unwind(|| RankQuant::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok() && r.unwrap().is_err(), "TVRQ dim=0 must Err");
}
{
let mut v = Vec::new();
v.extend_from_slice(b"TVBM");
v.push(1);
v.extend_from_slice(&0u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("dim0.tvbm", &v);
let r = std::panic::catch_unwind(|| Bitmap::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok() && r.unwrap().is_err(), "TVBM dim=0 must Err");
}
{
let mut v = Vec::new();
v.extend_from_slice(b"TVSB");
v.push(1);
v.extend_from_slice(&0u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("dim0.tvsb", &v);
let r = std::panic::catch_unwind(|| SignBitmap::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok() && r.unwrap().is_err(), "TVSB dim=0 must Err");
}
}
#[test]
fn delta_a5_tvbm_rejects_dim_over_max_dim() {
let dim = MAX_DIM_U32 + 1; assert_eq!(dim % 64, 0, "test fixture: 65536 must be a multiple of 64");
let mut v = Vec::new();
v.extend_from_slice(b"TVBM");
v.push(1);
v.extend_from_slice(&dim.to_le_bytes());
v.extend_from_slice(&100u32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("bm_dim_over_max.tvbm", &v);
let r = std::panic::catch_unwind(|| Bitmap::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok(), "Bitmap::load panicked on dim > MAX_DIM");
assert!(
r.unwrap().is_err(),
"Bitmap::load must reject dim > MAX_DIM even when dim % 64 == 0"
);
}
#[test]
fn delta_a6_tvsb_rejects_dim_over_max_sign_bitmap_dim() {
let dim = MAX_SIGN_BITMAP_DIM_U32 + 64; assert_eq!(dim % 64, 0, "test fixture: dim must be a multiple of 64");
let mut v = Vec::new();
v.extend_from_slice(b"TVSB");
v.push(1);
v.extend_from_slice(&dim.to_le_bytes());
v.extend_from_slice(&0u32.to_le_bytes()); let p = forge("sb_dim_over_max.tvsb", &v);
let r = std::panic::catch_unwind(|| SignBitmap::load(&p));
std::fs::remove_file(&p).ok();
assert!(
r.is_ok(),
"SignBitmap::load panicked on dim > MAX_SIGN_BITMAP_DIM"
);
assert!(
r.unwrap().is_err(),
"SignBitmap::load must reject dim > MAX_SIGN_BITMAP_DIM"
);
}
#[test]
fn delta_a7_all_loaders_reject_bad_version_bytes() {
for ver in [0u8, 2u8, 255u8] {
let mut v = Vec::new();
v.extend_from_slice(b"TVR1");
v.push(ver);
v.extend_from_slice(&1024u32.to_le_bytes());
v.extend_from_slice(&0u32.to_le_bytes());
let p = forge(&format!("ver_{ver}.tvr"), &v);
let r = std::panic::catch_unwind(|| Rank::load(&p));
std::fs::remove_file(&p).ok();
assert!(r.is_ok(), "Rank::load panicked on version {ver}");
assert!(
r.unwrap().is_err(),
"Rank::load must reject version byte {ver}"
);
}
}
#[test]
fn delta_a8_all_loaders_reject_all_ff_files() {
for len in [13usize, 14, 17, 32, 64, 256, 4096] {
let bytes = vec![0xFFu8; len];
let p = forge(&format!("all_ff_{len}.bin"), &bytes);
assert_all_loaders_reject(&p, &format!("all-0xFF len={len}"));
std::fs::remove_file(&p).ok();
}
}
#[test]
fn delta_b1_rank_symmetric_no_overflow_at_max_dim() {
let dim = u16::MAX as usize; let n = 3;
let corpus = make_corpus(8001, n, dim);
let mut idx = Rank::new(dim);
idx.add(&corpus);
let query = make_corpus(8002, 1, dim);
let norm = rank_norm(dim);
assert!(
norm.is_finite() && norm > 0.0,
"rank_norm({dim}) = {norm} must be finite and positive"
);
let res = idx.search(&query, n);
assert_eq!(res.k, n);
for &s in res.scores_for_query(0) {
assert!(
s.is_finite(),
"symmetric score at dim={dim} must be finite (no i64/i32 wrap, no norm overflow); got {s}"
);
}
let resa = idx.search_asymmetric(&query, n);
for &s in resa.scores_for_query(0) {
assert!(
s.is_finite(),
"asymmetric score at dim={dim} must be finite; got {s}"
);
}
}
#[test]
fn delta_c1_subset_empty_list_and_zero_k() {
let dim = 64;
let n = 32;
let corpus = make_corpus(8101, n, dim);
let mut idx = RankQuant::new(dim, 2);
idx.add(&corpus);
let query = make_corpus(8102, 1, dim);
let (s0, g0) = idx.search_asymmetric_subset(&query, &[], 5);
assert!(
s0.is_empty() && g0.is_empty(),
"empty candidate list must return empty"
);
let (s1, g1) = idx.search_asymmetric_subset(&query, &[0, 1, 2], 0);
assert!(s1.is_empty() && g1.is_empty(), "k == 0 must return empty");
}
#[test]
fn delta_c2_subset_duplicate_ids_returned_per_occurrence() {
let dim = 64;
let n = 32;
let corpus = make_corpus(8201, n, dim);
let mut idx = RankQuant::new(dim, 2);
idx.add(&corpus);
let query = make_corpus(8202, 1, dim);
let cands: Vec<u32> = vec![7, 7, 7];
let (scores, global) = idx.search_asymmetric_subset(&query, &cands, 3);
assert_eq!(scores.len(), 3);
assert_eq!(global.len(), 3);
assert!(
global.iter().all(|&g| g == 7),
"duplicate candidate list [7,7,7] must map every result to global id 7; got {global:?}",
);
let s0 = scores[0];
assert!(s0.is_finite(), "duplicate-candidate score must be finite");
for &s in &scores {
assert_eq!(
s, s0,
"all occurrences of one duplicated doc must score identically"
);
}
let (single, _) = idx.search_asymmetric_subset(&query, &[7], 1);
assert!(
(single[0] - s0).abs() < 1e-6,
"duplicate-occurrence score {s0} must equal the single-occurrence score {}",
single[0],
);
}
#[test]
fn delta_c3_subset_k_greater_than_m_clamps() {
let dim = 64;
let n = 32;
let corpus = make_corpus(8301, n, dim);
let mut idx = RankQuant::new(dim, 2);
idx.add(&corpus);
let query = make_corpus(8302, 1, dim);
let cands: Vec<u32> = vec![3, 3, 9];
let (scores, global) = idx.search_asymmetric_subset(&query, &cands, 10);
assert_eq!(scores.len(), 3, "k must clamp to candidate count m");
assert_eq!(global.len(), 3);
for &g in &global {
assert!(g >= 0, "unexpected sentinel id {g}");
assert!(
cands.contains(&(g as u32)),
"result id {g} not in candidate set {cands:?}"
);
}
}
#[test]
#[should_panic(expected = "candidate id out of range")]
fn delta_c4_subset_dup_plus_oob_still_rejected() {
let dim = 64;
let n = 32;
let corpus = make_corpus(8401, n, dim);
let mut idx = RankQuant::new(dim, 2);
idx.add(&corpus);
let query = make_corpus(8402, 1, dim);
let _ = idx.search_asymmetric_subset(&query, &[5, 999, 5], 3);
}
#[test]
fn delta_d1_search_before_any_add() {
let dim = 128;
let query = make_corpus(8501, 1, dim);
let idx = Rank::new(dim);
let r = idx.search(&query, 5);
assert_eq!(r.k, 0);
assert!(r.scores.is_empty() && r.indices.is_empty());
let idx = RankQuant::new(dim, 2);
assert_eq!(idx.search(&query, 5).k, 0);
assert_eq!(idx.search_asymmetric(&query, 5).k, 0);
let idx = Bitmap::new(dim, 32);
assert_eq!(idx.search(&query, 5).k, 0);
assert!(idx.top_m_candidates(&query, 5).is_empty());
let idx = SignBitmap::new(dim);
assert!(idx.top_m_candidates(&query, 5).is_empty());
let batched = idx.top_m_candidates_batched(&make_corpus(8502, 3, dim), 5);
assert_eq!(batched.len(), 3);
assert!(batched.iter().all(|v| v.is_empty()));
let mut idx = Rank::new(dim);
idx.add(&[]);
assert_eq!(idx.len(), 0);
}
#[test]
fn delta_d2_body_overlap_empty_doc_ids_and_empty_index() {
let dim = 128;
let n_top = 32;
let mut idx = Bitmap::new(dim, n_top);
idx.add(&make_corpus(8601, 8, dim));
let q = make_corpus(8602, 1, dim);
let qb = idx.build_query_bitmap_fp32(&q);
let mut out: Vec<u32> = Vec::new();
idx.body_overlap_scores_subset(&qb, &[], &mut out);
assert!(out.is_empty(), "empty doc_ids must leave out empty");
let empty = Bitmap::new(dim, n_top);
let qb_empty = empty.build_query_bitmap_fp32(&q);
let mut out1 = vec![0u32; 1];
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
empty.body_overlap_scores_subset(&qb_empty, &[0], &mut out1)
}));
assert!(
r.is_err(),
"body_overlap_scores_subset must reject id 0 against an empty index"
);
}
#[test]
fn delta_d3_swap_remove_to_empty_then_readd() {
let dim = 64;
let mut idx = Rank::new(dim);
idx.add(&make_corpus(8701, 1, dim));
let moved = idx.swap_remove(0);
assert_eq!(moved, 0, "removing the sole element reports last index 0");
assert!(idx.is_empty());
let q = make_corpus(8702, 1, dim);
assert_eq!(idx.search(&q, 5).k, 0, "search over emptied index is empty");
let mut idx = Rank::new(dim);
idx.add(&make_corpus(8703, 4, dim)); let last_moved = idx.swap_remove(1); assert_eq!(last_moved, 3);
assert_eq!(idx.len(), 3);
idx.add(&make_corpus(8704, 2, dim)); assert_eq!(idx.len(), 5);
let res = idx.search(&q, 100); let valid = res.indices_for_query(0).iter().filter(|&&i| i >= 0).count();
assert_eq!(
valid, 5,
"all 5 live docs must be returned after remove+readd"
);
}
#[test]
fn delta_d4_large_nq_small_k() {
let dim = 64;
let n = 8;
let mut idx = Rank::new(dim);
idx.add(&make_corpus(8801, n, dim));
let nq = 5000;
let queries = make_corpus(8802, nq, dim);
let res = idx.search(&queries, 2);
assert_eq!(res.nq, nq);
assert_eq!(res.k, 2);
assert_eq!(res.scores.len(), nq * 2);
assert_eq!(res.indices.len(), nq * 2);
for qi in [0usize, nq / 2, nq - 1] {
let valid = res
.indices_for_query(qi)
.iter()
.filter(|&&i| i >= 0)
.count();
assert_eq!(
valid, 2,
"query {qi} must return k=2 live results (n=8 >= 2)"
);
}
}
#[test]
#[should_panic(expected = "byte-LUT path only supports bits")]
fn delta_e1_byte_lut_panics_on_b1_index() {
let dim = 64;
let mut idx = RankQuant::new(dim, 1);
idx.add(&make_corpus(8901, 8, dim));
let query = make_corpus(8902, 1, dim);
let _ = search_asymmetric_byte_lut(&idx, &query, 3);
}