use std::collections::VecDeque;
const CHUNK_TARGET: usize = 128;
const SPLIT_AT: usize = CHUNK_TARGET * 2;
#[derive(Clone, Debug)]
struct Chunk {
heights: Vec<u32>,
measured: Vec<bool>,
sum: u64,
}
impl Chunk {
fn new() -> Chunk {
Chunk {
heights: Vec::with_capacity(CHUNK_TARGET),
measured: Vec::with_capacity(CHUNK_TARGET),
sum: 0,
}
}
fn len(&self) -> usize {
self.heights.len()
}
fn recompute(&mut self) {
self.sum = self.heights.iter().map(|h| u64::from(*h)).sum();
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Hit {
pub row: usize,
pub offset: u32,
}
#[derive(Debug, Default)]
pub struct HeightIndex {
chunks: VecDeque<Chunk>,
prefix: Vec<u64>,
prefix_valid: usize,
len: usize,
}
impl HeightIndex {
pub fn new() -> HeightIndex {
HeightIndex::default()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn total_height(&mut self) -> u64 {
self.repair_prefix();
self.prefix.last().copied().unwrap_or(0)
}
pub fn height_at(&self, row: usize) -> u32 {
match self.locate_chunk(row) {
Some((ci, within)) => self.chunks[ci].heights[within],
None => 0,
}
}
pub fn is_measured(&self, row: usize) -> bool {
match self.locate_chunk(row) {
Some((ci, within)) => self.chunks[ci].measured[within],
None => false,
}
}
pub fn unmeasured_count(&self) -> usize {
self.chunks
.iter()
.map(|c| c.measured.iter().filter(|m| !**m).count())
.sum()
}
pub fn push_back(&mut self, height: u32, measured: bool) {
let need_new = match self.chunks.back() {
None => true,
Some(c) => c.len() >= CHUNK_TARGET,
};
if need_new {
self.chunks.push_back(Chunk::new());
}
let ci = self.chunks.len() - 1;
let c = &mut self.chunks[ci];
c.heights.push(height);
c.measured.push(measured);
c.sum += u64::from(height);
self.len += 1;
self.invalidate_from(ci);
}
pub fn push_front(&mut self, height: u32, measured: bool) {
let need_new = match self.chunks.front() {
None => true,
Some(c) => c.len() >= CHUNK_TARGET,
};
if need_new {
self.chunks.push_front(Chunk::new());
}
let c = &mut self.chunks[0];
c.heights.insert(0, height);
c.measured.insert(0, true & measured);
c.sum += u64::from(height);
self.len += 1;
self.invalidate_from(0);
}
pub fn insert(&mut self, row: usize, height: u32, measured: bool) {
if row >= self.len {
self.push_back(height, measured);
return;
}
let (ci, within) = match self.locate_chunk(row) {
Some(x) => x,
None => {
self.push_back(height, measured);
return;
}
};
{
let c = &mut self.chunks[ci];
c.heights.insert(within, height);
c.measured.insert(within, measured);
c.sum += u64::from(height);
}
self.len += 1;
if self.chunks[ci].len() >= SPLIT_AT {
self.split_chunk(ci);
}
self.invalidate_from(ci);
}
pub fn remove(&mut self, row: usize) -> Option<u32> {
let (ci, within) = self.locate_chunk(row)?;
let h = {
let c = &mut self.chunks[ci];
let h = c.heights.remove(within);
c.measured.remove(within);
c.sum -= u64::from(h);
h
};
self.len -= 1;
if self.chunks[ci].len() == 0 && self.chunks.len() > 1 {
self.chunks.remove(ci);
}
self.invalidate_from(ci.min(self.chunks.len().saturating_sub(1)));
Some(h)
}
pub fn drain_front(&mut self, n: usize) {
let mut left = n.min(self.len);
while left > 0 {
let front_len = match self.chunks.front() {
Some(c) => c.len(),
None => break,
};
if front_len <= left {
let c = self.chunks.pop_front().expect("front exists");
left -= front_len;
self.len -= front_len;
let _ = c;
} else {
let c = self.chunks.front_mut().expect("front exists");
c.heights.drain(..left);
c.measured.drain(..left);
c.recompute();
self.len -= left;
left = 0;
}
}
self.invalidate_from(0);
}
pub fn set_height(&mut self, row: usize, height: u32, measured: bool) {
let Some((ci, within)) = self.locate_chunk(row) else {
return;
};
let c = &mut self.chunks[ci];
let old = c.heights[within];
if old == height && c.measured[within] == measured {
return;
}
c.sum = c.sum - u64::from(old) + u64::from(height);
c.heights[within] = height;
c.measured[within] = measured;
self.invalidate_from(ci);
}
pub fn invalidate_all_measurements(&mut self) {
for c in &mut self.chunks {
c.measured.fill(false);
}
}
pub fn offset_of(&mut self, row: usize) -> u64 {
if row == 0 || self.len == 0 {
return 0;
}
self.repair_prefix();
let mut remaining = row.min(self.len);
let mut acc = 0u64;
for (i, c) in self.chunks.iter().enumerate() {
if remaining >= c.len() {
acc = self.prefix[i + 1];
remaining -= c.len();
if remaining == 0 {
return acc;
}
} else {
for h in &c.heights[..remaining] {
acc += u64::from(*h);
}
return acc;
}
}
acc
}
pub fn locate(&mut self, y: u64) -> Option<Hit> {
if self.len == 0 {
return None;
}
self.repair_prefix();
let total = self.prefix.last().copied().unwrap_or(0);
if y >= total {
let row = self.len - 1;
return Some(Hit {
row,
offset: self.height_at(row),
});
}
let mut lo = 0usize;
let mut hi = self.chunks.len();
while lo + 1 < hi {
let mid = lo + (hi - lo) / 2;
if self.prefix[mid] <= y {
lo = mid;
} else {
hi = mid;
}
}
let ci = lo;
let mut acc = self.prefix[ci];
let start_row = self.chunk_start_row(ci);
for (i, h) in self.chunks[ci].heights.iter().enumerate() {
let h64 = u64::from(*h);
if acc + h64 > y {
return Some(Hit {
row: start_row + i,
offset: (y - acc) as u32,
});
}
acc += h64;
}
Some(Hit {
row: self.len - 1,
offset: 0,
})
}
fn chunk_start_row(&self, ci: usize) -> usize {
self.chunks.iter().take(ci).map(|c| c.len()).sum()
}
fn locate_chunk(&self, row: usize) -> Option<(usize, usize)> {
if row >= self.len {
return None;
}
let mut acc = 0usize;
for (i, c) in self.chunks.iter().enumerate() {
if row < acc + c.len() {
return Some((i, row - acc));
}
acc += c.len();
}
None
}
fn split_chunk(&mut self, ci: usize) {
let at = self.chunks[ci].len() / 2;
let tail_h = self.chunks[ci].heights.split_off(at);
let tail_m = self.chunks[ci].measured.split_off(at);
self.chunks[ci].recompute();
let mut tail = Chunk {
heights: tail_h,
measured: tail_m,
sum: 0,
};
tail.recompute();
self.chunks.insert(ci + 1, tail);
}
fn invalidate_from(&mut self, ci: usize) {
self.prefix_valid = self.prefix_valid.min(ci);
}
fn repair_prefix(&mut self) {
let n = self.chunks.len();
if self.prefix.len() != n + 1 {
self.prefix.resize(n + 1, 0);
self.prefix_valid = self.prefix_valid.min(n);
}
if self.prefix_valid >= n {
return;
}
let start = self.prefix_valid;
let mut acc = self.prefix[start];
for i in start..n {
acc += self.chunks[i].sum;
self.prefix[i + 1] = acc;
}
self.prefix_valid = n;
}
}