use crate::bits::bit_vec::{BitVec, BitVecU};
use crate::bits::test_unaligned;
use crate::dict::EliasFanoBuilder;
use crate::dict::elias_fano::EfSeq;
use crate::traits::iter::{IntoIteratorFrom, UncheckedIterator};
use crate::traits::{Backend, BitVecValueOps, TryIntoUnaligned, Word};
use crate::utils::PrimitiveUnsignedExt;
use mem_dbg::*;
use value_traits::slices::SliceByValue;
#[derive(Debug, Clone, MemSize, MemDbg)]
#[cfg_attr(
feature = "epserde",
derive(epserde::Epserde),
epserde(bound(
deser = "for<'a> <B as epserde::deser::DeserInner>::DeserType<'a>: Backend<Word = B::Word>"
))
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CompIntList<B: Backend = BitVec<Box<[usize]>>, D = EfSeq<u64>> {
n: usize,
min: B::Word,
delimiters: D,
data: B,
all_widths_unaligned: bool,
}
impl<V: Word> CompIntList<BitVec<Box<[V]>>> {
#[must_use]
pub fn new<I: ?Sized>(min: V, values: &I) -> Self
where
for<'a> &'a I: IntoIterator<Item = &'a V>,
{
let mut n = 0;
let mut total_bits = 0u64;
let mut all_widths_unaligned = true;
for &v in values {
assert!(v >= min, "CompIntList: value must be >= the lower bound");
let offset = v - min + V::ONE;
let width = (offset.bit_len() - 1) as usize;
total_bits += width as u64;
if !test_unaligned!(V, width) {
all_widths_unaligned = false;
}
n += 1;
}
let mut efb = EliasFanoBuilder::new(n + 1, total_bits);
let mut pos = 0;
unsafe { efb.push_unchecked(0) };
let mut data: BitVec<Vec<V>> = BitVec::new(0);
data.reserve(total_bits as usize);
for &v in values {
let offset = v - min + V::ONE;
let width = (offset.bit_len() - 1) as usize;
let bits = offset ^ (V::ONE << width);
data.append_value(bits, width);
pos += width as u64;
unsafe { efb.push_unchecked(pos) };
}
let delimiters = efb.build_with_seq();
let data = data.into_padded();
CompIntList {
n,
min,
delimiters,
data,
all_widths_unaligned,
}
}
}
impl<B: Backend<Word: Word> + BitVecValueOps<B::Word>, D: SliceByValue<Value = u64>>
CompIntList<B, D>
{
pub unsafe fn map_delimiters<F, D2>(self, func: F) -> CompIntList<B, D2>
where
F: FnOnce(D) -> D2,
D2: SliceByValue<Value = u64>,
{
CompIntList {
n: self.n,
min: self.min,
delimiters: func(self.delimiters),
data: self.data,
all_widths_unaligned: self.all_widths_unaligned,
}
}
pub unsafe fn map_data<F, B2>(self, func: F) -> CompIntList<B2, D>
where
F: FnOnce(B) -> B2,
B2: Backend<Word = B::Word> + BitVecValueOps<B2::Word>,
{
CompIntList {
n: self.n,
min: self.min,
delimiters: self.delimiters,
data: func(self.data),
all_widths_unaligned: self.all_widths_unaligned,
}
}
pub fn into_inner(self) -> D {
self.delimiters
}
}
impl<B: Backend<Word: Word> + BitVecValueOps<B::Word>, D: SliceByValue<Value = u64>> SliceByValue
for CompIntList<B, D>
where
for<'a> &'a D: IntoIteratorFrom,
for<'a> <&'a D as IntoIteratorFrom>::IntoIterFrom: UncheckedIterator<Item = u64>,
{
type Value = B::Word;
#[inline(always)]
fn len(&self) -> usize {
self.n
}
#[inline]
unsafe fn get_value_unchecked(&self, index: usize) -> B::Word {
let mut iter = (&self.delimiters).into_iter_from(index);
let start = unsafe { iter.next_unchecked() } as usize;
let end = unsafe { iter.next_unchecked() } as usize;
let width = end - start;
let bits = unsafe { self.data.get_value_unchecked(start, width) };
let stored = (B::Word::ONE << width) | bits;
(stored - B::Word::ONE) + self.min
}
}
impl<V: Word, D: TryIntoUnaligned + SliceByValue<Value = u64>> TryIntoUnaligned
for CompIntList<BitVec<Box<[V]>>, D>
where
D::Unaligned: SliceByValue<Value = u64>,
{
type Unaligned = CompIntList<BitVecU<Box<[V]>>, D::Unaligned>;
fn try_into_unaligned(
self,
) -> Result<Self::Unaligned, crate::traits::UnalignedConversionError> {
if !self.all_widths_unaligned {
return Err(crate::traits::UnalignedConversionError(
"CompIntList contains values whose bit widths do not satisfy the \
constraints for unaligned reads"
.to_string(),
));
}
Ok(CompIntList {
n: self.n,
min: self.min,
delimiters: self.delimiters.try_into_unaligned()?,
data: self.data.try_into_unaligned()?,
all_widths_unaligned: true,
})
}
}
impl<V: Word, D, D2: SliceByValue<Value = u64>> From<CompIntList<BitVecU<Box<[V]>>, D>>
for CompIntList<BitVec<Box<[V]>>, D2>
where
D: Into<D2>,
{
fn from(c: CompIntList<BitVecU<Box<[V]>>, D>) -> Self {
CompIntList {
n: c.n,
min: c.min,
delimiters: c.delimiters.into(),
data: c.data.into(),
all_widths_unaligned: c.all_widths_unaligned,
}
}
}