use std::collections::BTreeMap;
use brk_error::Error;
use brk_types::{Height, TimePeriod, Timestamp};
use vecdb::{ReadableVec, VecValue};
use super::block_bucket::BlockBucket;
use crate::Query;
fn time_div(period: TimePeriod) -> u32 {
match period {
TimePeriod::Day | TimePeriod::ThreeDays => 1,
TimePeriod::Week => 300,
TimePeriod::Month => 1800,
TimePeriod::ThreeMonths => 7200,
TimePeriod::SixMonths => 10800,
TimePeriod::Year | TimePeriod::TwoYears => 28800,
TimePeriod::ThreeYears => 43200,
TimePeriod::All => 86400,
}
}
pub const fn round_half_up(sum: u64, n: u64) -> u64 {
(sum + n / 2) / n
}
pub struct BlockWindow {
pub start: Height,
pub end: Height,
pub buckets: Vec<BlockBucket>,
pub len: usize,
}
impl BlockWindow {
pub fn new(query: &Query, period: TimePeriod) -> brk_error::Result<Self> {
let start = super::start_height(query, period)?;
let end = query.height() + 1usize;
let div = time_div(period);
let timestamps: Vec<Timestamp> = query
.indexer()
.vecs()
.blocks
.timestamp
.collect_range(start, end);
let mut groups: BTreeMap<u32, Vec<usize>> = BTreeMap::new();
for (i, ts) in timestamps.iter().enumerate() {
groups.entry(**ts / div).or_default().push(i);
}
let len = timestamps.len();
let buckets = groups
.into_values()
.map(|offsets| {
let n = offsets.len() as u64;
let sum_h: u64 = offsets.iter().map(|&i| u64::from(start + i)).sum();
let sum_ts: u64 = offsets.iter().map(|&i| u64::from(timestamps[i])).sum();
BlockBucket::new(
Height::from(round_half_up(sum_h, n)),
Timestamp::from(round_half_up(sum_ts, n) as u32),
offsets,
)
})
.collect();
Ok(Self {
start,
end,
buckets,
len,
})
}
pub fn read<V, T>(&self, vec: &V) -> brk_error::Result<Vec<T>>
where
V: ReadableVec<Height, T>,
T: VecValue,
{
let values = vec.collect_range(self.start, self.end);
if values.len() < self.len {
return Err(Error::Internal(
"BlockWindow::read: value vec shorter than window (per-vec stamp lag)",
));
}
Ok(values)
}
}