const DENSE_THRESHOLD: usize = 64;
const PARTITION_THRESHOLD: usize = 256;
const OPTIMAL_THRESHOLD: usize = 4096;
use super::basic::EliasFano;
use super::clustered::ClusteredEliasFano;
use super::optimal::OptimalPartitionedEliasFano;
use super::partitioned::PartitionedEliasFano;
const RUN_HEAVY_SPAN_FACTOR: u64 = 2;
#[inline]
fn is_run_heavy(first: u64, last: u64, n: usize) -> bool {
let span = last - first + 1;
span <= RUN_HEAVY_SPAN_FACTOR * n as u64
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum HybridPostingList {
Dense(Vec<u32>),
EliasFano(EliasFano),
Partitioned(PartitionedEliasFano),
Optimal(OptimalPartitionedEliasFano),
Clustered(ClusteredEliasFano),
}
impl HybridPostingList {
pub fn from_sorted(values: &[u32]) -> Self {
let n = values.len();
if n <= DENSE_THRESHOLD {
Self::Dense(values.to_vec())
} else if is_run_heavy(values[0] as u64, values[n - 1] as u64, n) {
Self::Clustered(ClusteredEliasFano::from_sorted(values))
} else if n <= PARTITION_THRESHOLD {
Self::EliasFano(EliasFano::from_sorted(values))
} else if n <= OPTIMAL_THRESHOLD {
Self::Partitioned(PartitionedEliasFano::from_sorted(values))
} else {
Self::Optimal(OptimalPartitionedEliasFano::from_sorted(values))
}
}
pub fn from_sorted_u64(values: &[u64]) -> Self {
let n = values.len();
if n <= DENSE_THRESHOLD {
Self::Dense(values.iter().map(|&v| v as u32).collect())
} else if is_run_heavy(values[0], values[n - 1], n) {
Self::Clustered(ClusteredEliasFano::from_sorted_u64(values))
} else if n <= PARTITION_THRESHOLD {
Self::EliasFano(EliasFano::from_sorted_u64(values))
} else if n <= OPTIMAL_THRESHOLD {
Self::Partitioned(PartitionedEliasFano::from_sorted_u64(values))
} else {
Self::Optimal(OptimalPartitionedEliasFano::from_sorted_u64(values))
}
}
pub fn with_encoding(values: &[u32], encoding: PostingEncoding) -> Self {
match encoding {
PostingEncoding::Dense => Self::Dense(values.to_vec()),
PostingEncoding::EliasFano => Self::EliasFano(EliasFano::from_sorted(values)),
PostingEncoding::Partitioned => {
Self::Partitioned(PartitionedEliasFano::from_sorted(values))
}
PostingEncoding::Optimal => {
Self::Optimal(OptimalPartitionedEliasFano::from_sorted(values))
}
PostingEncoding::Clustered => {
Self::Clustered(ClusteredEliasFano::from_sorted(values))
}
}
}
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Dense(v) => v.len(),
Self::EliasFano(ef) => ef.len(),
Self::Partitioned(pef) => pef.len(),
Self::Optimal(opef) => opef.len(),
Self::Clustered(cef) => cef.len(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn size_bytes(&self) -> usize {
match self {
Self::Dense(v) => v.len() * 4 + 8, Self::EliasFano(ef) => ef.size_bytes() + 8,
Self::Partitioned(pef) => pef.size_bytes() + 8,
Self::Optimal(opef) => opef.size_bytes() + 8,
Self::Clustered(cef) => cef.size_bytes() + 8,
}
}
#[inline]
pub fn bits_per_element(&self) -> f64 {
if self.is_empty() {
return 0.0;
}
(self.size_bytes() * 8) as f64 / self.len() as f64
}
pub fn get(&self, index: usize) -> Option<u64> {
match self {
Self::Dense(v) => v.get(index).map(|&x| x as u64),
Self::EliasFano(ef) => ef.get(index),
Self::Partitioned(pef) => pef.get(index),
Self::Optimal(opef) => opef.get(index),
Self::Clustered(cef) => cef.get(index),
}
}
#[inline]
pub fn next_geq(&self, target: u64) -> Option<(usize, u64)> {
match self {
Self::Dense(v) => {
match v.binary_search(&(target as u32)) {
Ok(i) => Some((i, v[i] as u64)),
Err(i) => {
if i < v.len() {
Some((i, v[i] as u64))
} else {
None
}
}
}
}
Self::EliasFano(ef) => ef.next_geq(target),
Self::Partitioned(pef) => pef.next_geq(target),
Self::Optimal(opef) => opef.next_geq(target),
Self::Clustered(cef) => cef.next_geq(target),
}
}
pub fn intersect_count(&self, other: &Self) -> usize {
if let (Self::Clustered(a), Self::Clustered(b)) = (self, other) {
return a.intersect_count(b);
}
let mut count = 0usize;
let mut probe = 0u64;
while let Some((_, va)) = self.next_geq(probe) {
match other.next_geq(va) {
Some((_, vb)) if vb == va => {
count += 1;
probe = va + 1;
}
Some((_, vb)) => probe = vb,
None => break,
}
}
count
}
pub fn encoding(&self) -> PostingEncoding {
match self {
Self::Dense(_) => PostingEncoding::Dense,
Self::EliasFano(_) => PostingEncoding::EliasFano,
Self::Partitioned(_) => PostingEncoding::Partitioned,
Self::Optimal(_) => PostingEncoding::Optimal,
Self::Clustered(_) => PostingEncoding::Clustered,
}
}
}
impl super::PostingList for HybridPostingList {
fn len(&self) -> usize {
self.len()
}
fn get(&self, index: usize) -> Option<u64> {
self.get(index)
}
fn next_geq(&self, target: u64) -> Option<(usize, u64)> {
self.next_geq(target)
}
fn size_bytes(&self) -> usize {
self.size_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PostingEncoding {
Dense,
EliasFano,
Partitioned,
Optimal,
Clustered,
}