use std::collections::HashMap;
use std::hash::Hash;
pub(crate) struct HeightIndex<K: Eq + Hash + Clone> {
keys: Vec<K>,
heights: Vec<f32>,
positions: HashMap<K, usize>,
tree: Vec<f64>,
total: f64,
}
impl<K: Eq + Hash + Clone> HeightIndex<K> {
pub(crate) fn new() -> Self {
HeightIndex {
keys: Vec::new(),
heights: Vec::new(),
positions: HashMap::new(),
tree: vec![0.0],
total: 0.0,
}
}
pub(crate) fn from_heights(
keys: impl IntoIterator<Item = K>,
height: impl Fn(&K) -> f32,
) -> Self {
let keys: Vec<K> = keys.into_iter().collect();
let heights: Vec<f32> = keys.iter().map(height).collect();
let mut index = HeightIndex {
positions: HashMap::new(),
keys,
heights,
tree: Vec::new(),
total: 0.0,
};
index.rebuild();
index
}
pub(crate) fn push(&mut self, key: K, height: f32) {
let duplicate = self.positions.insert(key.clone(), self.keys.len());
debug_assert!(
duplicate.is_none(),
"the height index found a duplicate key, but its keys must be unique"
);
self.keys.push(key);
self.heights.push(height);
let count = self.keys.len();
let covered = count & count.wrapping_neg();
let entry = self.prefix(count - 1) - self.prefix(count - covered) + f64::from(height);
self.tree.push(entry);
let mut at = count + covered;
while at < self.tree.len() {
self.tree[at] += f64::from(height);
at += at & at.wrapping_neg();
}
self.total = self.prefix(count);
}
pub(crate) fn set_height(&mut self, index: usize, height: f32) {
let delta = f64::from(height) - f64::from(self.heights[index]);
self.heights[index] = height;
let count = self.keys.len();
self.add(index + 1, delta);
self.total = self.prefix(count);
}
pub(crate) fn offset_of(&self, index: usize) -> f32 {
self.prefix(index.min(self.keys.len())) as f32
}
pub(crate) fn index_at(&self, offset: f32) -> usize {
if self.keys.is_empty() {
return 0;
}
let mut pos = 0usize;
let mut sum = 0.0f64;
let mut bit = self.keys.len().next_power_of_two();
while bit > 0 {
let next = pos + bit;
if next <= self.keys.len() {
let reached = sum + self.tree[next];
if reached as f32 <= offset {
pos = next;
sum = reached;
}
}
bit >>= 1;
}
pos.min(self.keys.len() - 1)
}
pub(crate) fn height(&self, index: usize) -> f32 {
self.heights[index]
}
pub(crate) fn total(&self) -> f32 {
self.total as f32
}
pub(crate) fn len(&self) -> usize {
self.keys.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub(crate) fn keys(&self) -> &[K] {
&self.keys
}
pub(crate) fn key(&self, index: usize) -> &K {
&self.keys[index]
}
pub(crate) fn position(&self, key: &K) -> Option<usize> {
self.positions.get(key).copied()
}
fn add(&mut self, mut at: usize, delta: f64) {
while at < self.tree.len() {
self.tree[at] += delta;
at += at & at.wrapping_neg();
}
}
fn prefix(&self, mut count: usize) -> f64 {
let mut sum = 0.0;
while count > 0 {
sum += self.tree[count];
count -= count & count.wrapping_neg();
}
sum
}
fn rebuild(&mut self) {
self.positions = self
.keys
.iter()
.enumerate()
.map(|(at, key)| (key.clone(), at))
.collect();
debug_assert!(
self.positions.len() == self.keys.len(),
"the height index found a duplicate key, but its keys must be unique"
);
let count = self.keys.len();
self.tree = vec![0.0; count + 1];
for at in 1..=count {
self.tree[at] += f64::from(self.heights[at - 1]);
let parent = at + (at & at.wrapping_neg());
if parent <= count {
self.tree[parent] += self.tree[at];
}
}
self.total = self.prefix(count);
}
}
impl<K: Eq + Hash + Clone> Default for HeightIndex<K> {
fn default() -> Self {
HeightIndex::new()
}
}
#[cfg(test)]
#[path = "height_index_tests.rs"]
mod tests;