use std::{
collections::BTreeMap,
iter::Sum,
ops::{Deref, Div},
};
use brk_error::{Error, Result};
use brk_types::{Height, TimePeriod, Timestamp};
use vecdb::{ReadableVec, VecValue};
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,
}
}
const fn round_half_up(sum: u64, n: u64) -> u64 {
(sum + n / 2) / n
}
pub struct BlockBucket {
pub avg_height: Height,
pub avg_timestamp: Timestamp,
offsets: Vec<usize>,
}
impl BlockBucket {
pub fn mean<T>(&self, values: &[T]) -> T
where
T: Copy + Sum + Div<usize, Output = T>,
{
self.offsets.iter().map(|&i| values[i]).sum::<T>() / self.offsets.len()
}
pub fn mean_rounded<T>(&self, values: &[T]) -> T
where
T: Copy + Deref<Target = u64> + From<u64>,
{
let n = self.offsets.len() as u64;
let sum: u64 = self.offsets.iter().map(|&i| *values[i]).sum();
T::from(round_half_up(sum, 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) -> Result<Self> {
let start = query.start_height(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 {
avg_height: Height::from(round_half_up(sum_h, n)),
avg_timestamp: 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) -> 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)
}
}