use crate::error::{Result, ZiporaError};
use crate::algorithms::bit_ops::select_in_word;
use crate::succinct::BitVector;
use std::cmp::Ordering;
use super::basic::EliasFano;
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct PefChunkMeta {
pub(crate) min_value: u64,
pub(crate) low_offset: u32,
pub(crate) high_offset: u32,
pub(crate) count: u16,
pub(crate) high_len_bits: u16,
pub(crate) high_words: u16,
pub(crate) low_words: u16,
pub(crate) low_bit_width: u8,
}
pub(crate) struct ChunkView<'a> {
pub(crate) low_bits: &'a [u64],
pub(crate) high_bits: &'a [u64],
pub(crate) low_bit_width: u32,
pub(crate) count: usize,
pub(crate) min_value: u64,
pub(crate) high_len_bits: usize,
}
#[inline]
pub(crate) fn chunk_get_delta(chunk: &ChunkView<'_>, local_idx: usize) -> u64 {
let high_pos = chunk_select1(chunk, local_idx);
let high_val = (high_pos - local_idx) as u64;
let low = chunk_get_low(chunk, local_idx);
(high_val << chunk.low_bit_width) | low
}
#[inline]
pub(crate) fn chunk_select1(chunk: &ChunkView<'_>, rank: usize) -> usize {
let mut remaining = rank;
for (word_idx, &word) in chunk.high_bits.iter().enumerate() {
let ones = word.count_ones() as usize;
if remaining < ones {
return word_idx * 64 + select_in_word(word, remaining);
}
remaining -= ones;
}
chunk.high_len_bits
}
#[inline]
pub(crate) fn chunk_get_low(chunk: &ChunkView<'_>, index: usize) -> u64 {
if chunk.low_bit_width == 0 { return 0; }
let bit_pos = index as u64 * chunk.low_bit_width as u64;
let word_idx = (bit_pos / 64) as usize;
let bit_idx = (bit_pos % 64) as u32;
let w0 = chunk.low_bits[word_idx];
let w1 = chunk.low_bits[word_idx + 1];
let combined = w0 as u128 | ((w1 as u128) << 64);
(combined >> bit_idx) as u64 & ((1u64 << chunk.low_bit_width) - 1)
}
#[inline]
pub(crate) fn chunk_skip_to_high(chunk: &ChunkView<'_>, target_high: usize) -> (usize, usize) {
if target_high == 0 {
return (0, 0);
}
let mut zeros_remaining = target_high;
let mut ones_count = 0usize;
let last_wi = chunk.high_bits.len().saturating_sub(1);
for (wi, &w) in chunk.high_bits.iter().enumerate() {
let valid_bits = if wi == last_wi {
let rem = chunk.high_len_bits % 64;
if rem == 0 && chunk.high_len_bits > 0 { 64 } else { rem }
} else {
64
};
let valid_mask = if valid_bits >= 64 { u64::MAX } else { (1u64 << valid_bits) - 1 };
let masked = w & valid_mask;
let ones = masked.count_ones() as usize;
let zeros = valid_bits - ones;
if zeros_remaining <= zeros {
let inverted = (!w) & valid_mask;
let zero_pos_in_word = select_in_word(inverted, zeros_remaining - 1);
let abs_pos = wi * 64 + zero_pos_in_word;
let bits_up_to = zero_pos_in_word + 1;
let partial_mask = if bits_up_to >= 64 { u64::MAX } else { (1u64 << bits_up_to) - 1 };
ones_count += (w & partial_mask).count_ones() as usize;
return (ones_count, abs_pos + 1);
}
zeros_remaining -= zeros;
ones_count += ones;
}
(chunk.count, chunk.high_len_bits)
}
#[inline]
pub(crate) fn chunk_scan_geq(
chunk: &ChunkView<'_>,
target_delta: u64,
start_idx: usize,
start_pos: usize,
) -> Option<(usize, u64, usize)> {
let mut idx = start_idx;
let mut bit_pos = start_pos;
let mut word_idx = bit_pos / 64;
if word_idx >= chunk.high_bits.len() {
return None;
}
let start_bit = bit_pos % 64;
let mut word = chunk.high_bits[word_idx] >> start_bit;
bit_pos = word_idx * 64 + start_bit;
while idx < chunk.count {
if word == 0 {
word_idx += 1;
if word_idx >= chunk.high_bits.len() { break; }
word = chunk.high_bits[word_idx];
bit_pos = word_idx * 64;
continue;
}
let tz = word.trailing_zeros() as usize;
bit_pos += tz;
word >>= tz;
let high_val = (bit_pos - idx) as u64;
let low = chunk_get_low(chunk, idx);
let delta = (high_val << chunk.low_bit_width) | low;
if delta >= target_delta {
return Some((idx, delta, bit_pos));
}
idx += 1;
bit_pos += 1;
word >>= 1;
}
None
}
#[inline]
pub(crate) fn chunk_first_one_cached(high_bits: &[u64]) -> usize {
for (wi, &w) in high_bits.iter().enumerate() {
if w != 0 {
return wi * 64 + w.trailing_zeros() as usize;
}
}
0
}