#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Selection {
indices: Vec<u32>,
}
impl Selection {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self { indices: Vec::with_capacity(capacity) }
}
#[must_use]
pub fn identity(len: usize) -> Self {
Self { indices: (0..len as u32).collect() }
}
pub fn from_predicate(len: usize, keep: impl Fn(usize) -> bool) -> Self {
let mut selection = Self::with_capacity(len);
for index in 0..len {
if keep(index) {
selection.push(index);
}
}
selection
}
pub fn push(&mut self, index: usize) {
self.indices.push(u32::try_from(index).expect("a position past four billion"));
}
#[must_use]
pub fn len(&self) -> usize {
self.indices.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.indices.is_empty()
}
#[must_use]
pub fn get(&self, slot: usize) -> Option<usize> {
self.indices.get(slot).map(|&index| index as usize)
}
#[must_use]
pub fn indices(&self) -> &[u32] {
&self.indices
}
pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
self.indices.iter().map(|&index| index as usize)
}
#[must_use]
pub fn selectivity(&self, len: usize) -> f64 {
if len == 0 { 1.0 } else { self.len() as f64 / len as f64 }
}
#[must_use]
pub fn compose(&self, earlier: &Self) -> Self {
let indices =
self.indices.iter().filter_map(|&slot| earlier.indices.get(slot as usize).copied());
Self { indices: indices.collect() }
}
}
#[cfg(test)]
mod tests {
use super::Selection;
#[test]
fn nothing_selected_is_not_the_same_as_no_selection() {
let none = Selection::empty();
assert_eq!(none.len(), 0);
assert!(none.is_empty());
assert_eq!(none.selectivity(1024), 0.0);
}
#[test]
fn a_predicate_selection_keeps_the_positions_in_order() {
let selection = Selection::from_predicate(10, |i| i % 3 == 0);
assert_eq!(selection.indices(), &[0, 3, 6, 9]);
assert_eq!(selection.get(2), Some(6));
assert_eq!(selection.get(4), None);
assert!((selection.selectivity(10) - 0.4).abs() < f64::EPSILON);
}
#[test]
fn composing_two_filters_indexes_all_the_way_back() {
let first = Selection::from_predicate(16, |i| i % 2 == 0);
let second = Selection::from_predicate(first.len(), |i| i % 3 == 0);
assert_eq!(second.compose(&first).indices(), &[0, 6, 12]);
}
#[test]
fn composing_with_the_identity_changes_nothing() {
let selection = Selection::from_predicate(8, |i| i > 4);
assert_eq!(selection.compose(&Selection::identity(8)), selection);
}
#[test]
fn selectivity_over_nothing_is_one_rather_than_a_division_by_zero() {
assert!((Selection::empty().selectivity(0) - 1.0).abs() < f64::EPSILON);
}
}