use crate::bitvector::{BitVectorBitPositionsIter, BitVectorIter};
use crate::utils::select_in_word;
use crate::BitVector;
use crate::{AccessBin, SelectBin};
use mem_dbg::{MemDbg, MemSize};
use serde::{Deserialize, Serialize};
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::_popcnt64;
const BLOCK_SIZE: usize = 1024;
const SUBBLOCK_SIZE: usize = 32;
const MAX_IN_BLOCK_DISTACE: usize = 1 << 16;
#[derive(Default, Debug, Clone, Serialize, Deserialize, MemSize, MemDbg, PartialEq)]
pub struct DArray<const SELECT0_SUPPORT: bool = false> {
bv: BitVector,
ones_inventories: Inventories<true>,
zeroes_inventories: Option<Inventories<false>>,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, MemSize, MemDbg, PartialEq)]
struct Inventories<const BIT: bool> {
n_sets: usize, block_inventory: Box<[i64]>,
subblock_inventory: Box<[u16]>,
overflow_positions: Box<[usize]>,
}
impl<const BIT: bool> Inventories<BIT> {
fn new(bv: &BitVector) -> Self {
let mut block_inventory = Vec::new();
let mut subblock_inventory = Vec::new();
let mut overflow_positions = Vec::new();
let mut curr_block_positions = Vec::with_capacity(BLOCK_SIZE);
let mut n_sets = 0;
if !BIT {
for curr_pos in bv.zeros() {
curr_block_positions.push(curr_pos);
if curr_block_positions.len() == BLOCK_SIZE {
Self::flush_block(
&curr_block_positions,
&mut block_inventory,
&mut subblock_inventory,
&mut overflow_positions,
);
curr_block_positions.clear()
}
n_sets += 1;
}
} else {
for curr_pos in bv.ones() {
curr_block_positions.push(curr_pos);
if curr_block_positions.len() == BLOCK_SIZE {
Self::flush_block(
&curr_block_positions,
&mut block_inventory,
&mut subblock_inventory,
&mut overflow_positions,
);
curr_block_positions.clear()
}
n_sets += 1;
}
}
Self::flush_block(
&curr_block_positions,
&mut block_inventory,
&mut subblock_inventory,
&mut overflow_positions,
);
Self {
n_sets,
block_inventory: block_inventory.into_boxed_slice(),
subblock_inventory: subblock_inventory.into_boxed_slice(),
overflow_positions: overflow_positions.into_boxed_slice(),
}
}
fn flush_block(
curr_positions: &[usize],
block_inventory: &mut Vec<i64>,
subblock_inventory: &mut Vec<u16>,
overflow_positions: &mut Vec<usize>,
) {
if curr_positions.is_empty() {
return;
}
if curr_positions.last().unwrap() - curr_positions.first().unwrap() < MAX_IN_BLOCK_DISTACE {
let v = *curr_positions.first().unwrap();
block_inventory.push(v as i64);
for i in (0..curr_positions.len()).step_by(SUBBLOCK_SIZE) {
let dist = (curr_positions[i] - v) as u16;
subblock_inventory.push(dist);
}
} else {
let v: i64 = (-(overflow_positions.len() as i64)) - 1;
block_inventory.push(v);
overflow_positions.extend(curr_positions.iter());
subblock_inventory.extend(std::iter::repeat_n(u16::MAX, curr_positions.len()));
}
}
}
impl<const SELECT0_SUPPORT: bool> DArray<SELECT0_SUPPORT> {
#[must_use]
pub fn new(bv: BitVector) -> Self {
let ones_inventories = Inventories::new(&bv);
let zeroes_inventories = if SELECT0_SUPPORT {
Some(Inventories::new(&bv))
} else {
None
};
DArray {
bv,
ones_inventories,
zeroes_inventories,
}
}
#[must_use]
pub fn ones(&self) -> BitVectorBitPositionsIter<'_, true> {
self.bv.ones()
}
#[must_use]
pub fn ones_with_pos(&self, pos: usize) -> BitVectorBitPositionsIter<'_, true> {
self.bv.ones_with_pos(pos)
}
#[must_use]
pub fn zeros(&self) -> BitVectorBitPositionsIter<'_, false> {
self.bv.zeros()
}
#[must_use]
pub fn zeros_with_pos(&self, pos: usize) -> BitVectorBitPositionsIter<'_, false> {
self.bv.zeros_with_pos(pos)
}
pub fn iter(&self) -> BitVectorIter<'_> {
self.bv.iter()
}
#[must_use]
pub fn count_ones(&self) -> usize {
self.ones_inventories.n_sets
}
#[must_use]
pub fn count_zeros(&self) -> usize {
self.bv.len() - self.ones_inventories.n_sets
}
#[must_use]
pub fn len(&self) -> usize {
self.bv.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bv.len() == 0
}
#[inline(always)]
fn select<const BIT: bool>(&self, i: usize, inventories: &Inventories<BIT>) -> Option<usize> {
if i >= inventories.n_sets {
return None;
}
let block = i / BLOCK_SIZE;
let block_pos = inventories.block_inventory[block];
if block_pos < 0 {
let overflow_pos: usize = (-block_pos - 1) as usize;
let idx = overflow_pos + (i & (BLOCK_SIZE - 1));
return Some(inventories.overflow_positions[idx]);
}
let subblock = i / SUBBLOCK_SIZE;
let start_pos = (block_pos as usize) + (inventories.subblock_inventory[subblock] as usize);
let mut reminder = i & (SUBBLOCK_SIZE - 1);
if reminder == 0 {
return Some(start_pos);
}
let mut word_idx = start_pos >> 6;
let word_shift = start_pos & 63;
let mut word = if !BIT {
!self.bv.get_word(word_idx) & (std::u64::MAX << word_shift) } else {
self.bv.get_word(word_idx) & (std::u64::MAX << word_shift)
};
loop {
let popcnt;
#[cfg(not(target_arch = "x86_64"))]
{
popcnt = word.count_ones() as usize;
}
#[cfg(target_arch = "x86_64")]
{
unsafe {
popcnt = _popcnt64(word as i64) as usize;
}
}
if reminder < popcnt {
break;
}
reminder -= popcnt;
word_idx += 1;
word = self.bv.get_word(word_idx);
if !BIT {
word = !word; }
}
let select_intra = select_in_word(word, reminder as u64) as usize;
Some((word_idx << 6) + select_intra)
}
}
impl<const SELECT0_SUPPORT: bool> AccessBin for DArray<SELECT0_SUPPORT> {
#[inline(always)]
fn get(&self, i: usize) -> Option<bool> {
self.bv.get(i)
}
#[inline(always)]
unsafe fn get_unchecked(&self, i: usize) -> bool {
self.bv.get_unchecked(i)
}
}
impl<const SELECT0_SUPPORT: bool> SelectBin for DArray<SELECT0_SUPPORT> {
#[inline(always)]
fn select1(&self, i: usize) -> Option<usize> {
self.select(i, &self.ones_inventories)
}
#[inline(always)]
unsafe fn select1_unchecked(&self, i: usize) -> usize {
self.select(i, &self.ones_inventories).unwrap()
}
#[inline(always)]
fn select0(&self, i: usize) -> Option<usize> {
assert!(SELECT0_SUPPORT);
self.select(i, self.zeroes_inventories.as_ref().unwrap())
}
#[inline(always)]
unsafe fn select0_unchecked(&self, i: usize) -> usize {
assert!(SELECT0_SUPPORT);
self.select(i, self.zeroes_inventories.as_ref().unwrap())
.unwrap()
}
}
impl<const SELECT0_SUPPORT: bool> FromIterator<bool> for DArray<SELECT0_SUPPORT> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = bool>,
{
DArray::<SELECT0_SUPPORT>::new(BitVector::from_iter(iter))
}
}
impl<V, const SELECT0_SUPPORT: bool> FromIterator<V> for DArray<SELECT0_SUPPORT>
where
V: crate::bitvector::MyPrimInt + PartialOrd,
<V as TryInto<usize>>::Error: std::fmt::Debug,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = V>,
<V as TryInto<usize>>::Error: std::fmt::Debug,
{
let data: Vec<_> = iter.into_iter().collect();
assert!(
data.windows(2).all(|w| w[0] < w[1]),
"Sequence must be strictly increasing"
);
DArray::<SELECT0_SUPPORT>::new(BitVector::from_iter(data))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::perf_and_test_utils::{gen_strictly_increasing_sequence, negate_vector};
#[test]
fn test_select1() {
let bv = BitVector::default();
let v: Vec<usize> = bv.ones().collect();
assert!(v.is_empty());
let vv: Vec<usize> = vec![0, 12, 33, 42, 55, 61, 62, 63, 128, 129, 254, 1026];
let da: DArray<false> = vv.iter().copied().collect();
for (i, &sel) in vv.iter().enumerate() {
let res = da.select1(i);
assert_eq!(res.unwrap(), sel);
}
let res = da.select1(vv.len());
assert_eq!(res, None);
let vv = gen_strictly_increasing_sequence(1024 * 4, 1 << 20);
let da: DArray<false> = vv.iter().copied().collect();
for (i, &sel) in vv.iter().enumerate() {
let res = da.select1(i);
assert_eq!(res.unwrap(), sel);
}
}
#[test]
fn test_select0() {
let bv = BitVector::default();
let v: Vec<usize> = bv.ones().collect();
assert!(v.is_empty());
let vv: Vec<usize> = vec![0, 12, 33, 42, 55, 61, 62, 63, 128, 129, 254, 1026];
let da: DArray<true> = vv.iter().copied().collect();
for (i, &sel) in negate_vector(&vv).iter().enumerate() {
let res = da.select0(i);
assert_eq!(res.unwrap(), sel);
}
let vv = gen_strictly_increasing_sequence(1024 * 4, 1 << 20);
let da: DArray<true> = vv.iter().copied().collect();
for (i, &sel) in negate_vector(&vv).iter().enumerate() {
let res = da.select0(i);
assert_eq!(res.unwrap(), sel);
}
}
}