use crate::{utils::prefetch_read_NTA, AccessBin, BitVector, RankBin, SelectBin};
use mem_dbg::{MemDbg, MemSize};
use serde::{Deserialize, Serialize};
const BLOCK_SIZE: usize = 8; const SUPERBLOCK_SIZE: usize = 8 * BLOCK_SIZE;
const SELECT_ONES_PER_HINT: usize = 64 * SUPERBLOCK_SIZE * 2; const SELECT_ZEROS_PER_HINT: usize = SELECT_ONES_PER_HINT;
#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize, MemSize, MemDbg, Debug)]
pub struct RSWide {
bv: BitVector,
superblock_metadata: Box<[u128]>, select_samples: [Box<[usize]>; 2],
count_zeros: usize,
}
impl RSWide {
pub fn new(bv: BitVector) -> Self {
let mut superblock_metadata = Vec::new();
let mut total_rank: u128 = 0;
let mut cur_metadata: u128 = 0;
let mut word_pop: u128 = 0;
let mut zeros_so_far: u128 = 0;
let mut select_samples: [Vec<usize>; 2] = [Vec::new(), Vec::new()];
let mut cur_hint_0 = 0;
let mut cur_hint_1 = 0;
select_samples[0].push(0);
select_samples[1].push(0);
for (b, &dl) in bv.data.iter().enumerate() {
if b % 8 == 0 {
total_rank += word_pop;
word_pop = 0;
cur_metadata = 0;
cur_metadata |= total_rank;
} else {
cur_metadata <<= 12;
cur_metadata |= word_pop;
}
word_pop += dl.count_ones() as u128;
if (total_rank + word_pop) / SELECT_ONES_PER_HINT as u128 > cur_hint_1 {
select_samples[1].push(b / 8);
cur_hint_1 += 1;
}
zeros_so_far += dl.count_zeros() as u128;
if (zeros_so_far / SELECT_ZEROS_PER_HINT as u128) > cur_hint_0 {
select_samples[0].push(b / 8);
cur_hint_0 += 1;
}
if (b + 1) % 8 == 0 {
superblock_metadata.push(cur_metadata);
}
}
total_rank += word_pop;
let left: usize = bv.data.len() % 8;
if left != 0 {
for _ in left..8 {
cur_metadata <<= 12;
cur_metadata |= word_pop;
}
superblock_metadata.push(cur_metadata);
}
cur_metadata = 0;
cur_metadata |= total_rank;
cur_metadata <<= 128 - 44;
superblock_metadata.push(cur_metadata);
superblock_metadata.shrink_to_fit();
select_samples[0].push(superblock_metadata.len() - 1);
select_samples[1].push(superblock_metadata.len() - 1);
let count_zeros = bv.len() - total_rank as usize;
Self {
bv,
superblock_metadata: superblock_metadata.into_boxed_slice(),
select_samples: select_samples
.into_iter()
.map(|x| x.into_boxed_slice())
.collect::<Vec<_>>()
.try_into()
.unwrap(),
count_zeros,
}
}
#[inline(always)]
pub fn count_ones(&self) -> usize {
self.bv.len() - self.count_zeros()
}
#[inline(always)]
pub fn count_zeros(&self) -> usize {
self.count_zeros
}
#[inline(always)]
pub fn len(&self) -> usize {
self.bv.len()
}
#[inline]
pub fn bit_vector(&self) -> &BitVector {
&self.bv
}
#[inline(always)]
fn superblock_rank(&self, block: usize) -> usize {
(self.superblock_metadata[block] >> (128 - 44)) as usize
}
#[inline]
pub fn prefetch_info(&self, pos: usize) {
prefetch_read_NTA(&self.superblock_metadata, pos / 512);
}
#[inline]
pub fn prefetch_data(&self, pos: usize) {
self.bv.prefetch_line(pos / 512);
}
#[inline(always)]
fn sub_block_rank(&self, sub_block: usize) -> usize {
let mut result = 0;
let superblock = sub_block / (SUPERBLOCK_SIZE / BLOCK_SIZE);
result += self.superblock_rank(superblock);
let left = sub_block % (SUPERBLOCK_SIZE / BLOCK_SIZE);
if left != 0 {
result += ((self.superblock_metadata[superblock] >> ((7 - left) * 12)) & 0b111111111111)
as usize;
}
result
}
#[inline(always)]
fn select1_subblock(&self, i: usize) -> (usize, usize) {
let mut position;
let hint = i / SELECT_ONES_PER_HINT;
let mut hint_start = self.select_samples[1][hint];
let hint_end = 1 + self.select_samples[1][hint + 1];
while hint_start < hint_end {
if self.superblock_rank(hint_start) > i {
break;
}
hint_start += 1;
}
position = hint_start - 1;
position *= SUPERBLOCK_SIZE / BLOCK_SIZE;
for j in 0..(SUPERBLOCK_SIZE / BLOCK_SIZE) {
if self.sub_block_rank(position + j) > i {
position += j - 1;
break;
}
if j == 7 {
position += j;
}
}
let rank = self.sub_block_rank(position);
(position, rank)
}
#[inline(always)]
fn select0_subblock(&self, i: usize) -> (usize, usize) {
let mut position;
let hint = i / SELECT_ZEROS_PER_HINT;
let mut hint_start = self.select_samples[0][hint];
let hint_end = 1 + self.select_samples[0][hint + 1];
let max_rank_for_block = SUPERBLOCK_SIZE * 64;
while hint_start < hint_end {
if max_rank_for_block * hint_start - self.superblock_rank(hint_start) > i {
break;
}
hint_start += 1;
}
position = hint_start - 1;
position *= SUPERBLOCK_SIZE / BLOCK_SIZE;
let max_rank_for_subblock = BLOCK_SIZE * 64;
for j in 0..(SUPERBLOCK_SIZE / BLOCK_SIZE) {
let rank0 = max_rank_for_subblock * (position + j) - self.sub_block_rank(position + j);
if rank0 > i {
position += j - 1;
break;
}
if j == 7 {
position += j;
}
}
let rank = max_rank_for_subblock * position - self.sub_block_rank(position);
(position, rank)
}
}
impl AccessBin for RSWide {
#[inline(always)]
fn get(&self, i: usize) -> Option<bool> {
if i >= self.bv.len() {
return None;
}
Some(unsafe { self.get_unchecked(i) })
}
#[inline(always)]
unsafe fn get_unchecked(&self, i: usize) -> bool {
self.bv.get_unchecked(i)
}
}
impl RankBin for RSWide {
#[inline(always)]
fn rank1(&self, i: usize) -> Option<usize> {
if self.bv.is_empty() || i > self.bv.len() {
return None;
}
Some(unsafe { self.rank1_unchecked(i) })
}
#[inline(always)]
unsafe fn rank1_unchecked(&self, i: usize) -> usize {
if i == 0 {
return 0;
}
let i = i - 1;
let sub_block = i >> 9;
let mut result = self.sub_block_rank(sub_block);
let sub_left = (i & 511) as i32 + 1;
result += if sub_left == 0 {
0
} else {
self.bv.data[sub_block].rank1(sub_left as usize).unwrap()
};
result
}
#[inline(always)]
fn prefetch(&self, pos: usize) {
let pos = pos.wrapping_sub(1);
prefetch_read_NTA(&self.bv.data, pos >> 9);
let sub_block = pos >> 9;
let superblock = sub_block / (SUPERBLOCK_SIZE / BLOCK_SIZE);
prefetch_read_NTA(&self.superblock_metadata, superblock);
}
fn count_zeros(&self) -> usize {
self.count_zeros()
}
}
impl SelectBin for RSWide {
#[inline(always)]
fn select1(&self, i: usize) -> Option<usize> {
if i >= self.count_ones() {
return None;
}
Some(unsafe { self.select1_unchecked(i) })
}
#[inline(always)]
unsafe fn select1_unchecked(&self, i: usize) -> usize {
let (block, rank) = self.select1_subblock(i);
let off = self.bv.data[block].select1_unchecked(i - rank);
block * 512 + off
}
#[inline(always)]
fn select0(&self, i: usize) -> Option<usize> {
if i >= self.count_zeros() {
return None;
}
Some(unsafe { self.select0_unchecked(i) })
}
#[inline(always)]
unsafe fn select0_unchecked(&self, i: usize) -> usize {
let (block, rank) = self.select0_subblock(i);
let off = self.bv.data[block].select0_unchecked(i - rank);
block * 512 + off
}
}
impl From<BitVector> for RSWide {
fn from(bv: BitVector) -> Self {
RSWide::new(bv)
}
}
#[cfg(test)]
mod tests;