use std::sync::Arc;
use brk_traversable::{Traversable, TreeNode, make_leaf};
use schemars::JsonSchema;
use serde::Serialize;
use vecdb::{
AnyExportableVec, AnyVec, CachedBoxedVec, Formattable, ReadableBoxedVec, ReadableVec, TypedVec,
VecIndex, VecValue, Version, short_type_name,
};
pub struct LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue,
T: VecValue,
{
name: Arc<str>,
base_version: Version,
source: ReadableBoxedVec<I, S>,
window_starts: CachedBoxedVec<I, I>,
inclusive: bool,
compute: Arc<dyn Fn(S, S, usize) -> T + Send + Sync>,
}
impl<I, S, T> LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue + Default,
T: VecValue,
{
pub fn new(
name: &str,
version: Version,
source: ReadableBoxedVec<I, S>,
window_starts: CachedBoxedVec<I, I>,
inclusive: bool,
compute: impl Fn(S, S, usize) -> T + Send + Sync + 'static,
) -> Self {
Self {
name: Arc::from(name),
base_version: version,
source,
window_starts,
inclusive,
compute: Arc::new(compute),
}
}
#[inline]
fn ago_index(&self, start: usize) -> Option<usize> {
if self.inclusive {
start.checked_sub(1)
} else {
Some(start)
}
}
#[inline]
fn count(&self, current: usize, start: usize) -> usize {
if self.inclusive {
current - start + 1
} else {
current - start
}
}
fn for_each_window(&self, from: usize, to: usize, mut each: impl FnMut(T)) {
let window_starts = self.window_starts.cached();
let to = to.min(self.len()).min(window_starts.len());
if from >= to {
return;
}
let starts = &window_starts[from..to];
let current = self.source.collect_range_dyn(from, to);
let first_ago = starts
.iter()
.find_map(|start| self.ago_index(start.to_usize()));
let last_ago = starts
.iter()
.rev()
.find_map(|start| self.ago_index(start.to_usize()));
let ago = first_ago
.zip(last_ago)
.map(|(first, last)| self.source.collect_range_dyn(first, last + 1));
for (offset, (current, start)) in current.into_iter().zip(starts).enumerate() {
let start = start.to_usize();
let previous = self
.ago_index(start)
.and_then(|index| {
ago.as_ref()
.zip(first_ago)
.map(|(values, first)| values[index - first].clone())
})
.unwrap_or_default();
each((self.compute)(
current,
previous,
self.count(from + offset, start),
));
}
}
}
impl<I, S, T> Clone for LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue,
T: VecValue,
{
fn clone(&self) -> Self {
Self {
name: Arc::clone(&self.name),
base_version: self.base_version,
source: self.source.clone(),
window_starts: self.window_starts.clone(),
inclusive: self.inclusive,
compute: Arc::clone(&self.compute),
}
}
}
impl<I, S, T> AnyVec for LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue,
T: VecValue,
{
fn version(&self) -> Version {
self.base_version + self.source.version() + self.window_starts.version()
}
fn name(&self) -> &str {
&self.name
}
fn index_type_to_string(&self) -> &'static str {
I::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<I, S, T> TypedVec for LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue,
T: VecValue,
{
type I = I;
type T = T;
}
impl<I, S, T> ReadableVec<I, T> for LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue + Default,
T: VecValue,
{
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<T>) {
buf.reserve(to.saturating_sub(from));
self.for_each_window(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_window(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> {
let window_starts = self.window_starts.cached();
let start = window_starts.get(index)?.to_usize();
let current = self.source.collect_one_at(index)?;
let previous = self
.ago_index(start)
.and_then(|index| self.source.collect_one_at(index))
.unwrap_or_default();
Some((self.compute)(current, previous, self.count(index, start)))
}
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<I, S, T> Traversable for LazyWindowVec<I, S, T>
where
I: VecIndex,
S: VecValue + Default,
T: VecValue + 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::<I, T, _>(self)
}
}