use std::sync::Arc;
use brk_traversable::{Traversable, TreeNode, make_leaf};
use brk_types::{Day1, Height};
use schemars::JsonSchema;
use serde::Serialize;
use vecdb::{
AnyExportableVec, AnyVec, CachedBoxedVec, Formattable, PrintableIndex, ReadableBoxedVec,
ReadableVec, TypedVec, VecValue, Version, short_type_name,
};
pub struct LazySinceDayVec<S, T>
where
S: VecValue,
T: VecValue,
{
name: Arc<str>,
base_version: Version,
source: ReadableBoxedVec<Height, S>,
days: CachedBoxedVec<Height, Day1>,
start_day: Day1,
compute: Arc<dyn Fn(S, S) -> T + Send + Sync>,
}
impl<S, T> LazySinceDayVec<S, T>
where
S: VecValue + Default,
T: VecValue + Default,
{
pub fn new(
name: &str,
version: Version,
source: ReadableBoxedVec<Height, S>,
days: CachedBoxedVec<Height, Day1>,
start_day: Day1,
compute: impl Fn(S, S) -> T + Send + Sync + 'static,
) -> Self {
Self {
name: Arc::from(name),
base_version: version,
source,
days,
start_day,
compute: Arc::new(compute),
}
}
fn start_height(&self) -> usize {
let days = self.days.cached();
let mut left = 0;
let mut right = self.len().min(days.len());
while left < right {
let middle = left + (right - left) / 2;
if days[middle] < self.start_day {
left = middle + 1;
} else {
right = middle;
}
}
left
}
fn for_each_value(&self, from: usize, to: usize, mut each: impl FnMut(T)) {
let to = to.min(self.len());
if from >= to {
return;
}
let start = self.start_height();
let active_from = from.max(start).min(to);
for _ in from..active_from {
each(T::default());
}
if active_from == to {
return;
}
let before = start
.checked_sub(1)
.and_then(|index| self.source.collect_one_at(index))
.unwrap_or_default();
for current in self.source.collect_range_dyn(active_from, to) {
each((self.compute)(current, before.clone()));
}
}
}
impl<S, T> Clone for LazySinceDayVec<S, T>
where
S: VecValue,
T: VecValue,
{
fn clone(&self) -> Self {
Self {
name: Arc::clone(&self.name),
base_version: self.base_version,
source: self.source.clone(),
days: self.days.clone(),
start_day: self.start_day,
compute: Arc::clone(&self.compute),
}
}
}
impl<S, T> AnyVec for LazySinceDayVec<S, T>
where
S: VecValue,
T: VecValue,
{
fn version(&self) -> Version {
self.base_version + self.source.version() + self.days.version()
}
fn name(&self) -> &str {
&self.name
}
fn index_type_to_string(&self) -> &'static str {
<Height as PrintableIndex>::to_string()
}
fn len(&self) -> usize {
self.source.len()
}
fn value_type_to_size_of(&self) -> usize {
size_of::<T>()
}
fn value_type_to_string(&self) -> &'static str {
short_type_name::<T>()
}
fn region_names(&self) -> Vec<String> {
Vec::new()
}
}
impl<S, T> TypedVec for LazySinceDayVec<S, T>
where
S: VecValue,
T: VecValue,
{
type I = Height;
type T = T;
}
impl<S, T> ReadableVec<Height, T> for LazySinceDayVec<S, T>
where
S: VecValue + Default,
T: VecValue + Default,
{
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<T>) {
buf.reserve(to.saturating_sub(from));
self.for_each_value(from, to, |value| buf.push(value));
}
fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(T)) {
self.for_each_value(from, to, f);
}
fn fold_range_at<B, F: FnMut(B, T) -> B>(&self, from: usize, to: usize, init: B, f: F) -> B {
let mut values = Vec::with_capacity(to.saturating_sub(from));
self.read_into_at(from, to, &mut values);
values.into_iter().fold(init, f)
}
fn try_fold_range_at<B, E, F: FnMut(B, T) -> Result<B, E>>(
&self,
from: usize,
to: usize,
init: B,
f: F,
) -> Result<B, E> {
let mut values = Vec::with_capacity(to.saturating_sub(from));
self.read_into_at(from, to, &mut values);
values.into_iter().try_fold(init, f)
}
fn collect_one_at(&self, index: usize) -> Option<T> {
if index >= self.len() {
return None;
}
let start = self.start_height();
if index < start {
return Some(T::default());
}
let current = self.source.collect_one_at(index)?;
let before = start
.checked_sub(1)
.and_then(|index| self.source.collect_one_at(index))
.unwrap_or_default();
Some((self.compute)(current, before))
}
fn read_sorted_into_at(&self, indices: &[usize], out: &mut Vec<T>) {
out.reserve(indices.len());
indices
.iter()
.filter_map(|&index| self.collect_one_at(index))
.for_each(|value| out.push(value));
}
}
impl<S, T> Traversable for LazySinceDayVec<S, T>
where
S: VecValue + Default,
T: VecValue + Default + Formattable + Serialize + JsonSchema,
{
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
std::iter::once(self as &dyn AnyExportableVec)
}
fn to_tree_node(&self) -> TreeNode {
make_leaf::<Height, T, _>(self)
}
}