#![allow(dead_code)]
use crate::vbyte;
pub struct RetainedEdgesBuilder {
blob: Vec<u8>,
index: Vec<(u32, u32, u32)>,
}
pub struct RetainedEdges {
blob: Vec<u8>,
index: Vec<(u32, u32, u32)>,
}
impl RetainedEdgesBuilder {
pub fn new() -> Self {
RetainedEdgesBuilder {
blob: Vec::new(),
index: Vec::new(),
}
}
pub fn push_row(&mut self, from: u32, sorted_targets: &[u32]) {
debug_assert!(
sorted_targets.windows(2).all(|w| w[0] <= w[1]),
"push_row targets must be sorted ascending, got {sorted_targets:?}"
);
let offset = self.blob.len() as u32;
vbyte::encode_delta(sorted_targets, &mut self.blob);
self.index.push((from, offset, sorted_targets.len() as u32));
}
pub fn finish(mut self) -> RetainedEdges {
self.index.sort_by_key(|&(from, _, _)| from);
RetainedEdges {
blob: self.blob,
index: self.index,
}
}
}
impl Default for RetainedEdgesBuilder {
fn default() -> Self {
Self::new()
}
}
impl RetainedEdges {
pub fn targets_of(&self, from: u32) -> Vec<u32> {
match self.index.binary_search_by_key(&from, |&(f, _, _)| f) {
Ok(i) => {
let (_, offset, count) = self.index[i];
vbyte::decode_delta(&self.blob[offset as usize..], count as usize)
}
Err(_) => Vec::new(),
}
}
#[allow(clippy::wrong_self_convention)] pub fn from_rows(&self) -> Vec<u32> {
self.index.iter().map(|&(from, _, _)| from).collect()
}
pub fn compressed_len(&self) -> usize {
self.blob.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn retained_edges_roundtrip_targets() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(0, &[3, 7, 9]);
b.push_row(5, &[1, 2, 100]);
let re = b.finish();
assert_eq!(re.targets_of(0), vec![3, 7, 9]);
assert_eq!(re.targets_of(5), vec![1, 2, 100]);
assert_eq!(re.targets_of(1), Vec::<u32>::new());
assert_eq!(re.from_rows(), vec![0, 5]);
}
#[test]
fn retained_edges_stays_compressed() {
let mut b = RetainedEdgesBuilder::new();
for i in 0..1000u32 {
let base = i * 10;
b.push_row(i, &[base, base + 1, base + 2]);
}
let re = b.finish();
assert!(
re.compressed_len() < 1000 * 3 * 4,
"delta+vbyte should beat flat u32: got {}",
re.compressed_len()
);
}
#[test]
fn empty_row() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(2, &[]);
let re = b.finish();
assert_eq!(re.targets_of(2), Vec::<u32>::new());
assert!(re.from_rows().contains(&2));
}
#[test]
fn single_target_row() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(9, &[42]);
let re = b.finish();
assert_eq!(re.targets_of(9), vec![42]);
}
#[test]
fn rows_pushed_out_of_order_sorted_at_finish() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(5, &[50, 51]);
b.push_row(1, &[10, 11, 12]);
b.push_row(3, &[30]);
let re = b.finish();
assert_eq!(re.from_rows(), vec![1, 3, 5]);
assert_eq!(re.targets_of(1), vec![10, 11, 12]);
assert_eq!(re.targets_of(3), vec![30]);
assert_eq!(re.targets_of(5), vec![50, 51]);
}
#[test]
fn large_target_values() {
let mut b = RetainedEdgesBuilder::new();
let targets = [1u32, 1000, u32::MAX - 1];
b.push_row(7, &targets);
let re = b.finish();
assert_eq!(re.targets_of(7), targets.to_vec());
}
#[test]
fn absent_from_returns_empty() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(0, &[1, 2, 3]);
b.push_row(10, &[4, 5]);
let re = b.finish();
assert_eq!(re.targets_of(999), Vec::<u32>::new());
assert_eq!(re.targets_of(5), Vec::<u32>::new());
}
#[test]
fn compressed_len_is_blob_len() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(0, &[1, 2, 3]);
b.push_row(1, &[4, 5, 6]);
let re = b.finish();
assert!(re.compressed_len() > 0);
let mut e = RetainedEdgesBuilder::new();
e.push_row(0, &[]);
e.push_row(1, &[]);
let empty = e.finish();
assert_eq!(empty.compressed_len(), 0);
assert_eq!(empty.from_rows(), vec![0, 1]);
}
#[test]
fn empty_builder_has_no_rows() {
let re = RetainedEdgesBuilder::new().finish();
assert_eq!(re.from_rows(), Vec::<u32>::new());
assert_eq!(re.compressed_len(), 0);
assert_eq!(re.targets_of(0), Vec::<u32>::new());
}
#[test]
fn multi_row_offset_integrity() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(0, &[1]);
b.push_row(1, &[100, 200, 300, 400]);
b.push_row(2, &[]);
b.push_row(3, &[7, 8]);
let re = b.finish();
assert_eq!(re.targets_of(0), vec![1]);
assert_eq!(re.targets_of(1), vec![100, 200, 300, 400]);
assert_eq!(re.targets_of(2), Vec::<u32>::new());
assert_eq!(re.targets_of(3), vec![7, 8]);
}
#[test]
fn duplicate_from_rows_both_retained() {
let mut b = RetainedEdgesBuilder::new();
b.push_row(4, &[1, 2]);
b.push_row(4, &[9]);
let re = b.finish();
let fours = re.from_rows().iter().filter(|&&f| f == 4).count();
assert_eq!(fours, 2, "duplicate from rows must both be retained");
}
}