use super::{RankStructure, RankSupport, SelectSupport};
use crate::Build;
use crate::{bit_vectors::BitVec, profiling::HeapSize};
#[derive(Debug)]
pub struct SparseSamplingRank {
superblocks: Vec<usize>,
superblock_size: usize,
total_rank: usize,
k: usize,
}
impl SparseSamplingRank {
#[inline]
pub fn new(data: &BitVec, k: usize) -> Self {
assert!(k > 0, "k must be greater than 0");
let superblock_size = k * BitVec::CONTAINER_WIDTH;
let num_superblocks = data.len() / superblock_size;
let mut superblocks = Vec::with_capacity(num_superblocks + 1);
let mut rank = 0;
superblocks.push(rank);
let raw_data = data.raw_data();
for i in 0..num_superblocks {
for j in i * k..(i + 1) * k {
rank += unsafe { raw_data.get_unchecked(j) }.count_ones() as usize;
}
superblocks.push(rank);
}
let mut unsampled_rank = 0;
for i in num_superblocks * k..raw_data.len() {
unsampled_rank += unsafe { raw_data.get_unchecked(i) }.count_ones() as usize;
}
if unsampled_rank != 0 {
rank += unsampled_rank;
superblocks.push(rank);
}
Self {
superblocks,
superblock_size,
k,
total_rank: rank,
}
}
pub fn superblocks(&self) -> &[usize] {
&self.superblocks
}
pub fn superblock_size(&self) -> usize {
self.superblock_size
}
pub fn k(&self) -> usize {
self.k
}
#[inline]
pub(crate) unsafe fn select_with_hints(
&self,
data: &BitVec,
rank: usize,
left: usize,
right: usize,
) -> Option<usize> {
let mut left = left;
let mut right = right;
while right - left > 1 {
let mid = (left + right) / 2;
let mid_rank = *self.superblocks.get_unchecked(mid);
if mid_rank < rank {
left = mid;
} else {
right = mid;
}
}
let mut local_rank = *self.superblocks.get_unchecked(left);
let mut block_index = left * self.k;
let raw_data = data.raw_data();
let mut block_rank = raw_data.get_unchecked(block_index).count_ones() as usize;
while local_rank + block_rank < rank {
local_rank += block_rank;
block_index += 1;
block_rank = raw_data.get_unchecked(block_index).count_ones() as usize;
}
let mut block = *raw_data.get_unchecked(block_index);
let mut bit_index = 0;
while local_rank < rank {
if block & 0b1 == 0b1 {
local_rank += 1;
}
block >>= 1;
bit_index += 1;
}
Some(block_index * BitVec::CONTAINER_WIDTH + bit_index)
}
#[inline]
pub(crate) unsafe fn select0_with_hints(
&self,
data: &BitVec,
rank0: usize,
left: usize,
right: usize,
) -> Option<usize> {
let mut left = left;
let mut right = right;
while right - left > 1 {
let mid = (left + right) / 2;
let bits_before_mid = mid * self.superblock_size;
let mid_rank0 = bits_before_mid - *self.superblocks.get_unchecked(mid);
if mid_rank0 < rank0 {
left = mid;
} else {
right = mid;
}
}
let bits_before_left = left * self.superblock_size;
let mut local_rank0 = bits_before_left - *self.superblocks.get_unchecked(left);
let mut block_index = left * self.k;
let raw_data = data.raw_data();
let mut block_rank0 = raw_data.get_unchecked(block_index).count_zeros() as usize;
while local_rank0 + block_rank0 < rank0 {
local_rank0 += block_rank0;
block_index += 1;
block_rank0 = raw_data.get_unchecked(block_index).count_zeros() as usize;
}
let mut block = *raw_data.get_unchecked(block_index);
let mut bit_index = 0;
while local_rank0 < rank0 {
if block & 0b1 == 0b0 {
local_rank0 += 1;
}
block >>= 1;
bit_index += 1;
}
Some(block_index * BitVec::CONTAINER_WIDTH + bit_index)
}
}
impl HeapSize for SparseSamplingRank {
#[inline]
fn heap_size_in_bits(&self) -> usize {
self.superblocks.heap_size_in_bits()
}
}
impl RankSupport<BitVec> for SparseSamplingRank {
#[inline]
unsafe fn rank(&self, data: &BitVec, index: usize) -> Option<usize> {
if index == 0 {
return Some(0);
}
if index > data.len() {
return None;
}
let is = index / self.superblock_size;
let iw = index / BitVec::CONTAINER_WIDTH;
let mut rank = self.superblocks[is];
let raw_data = data.raw_data();
for i in is * self.k..iw {
rank += raw_data.get_unchecked(i).count_ones() as usize;
}
let block_offset = index % BitVec::CONTAINER_WIDTH;
let last_block = raw_data.get(iw).copied().unwrap_or(0);
let last_block_target = last_block & ((1 << block_offset) - 1);
let last_block_rank = last_block_target.count_ones() as usize;
Some(rank + last_block_rank)
}
}
impl SelectSupport<BitVec> for SparseSamplingRank {
#[inline]
unsafe fn select(&self, data: &BitVec, rank: usize) -> Option<usize> {
if rank == 0 {
return Some(0);
}
if rank > self.total_rank {
return None;
}
let left = 0;
let right = self.superblocks.len() - 1;
self.select_with_hints(data, rank, left, right)
}
#[inline]
unsafe fn select0(&self, data: &BitVec, rank0: usize) -> Option<usize> {
if rank0 == 0 {
return Some(0);
}
let total_rank0 = data.len() - self.total_rank;
if rank0 > total_rank0 {
return None;
}
let left = 0;
let right = self.superblocks.len() - 1;
self.select0_with_hints(data, rank0, left, right)
}
}
pub struct SparseSamplingRankSpec {
pub k: usize,
}
impl SparseSamplingRankSpec {
#[inline]
pub const fn new(k: usize) -> Self {
Self { k }
}
}
impl SparseSamplingRank {
#[inline]
pub const fn spec(k: usize) -> SparseSamplingRankSpec {
SparseSamplingRankSpec::new(k)
}
}
impl Build<BitVec, RankStructure<BitVec, SparseSamplingRank>> for SparseSamplingRankSpec {
#[inline]
fn build(&self, data: BitVec) -> RankStructure<BitVec, SparseSamplingRank> {
let sparse_sampling = SparseSamplingRank::new(&data, self.k);
unsafe { RankStructure::new(data, sparse_sampling) }
}
}
impl Build<&BitVec, SparseSamplingRank> for SparseSamplingRankSpec {
#[inline]
fn build(&self, data: &BitVec) -> SparseSamplingRank {
SparseSamplingRank::new(data, self.k)
}
}
#[cfg(test)]
mod tests;