use crate::column::{BitVec, Ones};
use smallvec::SmallVec;
use std::ops::Range;
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum AtomSelection {
#[default]
Empty,
All(u32),
Range(Range<u32>),
Ranges(SmallVec<[Range<u32>; 4]>),
Sparse(Vec<u32>),
Dense(BitVec),
}
const DENSE_FRACTION: u32 = 16;
impl AtomSelection {
#[must_use]
pub fn range(atoms: Range<u32>) -> Self {
if atoms.start >= atoms.end {
Self::Empty
} else {
Self::Range(atoms)
}
}
#[must_use]
pub fn from_runs(runs: impl IntoIterator<Item = Range<u32>>) -> Self {
let mut ordered: Vec<Range<u32>> =
runs.into_iter().filter(|run| run.start < run.end).collect();
ordered.sort_by_key(|run| run.start);
let mut merged = SmallVec::<[Range<u32>; 4]>::new();
for run in ordered {
match merged.last_mut() {
Some(last) if run.start <= last.end => last.end = last.end.max(run.end),
_ => merged.push(run),
}
}
match merged.len() {
0 => Self::Empty,
1 => merged.pop().map_or(Self::Empty, Self::Range),
_ => Self::Ranges(merged),
}
}
#[must_use]
pub fn from_sorted(positions: Vec<u32>) -> Self {
let (Some(first), Some(last)) = (positions.first().copied(), positions.last().copied())
else {
return Self::Empty;
};
let span = u64::from(last - first) + 1;
if last < u32::MAX && span == positions.len() as u64 {
return Self::Range(first..last + 1);
}
if let Some(runs) = inline_runs(&positions) {
let covered: u64 = runs.iter().map(|run| u64::from(run.end - run.start)).sum();
if covered == positions.len() as u64 && runs.len() * 2 < positions.len() {
return Self::Ranges(runs);
}
}
if let Some(mask_len) = last.checked_add(1)
&& positions.len() as u64 * u64::from(DENSE_FRACTION) >= u64::from(mask_len)
{
return Self::Dense(mask_from_positions(mask_len, &positions));
}
Self::Sparse(positions)
}
#[must_use]
pub fn len(&self) -> u64 {
selection_len(self)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn contains(&self, atom: u32) -> bool {
match self {
Self::Empty => false,
Self::All(count) => atom < *count,
Self::Range(run) => run.contains(&atom),
Self::Ranges(runs) => {
let candidate = runs.partition_point(|run| run.end <= atom);
runs.get(candidate).is_some_and(|run| run.contains(&atom))
}
Self::Sparse(positions) => positions.binary_search(&atom).is_ok(),
Self::Dense(mask) => mask.test(atom),
}
}
#[must_use]
pub fn position_bound(&self) -> u32 {
match self {
Self::Empty => 0,
Self::All(count) => *count,
Self::Range(run) => run.end,
Self::Ranges(runs) => match runs.last() {
Some(run) => run.end,
None => 0,
},
Self::Sparse(positions) => match positions.last() {
Some(last) => last.saturating_add(1),
None => 0,
},
Self::Dense(mask) => mask.len(),
}
}
#[must_use]
pub fn iter(&self) -> SelectionIter<'_> {
match self {
Self::Empty => SelectionIter::Run(0..0),
Self::All(count) => SelectionIter::Run(0..*count),
Self::Range(run) => SelectionIter::Run(run.start..run.end),
Self::Ranges(runs) => SelectionIter::Runs {
runs,
next: 0,
current: 0..0,
},
Self::Sparse(positions) => SelectionIter::Sparse(positions.iter()),
Self::Dense(mask) => SelectionIter::Dense(mask.ones()),
}
}
}
#[derive(Clone, Debug)]
pub enum SelectionIter<'a> {
Run(Range<u32>),
Runs {
runs: &'a [Range<u32>],
next: usize,
current: Range<u32>,
},
Sparse(std::slice::Iter<'a, u32>),
Dense(Ones<'a>),
}
impl Iterator for SelectionIter<'_> {
type Item = u32;
fn next(&mut self) -> Option<u32> {
match self {
Self::Run(run) => run.next(),
Self::Runs {
runs,
next,
current,
} => loop {
if let Some(position) = current.next() {
return Some(position);
}
let run = runs.get(*next)?;
*next = next.checked_add(1)?;
*current = run.start..run.end;
},
Self::Sparse(positions) => positions.next().copied(),
Self::Dense(ones) => ones.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Self::Run(run) => run.size_hint(),
Self::Runs {
runs,
next,
current,
} => {
let remaining = match runs.get(*next..) {
Some(tail) => tail.iter().map(ExactSizeIterator::len).sum::<usize>(),
None => 0,
};
let total = current.len() + remaining;
(total, Some(total))
}
Self::Sparse(positions) => positions.size_hint(),
Self::Dense(ones) => ones.size_hint(),
}
}
}
impl ExactSizeIterator for SelectionIter<'_> {}
impl std::iter::FusedIterator for SelectionIter<'_> {}
impl FromIterator<u32> for AtomSelection {
fn from_iter<T: IntoIterator<Item = u32>>(iter: T) -> Self {
let mut positions: Vec<u32> = iter.into_iter().collect();
positions.sort_unstable();
positions.dedup();
Self::from_sorted(positions)
}
}
impl<'a> IntoIterator for &'a AtomSelection {
type Item = u32;
type IntoIter = SelectionIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn inline_runs(positions: &[u32]) -> Option<SmallVec<[Range<u32>; 4]>> {
let mut runs = SmallVec::<[Range<u32>; 4]>::new();
for position in positions.iter().copied() {
let end = position.checked_add(1)?;
if let Some(run) = runs.last_mut()
&& run.end == position
{
run.end = end;
continue;
}
if runs.len() == runs.inline_size() {
return None;
}
runs.push(position..end);
}
Some(runs)
}
fn mask_from_positions(len: u32, positions: &[u32]) -> BitVec {
let mut mask = BitVec::repeat(false, len);
for position in positions {
mask.set(*position, true);
}
mask
}
fn selection_len(selection: &AtomSelection) -> u64 {
match selection {
AtomSelection::Empty => 0,
AtomSelection::All(count) => u64::from(*count),
AtomSelection::Range(run) => u64::from(run.end - run.start),
AtomSelection::Ranges(runs) => runs.iter().map(|run| u64::from(run.end - run.start)).sum(),
AtomSelection::Sparse(positions) => positions.len() as u64,
AtomSelection::Dense(mask) => u64::from(mask.count_ones()),
}
}