use std::num::NonZeroUsize;
use fnv::FnvHashMap;
use crate::packed::traits::PackedElement;
pub mod list;
pub use list::*;
pub trait OneBasedIndex: Copy + Sized {
fn from_zero_based<I: Into<usize>>(ix: I) -> Self;
fn from_record_start<I: Into<usize>>(ix: I, width: usize) -> Self;
fn from_one_based<I: Into<usize>>(ix: I) -> Self;
fn to_zero_based(self) -> Option<usize>;
fn to_record_start(self, width: usize) -> Option<usize>;
fn to_record_ix(self, width: usize, ix: usize) -> Option<usize>;
fn from_vector_value(v: u64) -> Self;
fn to_vector_value(self) -> u64;
fn is_null(&self) -> bool;
fn null() -> Self;
}
impl<T: OneBasedIndex> PackedElement for T {
#[inline]
fn unpack(v: u64) -> Self {
Self::from_vector_value(v)
}
#[inline]
fn pack(self) -> u64 {
self.to_vector_value()
}
}
pub trait RecordIndex: Copy {
const RECORD_WIDTH: usize;
fn from_one_based_ix<I: OneBasedIndex>(ix: I) -> Option<Self>;
fn to_one_based_ix<I: OneBasedIndex>(self) -> I;
fn record_ix(self, offset: usize) -> usize;
#[inline]
fn at_0(self) -> usize {
self.record_ix(0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeRecordId(Option<NonZeroUsize>);
pub(crate) fn removed_id_map_as_u64<T>(
removed: &[T],
max_ix: T,
) -> FnvHashMap<T, T>
where
T: PackedElement + std::hash::Hash + Eq,
{
let mut result: FnvHashMap<T, T> = FnvHashMap::default();
let mut iter = removed.iter().copied();
let mut next_ix = if let Some(ix) = iter.next() {
ix.pack()
} else {
return result;
};
let first_ix = next_ix;
let mut previous = next_ix;
let mut insert_next = |start: u64, end: u64| {
for ix in (start + 1)..end {
result.insert(T::unpack(ix), T::unpack(next_ix));
next_ix += 1;
}
};
for old_ix in iter {
let old_ix = old_ix.pack();
if old_ix - previous > 1 {
insert_next(previous, old_ix);
}
previous = old_ix;
}
insert_next(previous, max_ix.pack() + 1);
for ix in 1..first_ix {
let ix = T::unpack(ix);
result.insert(ix, ix);
}
result
}
#[macro_export]
macro_rules! impl_one_based_index {
($for:ty) => {
impl OneBasedIndex for $for {
#[inline]
fn from_zero_based<I: Into<usize>>(ix: I) -> Self {
Self(NonZeroUsize::new(ix.into() + 1))
}
#[inline]
fn from_record_start<I: Into<usize>>(ix: I, width: usize) -> Self {
Self(NonZeroUsize::new((ix.into() / width) + 1))
}
#[inline]
fn from_one_based<I: Into<usize>>(ix: I) -> Self {
Self(NonZeroUsize::new(ix.into()))
}
#[inline]
fn to_zero_based(self) -> Option<usize> {
self.0.map(|u| u.get() - 1)
}
#[inline]
fn to_record_start(self, width: usize) -> Option<usize> {
self.0.map(|u| (u.get() - 1) * width)
}
#[inline]
fn to_record_ix(self, width: usize, ix: usize) -> Option<usize> {
self.0.map(|u| ((u.get() - 1) * width) + ix)
}
#[inline]
fn from_vector_value(v: u64) -> Self {
Self(NonZeroUsize::new(v as usize))
}
#[inline]
fn to_vector_value(self) -> u64 {
match self.0 {
None => 0,
Some(x) => x.get() as u64,
}
}
#[inline]
fn is_null(&self) -> bool {
self.0.is_none()
}
#[inline]
fn null() -> Self {
Self(None)
}
}
};
}
#[macro_export]
macro_rules! impl_one_based_index_default {
($index:ty) => {
impl Default for $index {
#[inline]
fn default() -> Self {
Self::null()
}
}
};
}
#[macro_export]
macro_rules! impl_space_usage_stack_newtype {
($type:ty) => {
impl succinct::SpaceUsage for $type {
#[inline]
fn is_stack_only() -> bool {
true
}
#[inline]
fn heap_bytes(&self) -> usize {
0
}
}
};
}
impl_one_based_index!(NodeRecordId);
impl_space_usage_stack_newtype!(NodeRecordId);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn removal_id_map_node_record_id() {
let _indices: Vec<NodeRecordId> = (1..=20)
.into_iter()
.map(|x| NodeRecordId::from_one_based(x as usize))
.collect();
let to_remove: Vec<NodeRecordId> = vec![4, 5, 6, 10, 11, 13, 15, 18]
.into_iter()
.map(|x| NodeRecordId::from_one_based(x as usize))
.collect();
let id_map = removed_id_map_as_u64(
&to_remove,
NodeRecordId::from_one_based(20usize),
);
let mut id_map_vec: Vec<(_, _)> =
id_map.iter().map(|(&k, &v)| (k, v)).collect::<Vec<_>>();
id_map_vec.sort();
let expected = vec![1, 2, 3, 7, 8, 9, 12, 14, 16, 17, 19, 20]
.into_iter()
.zip(1..)
.map(|(from, to)| {
(
NodeRecordId::from_one_based(from as usize),
NodeRecordId::from_one_based(to as usize),
)
})
.collect::<Vec<_>>();
assert_eq!(id_map_vec, expected);
}
}