use crate::bits::Bits;
use crate::isa::Isa;
use crate::word::Word;
pub trait Words {
type Word: Word;
#[must_use]
fn bit(&self, i: usize) -> bool;
fn set_bit(&mut self, i: usize);
fn clear_bit(&mut self, i: usize);
#[must_use]
#[doc(alias("popcount"))]
fn count_ones(&self) -> usize;
#[must_use]
#[doc(alias("rank1"))]
fn rank(&self, i: usize) -> usize;
#[must_use]
#[doc(alias("select1"))]
fn select(&self, k: usize) -> Option<usize>;
#[must_use]
fn next_set_from(&self, i: usize) -> Option<usize>;
#[must_use]
fn find_run(&self, k: u32) -> Option<usize>;
fn positions(&self) -> impl Iterator<Item = usize> + '_;
fn for_each_position(&self, f: impl FnMut(usize));
#[must_use]
fn count_ones_in<I: Isa>(&self, isa: I) -> usize;
#[must_use]
fn rank_in<I: Isa>(&self, i: usize, isa: I) -> usize;
#[must_use]
fn select_in<I: Isa>(&self, k: usize, isa: I) -> Option<usize>;
fn for_each_position_in<I: Isa>(&self, isa: I, f: impl FnMut(usize));
}
impl<W: Word> Words for [W] {
type Word = W;
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn bit(&self, i: usize) -> bool {
let bits = W::BITS as usize;
self[i / bits].bit((i % bits) as u32)
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn set_bit(&mut self, i: usize) {
let bits = W::BITS as usize;
let w = &mut self[i / bits];
*w = w.or(W::ONE.shl((i % bits) as u32));
}
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn clear_bit(&mut self, i: usize) {
let bits = W::BITS as usize;
let w = &mut self[i / bits];
*w = w.and(W::ONE.shl((i % bits) as u32).not());
}
#[inline]
fn count_ones(&self) -> usize {
crate::dispatch!(|cpu| self.count_ones_in(cpu))
}
#[inline]
fn rank(&self, i: usize) -> usize {
crate::dispatch!(|cpu| self.rank_in(i, cpu))
}
#[inline]
fn select(&self, k: usize) -> Option<usize> {
crate::dispatch!(|cpu| self.select_in(k, cpu))
}
#[inline]
fn for_each_position(&self, f: impl FnMut(usize)) {
crate::dispatch!(|cpu| self.for_each_position_in(cpu, f));
}
#[inline]
fn count_ones_in<I: Isa>(&self, isa: I) -> usize {
isa.run(|_| self.iter().map(|w| w.count_ones() as usize).sum())
}
#[inline]
fn rank_in<I: Isa>(&self, i: usize, isa: I) -> usize {
isa.run(|_| {
let bits = W::BITS as usize;
let (full, rest) = (i / bits, i % bits);
let head: usize = self
.iter()
.take(full)
.map(|w| w.count_ones() as usize)
.sum();
#[allow(clippy::cast_possible_truncation)]
let tail = self.get(full).map_or(0, |w| w.rank(rest as u32) as usize);
head + tail
})
}
#[inline]
fn select_in<I: Isa>(&self, mut k: usize, isa: I) -> Option<usize> {
isa.run(|cpu| {
for (wi, &w) in self.iter().enumerate() {
let n = w.count_ones() as usize;
if k < n {
#[allow(clippy::cast_possible_truncation)]
let inner = w.select_lowest_in(k as u32, cpu) as usize;
return Some(wi * W::BITS as usize + inner);
}
k -= n;
}
None
})
}
#[inline]
fn for_each_position_in<I: Isa>(&self, isa: I, mut f: impl FnMut(usize)) {
isa.run(|_| {
let bits = W::BITS as usize;
for (wi, &w) in self.iter().enumerate() {
let mut x = w;
while !x.is_zero() {
f(wi * bits + x.trailing_zeros() as usize);
x = x.clear_lowest_set();
}
}
});
}
#[inline]
fn next_set_from(&self, i: usize) -> Option<usize> {
let bits = W::BITS as usize;
let (wi, bit) = (i / bits, i % bits);
let first = self.get(wi)?;
#[allow(clippy::cast_possible_truncation)]
let masked = first.and(W::low_ones(bit as u32).not());
if let Some(p) = masked.first_set() {
return Some(wi * bits + p as usize);
}
self[wi + 1..]
.iter()
.enumerate()
.find_map(|(j, w)| w.first_set().map(|p| (wi + 1 + j) * bits + p as usize))
}
#[inline]
fn find_run(&self, k: u32) -> Option<usize> {
debug_assert!(
(1..=W::BITS).contains(&k),
"run length {k} outside 1..={}",
W::BITS
);
let bits = W::BITS as usize;
for (wi, &w) in self.iter().enumerate() {
if w.is_zero() {
continue;
}
if let Some(s) = w.run_starts(k).first_set() {
return Some(wi * bits + s as usize);
}
let suffix = w.leading_ones();
if suffix == 0 {
continue;
}
let prefix = self.get(wi + 1).map_or(0, |n| n.trailing_ones());
if suffix + prefix >= k {
return Some(wi * bits + (W::BITS - suffix) as usize);
}
}
None
}
#[inline]
fn positions(&self) -> impl Iterator<Item = usize> + '_ {
let bits = W::BITS as usize;
self.iter()
.enumerate()
.flat_map(move |(wi, w)| w.positions().map(move |p| wi * bits + p as usize))
}
}