use serde::{Deserialize, Serialize};
use crate::SpaceUsage;
use qwt::bitvector::{BitVector, BitVectorMut};
use qwt::darray::DArray;
use qwt::utils::msb;
use qwt::SelectBin;
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct EliasFano {
high_bits: DArray<false>,
low_bits: BitVector,
low_len: usize,
universe: usize,
num_vals: usize,
}
impl EliasFano {
pub fn from(data: &[usize]) -> EliasFano {
if data.is_empty() {
return EliasFano::default();
}
assert!(
data.windows(2).all(|w| w[0] <= w[1]),
"The sequence must be monotonically increasing."
);
let mut efb = EliasFanoBuilder::new(1 + data.last().unwrap(), data.len());
efb.extend(data.to_vec());
efb.build()
}
#[inline(always)]
pub fn select(&self, k: usize) -> Option<usize> {
if k >= self.num_vals {
return None;
}
Some(
((self.high_bits.select1(k)? - k) << self.low_len)
| if self.low_len > 0 {
unsafe {
self.low_bits
.get_bits_unchecked(k * self.low_len, self.low_len)
as usize
}
} else {
0
},
)
}
pub fn is_empty(&self) -> bool {
self.num_vals == 0
}
pub fn len(&self) -> usize {
self.num_vals
}
pub fn universe(&self) -> usize {
self.universe
}
}
pub struct EliasFanoBuilder {
high_bits: BitVectorMut,
low_bits: BitVectorMut,
universe: usize,
num_vals: usize,
pos: usize,
last: usize,
low_len: usize,
}
impl EliasFanoBuilder {
pub fn new(universe: usize, num_vals: usize) -> Self {
assert!(
num_vals > 0,
"The number of values num_vals must not be zero."
);
let low_len = msb(universe / num_vals) as usize;
Self {
high_bits: BitVectorMut::with_zeros((num_vals + 1) + (universe >> low_len) + 1),
low_bits: BitVectorMut::new(),
universe,
num_vals,
pos: 0,
last: 0,
low_len,
}
}
pub fn push(&mut self, val: usize) {
assert!(
self.last <= val,
"val must be no less than the last inserted one {}, but got {val}.",
self.last
);
assert!(
val < self.universe,
"val must be less than self.universe()={}, but got {val}.",
self.universe
);
assert!(
self.pos < self.num_vals,
"The number of pushed integers must not exceed self.num_vals()={}.",
self.num_vals
);
self.last = val;
let low_mask = (1 << self.low_len) - 1;
if self.low_len != 0 {
self.low_bits
.append_bits(val as u64 & low_mask, self.low_len);
}
self.high_bits.set((val >> self.low_len) + self.pos, true);
self.pos += 1;
}
pub fn extend<I>(&mut self, vals: I)
where
I: IntoIterator<Item = usize>,
{
for x in vals {
self.push(x);
}
}
pub fn build(self) -> EliasFano {
EliasFano {
high_bits: DArray::new(self.high_bits.into()),
low_bits: self.low_bits.into(),
num_vals: self.num_vals,
low_len: self.low_len,
universe: self.universe,
}
}
#[inline(always)]
pub const fn universe(&self) -> usize {
self.universe
}
#[inline(always)]
pub const fn num_vals(&self) -> usize {
self.num_vals
}
}
use qwt::SpaceUsage as QwtSpaceUsage;
impl SpaceUsage for EliasFano {
fn space_usage_byte(&self) -> usize {
self.high_bits.space_usage_byte()
+ self.low_bits.space_usage_byte()
+ QwtSpaceUsage::space_usage_byte(&self.low_len)
+ SpaceUsage::space_usage_byte(&self.universe)
+ SpaceUsage::space_usage_byte(&self.num_vals)
}
}
#[cfg(test)]
mod tests;