use std::borrow::Cow;
use bitvec::order::Lsb0;
use bitvec::slice::BitSlice;
use crate::{Analyze, StatType};
#[derive(Clone, PartialEq, Debug)]
pub enum Presence<'a, T: Copy> {
AllPresent(Vec<T>),
Bits {
bits: Cow<'a, BitSlice<u8, Lsb0>>,
values: Vec<T>,
},
}
impl<T: Copy> Presence<'_, T> {
#[inline]
#[must_use]
pub fn is_present(&self, idx: usize) -> bool {
match self {
Self::AllPresent(values) => idx < values.len(),
Self::Bits { bits, .. } => bits.get(idx).as_deref().copied().unwrap_or(false),
}
}
#[inline]
#[must_use]
pub fn feature_count(&self) -> usize {
match self {
Self::AllPresent(values) => values.len(),
Self::Bits { bits, .. } => bits.len(),
}
}
#[inline]
#[must_use]
pub fn dense_values(&self) -> &[T] {
match self {
Self::AllPresent(values) | Self::Bits { values, .. } => values,
}
}
#[inline]
#[must_use]
pub fn get(&self, idx: usize) -> Option<T> {
match self {
Self::AllPresent(values) => values.get(idx).copied(),
Self::Bits { bits, values } => {
if *bits.get(idx)? {
Some(values[bits[..idx].count_ones()])
} else {
None
}
}
}
}
#[must_use]
pub fn materialize(&self) -> Vec<Option<T>> {
self.iter_optional().collect()
}
#[must_use]
pub fn iter_optional(&self) -> PresenceOptIter<'_, T> {
match self {
Self::AllPresent(values) => PresenceOptIter {
bits: None,
values,
feat_idx: 0,
dense_idx: 0,
},
Self::Bits { bits, values } => PresenceOptIter {
bits: Some(bits),
values,
feat_idx: 0,
dense_idx: 0,
},
}
}
}
impl<T: Analyze + Copy> Analyze for Presence<'_, T> {
fn collect_statistic(&self, stat: StatType) -> usize {
if stat == StatType::DecodedMetaSize {
0
} else {
let bits_size = match self {
Self::AllPresent(_) => 0,
Self::Bits { bits, .. } => bits.len().div_ceil(8),
};
bits_size + self.dense_values().collect_statistic(stat)
}
}
}
pub struct PresenceOptIter<'p, T: Copy> {
bits: Option<&'p BitSlice<u8, Lsb0>>,
values: &'p [T],
feat_idx: usize,
dense_idx: usize,
}
impl<T: Copy> Iterator for PresenceOptIter<'_, T> {
type Item = Option<T>;
fn next(&mut self) -> Option<Option<T>> {
match self.bits {
None => {
let v = self.values.get(self.feat_idx).copied()?;
self.feat_idx += 1;
Some(Some(v))
}
Some(bits) => {
if self.feat_idx >= bits.len() {
return None;
}
let present = bits[self.feat_idx];
self.feat_idx += 1;
if present {
let v = self.values[self.dense_idx];
self.dense_idx += 1;
Some(Some(v))
} else {
Some(None)
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = match self.bits {
None => self.values.len().saturating_sub(self.feat_idx),
Some(bits) => bits.len().saturating_sub(self.feat_idx),
};
(remaining, Some(remaining))
}
}
impl<T: Copy> ExactSizeIterator for PresenceOptIter<'_, T> {}