use linkrs_core::{StorageError, StorageResult};
use super::super::mutable_csr::PACKED_CSR_DENSITY;
pub const DEFAULT_NODE_GROUP_BITS: u32 = 12;
pub const LEAF_REGION_ROWS: usize = 256;
pub fn group_id_for(vid: u32, group_bits: u32) -> usize {
(vid >> group_bits) as usize
}
pub fn group_base(group: usize, group_bits: u32) -> u32 {
(group as u32) << group_bits
}
pub fn group_size(group_bits: u32) -> usize {
1usize << group_bits
}
pub fn local_vid(vid: u32, group_bits: u32) -> u32 {
vid & ((group_size(group_bits) as u32).wrapping_sub(1))
}
pub fn validate_group_bits(group_bits: u32) -> StorageResult<()> {
if group_bits == 0 || group_bits > 20 {
return Err(StorageError::invalid_operation(format!(
"node_group_bits must be within 1..=20, got {}",
group_bits
)));
}
Ok(())
}
pub fn regions_per_group(group_size: usize) -> usize {
group_size.div_ceil(LEAF_REGION_ROWS).max(1)
}
pub fn region_id_for_local(local: u32) -> usize {
local as usize / LEAF_REGION_ROWS
}
pub fn region_local_range(region: usize, group_size: usize) -> (u32, u32) {
let start = (region * LEAF_REGION_ROWS).min(group_size) as u32;
let end = ((region + 1) * LEAF_REGION_ROWS).min(group_size) as u32;
(start, end)
}
pub const LEAF_HIGH_CSR_DENSITY: f32 = 1.0;
pub fn calibrator_tree_height(group_bits: u32) -> u32 {
group_bits.saturating_sub(LEAF_REGION_ROWS.trailing_zeros())
}
pub fn calibrator_max_density(level: u32, tree_height: u32) -> f32 {
if level == 0 || tree_height == 0 {
return LEAF_HIGH_CSR_DENSITY;
}
let step = (LEAF_HIGH_CSR_DENSITY - PACKED_CSR_DENSITY) / tree_height as f32;
PACKED_CSR_DENSITY + step * tree_height.saturating_sub(level.min(tree_height)) as f32
}
pub fn region_tail_gap(live: usize) -> usize {
if live == 0 {
return 0;
}
((live as f32 / PACKED_CSR_DENSITY).ceil() as usize).saturating_sub(live)
}