mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-16 05:28:12 -07:00
computer: simplified a bunch of things
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
//! Base generic struct with 2 type parameters — one per rolling window duration.
|
||||
//!
|
||||
//! Foundation for tx-derived rolling window types (1h, 24h — actual time-based).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct BlockWindows<A, B = A> {
|
||||
#[traversable(rename = "1h")]
|
||||
pub _1h: A,
|
||||
#[traversable(rename = "24h")]
|
||||
pub _24h: B,
|
||||
}
|
||||
|
||||
@@ -314,3 +314,108 @@ where
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute distribution stats from windowed ranges of a source vec.
|
||||
///
|
||||
/// For each index `i`, reads all source items from groups `window_starts[i]..=i`
|
||||
/// and computes average, min, max, median, and percentiles across the full window.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn compute_aggregations_windowed<I, T, A>(
|
||||
max_from: I,
|
||||
source: &impl ReadableVec<A, T>,
|
||||
first_indexes: &impl ReadableVec<I, A>,
|
||||
count_indexes: &impl ReadableVec<I, StoredU64>,
|
||||
window_starts: &impl ReadableVec<I, I>,
|
||||
exit: &Exit,
|
||||
min: &mut EagerVec<PcoVec<I, T>>,
|
||||
max: &mut EagerVec<PcoVec<I, T>>,
|
||||
average: &mut EagerVec<PcoVec<I, T>>,
|
||||
median: &mut EagerVec<PcoVec<I, T>>,
|
||||
pct10: &mut EagerVec<PcoVec<I, T>>,
|
||||
pct25: &mut EagerVec<PcoVec<I, T>>,
|
||||
pct75: &mut EagerVec<PcoVec<I, T>>,
|
||||
pct90: &mut EagerVec<PcoVec<I, T>>,
|
||||
) -> Result<()>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
A: VecIndex + VecValue + CheckedSub<A>,
|
||||
{
|
||||
let combined_version =
|
||||
source.version() + first_indexes.version() + count_indexes.version() + window_starts.version();
|
||||
|
||||
let mut idx = max_from;
|
||||
for vec in [&mut *min, &mut *max, &mut *average, &mut *median, &mut *pct10, &mut *pct25, &mut *pct75, &mut *pct90] {
|
||||
idx = validate_and_start(vec, combined_version, idx)?;
|
||||
}
|
||||
let index = idx;
|
||||
|
||||
let start = index.to_usize();
|
||||
let fi_len = first_indexes.len();
|
||||
|
||||
let first_indexes_batch: Vec<A> = first_indexes.collect_range_at(start, fi_len);
|
||||
let count_indexes_batch: Vec<StoredU64> = count_indexes.collect_range_at(start, fi_len);
|
||||
let window_starts_batch: Vec<I> = window_starts.collect_range_at(start, fi_len);
|
||||
|
||||
let zero = T::from(0_usize);
|
||||
|
||||
first_indexes_batch
|
||||
.iter()
|
||||
.zip(count_indexes_batch.iter())
|
||||
.zip(window_starts_batch.iter())
|
||||
.enumerate()
|
||||
.try_for_each(|(j, ((fi, ci), ws))| -> Result<()> {
|
||||
let idx = start + j;
|
||||
let window_start_offset = ws.to_usize();
|
||||
|
||||
// Last tx index (exclusive) of current block
|
||||
let count = u64::from(*ci) as usize;
|
||||
let range_end_usize = fi.to_usize() + count;
|
||||
|
||||
// First tx index of the window start block
|
||||
let range_start_usize = if window_start_offset >= start {
|
||||
first_indexes_batch[window_start_offset - start].to_usize()
|
||||
} else {
|
||||
first_indexes
|
||||
.collect_one_at(window_start_offset)
|
||||
.unwrap()
|
||||
.to_usize()
|
||||
};
|
||||
|
||||
let effective_count = range_end_usize.saturating_sub(range_start_usize);
|
||||
|
||||
if effective_count == 0 {
|
||||
for vec in [&mut *min, &mut *max, &mut *average, &mut *median, &mut *pct10, &mut *pct25, &mut *pct75, &mut *pct90] {
|
||||
vec.truncate_push_at(idx, zero)?;
|
||||
}
|
||||
} else {
|
||||
let mut values: Vec<T> =
|
||||
source.collect_range_at(range_start_usize, range_end_usize);
|
||||
|
||||
// Compute sum before sorting
|
||||
let len = values.len();
|
||||
let sum_val = values.iter().copied().fold(T::from(0), |a, b| a + b);
|
||||
let avg = sum_val / len;
|
||||
|
||||
values.sort_unstable();
|
||||
|
||||
max.truncate_push_at(idx, *values.last().unwrap())?;
|
||||
pct90.truncate_push_at(idx, get_percentile(&values, 0.90))?;
|
||||
pct75.truncate_push_at(idx, get_percentile(&values, 0.75))?;
|
||||
median.truncate_push_at(idx, get_percentile(&values, 0.50))?;
|
||||
pct25.truncate_push_at(idx, get_percentile(&values, 0.25))?;
|
||||
pct10.truncate_push_at(idx, get_percentile(&values, 0.10))?;
|
||||
min.truncate_push_at(idx, *values.first().unwrap())?;
|
||||
average.truncate_push_at(idx, avg)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let _lock = exit.lock();
|
||||
for vec in [min, max, average, median, pct10, pct25, pct75, pct90] {
|
||||
vec.write()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,43 +7,44 @@ use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
indexes_from,
|
||||
internal::{ComputedVecValue, Indexes, NumericValue},
|
||||
ComputeIndexes,
|
||||
use vecdb::{
|
||||
Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Rw, StorageMode, VecIndex,
|
||||
};
|
||||
|
||||
pub type EagerIndexesInner<T, M> = Indexes<
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute5, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute30, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour4, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour12, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Day1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Day3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Week1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month6, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Year1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Year10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<HalvingEpoch, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<DifficultyEpoch, T>>>,
|
||||
>;
|
||||
use crate::{
|
||||
ComputeIndexes, indexes, indexes_from,
|
||||
internal::{ComputedVecValue, Indexes, NumericValue},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct EagerIndexes<T, M: StorageMode = Rw>(pub EagerIndexesInner<T, M>)
|
||||
pub struct EagerIndexes<T, M: StorageMode = Rw>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute5, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Minute30, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour4, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Hour12, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Day1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Day3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Week1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month3, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Month6, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Year1, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<Year10, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<HalvingEpoch, T>>>,
|
||||
<M as StorageMode>::Stored<EagerVec<PcoVec<DifficultyEpoch, T>>>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Minute1, Minute10, Minute30, Minute5, Month1,
|
||||
Month3, Month6, Version, Week1, Year1, Year10, Hour1, Hour4, Hour12,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10,
|
||||
Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
@@ -17,29 +17,30 @@ use crate::{
|
||||
internal::{ComputedVecValue, EagerIndexes, Indexes},
|
||||
};
|
||||
|
||||
pub type LazyEagerIndexesInner<T, S> = Indexes<
|
||||
LazyVecFrom1<Minute1, T, Minute1, S>,
|
||||
LazyVecFrom1<Minute5, T, Minute5, S>,
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
LazyVecFrom1<Hour4, T, Hour4, S>,
|
||||
LazyVecFrom1<Hour12, T, Hour12, S>,
|
||||
LazyVecFrom1<Day1, T, Day1, S>,
|
||||
LazyVecFrom1<Day3, T, Day3, S>,
|
||||
LazyVecFrom1<Week1, T, Week1, S>,
|
||||
LazyVecFrom1<Month1, T, Month1, S>,
|
||||
LazyVecFrom1<Month3, T, Month3, S>,
|
||||
LazyVecFrom1<Month6, T, Month6, S>,
|
||||
LazyVecFrom1<Year1, T, Year1, S>,
|
||||
LazyVecFrom1<Year10, T, Year10, S>,
|
||||
LazyVecFrom1<HalvingEpoch, T, HalvingEpoch, S>,
|
||||
LazyVecFrom1<DifficultyEpoch, T, DifficultyEpoch, S>,
|
||||
>;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyEagerIndexes<T, S>(pub LazyEagerIndexesInner<T, S>)
|
||||
pub struct LazyEagerIndexes<T, S>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyVecFrom1<Minute1, T, Minute1, S>,
|
||||
LazyVecFrom1<Minute5, T, Minute5, S>,
|
||||
LazyVecFrom1<Minute10, T, Minute10, S>,
|
||||
LazyVecFrom1<Minute30, T, Minute30, S>,
|
||||
LazyVecFrom1<Hour1, T, Hour1, S>,
|
||||
LazyVecFrom1<Hour4, T, Hour4, S>,
|
||||
LazyVecFrom1<Hour12, T, Hour12, S>,
|
||||
LazyVecFrom1<Day1, T, Day1, S>,
|
||||
LazyVecFrom1<Day3, T, Day3, S>,
|
||||
LazyVecFrom1<Week1, T, Week1, S>,
|
||||
LazyVecFrom1<Month1, T, Month1, S>,
|
||||
LazyVecFrom1<Month3, T, Month3, S>,
|
||||
LazyVecFrom1<Month6, T, Month6, S>,
|
||||
LazyVecFrom1<Year1, T, Year1, S>,
|
||||
LazyVecFrom1<Year10, T, Year10, S>,
|
||||
LazyVecFrom1<HalvingEpoch, T, HalvingEpoch, S>,
|
||||
LazyVecFrom1<DifficultyEpoch, T, DifficultyEpoch, S>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S: ComputedVecValue;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod block_windows;
|
||||
mod compute;
|
||||
mod distribution_stats;
|
||||
mod eager_indexes;
|
||||
@@ -8,6 +9,7 @@ mod single;
|
||||
mod traits;
|
||||
mod windows;
|
||||
|
||||
pub(crate) use block_windows::*;
|
||||
pub(crate) use compute::*;
|
||||
pub(crate) use distribution_stats::*;
|
||||
pub(crate) use eager_indexes::*;
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
//! Lazy binary transform from two SumCum sources, producing Last (cumulative) ratios only.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2};
|
||||
|
||||
use crate::{
|
||||
indexes_from,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedHeightDerivedLast,
|
||||
ComputedVecValue, LazyBinaryComputedFromHeightLast, LazyBinaryHeightDerivedLast,
|
||||
LazyBinaryTransformLast, LazyFromHeightLast, NumericValue,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryFromHeightLast<T, S1T = T, S2T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<LazyBinaryHeightDerivedLast<T, S1T, S2T>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Helper macro: given two deref-able sources whose `.$p` fields implement
|
||||
/// `ReadableCloneableVec`, build all 17 period fields of a `LazyBinaryHeightDerivedLast`.
|
||||
macro_rules! build_rest {
|
||||
($name:expr, $v:expr, $source1:expr, $source2:expr) => {{
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyBinaryTransformLast::from_vecs::<F>(
|
||||
$name,
|
||||
$v,
|
||||
$source1.$p.read_only_boxed_clone(),
|
||||
$source2.$p.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
Box::new(LazyBinaryHeightDerivedLast(indexes_from!(period)))
|
||||
}};
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryFromHeightLast<T, S1T, S2T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_computed_sum_cum<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightSumCum<S1T>,
|
||||
source2: &ComputedFromHeightSumCum<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: PartialOrd,
|
||||
S2T: PartialOrd,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height_cumulative.read_only_boxed_clone(),
|
||||
source2.height_cumulative.read_only_boxed_clone(),
|
||||
),
|
||||
rest: Box::new(LazyBinaryHeightDerivedLast::from_computed_sum_cum::<F>(name, v, source1, source2)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_computed_last<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
S2T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: Box::new(LazyBinaryHeightDerivedLast::from_computed_last::<F>(name, v, source1, source2)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_block_last_and_lazy_block_last<F, S2SourceT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &LazyFromHeightLast<S2T, S2SourceT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1T: NumericValue,
|
||||
S2SourceT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: Box::new(LazyBinaryHeightDerivedLast::from_block_last_and_lazy_block_last::<F, _>(
|
||||
name, v, source1, source2,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_lazy_block_last_and_block_last<F, S1SourceT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyFromHeightLast<S1T, S1SourceT>,
|
||||
source2: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S2T: NumericValue,
|
||||
S1SourceT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: Box::new(LazyBinaryHeightDerivedLast::from_lazy_block_last_and_block_last::<F, _>(
|
||||
name, v, source1, source2,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from a ComputedFromHeightLast and a LazyBinaryFromHeightLast.
|
||||
pub(crate) fn from_block_last_and_binary_block<F, S2aT, S2bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &LazyBinaryFromHeightLast<S2T, S2aT, S2bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1T: NumericValue,
|
||||
S2aT: ComputedVecValue + JsonSchema,
|
||||
S2bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: build_rest!(name, v, source1, source2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from two LazyBinaryFromHeightLast sources.
|
||||
pub(crate) fn from_both_binary_block<F, S1aT, S1bT, S2aT, S2bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyBinaryFromHeightLast<S1T, S1aT, S1bT>,
|
||||
source2: &LazyBinaryFromHeightLast<S2T, S2aT, S2bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
S2aT: ComputedVecValue + JsonSchema,
|
||||
S2bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: build_rest!(name, v, source1, source2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from separate height sources and two `ComputedHeightDerivedLast` structs.
|
||||
pub(crate) fn from_height_and_derived_last<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source1: ReadableBoxedVec<Height, S1T>,
|
||||
height_source2: ReadableBoxedVec<Height, S2T>,
|
||||
derived1: &ComputedHeightDerivedLast<S1T>,
|
||||
derived2: &ComputedHeightDerivedLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
S2T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(name, v, height_source1, height_source2),
|
||||
rest: build_rest!(name, v, derived1, derived2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from a ComputedFromHeightLast and a LazyBinaryComputedFromHeightLast.
|
||||
pub(crate) fn from_block_last_and_lazy_binary_computed_block_last<F, S2aT, S2bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &LazyBinaryComputedFromHeightLast<S2T, S2aT, S2bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1T: NumericValue,
|
||||
S2aT: ComputedVecValue + JsonSchema,
|
||||
S2bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: build_rest!(name, v, source1, source2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from two LazyBinaryComputedFromHeightLast sources.
|
||||
pub(crate) fn from_both_lazy_binary_computed_block_last<F, S1aT, S1bT, S2aT, S2bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyBinaryComputedFromHeightLast<S1T, S1aT, S1bT>,
|
||||
source2: &LazyBinaryComputedFromHeightLast<S2T, S2aT, S2bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
S2aT: ComputedVecValue + JsonSchema,
|
||||
S2bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: build_rest!(name, v, source1, source2),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from a LazyBinaryFromHeightLast and a LazyBinaryComputedFromHeightLast.
|
||||
pub(crate) fn from_binary_block_and_lazy_binary_block_last<F, S1aT, S1bT, S2aT, S2bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyBinaryFromHeightLast<S1T, S1aT, S1bT>,
|
||||
source2: &LazyBinaryComputedFromHeightLast<S2T, S2aT, S2bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
S2aT: ComputedVecValue + JsonSchema,
|
||||
S2bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
height: LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.height.read_only_boxed_clone(),
|
||||
source2.height.read_only_boxed_clone(),
|
||||
),
|
||||
rest: build_rest!(name, v, source1, source2),
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-31
@@ -1,38 +1,34 @@
|
||||
//! ComputedFromHeightCum - stored height + LazyLast + cumulative (from height).
|
||||
//! ComputedFromHeightCumulative - stored height + LazyAggVec + cumulative (from height).
|
||||
//!
|
||||
//! Like ComputedFromHeightCumSum but without RollingWindows.
|
||||
//! Like ComputedFromHeightCumulativeSum but without RollingWindows.
|
||||
//! Used for distribution metrics where rolling is optional per cohort.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, NumericValue},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightCum<T, M: StorageMode = Rw>
|
||||
pub struct ComputedFromHeightCumulative<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub last: ComputedFromHeightLast<T, M>,
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: ComputedFromHeightLast<T, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedFromHeightCum<T>
|
||||
impl<T> ComputedFromHeightCumulative<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
@@ -44,15 +40,11 @@ where
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
let cumulative = ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
let cumulative =
|
||||
ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
|
||||
Ok(Self { last, cumulative })
|
||||
Ok(Self { height, cumulative })
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then cumulative only (no rolling).
|
||||
@@ -65,25 +57,18 @@ where
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
compute_height(&mut self.last.height)?;
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.last.height, exit)?;
|
||||
Ok(())
|
||||
compute_height(&mut self.height)?;
|
||||
self.compute_rest(max_from, exit)
|
||||
}
|
||||
|
||||
/// Compute cumulative from already-filled height vec.
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.last.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+14
-22
@@ -1,32 +1,28 @@
|
||||
//! ComputedFromHeightCumFull - stored height + LazyLast + cumulative (from height) + RollingFull.
|
||||
//! ComputedFromHeightCumulativeFull - stored height + LazyAggVec + cumulative (from height) + RollingFull.
|
||||
//!
|
||||
//! For metrics with stored per-block data, cumulative sums, and rolling windows.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, NumericValue, RollingFull, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightCumFull<T, M: StorageMode = Rw>
|
||||
pub struct ComputedFromHeightCumulativeFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub last: ComputedFromHeightLast<T, M>,
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: ComputedFromHeightLast<T, M>,
|
||||
#[traversable(flatten)]
|
||||
@@ -35,7 +31,7 @@ where
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedFromHeightCumFull<T>
|
||||
impl<T> ComputedFromHeightCumulativeFull<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
@@ -47,17 +43,13 @@ where
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
let cumulative = ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
let cumulative =
|
||||
ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let rolling = RollingFull::forced_import(db, name, v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
last,
|
||||
height,
|
||||
cumulative,
|
||||
rolling,
|
||||
})
|
||||
@@ -75,12 +67,12 @@ where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_height(&mut self.last.height)?;
|
||||
compute_height(&mut self.height)?;
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.last.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
self.rolling
|
||||
.compute(max_from, windows, &self.last.height, exit)?;
|
||||
.compute(max_from, windows, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+15
-23
@@ -1,33 +1,29 @@
|
||||
//! ComputedFromHeightCumSum - stored height + LazyLast + cumulative (from height) + RollingWindows (sum).
|
||||
//! ComputedFromHeightCumulativeSum - stored height + LazyAggVec + cumulative (from height) + RollingWindows (sum).
|
||||
//!
|
||||
//! Like ComputedFromHeightCumFull but with rolling sum only (no distribution).
|
||||
//! Like ComputedFromHeightCumulativeFull but with rolling sum only (no distribution).
|
||||
//! Used for count metrics where distribution stats aren't meaningful.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightLast, NumericValue, RollingWindows, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightCumSum<T, M: StorageMode = Rw>
|
||||
pub struct ComputedFromHeightCumulativeSum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub last: ComputedFromHeightLast<T, M>,
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: ComputedFromHeightLast<T, M>,
|
||||
#[traversable(flatten)]
|
||||
@@ -36,7 +32,7 @@ where
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedFromHeightCumSum<T>
|
||||
impl<T> ComputedFromHeightCumulativeSum<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
@@ -48,17 +44,13 @@ where
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let last = ComputedFromHeightLast::forced_import(db, name, v, indexes)?;
|
||||
let cumulative = ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
let cumulative =
|
||||
ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let rolling = RollingWindows::forced_import(db, name, v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
last,
|
||||
height,
|
||||
cumulative,
|
||||
rolling,
|
||||
})
|
||||
@@ -75,12 +67,12 @@ where
|
||||
where
|
||||
T: Default + SubAssign,
|
||||
{
|
||||
compute_height(&mut self.last.height)?;
|
||||
compute_height(&mut self.height)?;
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, &self.last.height, exit)?;
|
||||
.compute_cumulative(max_from, &self.height, exit)?;
|
||||
self.rolling
|
||||
.compute_rolling_sum(max_from, windows, &self.last.height, exit)?;
|
||||
.compute_rolling_sum(max_from, windows, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//! ComputedFromHeight using Distribution aggregation (no sum/cumulative).
|
||||
//!
|
||||
//! Stored height data + LazyAggVec index views + rolling distribution windows.
|
||||
//! Use for block-based metrics where sum/cumulative would be misleading
|
||||
//! (e.g., activity counts that can't be deduplicated across blocks).
|
||||
|
||||
@@ -7,25 +8,22 @@ use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedDistribution, ComputedVecValue, NumericValue};
|
||||
use crate::internal::{ComputedVecValue, NumericValue, RollingDistribution, WindowStarts};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedDistribution<T>>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingDistribution<T, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
@@ -43,14 +41,26 @@ where
|
||||
let v = version + VERSION;
|
||||
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
let rolling = RollingDistribution::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let rest = ComputedHeightDerivedDistribution::forced_import(
|
||||
name,
|
||||
height.read_only_boxed_clone(),
|
||||
v,
|
||||
indexes,
|
||||
);
|
||||
Ok(Self { height, rolling })
|
||||
}
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
/// Compute height data via closure, then rolling distribution.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
self.rolling
|
||||
.compute_distribution(max_from, windows, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//! ComputedFromHeight with full stats aggregation.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedFull, ComputedVecValue, NumericValue};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedFull<T, M>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedFromHeightFull<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
|
||||
let rest = ComputedHeightDerivedFull::forced_import(
|
||||
db,
|
||||
name,
|
||||
height.read_only_boxed_clone(),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
rest: Box::new(rest),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
use vecdb::{
|
||||
BinaryTransform, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableCloneableVec,
|
||||
ReadableVec, Rw, StorageMode, VecValue,
|
||||
};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
@@ -41,9 +44,39 @@ where
|
||||
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedLast::forced_import(name, height.read_only_boxed_clone(), v, indexes);
|
||||
let rest = ComputedHeightDerivedLast::forced_import(
|
||||
name,
|
||||
height.read_only_boxed_clone(),
|
||||
v,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
Ok(Self {
|
||||
height,
|
||||
rest: Box::new(rest),
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly compute this vec as a binary transform of two sources.
|
||||
pub(crate) fn compute_binary<S1T, S2T, F>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
source1: &impl ReadableVec<Height, S1T>,
|
||||
source2: &impl ReadableVec<Height, S2T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S1T: VecValue,
|
||||
S2T: VecValue,
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
{
|
||||
self.height.compute_transform2(
|
||||
max_from,
|
||||
source1,
|
||||
source2,
|
||||
|(h, s1, s2, ..)| (h, F::apply(s1, s2)),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
//! LazyBinaryComputedFromHeightDistribution - lazy binary transform with distribution stats.
|
||||
//!
|
||||
//! Height-level values are lazy: `transform(source1[h], source2[h])`.
|
||||
//! Uses Distribution aggregation (no sum/cumulative) - appropriate for ratios.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedHeightDerivedDistribution, ComputedVecValue, NumericValue},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy binary transform at height with distribution stats (no sum/cumulative).
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryComputedFromHeightDistribution<T, S1T = T, S2T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedDistribution<T>>,
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryComputedFromHeightDistribution<T, S1T, S2T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<Height, S1T>,
|
||||
source2: ReadableBoxedVec<Height, S2T>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height = LazyVecFrom2::transformed::<F>(name, v, source1, source2);
|
||||
|
||||
let rest = ComputedHeightDerivedDistribution::forced_import(
|
||||
name,
|
||||
height.read_only_boxed_clone(),
|
||||
v,
|
||||
indexes,
|
||||
);
|
||||
|
||||
Self { height, rest: Box::new(rest) }
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
//! LazyBinaryComputedFromHeightFull - block full with lazy binary transform at height level.
|
||||
//!
|
||||
//! Height-level values are lazy: `transform(source1[h], source2[h])`.
|
||||
//! Cumulative, day1 stats, and difficultyepoch are stored since they
|
||||
//! require aggregation across heights.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{ComputedHeightDerivedFull, ComputedVecValue, NumericValue},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Block full aggregation with lazy binary transform at height + computed derived indexes.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryComputedFromHeightFull<T, S1T = T, S2T = T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedFull<T, M>>,
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryComputedFromHeightFull<T, S1T, S2T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import<F: BinaryTransform<S1T, S2T, T>>(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<Height, S1T>,
|
||||
source2: ReadableBoxedVec<Height, S2T>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height = LazyVecFrom2::transformed::<F>(name, v, source1, source2);
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedFull::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?;
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.rest
|
||||
.compute_cumulative(starting_indexes, &self.height, exit)
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
//! LazyBinaryComputedFromHeightLast - block last with lazy binary transform at height level.
|
||||
//!
|
||||
//! Height-level value is lazy: `transform(source1[h], source2[h])`.
|
||||
//! Day1 last is stored since it requires finding the last value within each date
|
||||
//! (which may span multiple heights with varying prices).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedHeightDerivedLast, ComputedVecValue, NumericValue},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Block last aggregation with lazy binary transform at height + computed derived indexes.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryComputedFromHeightLast<T, S1T = T, S2T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub rest: Box<ComputedHeightDerivedLast<T>>,
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryComputedFromHeightLast<T, S1T, S2T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<Height, S1T>,
|
||||
source2: ReadableBoxedVec<Height, S2T>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height = LazyVecFrom2::transformed::<F>(name, v, source1, source2);
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedLast::forced_import(name, height.read_only_boxed_clone(), v, indexes);
|
||||
|
||||
Self { height, rest: Box::new(rest) }
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//! LazyBinaryComputedFromHeightSumCum - block sum_cum with lazy binary transform at height level.
|
||||
//!
|
||||
//! Height-level sum is lazy: `transform(source1[h], source2[h])`.
|
||||
//! Cumulative and day1 stats are stored since they require aggregation
|
||||
//! across heights.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes,
|
||||
indexes,
|
||||
internal::{ComputedHeightDerivedSumCum, ComputedVecValue, NumericValue},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Block sum_cum aggregation with lazy binary transform at height + computed derived indexes.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryComputedFromHeightSumCum<T, S1T = T, S2T = T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub rest: Box<ComputedHeightDerivedSumCum<T, M>>,
|
||||
}
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryComputedFromHeightSumCum<T, S1T, S2T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import<F: BinaryTransform<S1T, S2T, T>>(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<Height, S1T>,
|
||||
source2: ReadableBoxedVec<Height, S2T>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height = LazyVecFrom2::transformed::<F>(name, v, source1, source2);
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedSumCum::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?;
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.rest
|
||||
.derive_from(starting_indexes, &self.height, exit)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,34 @@
|
||||
//! LazyComputedFromHeightFull - block full with lazy height transform.
|
||||
//! LazyComputedFromHeightCumulativeFull - block full with lazy height transform + cumulative + rolling.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, LazyVecFrom1, UnaryTransform, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, LazyVecFrom1, ReadableCloneableVec, Rw, StorageMode, UnaryTransform};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes,
|
||||
indexes,
|
||||
internal::{ComputedVecValue, ComputedHeightDerivedFull, NumericValue},
|
||||
internal::{ComputedHeightDerivedCumulativeFull, ComputedVecValue, NumericValue, WindowStarts},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Block full aggregation with lazy height transform + computed derived indexes.
|
||||
/// Block full aggregation with lazy height transform + cumulative + rolling windows.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyComputedFromHeightFull<T, S = T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
T: NumericValue + JsonSchema,
|
||||
S: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: LazyVecFrom1<Height, T, Height, S>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedFull<T, M>>,
|
||||
pub rest: Box<ComputedHeightDerivedCumulativeFull<T, M>>,
|
||||
}
|
||||
|
||||
impl<T, S> LazyComputedFromHeightFull<T, S>
|
||||
@@ -46,18 +47,29 @@ where
|
||||
|
||||
let height = LazyVecFrom1::transformed::<F>(name, v, source.read_only_boxed_clone());
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedFull::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?;
|
||||
let rest = ComputedHeightDerivedCumulativeFull::forced_import(
|
||||
db,
|
||||
name,
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
Ok(Self {
|
||||
height,
|
||||
rest: Box::new(rest),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.rest
|
||||
.compute_cumulative(starting_indexes, &self.height, exit)
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + SubAssign + Copy + Ord,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.rest.compute(max_from, windows, &self.height, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
//! LazyComputedFromHeightSumCum - block sum+cumulative with lazy height transform.
|
||||
//!
|
||||
//! Use this when you need:
|
||||
//! - Lazy height (binary transform from two sources)
|
||||
//! - Stored cumulative and day1 aggregates
|
||||
//! - Lazy coarser period lookups
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, LazyVecFrom2, Rw, StorageMode};
|
||||
|
||||
use crate::{indexes, ComputeIndexes};
|
||||
|
||||
use crate::internal::{ComputedVecValue, ComputedHeightDerivedSumCum, NumericValue};
|
||||
|
||||
/// Block sum+cumulative with lazy binary height transform + computed derived indexes.
|
||||
///
|
||||
/// Height is a lazy binary transform (e.g., mask × source, or price × sats).
|
||||
/// Cumulative and day1 are stored (computed from lazy height).
|
||||
/// Coarser periods are lazy lookups.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyComputedFromHeightSumCum<T, S1T = T, S2T = T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<ComputedHeightDerivedSumCum<T, M>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T, S2T> LazyComputedFromHeightSumCum<T, S1T, S2T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let rest = ComputedHeightDerivedSumCum::forced_import(
|
||||
db,
|
||||
name,
|
||||
height.read_only_boxed_clone(),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.rest
|
||||
.derive_from(starting_indexes, &self.height, exit)
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//! Lazy unary transform from height with Full aggregation.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightFull, ComputedVecValue, LazyHeightDerivedFull,
|
||||
NumericValue,
|
||||
};
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyFromHeightFull<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "base")]
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: Box<LazyHeightDerivedFull<T, S1T>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T> LazyFromHeightFull<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: ReadableBoxedVec<Height, S1T>,
|
||||
source: &ComputedFromHeightFull<S1T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
rest: Box::new(LazyHeightDerivedFull::from_derived_computed::<F>(name, v, &source.rest)),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,11 @@ use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightLast,
|
||||
ComputedVecValue, LazyBinaryComputedFromHeightLast, LazyBinaryFromHeightLast,
|
||||
LazyHeightDerivedLast, NumericValue,
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedVecValue, LazyHeightDerivedLast, NumericValue,
|
||||
},
|
||||
};
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
@@ -48,22 +49,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_lazy_binary_computed<F, S1aT, S1bT>(
|
||||
pub(crate) fn from_height_source<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: ReadableBoxedVec<Height, S1T>,
|
||||
source: &LazyBinaryComputedFromHeightLast<S1T, S1aT, S1bT>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self
|
||||
where
|
||||
F: UnaryTransform<S1T, T>,
|
||||
S1T: NumericValue,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
rest: Box::new(LazyHeightDerivedLast::from_derived_computed::<F>(name, v, &source.rest)),
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source.clone()),
|
||||
rest: Box::new(LazyHeightDerivedLast::from_height_source::<F>(name, v, height_source, indexes)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,21 +82,4 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Create by unary-transforming a LazyBinaryFromHeightLast source.
|
||||
pub(crate) fn from_binary<F, S1aT, S1bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &LazyBinaryFromHeightLast<S1T, S1aT, S1bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: UnaryTransform<S1T, T>,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, source.height.read_only_boxed_clone()),
|
||||
rest: Box::new(LazyHeightDerivedLast::from_binary::<F, _, _>(name, v, &source.rest)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
//! Lazy unary transform from height with SumCum aggregation.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightSumCum, ComputedHeightDerivedSumCum, ComputedVecValue,
|
||||
LazyHeightDerivedSumCum, NumericValue,
|
||||
};
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyFromHeightSumCum<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub rest: Box<LazyHeightDerivedSumCum<T, S1T>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T> LazyFromHeightSumCum<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: ReadableBoxedVec<Height, S1T>,
|
||||
source: &ComputedFromHeightSumCum<S1T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
rest: Box::new(LazyHeightDerivedSumCum::from_derived_computed::<F>(name, v, &source.rest)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_derived<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: ReadableBoxedVec<Height, S1T>,
|
||||
source: &ComputedHeightDerivedSumCum<S1T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, v, height_source),
|
||||
rest: Box::new(LazyHeightDerivedSumCum::from_derived_computed::<F>(name, v, source)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{BinaryTransform, ReadableBoxedVec, LazyVecFrom1, LazyVecFrom2, UnaryTransform};
|
||||
|
||||
use crate::internal::LazyDerivedValuesHeight;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyFromHeightValue {
|
||||
#[traversable(rename = "sats")]
|
||||
pub sats: LazyVecFrom1<Height, Sats, Height, Sats>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub rest: LazyDerivedValuesHeight,
|
||||
}
|
||||
|
||||
impl LazyFromHeightValue {
|
||||
pub(crate) fn from_sources<SatsTransform, BitcoinTransform, DollarsTransform>(
|
||||
name: &str,
|
||||
sats_source: ReadableBoxedVec<Height, Sats>,
|
||||
price_source: ReadableBoxedVec<Height, Dollars>,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
DollarsTransform: BinaryTransform<Dollars, Sats, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = LazyVecFrom1::transformed::<SatsTransform>(name, v, sats_source.clone());
|
||||
|
||||
let btc = LazyVecFrom1::transformed::<BitcoinTransform>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats_source.clone(),
|
||||
);
|
||||
|
||||
let usd = LazyVecFrom2::transformed::<DollarsTransform>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
price_source,
|
||||
sats_source,
|
||||
);
|
||||
|
||||
Self {
|
||||
sats,
|
||||
rest: LazyDerivedValuesHeight { btc, usd },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,41 @@
|
||||
mod binary_last;
|
||||
mod constant;
|
||||
mod cum;
|
||||
mod cum_rolling_full;
|
||||
mod cum_rolling_sum;
|
||||
mod cumulative;
|
||||
mod cumulative_rolling_full;
|
||||
mod cumulative_rolling_sum;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod last;
|
||||
mod lazy_binary_computed_distribution;
|
||||
mod lazy_binary_computed_full;
|
||||
mod lazy_binary_computed_last;
|
||||
mod lazy_binary_computed_sum_cum;
|
||||
mod lazy_computed_full;
|
||||
mod lazy_computed_sum_cum;
|
||||
mod lazy_full;
|
||||
mod lazy_last;
|
||||
mod lazy_sum_cum;
|
||||
mod lazy_value;
|
||||
mod percentiles;
|
||||
mod price;
|
||||
mod ratio;
|
||||
mod stddev;
|
||||
mod stored_value_last;
|
||||
mod sum_cum;
|
||||
mod value_change;
|
||||
mod value_ema;
|
||||
mod value_full;
|
||||
mod value_last;
|
||||
mod value_lazy_binary_last;
|
||||
mod value_lazy_computed_cum;
|
||||
mod value_lazy_computed_cumulative;
|
||||
mod value_lazy_last;
|
||||
mod value_lazy_sum_cum;
|
||||
mod value_sum_cum;
|
||||
mod value_sum_cumulative;
|
||||
|
||||
pub use binary_last::*;
|
||||
pub use constant::*;
|
||||
pub use cum::*;
|
||||
pub use cum_rolling_full::*;
|
||||
pub use cum_rolling_sum::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_rolling_full::*;
|
||||
pub use cumulative_rolling_sum::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use last::*;
|
||||
pub use lazy_binary_computed_distribution::*;
|
||||
pub use lazy_binary_computed_full::*;
|
||||
pub use lazy_binary_computed_last::*;
|
||||
pub use lazy_binary_computed_sum_cum::*;
|
||||
pub use lazy_computed_full::*;
|
||||
pub use lazy_computed_sum_cum::*;
|
||||
pub use lazy_full::*;
|
||||
pub use lazy_last::*;
|
||||
pub use lazy_sum_cum::*;
|
||||
pub use lazy_value::*;
|
||||
pub use percentiles::*;
|
||||
pub use price::*;
|
||||
pub use ratio::*;
|
||||
pub use stddev::*;
|
||||
pub use stored_value_last::*;
|
||||
pub use sum_cum::*;
|
||||
pub use value_change::*;
|
||||
pub use value_ema::*;
|
||||
pub use value_full::*;
|
||||
pub use value_last::*;
|
||||
pub use value_lazy_binary_last::*;
|
||||
pub use value_lazy_computed_cum::*;
|
||||
pub use value_lazy_computed_cumulative::*;
|
||||
pub use value_lazy_last::*;
|
||||
pub use value_lazy_sum_cum::*;
|
||||
pub use value_sum_cum::*;
|
||||
pub use value_sum_cumulative::*;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::{Traversable, TreeNode};
|
||||
use brk_types::{Dollars, Height, StoredF32, Version};
|
||||
use vecdb::{
|
||||
AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec,
|
||||
};
|
||||
use vecdb::{AnyExportableVec, Database, ReadOnlyClone, Ro, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::indexes;
|
||||
use crate::internal::{ComputedFromHeightLast, Price, PriceFromHeight};
|
||||
use crate::internal::{ComputedFromHeightLast, Price};
|
||||
|
||||
pub const PERCENTILES: [u8; 19] = [
|
||||
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
|
||||
@@ -15,7 +13,10 @@ pub const PERCENTILES_LEN: usize = PERCENTILES.len();
|
||||
|
||||
/// Compute spot percentile rank by interpolating within percentile bands.
|
||||
/// Returns a value between 0 and 100 indicating where spot sits in the distribution.
|
||||
pub(crate) fn compute_spot_percentile_rank(percentile_prices: &[Dollars; PERCENTILES_LEN], spot: Dollars) -> StoredF32 {
|
||||
pub(crate) fn compute_spot_percentile_rank(
|
||||
percentile_prices: &[Dollars; PERCENTILES_LEN],
|
||||
spot: Dollars,
|
||||
) -> StoredF32 {
|
||||
if spot.is_nan() || percentile_prices[0].is_nan() {
|
||||
return StoredF32::NAN;
|
||||
}
|
||||
@@ -83,7 +84,8 @@ impl PercentilesVecs {
|
||||
let vecs = PERCENTILES.map(|p| {
|
||||
compute.then(|| {
|
||||
let metric_name = format!("{prefix}_pct{p:02}");
|
||||
PriceFromHeight::forced_import(db, &metric_name, version + VERSION, indexes).unwrap()
|
||||
Price::forced_import(db, &metric_name, version + VERSION, indexes)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
@@ -98,7 +100,7 @@ impl PercentilesVecs {
|
||||
) -> Result<()> {
|
||||
for (i, vec) in self.vecs.iter_mut().enumerate() {
|
||||
if let Some(v) = vec {
|
||||
v.height.truncate_push(height, percentile_prices[i])?;
|
||||
v.usd.height.truncate_push(height, percentile_prices[i])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -107,7 +109,7 @@ impl PercentilesVecs {
|
||||
/// Validate computed versions or reset if mismatched.
|
||||
pub(crate) fn validate_computed_version_or_reset(&mut self, version: Version) -> Result<()> {
|
||||
for vec in self.vecs.iter_mut().flatten() {
|
||||
vec.height.validate_computed_version_or_reset(version)?;
|
||||
vec.usd.height.validate_computed_version_or_reset(version)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -118,7 +120,10 @@ impl ReadOnlyClone for PercentilesVecs {
|
||||
|
||||
fn read_only_clone(&self) -> Self::ReadOnly {
|
||||
PercentilesVecs {
|
||||
vecs: self.vecs.each_ref().map(|v| v.as_ref().map(|p| p.read_only_clone())),
|
||||
vecs: self
|
||||
.vecs
|
||||
.each_ref()
|
||||
.map(|v| v.as_ref().map(|p| p.read_only_clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,35 +5,25 @@
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Dollars, SatsFract, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use brk_types::{Dollars, SatsFract, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, Database, ReadableCloneableVec, UnaryTransform};
|
||||
use vecdb::{Database, ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use super::{ComputedFromHeightLast, LazyBinaryFromHeightLast, LazyFromHeightLast};
|
||||
use super::{ComputedFromHeightLast, LazyFromHeightLast};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, DollarsToSatsFract, NumericValue},
|
||||
};
|
||||
|
||||
/// Generic price metric with both USD and sats representations.
|
||||
///
|
||||
/// Derefs to the usd metric, so existing code works unchanged.
|
||||
/// Access `.sats` for the sats exchange rate version.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct Price<U> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub usd: U,
|
||||
pub sats: LazyFromHeightLast<SatsFract, Dollars>,
|
||||
}
|
||||
|
||||
// --- PriceFromHeight ---
|
||||
|
||||
pub type PriceFromHeight = Price<ComputedFromHeightLast<Dollars>>;
|
||||
|
||||
impl Price<ComputedFromHeightLast<Dollars>> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
@@ -42,28 +32,16 @@ impl Price<ComputedFromHeightLast<Dollars>> {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let usd = ComputedFromHeightLast::forced_import(db, name, version, indexes)?;
|
||||
Ok(Self::from_computed(name, version, usd))
|
||||
}
|
||||
|
||||
pub(crate) fn from_computed(
|
||||
name: &str,
|
||||
version: Version,
|
||||
usd: ComputedFromHeightLast<Dollars>,
|
||||
) -> Self {
|
||||
let sats = LazyFromHeightLast::from_computed::<DollarsToSatsFract>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
usd.height.read_only_boxed_clone(),
|
||||
&usd,
|
||||
);
|
||||
Self { usd, sats }
|
||||
Ok(Self { usd, sats })
|
||||
}
|
||||
}
|
||||
|
||||
// --- LazyPriceFromHeight ---
|
||||
|
||||
pub type LazyPriceFromHeight<ST> = Price<LazyFromHeightLast<Dollars, ST>>;
|
||||
|
||||
impl<ST> Price<LazyFromHeightLast<Dollars, ST>>
|
||||
where
|
||||
ST: ComputedVecValue + NumericValue + JsonSchema + 'static,
|
||||
@@ -88,106 +66,3 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// --- LazyPriceFromCents ---
|
||||
|
||||
pub type LazyPriceFromCents = Price<LazyFromHeightLast<Dollars, Cents>>;
|
||||
|
||||
// --- LazyBinaryPriceFromHeight ---
|
||||
|
||||
pub type LazyBinaryPriceFromHeight = Price<LazyBinaryFromHeightLast<Dollars, Dollars, Dollars>>;
|
||||
|
||||
impl Price<LazyBinaryFromHeightLast<Dollars, Dollars, Dollars>> {
|
||||
/// Create from a PriceFromHeight (source1) and a LazyPriceFromCents (source2).
|
||||
pub(crate) fn from_price_and_lazy_price<F: BinaryTransform<Dollars, Dollars, Dollars>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &PriceFromHeight,
|
||||
source2: &LazyPriceFromCents,
|
||||
) -> Self {
|
||||
let usd = LazyBinaryFromHeightLast::from_block_last_and_lazy_block_last::<F, Cents>(
|
||||
name,
|
||||
version,
|
||||
&source1.usd,
|
||||
&source2.usd,
|
||||
);
|
||||
|
||||
let sats = LazyFromHeightLast::from_binary::<DollarsToSatsFract, _, _>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
|
||||
Self { usd, sats }
|
||||
}
|
||||
|
||||
/// Create from a LazyPriceFromCents (source1) and a PriceFromHeight (source2).
|
||||
pub(crate) fn from_lazy_price_and_price<F: BinaryTransform<Dollars, Dollars, Dollars>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyPriceFromCents,
|
||||
source2: &PriceFromHeight,
|
||||
) -> Self {
|
||||
let usd = LazyBinaryFromHeightLast::from_lazy_block_last_and_block_last::<F, Cents>(
|
||||
name,
|
||||
version,
|
||||
&source1.usd,
|
||||
&source2.usd,
|
||||
);
|
||||
|
||||
let sats = LazyFromHeightLast::from_binary::<DollarsToSatsFract, _, _>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
|
||||
Self { usd, sats }
|
||||
}
|
||||
}
|
||||
|
||||
// --- Price bands (for stddev/ratio) ---
|
||||
|
||||
impl<S2T> Price<LazyBinaryFromHeightLast<Dollars, Dollars, S2T>>
|
||||
where
|
||||
S2T: ComputedVecValue + NumericValue + JsonSchema,
|
||||
{
|
||||
/// Create a price band from a computed price and a computed band.
|
||||
pub(crate) fn from_computed_price_and_band<F: BinaryTransform<Dollars, S2T, Dollars>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
price: &ComputedFromHeightLast<Dollars>,
|
||||
band: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self {
|
||||
let usd = LazyBinaryFromHeightLast::from_computed_last::<F>(name, version, price, band);
|
||||
|
||||
let sats = LazyFromHeightLast::from_binary::<DollarsToSatsFract, _, _>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
|
||||
Self { usd, sats }
|
||||
}
|
||||
|
||||
/// Create a price band from a lazy price and a computed band.
|
||||
pub(crate) fn from_lazy_price_and_band<F: BinaryTransform<Dollars, S2T, Dollars>, S1T>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
price: &LazyFromHeightLast<Dollars, S1T>,
|
||||
band: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let usd = LazyBinaryFromHeightLast::from_lazy_block_last_and_block_last::<F, S1T>(
|
||||
name, version, price, band,
|
||||
);
|
||||
|
||||
let sats = LazyFromHeightLast::from_binary::<DollarsToSatsFract, _, _>(
|
||||
&format!("{name}_sats"),
|
||||
version,
|
||||
&usd,
|
||||
);
|
||||
|
||||
Self { usd, sats }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, StoredF32, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, WritableVec, ReadableVec,
|
||||
PcoVec, Rw, StorageMode, VecIndex,
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
blocks, indexes, ComputeIndexes,
|
||||
internal::{
|
||||
ComputedFromHeightStdDev, ComputedVecValue, LazyBinaryFromHeightLast,
|
||||
LazyFromHeightLast, Price, PriceTimesRatio, StandardDeviationVecsOptions,
|
||||
},
|
||||
ComputeIndexes, blocks, indexes,
|
||||
internal::{ComputedFromHeightStdDev, Price, StandardDeviationVecsOptions},
|
||||
prices,
|
||||
utils::get_percentile,
|
||||
};
|
||||
|
||||
use super::{ComputedFromHeightLast, PriceFromHeight};
|
||||
use super::ComputedFromHeightLast;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ComputedFromHeightRatio<M: StorageMode = Rw> {
|
||||
@@ -32,12 +28,12 @@ pub struct ComputedFromHeightRatio<M: StorageMode = Rw> {
|
||||
pub ratio_pct5: Option<ComputedFromHeightLast<StoredF32, M>>,
|
||||
pub ratio_pct2: Option<ComputedFromHeightLast<StoredF32, M>>,
|
||||
pub ratio_pct1: Option<ComputedFromHeightLast<StoredF32, M>>,
|
||||
pub ratio_pct99_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct98_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct95_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct5_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct2_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct1_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub ratio_pct99_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub ratio_pct98_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub ratio_pct95_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub ratio_pct5_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub ratio_pct2_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub ratio_pct1_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
|
||||
pub ratio_sd: Option<ComputedFromHeightStdDev<M>>,
|
||||
pub ratio_4y_sd: Option<ComputedFromHeightStdDev<M>>,
|
||||
@@ -74,10 +70,7 @@ impl ComputedFromHeightRatio {
|
||||
// Only compute price internally when metric_price is None
|
||||
let price = metric_price
|
||||
.is_none()
|
||||
.then(|| PriceFromHeight::forced_import(db, name, v, indexes).unwrap());
|
||||
|
||||
// Use provided metric_price, falling back to internally computed price
|
||||
let effective_price = metric_price.or(price.as_ref().map(|p| &p.usd));
|
||||
.then(|| Price::forced_import(db, name, v, indexes).unwrap());
|
||||
|
||||
macro_rules! import_sd {
|
||||
($suffix:expr, $days:expr) => {
|
||||
@@ -88,7 +81,6 @@ impl ComputedFromHeightRatio {
|
||||
v,
|
||||
indexes,
|
||||
StandardDeviationVecsOptions::default().add_all(),
|
||||
effective_price,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
@@ -103,14 +95,19 @@ impl ComputedFromHeightRatio {
|
||||
|
||||
macro_rules! lazy_usd {
|
||||
($ratio:expr, $suffix:expr) => {
|
||||
effective_price.zip($ratio.as_ref()).map(|(mp, r)| {
|
||||
Price::from_computed_price_and_band::<PriceTimesRatio>(
|
||||
&format!("{name}_{}", $suffix),
|
||||
v,
|
||||
mp,
|
||||
r,
|
||||
)
|
||||
})
|
||||
if !extended {
|
||||
None
|
||||
} else {
|
||||
$ratio.as_ref().map(|_| {
|
||||
Price::forced_import(
|
||||
db,
|
||||
&format!("{name}_{}", $suffix),
|
||||
v,
|
||||
indexes,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,10 +135,9 @@ impl ComputedFromHeightRatio {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn forced_import_from_lazy<S1T: ComputedVecValue + JsonSchema>(
|
||||
pub(crate) fn forced_import_from_lazy(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
metric_price: &LazyFromHeightLast<Dollars, S1T>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
extended: bool,
|
||||
@@ -169,7 +165,6 @@ impl ComputedFromHeightRatio {
|
||||
v,
|
||||
indexes,
|
||||
StandardDeviationVecsOptions::default().add_all(),
|
||||
Some(metric_price),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
@@ -184,13 +179,9 @@ impl ComputedFromHeightRatio {
|
||||
|
||||
macro_rules! lazy_usd {
|
||||
($ratio:expr, $suffix:expr) => {
|
||||
$ratio.as_ref().map(|r| {
|
||||
Price::from_lazy_price_and_band::<PriceTimesRatio, S1T>(
|
||||
&format!("{name}_{}", $suffix),
|
||||
v,
|
||||
metric_price,
|
||||
r,
|
||||
)
|
||||
$ratio.as_ref().map(|_| {
|
||||
Price::forced_import(db, &format!("{name}_{}", $suffix), v, indexes)
|
||||
.unwrap()
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -398,6 +389,51 @@ impl ComputedFromHeightRatio {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute USD ratio bands: usd_band = metric_price * ratio_percentile
|
||||
pub(crate) fn compute_usd_bands(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
metric_price: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
use crate::internal::PriceTimesRatio;
|
||||
|
||||
macro_rules! compute_band {
|
||||
($usd_field:ident, $band_field:ident) => {
|
||||
if let Some(usd) = self.$usd_field.as_mut() {
|
||||
if let Some(band) = self.$band_field.as_ref() {
|
||||
usd.usd
|
||||
.compute_binary::<Dollars, StoredF32, PriceTimesRatio>(
|
||||
starting_indexes.height,
|
||||
metric_price,
|
||||
&band.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
compute_band!(ratio_pct99_usd, ratio_pct99);
|
||||
compute_band!(ratio_pct98_usd, ratio_pct98);
|
||||
compute_band!(ratio_pct95_usd, ratio_pct95);
|
||||
compute_band!(ratio_pct5_usd, ratio_pct5);
|
||||
compute_band!(ratio_pct2_usd, ratio_pct2);
|
||||
compute_band!(ratio_pct1_usd, ratio_pct1);
|
||||
|
||||
// Stddev USD bands
|
||||
macro_rules! compute_sd_usd {
|
||||
($($field:ident),*) => {
|
||||
$(if let Some(sd) = self.$field.as_mut() {
|
||||
sd.compute_usd_bands(starting_indexes, metric_price, exit)?;
|
||||
})*
|
||||
};
|
||||
}
|
||||
compute_sd_usd!(ratio_sd, ratio_4y_sd, ratio_2y_sd, ratio_1y_sd);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mut_ratio_vecs(&mut self) -> Vec<&mut EagerVec<PcoVec<Height, StoredF32>>> {
|
||||
macro_rules! collect_vecs {
|
||||
($($field:ident),*) => {{
|
||||
|
||||
@@ -3,18 +3,14 @@ use std::mem;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Height, StoredF32, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, WritableVec, ReadableVec,
|
||||
PcoVec, Rw, StorageMode, VecIndex,
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode, VecIndex,
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{blocks, indexes, ComputeIndexes};
|
||||
use crate::{ComputeIndexes, blocks, indexes};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedFromHeightLast, ComputedVecValue, LazyBinaryFromHeightLast, LazyFromHeightLast,
|
||||
Price, PriceTimesRatio,
|
||||
};
|
||||
use crate::internal::{ComputedFromHeightLast, Price};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StandardDeviationVecsOptions {
|
||||
@@ -67,19 +63,19 @@ pub struct ComputedFromHeightStdDev<M: StorageMode = Rw> {
|
||||
pub m2_5sd: Option<ComputedFromHeightLast<StoredF32, M>>,
|
||||
pub m3sd: Option<ComputedFromHeightLast<StoredF32, M>>,
|
||||
|
||||
pub _0sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p0_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p1sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p1_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p2sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p2_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub p3sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m0_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m1sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m1_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m2sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m2_5sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub m3sd_usd: Option<Price<LazyBinaryFromHeightLast<Dollars, Dollars, StoredF32>>>,
|
||||
pub _0sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p0_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p1sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p1_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p2sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p2_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub p3sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m0_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m1sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m1_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m2sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m2_5sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
pub m3sd_usd: Option<Price<ComputedFromHeightLast<Dollars, M>>>,
|
||||
}
|
||||
|
||||
impl ComputedFromHeightStdDev {
|
||||
@@ -91,7 +87,6 @@ impl ComputedFromHeightStdDev {
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
options: StandardDeviationVecsOptions,
|
||||
metric_price: Option<&ComputedFromHeightLast<Dollars>>,
|
||||
) -> Result<Self> {
|
||||
let version = parent_version + Version::TWO;
|
||||
|
||||
@@ -121,23 +116,21 @@ impl ComputedFromHeightStdDev {
|
||||
let m2_5sd = options.bands().then(|| import!("m2_5sd"));
|
||||
let m3sd = options.bands().then(|| import!("m3sd"));
|
||||
|
||||
// Create USD bands using the metric price (the denominator of the ratio).
|
||||
// This converts ratio bands back to USD: usd_band = metric_price * ratio_band
|
||||
// Import USD price band vecs (computed eagerly at compute time)
|
||||
macro_rules! lazy_usd {
|
||||
($band:expr, $suffix:expr) => {
|
||||
if !options.price_bands() {
|
||||
None
|
||||
} else if let Some(mp) = metric_price {
|
||||
$band.as_ref().map(|b| {
|
||||
Price::from_computed_price_and_band::<PriceTimesRatio>(
|
||||
} else {
|
||||
$band.as_ref().map(|_| {
|
||||
Price::forced_import(
|
||||
db,
|
||||
&format!("{name}_{}", $suffix),
|
||||
version,
|
||||
mp,
|
||||
b,
|
||||
indexes,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -177,15 +170,13 @@ impl ComputedFromHeightStdDev {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn forced_import_from_lazy<S1T: ComputedVecValue + JsonSchema>(
|
||||
pub(crate) fn forced_import_from_lazy(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
days: usize,
|
||||
parent_version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
options: StandardDeviationVecsOptions,
|
||||
metric_price: Option<&LazyFromHeightLast<Dollars, S1T>>,
|
||||
) -> Result<Self> {
|
||||
let version = parent_version + Version::TWO;
|
||||
|
||||
@@ -216,21 +207,21 @@ impl ComputedFromHeightStdDev {
|
||||
let m3sd = options.bands().then(|| import!("m3sd"));
|
||||
|
||||
// For lazy metric price, use from_lazy_block_last_and_block_last.
|
||||
// PriceTimesRatio: BinaryTransform<Dollars, StoredF32, Dollars>
|
||||
// source1 = metric_price (Dollars, lazy), source2 = band (StoredF32, computed)
|
||||
macro_rules! lazy_usd {
|
||||
($band:expr, $suffix:expr) => {
|
||||
metric_price
|
||||
.zip($band.as_ref())
|
||||
.filter(|_| options.price_bands())
|
||||
.map(|(mp, b)| {
|
||||
Price::from_lazy_price_and_band::<PriceTimesRatio, S1T>(
|
||||
if !options.price_bands() {
|
||||
None
|
||||
} else {
|
||||
$band.as_ref().map(|_| {
|
||||
Price::forced_import(
|
||||
db,
|
||||
&format!("{name}_{}", $suffix),
|
||||
version,
|
||||
mp,
|
||||
b,
|
||||
indexes,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -277,29 +268,21 @@ impl ComputedFromHeightStdDev {
|
||||
// 1. Compute SMA using the appropriate lookback vec (or full-history SMA)
|
||||
if self.days != usize::MAX {
|
||||
let window_starts = blocks.count.start_vec(self.days);
|
||||
self.sma
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.height
|
||||
.compute_rolling_average(
|
||||
starting_indexes.height,
|
||||
window_starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
self.sma.as_mut().unwrap().height.compute_rolling_average(
|
||||
starting_indexes.height,
|
||||
window_starts,
|
||||
source,
|
||||
exit,
|
||||
)?;
|
||||
} else {
|
||||
// Full history SMA (days == usize::MAX)
|
||||
self.sma
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.height
|
||||
.compute_sma_(
|
||||
starting_indexes.height,
|
||||
source,
|
||||
self.days,
|
||||
exit,
|
||||
None,
|
||||
)?;
|
||||
self.sma.as_mut().unwrap().height.compute_sma_(
|
||||
starting_indexes.height,
|
||||
source,
|
||||
self.days,
|
||||
exit,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
|
||||
let sma_opt: Option<&EagerVec<PcoVec<Height, StoredF32>>> = None;
|
||||
@@ -407,7 +390,8 @@ impl ComputedFromHeightStdDev {
|
||||
// This is the population SD of all daily values relative to the current SMA
|
||||
let sd = if n > 0 {
|
||||
let nf = n as f64;
|
||||
let variance = welford_sum_sq / nf - 2.0 * avg_f64 * welford_sum / nf + avg_f64 * avg_f64;
|
||||
let variance =
|
||||
welford_sum_sq / nf - 2.0 * avg_f64 * welford_sum / nf + avg_f64 * avg_f64;
|
||||
StoredF32::from(variance.max(0.0).sqrt() as f32)
|
||||
} else {
|
||||
StoredF32::from(0.0_f32)
|
||||
@@ -471,6 +455,48 @@ impl ComputedFromHeightStdDev {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute USD price bands: usd_band = metric_price * band_ratio
|
||||
pub(crate) fn compute_usd_bands(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
metric_price: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
use crate::internal::PriceTimesRatio;
|
||||
|
||||
macro_rules! compute_band {
|
||||
($usd_field:ident, $band_field:ident) => {
|
||||
if let Some(usd) = self.$usd_field.as_mut() {
|
||||
if let Some(band) = self.$band_field.as_ref() {
|
||||
usd.usd
|
||||
.compute_binary::<Dollars, StoredF32, PriceTimesRatio>(
|
||||
starting_indexes.height,
|
||||
metric_price,
|
||||
&band.height,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
compute_band!(_0sd_usd, sma);
|
||||
compute_band!(p0_5sd_usd, p0_5sd);
|
||||
compute_band!(p1sd_usd, p1sd);
|
||||
compute_band!(p1_5sd_usd, p1_5sd);
|
||||
compute_band!(p2sd_usd, p2sd);
|
||||
compute_band!(p2_5sd_usd, p2_5sd);
|
||||
compute_band!(p3sd_usd, p3sd);
|
||||
compute_band!(m0_5sd_usd, m0_5sd);
|
||||
compute_band!(m1sd_usd, m1sd);
|
||||
compute_band!(m1_5sd_usd, m1_5sd);
|
||||
compute_band!(m2sd_usd, m2sd);
|
||||
compute_band!(m2_5sd_usd, m2_5sd);
|
||||
compute_band!(m3sd_usd, m3sd);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mut_stateful_computed(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = &mut ComputedFromHeightLast<StoredF32>> {
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
//! ComputedFromHeight using SumCum aggregation.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableCloneableVec, Rw, StorageMode,
|
||||
};
|
||||
|
||||
use crate::{ComputeIndexes, indexes};
|
||||
|
||||
use crate::internal::{ComputedHeightDerivedSumCum, ComputedVecValue, NumericValue};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedFromHeightSumCum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub rest: Box<ComputedHeightDerivedSumCum<T, M>>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedFromHeightSumCum<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, v)?;
|
||||
|
||||
let rest =
|
||||
ComputedHeightDerivedSumCum::forced_import(db, name, height.read_only_boxed_clone(), v, indexes)?;
|
||||
|
||||
Ok(Self { height, rest: Box::new(rest) })
|
||||
}
|
||||
|
||||
/// Compute height_cumulative from self.height.
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.rest.derive_from(starting_indexes, &self.height, exit)
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
mut compute: impl FnMut(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute(&mut self.height)?;
|
||||
self.compute_cumulative(starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,27 @@
|
||||
//! Value type for Full pattern from Height.
|
||||
//!
|
||||
//! Height-level USD stats are lazy: `sats * price`.
|
||||
//! Cumulative and day1 stats are stored since they require aggregation
|
||||
//! across heights with varying prices.
|
||||
//! Height-level USD stats are stored (eagerly computed from sats × price).
|
||||
//! Uses CumFull: stored base + cumulative + rolling windows.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, ReadableCloneableVec, PcoVec, Rw, StorageMode};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{
|
||||
ComputedFromHeightFull, LazyBinaryComputedFromHeightFull, LazyFromHeightFull,
|
||||
SatsTimesPrice, SatsToBitcoin,
|
||||
},
|
||||
indexes,
|
||||
internal::{ComputedFromHeightCumulativeFull, LazyFromHeightLast, SatsToBitcoin, WindowStarts},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightFull<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightFull<Sats, M>,
|
||||
pub btc: LazyFromHeightFull<Bitcoin, Sats>,
|
||||
pub usd: LazyBinaryComputedFromHeightFull<Dollars, Sats, Dollars, M>,
|
||||
pub sats: ComputedFromHeightCumulativeFull<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightCumulativeFull<Dollars, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE; // Bumped for lazy height dollars
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
|
||||
impl ValueFromHeightFull {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -33,44 +29,45 @@ impl ValueFromHeightFull {
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightFull::forced_import(db, name, v, indexes)?;
|
||||
let sats = ComputedFromHeightCumulativeFull::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightFull::from_computed::<SatsToBitcoin>(
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd = LazyBinaryComputedFromHeightFull::forced_import::<SatsTimesPrice>(
|
||||
db,
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
prices.usd.price.read_only_boxed_clone(),
|
||||
indexes,
|
||||
)?;
|
||||
let usd =
|
||||
ComputedFromHeightCumulativeFull::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
usd,
|
||||
})
|
||||
Ok(Self { sats, btc, usd })
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
mut compute: impl FnMut(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute(&mut self.sats.height)?;
|
||||
self.sats.rest.compute_cumulative(starting_indexes, &self.sats.height, exit)?;
|
||||
self.usd.compute_cumulative(starting_indexes, exit)?;
|
||||
Ok(())
|
||||
self.sats.compute(max_from, windows, exit, compute_sats)?;
|
||||
|
||||
self.usd.compute(max_from, windows, exit, |vec| {
|
||||
Ok(vec.compute_transform2(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&prices.usd.price,
|
||||
|(h, sats, price, ..)| {
|
||||
let btc = *sats as f64 / 100_000_000.0;
|
||||
(h, Dollars::from(*price * btc))
|
||||
},
|
||||
exit,
|
||||
)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
//! Value type for Last pattern from Height.
|
||||
//!
|
||||
//! Height-level USD value is lazy: `sats * price`.
|
||||
//! Height-level USD value is stored (eagerly computed from sats × price).
|
||||
//! Day1 last is stored since it requires finding the last value within each date.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Sats, Version};
|
||||
use vecdb::{Database, ReadableCloneableVec, Rw, StorageMode};
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightLast, LazyBinaryComputedFromHeightLast, LazyFromHeightLast,
|
||||
SatsTimesPrice, SatsToBitcoin,
|
||||
},
|
||||
prices,
|
||||
indexes, prices,
|
||||
internal::{ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightLast<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightLast<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: LazyBinaryComputedFromHeightLast<Dollars, Sats, Dollars>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE; // Bumped for lazy height dollars
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
|
||||
impl ValueFromHeightLast {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -32,7 +28,6 @@ impl ValueFromHeightLast {
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
@@ -45,13 +40,7 @@ impl ValueFromHeightLast {
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = LazyBinaryComputedFromHeightLast::forced_import::<SatsTimesPrice>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
prices.usd.price.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
@@ -59,4 +48,24 @@ impl ValueFromHeightLast {
|
||||
usd,
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly compute USD height values: sats[h] * price[h].
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.usd.height.compute_transform2(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&prices.usd.price,
|
||||
|(h, sats, price, ..)| {
|
||||
let btc = *sats as f64 / 100_000_000.0;
|
||||
(h, Dollars::from(*price * btc))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
//! Lazy binary value wrapper combining height (with price) + all derived last transforms.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{BinaryTransform, ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use super::LazyFromHeightValue;
|
||||
use crate::internal::{LazyValueHeightDerivedLast, ValueFromHeightLast};
|
||||
use crate::prices;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy binary value wrapper with height (using price binary transform) + all derived last transforms.
|
||||
///
|
||||
/// Use this when the height-level dollars need a binary transform (e.g., price * sats)
|
||||
/// rather than a unary transform from existing dollars.
|
||||
///
|
||||
/// All coarser-than-height periods (minute1 through difficultyepoch) use unary transforms
|
||||
/// on the pre-computed values from the source.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyBinaryValueFromHeightLast {
|
||||
#[traversable(flatten)]
|
||||
pub height: LazyFromHeightValue,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub rest: Box<LazyValueHeightDerivedLast>,
|
||||
}
|
||||
|
||||
impl LazyBinaryValueFromHeightLast {
|
||||
pub(crate) fn from_block_source<
|
||||
SatsTransform,
|
||||
BitcoinTransform,
|
||||
HeightDollarsTransform,
|
||||
DateDollarsTransform,
|
||||
>(
|
||||
name: &str,
|
||||
source: &ValueFromHeightLast,
|
||||
prices: &prices::Vecs,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
HeightDollarsTransform: BinaryTransform<Dollars, Sats, Dollars>,
|
||||
DateDollarsTransform: UnaryTransform<Dollars, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let price_source = prices.usd.price.read_only_boxed_clone();
|
||||
|
||||
let height = LazyFromHeightValue::from_sources::<
|
||||
SatsTransform,
|
||||
BitcoinTransform,
|
||||
HeightDollarsTransform,
|
||||
>(name, source.sats.height.read_only_boxed_clone(), price_source, v);
|
||||
|
||||
let rest =
|
||||
LazyValueHeightDerivedLast::from_block_source::<SatsTransform, BitcoinTransform, DateDollarsTransform>(
|
||||
name, source, v,
|
||||
);
|
||||
|
||||
Self { height, rest: Box::new(rest) }
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//! Value type with stored sats height + cumulative, lazy btc + lazy dollars.
|
||||
//!
|
||||
//! Like LazyComputedValueFromHeightSumCum but with Cum (no old period aggregations).
|
||||
//! - Sats: stored height + cumulative (ComputedFromHeightCum)
|
||||
//! - BTC: lazy transform from sats (LazyFromHeightLast)
|
||||
//! - USD: lazy binary (price × sats), LazyLast per index (no stored cumulative)
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightCum, LazyBinaryComputedFromHeightLast, LazyFromHeightLast,
|
||||
PriceTimesSats, SatsToBitcoin,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
/// Value wrapper with stored sats height + cumulative, lazy btc + lazy usd.
|
||||
#[derive(Traversable)]
|
||||
pub struct LazyComputedValueFromHeightCum<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCum<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: LazyBinaryComputedFromHeightLast<Dollars, Dollars, Sats>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl LazyComputedValueFromHeightCum {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCum::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = LazyBinaryComputedFromHeightLast::forced_import::<PriceTimesSats>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
prices.usd.price.read_only_boxed_clone(),
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
}
|
||||
|
||||
/// Compute cumulative from already-filled sats height vec.
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats.compute_cumulative(max_from, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! Value type with stored sats height + cumulative, stored usd, lazy btc.
|
||||
//!
|
||||
//! - Sats: stored height + cumulative (ComputedFromHeightCumulative)
|
||||
//! - BTC: lazy transform from sats (LazyFromHeightLast)
|
||||
//! - USD: stored (eagerly computed from price × sats)
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightCumulative, ComputedFromHeightLast, LazyFromHeightLast, SatsToBitcoin},
|
||||
prices,
|
||||
};
|
||||
|
||||
/// Value wrapper with stored sats height + cumulative, lazy btc + stored usd.
|
||||
#[derive(Traversable)]
|
||||
pub struct LazyComputedValueFromHeightCumulative<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCumulative<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightLast<Dollars, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE; // Bumped for stored height dollars
|
||||
|
||||
impl LazyComputedValueFromHeightCumulative {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCumulative::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd = ComputedFromHeightLast::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
}
|
||||
|
||||
/// Compute cumulative + USD from already-filled sats height vec.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats.compute_rest(max_from, exit)?;
|
||||
|
||||
self.usd.height.compute_transform2(
|
||||
max_from,
|
||||
&prices.usd.price,
|
||||
&self.sats.height,
|
||||
|(h, price, sats, ..)| {
|
||||
let btc = *sats as f64 / 100_000_000.0;
|
||||
(h, Dollars::from(*price * btc))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
//! Lazy value wrapper for ValueFromHeightLast - all transforms are lazy.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Dollars, Sats, Version};
|
||||
use brk_types::{Bitcoin, Dollars, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
use crate::internal::{
|
||||
LazyValueHeight, LazyValueHeightDerivedLast, SatsToBitcoin, ValueFromHeightLast,
|
||||
};
|
||||
use crate::internal::{LazyValueHeight, LazyValueHeightDerivedLast, ValueFromHeightLast};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
@@ -24,22 +22,23 @@ pub struct LazyValueFromHeightLast {
|
||||
}
|
||||
|
||||
impl LazyValueFromHeightLast {
|
||||
pub(crate) fn from_block_source<SatsTransform, DollarsTransform>(
|
||||
pub(crate) fn from_block_source<SatsTransform, BitcoinTransform, DollarsTransform>(
|
||||
name: &str,
|
||||
source: &ValueFromHeightLast,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
DollarsTransform: UnaryTransform<Dollars, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let height =
|
||||
LazyValueHeight::from_block_source::<SatsTransform, DollarsTransform>(name, source, v);
|
||||
LazyValueHeight::from_block_source::<SatsTransform, BitcoinTransform, DollarsTransform>(name, source, v);
|
||||
|
||||
let rest =
|
||||
LazyValueHeightDerivedLast::from_block_source::<SatsTransform, SatsToBitcoin, DollarsTransform>(
|
||||
LazyValueHeightDerivedLast::from_block_source::<SatsTransform, BitcoinTransform, DollarsTransform>(
|
||||
name, source, v,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//! Value type with lazy binary height + stored derived SumCum.
|
||||
//!
|
||||
//! Use this when the height-level sats is a lazy binary transform (e.g., mask × source).
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
BinaryTransform, Database, Exit, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, Rw,
|
||||
StorageMode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{
|
||||
ComputedVecValue, LazyComputedFromHeightSumCum, LazyFromHeightSumCum, PriceTimesSats,
|
||||
SatsToBitcoin,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
/// Value wrapper with lazy binary height + stored derived SumCum.
|
||||
///
|
||||
/// Sats height is a lazy binary transform (e.g., mask × source).
|
||||
/// Dollars height is also lazy (price × sats).
|
||||
/// Cumulative and day1 are stored.
|
||||
#[derive(Traversable)]
|
||||
pub struct LazyValueFromHeightSumCum<S1T, S2T, M: StorageMode = Rw>
|
||||
where
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub sats: LazyComputedFromHeightSumCum<Sats, S1T, S2T, M>,
|
||||
pub btc: LazyFromHeightSumCum<Bitcoin, Sats>,
|
||||
pub usd: LazyComputedFromHeightSumCum<Dollars, Dollars, Sats, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<S1T, S2T> LazyValueFromHeightSumCum<S1T, S2T>
|
||||
where
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import<F>(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
source1: ReadableBoxedVec<Height, S1T>,
|
||||
source2: ReadableBoxedVec<Height, S2T>,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self>
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, Sats>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats_height = LazyVecFrom2::transformed::<F>(name, v, source1, source2);
|
||||
let sats = LazyComputedFromHeightSumCum::forced_import(db, name, v, indexes, sats_height)?;
|
||||
|
||||
let btc = LazyFromHeightSumCum::from_derived::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats.rest,
|
||||
);
|
||||
|
||||
let usd_height = LazyVecFrom2::transformed::<PriceTimesSats>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
prices.usd.price.read_only_boxed_clone(),
|
||||
sats.height.read_only_boxed_clone(),
|
||||
);
|
||||
|
||||
let usd = LazyComputedFromHeightSumCum::forced_import(
|
||||
db,
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
indexes,
|
||||
usd_height,
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
usd,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.sats.compute_cumulative(starting_indexes, exit)?;
|
||||
self.usd.compute_cumulative(starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
//! Value type for SumCum pattern from Height.
|
||||
//!
|
||||
//! Height-level USD sum is lazy: `sats * price`.
|
||||
//! Cumulative and day1 stats are stored since they require aggregation
|
||||
//! across heights with varying prices.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, ReadableCloneableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes,
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightSumCum, LazyBinaryComputedFromHeightSumCum, LazyFromHeightSumCum,
|
||||
SatsTimesPrice, SatsToBitcoin,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightSumCum<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightSumCum<Sats, M>,
|
||||
pub btc: LazyFromHeightSumCum<Bitcoin, Sats>,
|
||||
pub usd: LazyBinaryComputedFromHeightSumCum<Dollars, Sats, Dollars, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ONE; // Bumped for lazy height dollars
|
||||
|
||||
impl ValueFromHeightSumCum {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
prices: &prices::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightSumCum::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightSumCum::from_computed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
&sats,
|
||||
);
|
||||
|
||||
let usd = LazyBinaryComputedFromHeightSumCum::forced_import::<SatsTimesPrice>(
|
||||
db,
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
prices.usd.price.read_only_boxed_clone(),
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
|
||||
Ok(Self {
|
||||
sats,
|
||||
btc,
|
||||
usd,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
mut compute: impl FnMut(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
compute(&mut self.sats.height)?;
|
||||
self.sats.compute_cumulative(starting_indexes, exit)?;
|
||||
self.usd.compute_cumulative(starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! Value type for SumCumulative pattern from Height.
|
||||
//!
|
||||
//! Height-level USD sum is stored (eagerly computed from sats × price).
|
||||
//! Uses CumSum: stored base + cumulative + rolling sum windows.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes, prices,
|
||||
internal::{ComputedFromHeightCumulativeSum, LazyFromHeightLast, SatsToBitcoin, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ValueFromHeightSumCumulative<M: StorageMode = Rw> {
|
||||
pub sats: ComputedFromHeightCumulativeSum<Sats, M>,
|
||||
pub btc: LazyFromHeightLast<Bitcoin, Sats>,
|
||||
pub usd: ComputedFromHeightCumulativeSum<Dollars, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::TWO; // Bumped for stored height dollars
|
||||
|
||||
impl ValueFromHeightSumCumulative {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = ComputedFromHeightCumulativeSum::forced_import(db, name, v, indexes)?;
|
||||
|
||||
let btc = LazyFromHeightLast::from_height_source::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let usd =
|
||||
ComputedFromHeightCumulativeSum::forced_import(db, &format!("{name}_usd"), v, indexes)?;
|
||||
|
||||
Ok(Self { sats, btc, usd })
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
prices: &prices::Vecs,
|
||||
exit: &Exit,
|
||||
compute_sats: impl FnOnce(&mut EagerVec<PcoVec<Height, Sats>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.sats.compute(max_from, windows, exit, compute_sats)?;
|
||||
|
||||
self.usd.compute(max_from, windows, exit, |vec| {
|
||||
Ok(vec.compute_transform2(
|
||||
max_from,
|
||||
&self.sats.height,
|
||||
&prices.usd.price,
|
||||
|(h, sats, price, ..)| {
|
||||
let btc = *sats as f64 / 100_000_000.0;
|
||||
(h, Dollars::from(*price * btc))
|
||||
},
|
||||
exit,
|
||||
)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
+16
-14
@@ -3,19 +3,16 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{TxIndex, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use brk_types::TxIndex;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, LazyVecFrom2, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, LazyVecFrom2, ReadableVec, Rw, StorageMode, Version};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{ComputedVecValue, TxDerivedDistribution, NumericValue},
|
||||
internal::{BlockWindowStarts, ComputedVecValue, NumericValue, TxDerivedDistribution},
|
||||
};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyFromTxDistribution<T, S1, S2, M: StorageMode = Rw>
|
||||
where
|
||||
@@ -24,8 +21,6 @@ where
|
||||
S2: ComputedVecValue,
|
||||
{
|
||||
pub txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub distribution: TxDerivedDistribution<T, M>,
|
||||
}
|
||||
@@ -41,10 +36,8 @@ where
|
||||
name: &str,
|
||||
version: Version,
|
||||
txindex: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, v, indexes)?;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version)?;
|
||||
Ok(Self {
|
||||
txindex,
|
||||
distribution,
|
||||
@@ -56,12 +49,21 @@ where
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
block_windows: &BlockWindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>: ReadableVec<TxIndex, T>,
|
||||
{
|
||||
self.distribution
|
||||
.derive_from(indexer, indexes, starting_indexes, &self.txindex, exit)
|
||||
self.distribution.derive_from(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
block_windows,
|
||||
&self.txindex,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
mod lazy_distribution;
|
||||
mod distribution;
|
||||
|
||||
pub use lazy_distribution::*;
|
||||
pub use distribution::*;
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
//! Lazy binary transform for derived block with Last aggregation only.
|
||||
//!
|
||||
//! Newtype on `Indexes` with `LazyBinaryTransformLast` per field.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30,
|
||||
Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes_from,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedFromHeightSumCum, ComputedVecValue, Indexes,
|
||||
LazyBinaryTransformLast, LazyFromHeightLast, NumericValue,
|
||||
},
|
||||
};
|
||||
|
||||
pub type LazyBinaryHeightDerivedLastInner<T, S1T, S2T> = Indexes<
|
||||
LazyBinaryTransformLast<Minute1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Minute5, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Minute10, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Minute30, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Hour1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Hour4, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Hour12, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Day1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Day3, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Week1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Month1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Month3, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Month6, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Year1, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<Year10, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<HalvingEpoch, T, S1T, S2T>,
|
||||
LazyBinaryTransformLast<DifficultyEpoch, T, S1T, S2T>,
|
||||
>;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyBinaryHeightDerivedLast<T, S1T = T, S2T = T>(
|
||||
pub LazyBinaryHeightDerivedLastInner<T, S1T, S2T>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T, S2T> LazyBinaryHeightDerivedLast<T, S1T, S2T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_computed_sum_cum<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightSumCum<S1T>,
|
||||
source2: &ComputedFromHeightSumCum<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: PartialOrd,
|
||||
S2T: PartialOrd,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyBinaryTransformLast::from_vecs::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.$p.cumulative.read_only_boxed_clone(),
|
||||
source2.$p.cumulative.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
|
||||
pub(crate) fn from_computed_last<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
S2T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyBinaryTransformLast::from_lazy_last::<F, _, _, _, _>(
|
||||
name,
|
||||
v,
|
||||
&source1.$p,
|
||||
&source2.$p,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
|
||||
pub(crate) fn from_lazy_block_last_and_block_last<F, S1SourceT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyFromHeightLast<S1T, S1SourceT>,
|
||||
source2: &ComputedFromHeightLast<S2T>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S2T: NumericValue,
|
||||
S1SourceT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyBinaryTransformLast::from_vecs::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.$p.read_only_boxed_clone(),
|
||||
source2.$p.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
|
||||
pub(crate) fn from_block_last_and_lazy_block_last<F, S2SourceT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &ComputedFromHeightLast<S1T>,
|
||||
source2: &LazyFromHeightLast<S2T, S2SourceT>,
|
||||
) -> Self
|
||||
where
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1T: NumericValue,
|
||||
S2SourceT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyBinaryTransformLast::from_vecs::<F>(
|
||||
name,
|
||||
v,
|
||||
source1.$p.read_only_boxed_clone(),
|
||||
source2.$p.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
}
|
||||
+10
-20
@@ -1,7 +1,7 @@
|
||||
//! ComputedHeightDerivedCumFull - LazyLast index views + cumulative (from height) + RollingFull.
|
||||
//! ComputedHeightDerivedCumulativeFull - LazyAggVec index views + cumulative (from height) + RollingFull.
|
||||
//!
|
||||
//! For metrics derived from indexer sources (no stored height vec).
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyLast index views too.
|
||||
//! Cumulative gets its own ComputedFromHeightLast so it has LazyAggVec index views too.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
@@ -9,23 +9,19 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableBoxedVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedHeightDerivedLast, NumericValue, RollingFull, WindowStarts,
|
||||
},
|
||||
internal::{ComputedFromHeightLast, NumericValue, RollingFull, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedHeightDerivedCumFull<T, M: StorageMode = Rw>
|
||||
pub struct ComputedHeightDerivedCumulativeFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
#[traversable(flatten)]
|
||||
pub last: ComputedHeightDerivedLast<T>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: ComputedFromHeightLast<T, M>,
|
||||
#[traversable(flatten)]
|
||||
@@ -34,30 +30,23 @@ where
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedHeightDerivedCumFull<T>
|
||||
impl<T> ComputedHeightDerivedCumulativeFull<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
height_source: ReadableBoxedVec<Height, T>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let last = ComputedHeightDerivedLast::forced_import(name, height_source, v, indexes);
|
||||
let cumulative = ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{name}_cumulative"),
|
||||
v,
|
||||
indexes,
|
||||
)?;
|
||||
let cumulative =
|
||||
ComputedFromHeightLast::forced_import(db, &format!("{name}_cumulative"), v, indexes)?;
|
||||
let rolling = RollingFull::forced_import(db, name, v, indexes)?;
|
||||
|
||||
Ok(Self {
|
||||
last,
|
||||
cumulative,
|
||||
rolling,
|
||||
})
|
||||
@@ -77,7 +66,8 @@ where
|
||||
self.cumulative
|
||||
.height
|
||||
.compute_cumulative(max_from, height_source, exit)?;
|
||||
self.rolling.compute(max_from, windows, height_source, exit)?;
|
||||
self.rolling
|
||||
.compute(max_from, windows, height_source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
//! ComputedHeightDerivedDistribution - lazy time periods + epochs.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, LazyDistribution, NumericValue},
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedHeightDerivedDistribution<T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub minute1: LazyDistribution<Minute1, T, Height, Height>,
|
||||
pub minute5: LazyDistribution<Minute5, T, Height, Height>,
|
||||
pub minute10: LazyDistribution<Minute10, T, Height, Height>,
|
||||
pub minute30: LazyDistribution<Minute30, T, Height, Height>,
|
||||
pub hour1: LazyDistribution<Hour1, T, Height, Height>,
|
||||
pub hour4: LazyDistribution<Hour4, T, Height, Height>,
|
||||
pub hour12: LazyDistribution<Hour12, T, Height, Height>,
|
||||
pub day1: LazyDistribution<Day1, T, Height, Height>,
|
||||
pub day3: LazyDistribution<Day3, T, Height, Height>,
|
||||
pub week1: LazyDistribution<Week1, T, Height, Height>,
|
||||
pub month1: LazyDistribution<Month1, T, Height, Height>,
|
||||
pub month3: LazyDistribution<Month3, T, Height, Height>,
|
||||
pub month6: LazyDistribution<Month6, T, Height, Height>,
|
||||
pub year1: LazyDistribution<Year1, T, Height, Height>,
|
||||
pub year10: LazyDistribution<Year10, T, Height, Height>,
|
||||
pub halvingepoch: LazyDistribution<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
pub difficultyepoch: LazyDistribution<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedHeightDerivedDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
name: &str,
|
||||
height_source: ReadableBoxedVec<Height, T>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyDistribution::from_height_source(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
indexes.$idx.first_height.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazyDistribution::from_source(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
indexes.$idx.identity.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self {
|
||||
minute1: period!(minute1),
|
||||
minute5: period!(minute5),
|
||||
minute10: period!(minute10),
|
||||
minute30: period!(minute30),
|
||||
hour1: period!(hour1),
|
||||
hour4: period!(hour4),
|
||||
hour12: period!(hour12),
|
||||
day1: period!(day1),
|
||||
day3: period!(day3),
|
||||
week1: period!(week1),
|
||||
month1: period!(month1),
|
||||
month3: period!(month3),
|
||||
month6: period!(month6),
|
||||
year1: period!(year1),
|
||||
year10: period!(year10),
|
||||
halvingepoch: epoch!(halvingepoch),
|
||||
difficultyepoch: epoch!(difficultyepoch),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
//! ComputedHeightDerivedFull - height_cumulative (stored) + lazy time periods + epochs.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, CumulativeVec, LazyFull, NumericValue},
|
||||
ComputeIndexes,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedHeightDerivedFull<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "cumulative")]
|
||||
pub height_cumulative: CumulativeVec<Height, T, M>,
|
||||
pub minute1: LazyFull<Minute1, T, Height, Height>,
|
||||
pub minute5: LazyFull<Minute5, T, Height, Height>,
|
||||
pub minute10: LazyFull<Minute10, T, Height, Height>,
|
||||
pub minute30: LazyFull<Minute30, T, Height, Height>,
|
||||
pub hour1: LazyFull<Hour1, T, Height, Height>,
|
||||
pub hour4: LazyFull<Hour4, T, Height, Height>,
|
||||
pub hour12: LazyFull<Hour12, T, Height, Height>,
|
||||
pub day1: LazyFull<Day1, T, Height, Height>,
|
||||
pub day3: LazyFull<Day3, T, Height, Height>,
|
||||
pub week1: LazyFull<Week1, T, Height, Height>,
|
||||
pub month1: LazyFull<Month1, T, Height, Height>,
|
||||
pub month3: LazyFull<Month3, T, Height, Height>,
|
||||
pub month6: LazyFull<Month6, T, Height, Height>,
|
||||
pub year1: LazyFull<Year1, T, Height, Height>,
|
||||
pub year10: LazyFull<Year10, T, Height, Height>,
|
||||
pub halvingepoch: LazyFull<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
pub difficultyepoch: LazyFull<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedHeightDerivedFull<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
height_source: ReadableBoxedVec<Height, T>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
let height_cumulative = CumulativeVec::forced_import(db, name, v)?;
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyFull::from_height_source(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
height_cumulative.read_only_boxed_clone(),
|
||||
indexes.$idx.first_height.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazyFull::from_stats_aggregate(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
height_source.clone(),
|
||||
height_source.clone(),
|
||||
height_source.clone(),
|
||||
height_cumulative.read_only_boxed_clone(),
|
||||
height_source.clone(),
|
||||
indexes.$idx.identity.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let minute1 = period!(minute1);
|
||||
let minute5 = period!(minute5);
|
||||
let minute10 = period!(minute10);
|
||||
let minute30 = period!(minute30);
|
||||
let hour1 = period!(hour1);
|
||||
let hour4 = period!(hour4);
|
||||
let hour12 = period!(hour12);
|
||||
let day1 = period!(day1);
|
||||
let day3 = period!(day3);
|
||||
let week1 = period!(week1);
|
||||
let month1 = period!(month1);
|
||||
let month3 = period!(month3);
|
||||
let month6 = period!(month6);
|
||||
let year1 = period!(year1);
|
||||
let year10 = period!(year10);
|
||||
let halvingepoch = epoch!(halvingepoch);
|
||||
let difficultyepoch = epoch!(difficultyepoch);
|
||||
|
||||
Ok(Self {
|
||||
height_cumulative,
|
||||
minute1,
|
||||
minute5,
|
||||
minute10,
|
||||
minute30,
|
||||
hour1,
|
||||
hour4,
|
||||
hour12,
|
||||
day1,
|
||||
day3,
|
||||
week1,
|
||||
month1,
|
||||
month3,
|
||||
month6,
|
||||
year1,
|
||||
year10,
|
||||
halvingepoch,
|
||||
difficultyepoch,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_cumulative(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
height_source: &impl ReadableVec<Height, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
Ok(self.height_cumulative
|
||||
.0
|
||||
.compute_cumulative(starting_indexes.height, height_source, exit)?)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,43 @@
|
||||
//! ComputedHeightDerivedLast - lazy time periods + epochs (last value).
|
||||
//!
|
||||
//! Newtype on `Indexes` with `LazyLast` per field.
|
||||
//! ComputedHeightDerivedLast — sparse time periods + dense epochs (last value).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, ReadableCloneableVec};
|
||||
use vecdb::{LazyAggVec, ReadableBoxedVec, ReadableCloneableVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
indexes_from,
|
||||
internal::{ComputedVecValue, Indexes, LazyLast, NumericValue},
|
||||
indexes, indexes_from,
|
||||
internal::{ComputedVecValue, Indexes, NumericValue},
|
||||
};
|
||||
|
||||
/// All 17 time-period/epoch `LazyLast` vecs, packed as a newtype on `Indexes`.
|
||||
pub type ComputedHeightDerivedLastInner<T> = Indexes<
|
||||
LazyLast<Minute1, T, Height, Height>,
|
||||
LazyLast<Minute5, T, Height, Height>,
|
||||
LazyLast<Minute10, T, Height, Height>,
|
||||
LazyLast<Minute30, T, Height, Height>,
|
||||
LazyLast<Hour1, T, Height, Height>,
|
||||
LazyLast<Hour4, T, Height, Height>,
|
||||
LazyLast<Hour12, T, Height, Height>,
|
||||
LazyLast<Day1, T, Height, Height>,
|
||||
LazyLast<Day3, T, Height, Height>,
|
||||
LazyLast<Week1, T, Height, Height>,
|
||||
LazyLast<Month1, T, Height, Height>,
|
||||
LazyLast<Month3, T, Height, Height>,
|
||||
LazyLast<Month6, T, Height, Height>,
|
||||
LazyLast<Year1, T, Height, Height>,
|
||||
LazyLast<Year10, T, Height, Height>,
|
||||
LazyLast<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
LazyLast<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
>;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct ComputedHeightDerivedLast<T>(pub ComputedHeightDerivedLastInner<T>)
|
||||
pub struct ComputedHeightDerivedLast<T>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyAggVec<Minute1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute5, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute10, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Minute30, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Hour1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Hour4, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Hour12, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Day1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Day3, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Week1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Month1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Month3, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Month6, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Year1, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<Year10, Option<T>, Height, Height, T>,
|
||||
LazyAggVec<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
LazyAggVec<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
@@ -60,7 +57,7 @@ where
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyLast::from_height_source(
|
||||
LazyAggVec::sparse_from_first_index(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
@@ -71,7 +68,7 @@ where
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazyLast::from_source(
|
||||
LazyAggVec::from_source(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
//! Lazy aggregated Full for block-level sources.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30,
|
||||
Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedHeightDerivedFull, ComputedVecValue, LazyTransformFull, NumericValue,
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyHeightDerivedFull<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub minute1: LazyTransformFull<Minute1, T, S1T>,
|
||||
pub minute5: LazyTransformFull<Minute5, T, S1T>,
|
||||
pub minute10: LazyTransformFull<Minute10, T, S1T>,
|
||||
pub minute30: LazyTransformFull<Minute30, T, S1T>,
|
||||
pub hour1: LazyTransformFull<Hour1, T, S1T>,
|
||||
pub hour4: LazyTransformFull<Hour4, T, S1T>,
|
||||
pub hour12: LazyTransformFull<Hour12, T, S1T>,
|
||||
pub day1: LazyTransformFull<Day1, T, S1T>,
|
||||
pub day3: LazyTransformFull<Day3, T, S1T>,
|
||||
pub week1: LazyTransformFull<Week1, T, S1T>,
|
||||
pub month1: LazyTransformFull<Month1, T, S1T>,
|
||||
pub month3: LazyTransformFull<Month3, T, S1T>,
|
||||
pub month6: LazyTransformFull<Month6, T, S1T>,
|
||||
pub year1: LazyTransformFull<Year1, T, S1T>,
|
||||
pub year10: LazyTransformFull<Year10, T, S1T>,
|
||||
pub halvingepoch: LazyTransformFull<HalvingEpoch, T, S1T>,
|
||||
pub difficultyepoch: LazyTransformFull<DifficultyEpoch, T, S1T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T> LazyHeightDerivedFull<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_derived_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &ComputedHeightDerivedFull<S1T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyTransformFull::from_boxed::<F>(
|
||||
name,
|
||||
v,
|
||||
source.$p.average.read_only_boxed_clone(),
|
||||
source.$p.min.read_only_boxed_clone(),
|
||||
source.$p.max.read_only_boxed_clone(),
|
||||
source.$p.percentiles.pct10.read_only_boxed_clone(),
|
||||
source.$p.percentiles.pct25.read_only_boxed_clone(),
|
||||
source.$p.percentiles.median.read_only_boxed_clone(),
|
||||
source.$p.percentiles.pct75.read_only_boxed_clone(),
|
||||
source.$p.percentiles.pct90.read_only_boxed_clone(),
|
||||
source.$p.sum.read_only_boxed_clone(),
|
||||
source.$p.cumulative.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self {
|
||||
minute1: period!(minute1),
|
||||
minute5: period!(minute5),
|
||||
minute10: period!(minute10),
|
||||
minute30: period!(minute30),
|
||||
hour1: period!(hour1),
|
||||
hour4: period!(hour4),
|
||||
hour12: period!(hour12),
|
||||
day1: period!(day1),
|
||||
day3: period!(day3),
|
||||
week1: period!(week1),
|
||||
month1: period!(month1),
|
||||
month3: period!(month3),
|
||||
month6: period!(month6),
|
||||
year1: period!(year1),
|
||||
year10: period!(year10),
|
||||
halvingepoch: period!(halvingepoch),
|
||||
difficultyepoch: period!(difficultyepoch),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,92 @@
|
||||
//! Lazy aggregated Last for block-level sources.
|
||||
//!
|
||||
//! Newtype on `Indexes` with `LazyTransformLast` per field.
|
||||
//! LazyHeightDerivedLast — unary transform of height-derived last values.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour4, Hour12, Minute1, Minute5, Minute10,
|
||||
Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour4, Hour12, Minute1, Minute5,
|
||||
Minute10, Minute30, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableCloneableVec, UnaryTransform};
|
||||
use vecdb::{LazyVecFrom1, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform, VecIndex, VecValue};
|
||||
|
||||
use crate::{
|
||||
indexes_from,
|
||||
indexes, indexes_from,
|
||||
internal::{
|
||||
ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, Indexes,
|
||||
LazyBinaryHeightDerivedLast, LazyTransformLast, NumericValue,
|
||||
ComputedFromHeightLast, ComputedHeightDerivedLast, ComputedVecValue, Indexes, NumericValue,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyTransformLast<I, T, S1T = T>(pub LazyVecFrom1<I, T, I, S1T>)
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + PartialOrd + JsonSchema,
|
||||
S1T: VecValue;
|
||||
|
||||
impl<I, T, S1T> LazyTransformLast<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: VecValue + PartialOrd + JsonSchema + 'static,
|
||||
S1T: VecValue + JsonSchema,
|
||||
{
|
||||
fn from_boxed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self(LazyVecFrom1::transformed::<F>(name, version, source))
|
||||
}
|
||||
}
|
||||
|
||||
struct MapOption<F>(PhantomData<F>);
|
||||
|
||||
impl<F, S, T> UnaryTransform<Option<S>, Option<T>> for MapOption<F>
|
||||
where
|
||||
F: UnaryTransform<S, T>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn apply(value: Option<S>) -> Option<T> {
|
||||
value.map(F::apply)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyHeightDerivedLast<T, S1T = T>(
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub Indexes<
|
||||
LazyTransformLast<Minute1, T, S1T>,
|
||||
LazyTransformLast<Minute5, T, S1T>,
|
||||
LazyTransformLast<Minute10, T, S1T>,
|
||||
LazyTransformLast<Minute30, T, S1T>,
|
||||
LazyTransformLast<Hour1, T, S1T>,
|
||||
LazyTransformLast<Hour4, T, S1T>,
|
||||
LazyTransformLast<Hour12, T, S1T>,
|
||||
LazyTransformLast<Day1, T, S1T>,
|
||||
LazyTransformLast<Day3, T, S1T>,
|
||||
LazyTransformLast<Week1, T, S1T>,
|
||||
LazyTransformLast<Month1, T, S1T>,
|
||||
LazyTransformLast<Month3, T, S1T>,
|
||||
LazyTransformLast<Month6, T, S1T>,
|
||||
LazyTransformLast<Year1, T, S1T>,
|
||||
LazyTransformLast<Year10, T, S1T>,
|
||||
LazyTransformLast<Minute1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute5, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute10, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Minute30, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Hour1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Hour4, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Hour12, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Day1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Day3, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Week1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Month1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Month3, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Month6, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Year1, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<Year10, Option<T>, Option<S1T>>,
|
||||
LazyTransformLast<HalvingEpoch, T, S1T>,
|
||||
LazyTransformLast<DifficultyEpoch, T, S1T>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue;
|
||||
T: VecValue + PartialOrd + JsonSchema,
|
||||
S1T: VecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T> LazyHeightDerivedLast<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
T: VecValue + PartialOrd + JsonSchema + 'static,
|
||||
S1T: VecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
@@ -62,15 +96,20 @@ where
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
Self::from_derived_computed::<F>(name, version, &source.rest)
|
||||
}
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyTransformLast::from_boxed::<F>(name, v, source.rest.$p.read_only_boxed_clone())
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
pub(crate) fn from_height_source<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
height_source: ReadableBoxedVec<Height, S1T>,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let derived = ComputedHeightDerivedLast::forced_import(name, height_source, version, indexes);
|
||||
Self::from_derived_computed::<F>(name, version, &derived)
|
||||
}
|
||||
|
||||
pub(crate) fn from_derived_computed<F: UnaryTransform<S1T, T>>(
|
||||
@@ -84,15 +123,24 @@ where
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyTransformLast::from_boxed::<MapOption<F>>(
|
||||
name,
|
||||
v,
|
||||
source.$p.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($p:ident) => {
|
||||
LazyTransformLast::from_boxed::<F>(name, v, source.$p.read_only_boxed_clone())
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
Self(indexes_from!(period, epoch))
|
||||
}
|
||||
|
||||
/// Create by unary-transforming a LazyHeightDerivedLast source.
|
||||
pub(crate) fn from_lazy<F, S2T>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
@@ -106,32 +154,20 @@ where
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyTransformLast::from_boxed::<F>(name, v, source.$p.read_only_boxed_clone())
|
||||
LazyTransformLast::from_boxed::<MapOption<F>>(
|
||||
name,
|
||||
v,
|
||||
source.$p.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
}
|
||||
|
||||
/// Create by unary-transforming a LazyBinaryHeightDerivedLast source.
|
||||
pub(crate) fn from_binary<F, S1aT, S1bT>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &LazyBinaryHeightDerivedLast<S1T, S1aT, S1bT>,
|
||||
) -> Self
|
||||
where
|
||||
F: UnaryTransform<S1T, T>,
|
||||
S1aT: ComputedVecValue + JsonSchema,
|
||||
S1bT: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
macro_rules! epoch {
|
||||
($p:ident) => {
|
||||
LazyTransformLast::from_boxed::<F>(name, v, source.$p.read_only_boxed_clone())
|
||||
};
|
||||
}
|
||||
|
||||
Self(indexes_from!(period))
|
||||
Self(indexes_from!(period, epoch))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Lazy aggregated SumCum for block-level sources.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Hour1, Hour12, Hour4, Minute1, Minute10, Minute30,
|
||||
Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedHeightDerivedSumCum, ComputedVecValue, LazyTransformSumCum, NumericValue,
|
||||
};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct LazyHeightDerivedSumCum<T, S1T = T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub minute1: LazyTransformSumCum<Minute1, T, S1T>,
|
||||
pub minute5: LazyTransformSumCum<Minute5, T, S1T>,
|
||||
pub minute10: LazyTransformSumCum<Minute10, T, S1T>,
|
||||
pub minute30: LazyTransformSumCum<Minute30, T, S1T>,
|
||||
pub hour1: LazyTransformSumCum<Hour1, T, S1T>,
|
||||
pub hour4: LazyTransformSumCum<Hour4, T, S1T>,
|
||||
pub hour12: LazyTransformSumCum<Hour12, T, S1T>,
|
||||
pub day1: LazyTransformSumCum<Day1, T, S1T>,
|
||||
pub day3: LazyTransformSumCum<Day3, T, S1T>,
|
||||
pub week1: LazyTransformSumCum<Week1, T, S1T>,
|
||||
pub month1: LazyTransformSumCum<Month1, T, S1T>,
|
||||
pub month3: LazyTransformSumCum<Month3, T, S1T>,
|
||||
pub month6: LazyTransformSumCum<Month6, T, S1T>,
|
||||
pub year1: LazyTransformSumCum<Year1, T, S1T>,
|
||||
pub year10: LazyTransformSumCum<Year10, T, S1T>,
|
||||
pub halvingepoch: LazyTransformSumCum<HalvingEpoch, T, S1T>,
|
||||
pub difficultyepoch: LazyTransformSumCum<DifficultyEpoch, T, S1T>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T, S1T> LazyHeightDerivedSumCum<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_derived_computed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &ComputedHeightDerivedSumCum<S1T>,
|
||||
) -> Self
|
||||
where
|
||||
S1T: NumericValue,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
macro_rules! period {
|
||||
($p:ident) => {
|
||||
LazyTransformSumCum::from_boxed_sum_raw::<F>(
|
||||
name,
|
||||
v,
|
||||
source.$p.sum.read_only_boxed_clone(),
|
||||
source.$p.cumulative.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Self {
|
||||
minute1: period!(minute1),
|
||||
minute5: period!(minute5),
|
||||
minute10: period!(minute10),
|
||||
minute30: period!(minute30),
|
||||
hour1: period!(hour1),
|
||||
hour4: period!(hour4),
|
||||
hour12: period!(hour12),
|
||||
day1: period!(day1),
|
||||
day3: period!(day3),
|
||||
week1: period!(week1),
|
||||
month1: period!(month1),
|
||||
month3: period!(month3),
|
||||
month6: period!(month6),
|
||||
year1: period!(year1),
|
||||
year10: period!(year10),
|
||||
halvingepoch: period!(halvingepoch),
|
||||
difficultyepoch: period!(difficultyepoch),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,9 @@
|
||||
mod binary_last;
|
||||
mod cum_full;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod cumulative_full;
|
||||
mod last;
|
||||
mod lazy_full;
|
||||
mod lazy_last;
|
||||
mod lazy_sum_cum;
|
||||
mod sum_cum;
|
||||
mod value_lazy_last;
|
||||
|
||||
pub use binary_last::*;
|
||||
pub use cum_full::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use cumulative_full::*;
|
||||
pub use last::*;
|
||||
pub use lazy_full::*;
|
||||
pub use lazy_last::*;
|
||||
pub use lazy_sum_cum::*;
|
||||
pub use sum_cum::*;
|
||||
pub use value_lazy_last::*;
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
//! ComputedHeightDerivedSumCum - height cumulative (stored) + lazy time periods + epochs.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, CumulativeVec, LazySumCum, NumericValue},
|
||||
ComputeIndexes,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ComputedHeightDerivedSumCum<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(rename = "cumulative")]
|
||||
pub height_cumulative: CumulativeVec<Height, T, M>,
|
||||
pub minute1: LazySumCum<Minute1, T, Height, Height>,
|
||||
pub minute5: LazySumCum<Minute5, T, Height, Height>,
|
||||
pub minute10: LazySumCum<Minute10, T, Height, Height>,
|
||||
pub minute30: LazySumCum<Minute30, T, Height, Height>,
|
||||
pub hour1: LazySumCum<Hour1, T, Height, Height>,
|
||||
pub hour4: LazySumCum<Hour4, T, Height, Height>,
|
||||
pub hour12: LazySumCum<Hour12, T, Height, Height>,
|
||||
pub day1: LazySumCum<Day1, T, Height, Height>,
|
||||
pub day3: LazySumCum<Day3, T, Height, Height>,
|
||||
pub week1: LazySumCum<Week1, T, Height, Height>,
|
||||
pub month1: LazySumCum<Month1, T, Height, Height>,
|
||||
pub month3: LazySumCum<Month3, T, Height, Height>,
|
||||
pub month6: LazySumCum<Month6, T, Height, Height>,
|
||||
pub year1: LazySumCum<Year1, T, Height, Height>,
|
||||
pub year10: LazySumCum<Year10, T, Height, Height>,
|
||||
pub halvingepoch: LazySumCum<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
pub difficultyepoch: LazySumCum<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> ComputedHeightDerivedSumCum<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
height_source: ReadableBoxedVec<Height, T>,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
|
||||
let height_cumulative = CumulativeVec::forced_import(db, name, v)?;
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazySumCum::from_height_sources_sum_raw(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
height_cumulative.read_only_boxed_clone(),
|
||||
indexes.$idx.first_height.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazySumCum::from_sources_sum_raw(
|
||||
name,
|
||||
v,
|
||||
height_source.clone(),
|
||||
height_cumulative.read_only_boxed_clone(),
|
||||
indexes.$idx.identity.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let minute1 = period!(minute1);
|
||||
let minute5 = period!(minute5);
|
||||
let minute10 = period!(minute10);
|
||||
let minute30 = period!(minute30);
|
||||
let hour1 = period!(hour1);
|
||||
let hour4 = period!(hour4);
|
||||
let hour12 = period!(hour12);
|
||||
let day1 = period!(day1);
|
||||
let day3 = period!(day3);
|
||||
let week1 = period!(week1);
|
||||
let month1 = period!(month1);
|
||||
let month3 = period!(month3);
|
||||
let month6 = period!(month6);
|
||||
let year1 = period!(year1);
|
||||
let year10 = period!(year10);
|
||||
let halvingepoch = epoch!(halvingepoch);
|
||||
let difficultyepoch = epoch!(difficultyepoch);
|
||||
|
||||
Ok(Self {
|
||||
height_cumulative,
|
||||
minute1,
|
||||
minute5,
|
||||
minute10,
|
||||
minute30,
|
||||
hour1,
|
||||
hour4,
|
||||
hour12,
|
||||
day1,
|
||||
day3,
|
||||
week1,
|
||||
month1,
|
||||
month3,
|
||||
month6,
|
||||
year1,
|
||||
year10,
|
||||
halvingepoch,
|
||||
difficultyepoch,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive_from(
|
||||
&mut self,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
height_source: &impl ReadableVec<Height, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.height_cumulative
|
||||
.0
|
||||
.compute_cumulative(starting_indexes.height, height_source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
//! TxDerivedDistribution - computes TxIndex data to height Distribution + lazy time periods + epochs.
|
||||
//! TxDerivedDistribution - per-block + rolling window distribution stats from tx-level data.
|
||||
//!
|
||||
//! Computes true distribution stats (average, min, max, median, percentiles) by reading
|
||||
//! actual tx values for each scope: current block, last 1h, last 24h.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
Day1, Day3, DifficultyEpoch, HalvingEpoch, Height, Hour1, Hour12, Hour4, Minute1, Minute10,
|
||||
Minute30, Minute5, Month1, Month3, Month6, TxIndex, Version, Week1, Year1, Year10,
|
||||
};
|
||||
use brk_types::{Height, TxIndex};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode};
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, Version};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{ComputedVecValue, Distribution, LazyDistribution, NumericValue},
|
||||
internal::{BlockRollingDistribution, BlockWindowStarts, ComputedVecValue, Distribution, NumericValue},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -22,28 +22,11 @@ pub struct TxDerivedDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub height: Distribution<Height, T, M>,
|
||||
pub minute1: LazyDistribution<Minute1, T, Height, Height>,
|
||||
pub minute5: LazyDistribution<Minute5, T, Height, Height>,
|
||||
pub minute10: LazyDistribution<Minute10, T, Height, Height>,
|
||||
pub minute30: LazyDistribution<Minute30, T, Height, Height>,
|
||||
pub hour1: LazyDistribution<Hour1, T, Height, Height>,
|
||||
pub hour4: LazyDistribution<Hour4, T, Height, Height>,
|
||||
pub hour12: LazyDistribution<Hour12, T, Height, Height>,
|
||||
pub day1: LazyDistribution<Day1, T, Height, Height>,
|
||||
pub day3: LazyDistribution<Day3, T, Height, Height>,
|
||||
pub week1: LazyDistribution<Week1, T, Height, Height>,
|
||||
pub month1: LazyDistribution<Month1, T, Height, Height>,
|
||||
pub month3: LazyDistribution<Month3, T, Height, Height>,
|
||||
pub month6: LazyDistribution<Month6, T, Height, Height>,
|
||||
pub year1: LazyDistribution<Year1, T, Height, Height>,
|
||||
pub year10: LazyDistribution<Year10, T, Height, Height>,
|
||||
pub halvingepoch: LazyDistribution<HalvingEpoch, T, Height, HalvingEpoch>,
|
||||
pub difficultyepoch: LazyDistribution<DifficultyEpoch, T, Height, DifficultyEpoch>,
|
||||
pub block: Distribution<Height, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: BlockRollingDistribution<T, M>,
|
||||
}
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
impl<T> TxDerivedDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
@@ -52,71 +35,11 @@ where
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height = Distribution::forced_import(db, name, version + VERSION)?;
|
||||
let v = version + VERSION;
|
||||
let block = Distribution::forced_import(db, name, version)?;
|
||||
let rolling = BlockRollingDistribution::forced_import(db, name, version)?;
|
||||
|
||||
macro_rules! period {
|
||||
($idx:ident) => {
|
||||
LazyDistribution::from_height_source(
|
||||
name,
|
||||
v,
|
||||
height.boxed_average(),
|
||||
indexes.$idx.first_height.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! epoch {
|
||||
($idx:ident) => {
|
||||
LazyDistribution::from_source(
|
||||
name,
|
||||
v,
|
||||
height.boxed_average(),
|
||||
indexes.$idx.identity.read_only_boxed_clone(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
let minute1 = period!(minute1);
|
||||
let minute5 = period!(minute5);
|
||||
let minute10 = period!(minute10);
|
||||
let minute30 = period!(minute30);
|
||||
let hour1 = period!(hour1);
|
||||
let hour4 = period!(hour4);
|
||||
let hour12 = period!(hour12);
|
||||
let day1 = period!(day1);
|
||||
let day3 = period!(day3);
|
||||
let week1 = period!(week1);
|
||||
let month1 = period!(month1);
|
||||
let month3 = period!(month3);
|
||||
let month6 = period!(month6);
|
||||
let year1 = period!(year1);
|
||||
let year10 = period!(year10);
|
||||
let halvingepoch = epoch!(halvingepoch);
|
||||
let difficultyepoch = epoch!(difficultyepoch);
|
||||
|
||||
Ok(Self {
|
||||
height,
|
||||
minute1,
|
||||
minute5,
|
||||
minute10,
|
||||
minute30,
|
||||
hour1,
|
||||
hour4,
|
||||
hour12,
|
||||
day1,
|
||||
day3,
|
||||
week1,
|
||||
month1,
|
||||
month3,
|
||||
month6,
|
||||
year1,
|
||||
year10,
|
||||
halvingepoch,
|
||||
difficultyepoch,
|
||||
})
|
||||
Ok(Self { block, rolling })
|
||||
}
|
||||
|
||||
pub(crate) fn derive_from(
|
||||
@@ -124,25 +47,46 @@ where
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
block_windows: &BlockWindowStarts<'_>,
|
||||
txindex_source: &impl ReadableVec<TxIndex, T>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.derive_from_with_skip(indexer, indexes, starting_indexes, txindex_source, exit, 0)
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.derive_from_with_skip(
|
||||
indexer,
|
||||
indexes,
|
||||
starting_indexes,
|
||||
block_windows,
|
||||
txindex_source,
|
||||
exit,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
/// Derive from source, skipping first N transactions per block from all calculations.
|
||||
/// Derive from source, skipping first N transactions per block from per-block stats.
|
||||
///
|
||||
/// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats.
|
||||
/// Rolling window distributions do NOT skip (negligible impact over many blocks).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn derive_from_with_skip(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
block_windows: &BlockWindowStarts<'_>,
|
||||
txindex_source: &impl ReadableVec<TxIndex, T>,
|
||||
exit: &Exit,
|
||||
skip_count: usize,
|
||||
) -> Result<()> {
|
||||
self.height.compute_with_skip(
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
// Per-block distribution (supports skip for coinbase exclusion)
|
||||
self.block.compute_with_skip(
|
||||
starting_indexes.height,
|
||||
txindex_source,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
@@ -151,6 +95,26 @@ where
|
||||
skip_count,
|
||||
)?;
|
||||
|
||||
// 1h rolling: true distribution from all txs in last hour
|
||||
self.rolling._1h.compute_from_window(
|
||||
starting_indexes.height,
|
||||
txindex_source,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
block_windows._1h,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 24h rolling: true distribution from all txs in last 24 hours
|
||||
self.rolling._24h.compute_from_window(
|
||||
starting_indexes.height,
|
||||
txindex_source,
|
||||
&indexer.vecs.transactions.first_txindex,
|
||||
&indexes.height.txindex_count,
|
||||
block_windows._24h,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,40 @@ use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, Exit, ReadableBoxedVec, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version,
|
||||
Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version,
|
||||
};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
use crate::internal::{
|
||||
AverageVec, ComputedVecValue, MaxVec, MedianVec, MinVec, Pct10Vec, Pct25Vec, Pct75Vec,
|
||||
Pct90Vec,
|
||||
};
|
||||
|
||||
use super::{MinMaxAverage, Percentiles};
|
||||
|
||||
/// Distribution stats (average + minmax + percentiles)
|
||||
/// Distribution stats (average + min + max + percentiles) — flat 8-field struct.
|
||||
#[derive(Traversable)]
|
||||
pub struct Distribution<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub average: AverageVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub min_max_average: MinMaxAverage<I, T, M>,
|
||||
pub min: MinVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub percentiles: Percentiles<I, T, M>,
|
||||
pub max: MaxVec<I, T, M>,
|
||||
pub pct10: Pct10Vec<I, T, M>,
|
||||
pub pct25: Pct25Vec<I, T, M>,
|
||||
pub median: MedianVec<I, T, M>,
|
||||
pub pct75: Pct75Vec<I, T, M>,
|
||||
pub pct90: Pct90Vec<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
min_max_average: MinMaxAverage::forced_import(db, name, version)?,
|
||||
percentiles: Percentiles::forced_import(db, name, version)?,
|
||||
average: AverageVec::forced_import(db, name, version)?,
|
||||
min: MinVec::forced_import(db, name, version)?,
|
||||
max: MaxVec::forced_import(db, name, version)?,
|
||||
pct10: Pct10Vec::forced_import(db, name, version)?,
|
||||
pct25: Pct25Vec::forced_import(db, name, version)?,
|
||||
median: MedianVec::forced_import(db, name, version)?,
|
||||
pct75: Pct75Vec::forced_import(db, name, version)?,
|
||||
pct90: Pct90Vec::forced_import(db, name, version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,28 +63,63 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
|
||||
skip_count,
|
||||
None, // first
|
||||
None, // last
|
||||
Some(&mut self.min_max_average.minmax.min.0),
|
||||
Some(&mut self.min_max_average.minmax.max.0),
|
||||
Some(&mut self.min_max_average.average.0),
|
||||
Some(&mut self.min.0),
|
||||
Some(&mut self.max.0),
|
||||
Some(&mut self.average.0),
|
||||
None, // sum
|
||||
None, // cumulative
|
||||
Some(&mut self.percentiles.median.0),
|
||||
Some(&mut self.percentiles.pct10.0),
|
||||
Some(&mut self.percentiles.pct25.0),
|
||||
Some(&mut self.percentiles.pct75.0),
|
||||
Some(&mut self.percentiles.pct90.0),
|
||||
Some(&mut self.median.0),
|
||||
Some(&mut self.pct10.0),
|
||||
Some(&mut self.pct25.0),
|
||||
Some(&mut self.pct75.0),
|
||||
Some(&mut self.pct90.0),
|
||||
)
|
||||
}
|
||||
|
||||
// Boxed accessors
|
||||
pub(crate) fn boxed_average(&self) -> ReadableBoxedVec<I, T> {
|
||||
self.min_max_average.boxed_average()
|
||||
/// Compute distribution stats from all items in a rolling window of groups.
|
||||
///
|
||||
/// For each index `i`, reads all source items from groups `window_starts[i]..=i`
|
||||
/// and computes distribution stats across the entire window.
|
||||
pub(crate) fn compute_from_window<A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &impl ReadableVec<A, T>,
|
||||
first_indexes: &impl ReadableVec<I, A>,
|
||||
count_indexes: &impl ReadableVec<I, brk_types::StoredU64>,
|
||||
window_starts: &impl ReadableVec<I, I>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
|
||||
{
|
||||
crate::internal::compute_aggregations_windowed(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
window_starts,
|
||||
exit,
|
||||
&mut self.min.0,
|
||||
&mut self.max.0,
|
||||
&mut self.average.0,
|
||||
&mut self.median.0,
|
||||
&mut self.pct10.0,
|
||||
&mut self.pct25.0,
|
||||
&mut self.pct75.0,
|
||||
&mut self.pct90.0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> Distribution<I, T, Ro> {
|
||||
Distribution {
|
||||
min_max_average: self.min_max_average.read_only_clone(),
|
||||
percentiles: self.percentiles.read_only_clone(),
|
||||
average: self.average.read_only_clone(),
|
||||
min: self.min.read_only_clone(),
|
||||
max: self.max.read_only_clone(),
|
||||
pct10: self.pct10.read_only_clone(),
|
||||
pct25: self.pct25.read_only_clone(),
|
||||
median: self.median.read_only_clone(),
|
||||
pct75: self.pct75.read_only_clone(),
|
||||
pct90: self.pct90.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, Exit, ReadableBoxedVec, ReadableCloneableVec, ReadableVec, Ro, Rw, StorageMode,
|
||||
VecIndex, VecValue, Version,
|
||||
};
|
||||
use vecdb::{Database, Exit, ReadableVec, Ro, Rw, StorageMode, VecIndex, VecValue, Version};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
use super::{Distribution, SumCum};
|
||||
use super::{Distribution, SumCumulative};
|
||||
|
||||
/// Full stats aggregate: distribution + sum_cum
|
||||
/// Full stats aggregate: distribution + sum_cumulative
|
||||
/// Matches the common full_stats() pattern: average + minmax + percentiles + sum + cumulative
|
||||
#[derive(Traversable)]
|
||||
pub struct Full<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub distribution: Distribution<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub sum_cum: SumCum<I, T, M>,
|
||||
pub sum_cumulative: SumCumulative<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Full<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
distribution: Distribution::forced_import(db, name, version)?,
|
||||
sum_cum: SumCum::forced_import(db, name, version)?,
|
||||
sum_cumulative: SumCumulative::forced_import(db, name, version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,27 +49,23 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Full<I, T> {
|
||||
skip_count,
|
||||
None, // first
|
||||
None, // last
|
||||
Some(&mut self.distribution.min_max_average.minmax.min.0),
|
||||
Some(&mut self.distribution.min_max_average.minmax.max.0),
|
||||
Some(&mut self.distribution.min_max_average.average.0),
|
||||
Some(&mut self.sum_cum.sum.0),
|
||||
Some(&mut self.sum_cum.cumulative.0),
|
||||
Some(&mut self.distribution.percentiles.median.0),
|
||||
Some(&mut self.distribution.percentiles.pct10.0),
|
||||
Some(&mut self.distribution.percentiles.pct25.0),
|
||||
Some(&mut self.distribution.percentiles.pct75.0),
|
||||
Some(&mut self.distribution.percentiles.pct90.0),
|
||||
Some(&mut self.distribution.min.0),
|
||||
Some(&mut self.distribution.max.0),
|
||||
Some(&mut self.distribution.average.0),
|
||||
Some(&mut self.sum_cumulative.sum.0),
|
||||
Some(&mut self.sum_cumulative.cumulative.0),
|
||||
Some(&mut self.distribution.median.0),
|
||||
Some(&mut self.distribution.pct10.0),
|
||||
Some(&mut self.distribution.pct25.0),
|
||||
Some(&mut self.distribution.pct75.0),
|
||||
Some(&mut self.distribution.pct90.0),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn boxed_sum(&self) -> ReadableBoxedVec<I, T> {
|
||||
self.sum_cum.sum.0.read_only_boxed_clone()
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> Full<I, T, Ro> {
|
||||
Full {
|
||||
distribution: self.distribution.read_only_clone(),
|
||||
sum_cum: self.sum_cum.read_only_clone(),
|
||||
sum_cumulative: self.sum_cumulative.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Ro, Rw, StorageMode, VecIndex, Version};
|
||||
|
||||
use crate::internal::{ComputedVecValue, MaxVec, MinVec};
|
||||
|
||||
/// Min + Max
|
||||
#[derive(Traversable)]
|
||||
pub struct MinMax<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub min: MinVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub max: MaxVec<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> MinMax<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
min: MinVec::forced_import(db, name, version)?,
|
||||
max: MaxVec::forced_import(db, name, version)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> MinMax<I, T, Ro> {
|
||||
MinMax {
|
||||
min: self.min.read_only_clone(),
|
||||
max: self.max.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw, StorageMode, VecIndex, Version,
|
||||
};
|
||||
|
||||
use crate::internal::{AverageVec, ComputedVecValue};
|
||||
|
||||
use super::MinMax;
|
||||
|
||||
/// Average + MinMax (for TxIndex day1 aggregation - no percentiles)
|
||||
#[derive(Traversable)]
|
||||
pub struct MinMaxAverage<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub average: AverageVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub minmax: MinMax<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> MinMaxAverage<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
average: AverageVec::forced_import(db, name, version)?,
|
||||
minmax: MinMax::forced_import(db, name, version)?,
|
||||
})
|
||||
}
|
||||
|
||||
// Boxed accessors
|
||||
pub(crate) fn boxed_average(&self) -> ReadableBoxedVec<I, T> {
|
||||
self.average.0.read_only_boxed_clone()
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> MinMaxAverage<I, T, Ro> {
|
||||
MinMaxAverage {
|
||||
average: self.average.read_only_clone(),
|
||||
minmax: self.minmax.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod min_max;
|
||||
mod min_max_average;
|
||||
mod percentiles;
|
||||
mod sum_cum;
|
||||
mod sum_cumulative;
|
||||
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use min_max::*;
|
||||
pub use min_max_average::*;
|
||||
pub use percentiles::*;
|
||||
pub use sum_cum::*;
|
||||
pub use sum_cumulative::*;
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Ro, Rw, StorageMode, VecIndex, Version};
|
||||
|
||||
use crate::internal::{ComputedVecValue, MedianVec, Pct10Vec, Pct25Vec, Pct75Vec, Pct90Vec};
|
||||
|
||||
/// All percentiles (pct10, pct25, median, pct75, pct90)
|
||||
#[derive(Traversable)]
|
||||
pub struct Percentiles<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub pct10: Pct10Vec<I, T, M>,
|
||||
pub pct25: Pct25Vec<I, T, M>,
|
||||
pub median: MedianVec<I, T, M>,
|
||||
pub pct75: Pct75Vec<I, T, M>,
|
||||
pub pct90: Pct90Vec<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Percentiles<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
pct10: Pct10Vec::forced_import(db, name, version)?,
|
||||
pct25: Pct25Vec::forced_import(db, name, version)?,
|
||||
median: MedianVec::forced_import(db, name, version)?,
|
||||
pct75: Pct75Vec::forced_import(db, name, version)?,
|
||||
pct90: Pct90Vec::forced_import(db, name, version)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> Percentiles<I, T, Ro> {
|
||||
Percentiles {
|
||||
pct10: self.pct10.read_only_clone(),
|
||||
pct25: self.pct25.read_only_clone(),
|
||||
median: self.median.read_only_clone(),
|
||||
pct75: self.pct75.read_only_clone(),
|
||||
pct90: self.pct90.read_only_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -7,14 +7,14 @@ use crate::internal::{ComputedVecValue, CumulativeVec, SumVec};
|
||||
|
||||
/// Sum + Cumulative (12% of usage)
|
||||
#[derive(Traversable)]
|
||||
pub struct SumCum<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub struct SumCumulative<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub sum: SumVec<I, T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: CumulativeVec<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> SumCum<I, T> {
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> SumCumulative<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sum: SumVec::forced_import(db, name, version)?,
|
||||
@@ -22,8 +22,8 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> SumCum<I, T> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> SumCum<I, T, Ro> {
|
||||
SumCum {
|
||||
pub fn read_only_clone(&self) -> SumCumulative<I, T, Ro> {
|
||||
SumCumulative {
|
||||
sum: self.sum.read_only_clone(),
|
||||
cumulative: self.cumulative.read_only_clone(),
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats};
|
||||
use vecdb::{LazyVecFrom1, LazyVecFrom2};
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDerivedValuesHeight {
|
||||
pub btc: LazyVecFrom1<Height, Bitcoin, Height, Sats>,
|
||||
pub usd: LazyVecFrom2<Height, Dollars, Height, Dollars, Height, Sats>,
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Sats, Version};
|
||||
use vecdb::{ReadableCloneableVec, LazyVecFrom1, UnaryTransform};
|
||||
|
||||
use crate::internal::{SatsToBitcoin, ValueFromHeightLast};
|
||||
use crate::internal::ValueFromHeightLast;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
@@ -19,13 +19,14 @@ pub struct LazyValueHeight {
|
||||
}
|
||||
|
||||
impl LazyValueHeight {
|
||||
pub(crate) fn from_block_source<SatsTransform, DollarsTransform>(
|
||||
pub(crate) fn from_block_source<SatsTransform, BitcoinTransform, DollarsTransform>(
|
||||
name: &str,
|
||||
source: &ValueFromHeightLast,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
DollarsTransform: UnaryTransform<Dollars, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
@@ -33,7 +34,7 @@ impl LazyValueHeight {
|
||||
let sats =
|
||||
LazyVecFrom1::transformed::<SatsTransform>(name, v, source.sats.height.read_only_boxed_clone());
|
||||
|
||||
let btc = LazyVecFrom1::transformed::<SatsToBitcoin>(
|
||||
let btc = LazyVecFrom1::transformed::<BitcoinTransform>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
source.sats.height.read_only_boxed_clone(),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
mod derived_values;
|
||||
mod lazy_value;
|
||||
|
||||
pub use derived_values::*;
|
||||
pub use lazy_value::*;
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
//! Lazy average-value aggregation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyAverage<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyAverage);
|
||||
|
||||
impl<I, T, S1I, S2T> LazyAverage<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::from_source_inner(&format!("{name}_average"), version, source, len_source)
|
||||
}
|
||||
|
||||
fn from_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let to = to.min(mapping_len);
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(S1I::min_from(I::from(from)));
|
||||
for i in from..to {
|
||||
let start = S1I::min_from(I::from(i));
|
||||
let end = S1I::max_from(I::from(i), source_len) + 1;
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 || cursor.remaining() == 0 {
|
||||
continue;
|
||||
}
|
||||
let sum = cursor.fold(count, T::from(0), |s, v| s + v);
|
||||
f(sum / count);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyAverage<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self::from_height_source_inner(&format!("{name}_average"), version, source, first_height)
|
||||
}
|
||||
|
||||
fn from_height_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: ComputedVecValue + JsonSchema>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let Some(&first_h) = heights.first() else {
|
||||
return;
|
||||
};
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(first_h.to_usize());
|
||||
for idx in 0..(to - from) {
|
||||
let Some(&cur_h) = heights.get(idx) else {
|
||||
continue;
|
||||
};
|
||||
let first = cur_h.to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
let count = next_first.saturating_sub(first);
|
||||
if count == 0 || cursor.remaining() == 0 {
|
||||
continue;
|
||||
}
|
||||
let sum = cursor.fold(count, T::from(0), |s, v| s + v);
|
||||
f(sum / count);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
//! Lazy cumulative-only aggregation (takes last value from cumulative source).
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyCumulative<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyCumulative);
|
||||
|
||||
impl<I, T, S1I, S2T> LazyCumulative<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative_source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
for i in from..to {
|
||||
if i >= mapping_len {
|
||||
break;
|
||||
}
|
||||
let target = S1I::max_from(I::from(i), source_len);
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(format!("{name}_cumulative")),
|
||||
version: version + VERSION,
|
||||
source: cumulative_source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyCumulative<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
/// Create from a height-indexed cumulative source using an explicit first_height mapping.
|
||||
/// Looks up cumulative value at last height of the day.
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
cumulative_source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: VecValue>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
for idx in 0..(to - from) {
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
if next_first == 0 {
|
||||
continue;
|
||||
}
|
||||
let target = next_first - 1;
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(format!("{name}_cumulative")),
|
||||
version: version + VERSION,
|
||||
source: cumulative_source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//! Lazy distribution pattern (average, min, max + percentiles).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use super::{LazyAggPercentiles, LazyAverage, LazyMax, LazyMin};
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDistribution<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
#[traversable(flatten)]
|
||||
pub average: LazyAverage<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub min: LazyMin<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub max: LazyMax<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub percentiles: LazyAggPercentiles<I, T, S1I, S2T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> LazyDistribution<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
average: LazyAverage::from_source(name, v, source.clone(), len_source.clone()),
|
||||
min: LazyMin::from_source(name, v, source.clone(), len_source.clone()),
|
||||
max: LazyMax::from_source(name, v, source.clone(), len_source.clone()),
|
||||
percentiles: LazyAggPercentiles::from_source(name, v, source, len_source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyDistribution<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
average: LazyAverage::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
min: LazyMin::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
max: LazyMax::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
percentiles: LazyAggPercentiles::from_height_source(name, v, source, first_height),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
//! Lazy full stats aggregate (distribution + sum + cumulative).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use super::{LazyAggPercentiles, LazyAverage, LazyCumulative, LazyMax, LazyMin, LazySum};
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyFull<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
#[traversable(flatten)]
|
||||
pub average: LazyAverage<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub min: LazyMin<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub max: LazyMax<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub percentiles: LazyAggPercentiles<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub sum: LazySum<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: LazyCumulative<I, T, S1I, S2T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> LazyFull<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn from_stats_aggregate(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source_average: ReadableBoxedVec<S1I, T>,
|
||||
source_min: ReadableBoxedVec<S1I, T>,
|
||||
source_max: ReadableBoxedVec<S1I, T>,
|
||||
source_sum: ReadableBoxedVec<S1I, T>,
|
||||
source_cumulative: ReadableBoxedVec<S1I, T>,
|
||||
source_all: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
average: LazyAverage::from_source(name, v, source_average, len_source.clone()),
|
||||
min: LazyMin::from_source(name, v, source_min, len_source.clone()),
|
||||
max: LazyMax::from_source(name, v, source_max, len_source.clone()),
|
||||
percentiles: LazyAggPercentiles::from_source(name, v, source_all, len_source.clone()),
|
||||
sum: LazySum::from_source(name, v, source_sum, len_source.clone()),
|
||||
cumulative: LazyCumulative::from_source(name, v, source_cumulative, len_source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyFull<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
height_cumulative: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
average: LazyAverage::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
min: LazyMin::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
max: LazyMax::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
percentiles: LazyAggPercentiles::from_height_source(name, v, source.clone(), first_height.clone()),
|
||||
sum: LazySum::from_height_source(name, v, source, first_height.clone()),
|
||||
cumulative: LazyCumulative::from_height_source(name, v, height_cumulative, first_height),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
//! Lazy last-value aggregation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyLast<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyLast);
|
||||
|
||||
impl<I, T, S1I, S2T> LazyLast<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: VecValue,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
for i in from..to {
|
||||
if i >= mapping_len {
|
||||
break;
|
||||
}
|
||||
let target = S1I::max_from(I::from(i), source_len);
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyLast<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
/// Create from a height-indexed source using an explicit first_height mapping.
|
||||
/// For day1 d, looks up value at `first_height[d+1] - 1` (last height of the day).
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: VecValue>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
for idx in 0..(to - from) {
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
if next_first == 0 {
|
||||
continue;
|
||||
}
|
||||
let target = next_first - 1;
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
if let Some(v) = cursor.next() {
|
||||
f(v);
|
||||
}
|
||||
} else if let Some(v) = source.collect_one_at(target) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//! Lazy max-value aggregation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyMax<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyMax);
|
||||
|
||||
impl<I, T, S1I, S2T> LazyMax<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::from_source_inner(&format!("{name}_max"), version, source, len_source)
|
||||
}
|
||||
|
||||
fn from_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let to = to.min(mapping_len);
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(S1I::min_from(I::from(from)));
|
||||
for i in from..to {
|
||||
let start = S1I::min_from(I::from(i));
|
||||
let end = S1I::max_from(I::from(i), source_len) + 1;
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(first) = cursor.next() {
|
||||
f(cursor.fold(count - 1, first, |m, v| if v > m { v } else { m }));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyMax<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self::from_height_source_inner(&format!("{name}_max"), version, source, first_height)
|
||||
}
|
||||
|
||||
fn from_height_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: ComputedVecValue + JsonSchema>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let Some(&first_h) = heights.first() else {
|
||||
return;
|
||||
};
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(first_h.to_usize());
|
||||
for idx in 0..(to - from) {
|
||||
let Some(&cur_h) = heights.get(idx) else {
|
||||
continue;
|
||||
};
|
||||
let first = cur_h.to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
let count = next_first.saturating_sub(first);
|
||||
if count == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(first_val) = cursor.next() {
|
||||
f(cursor.fold(count - 1, first_val, |m, v| if v > m { v } else { m }));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
//! Lazy min-value aggregation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyMin<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyMin);
|
||||
|
||||
impl<I, T, S1I, S2T> LazyMin<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::from_source_inner(&format!("{name}_min"), version, source, len_source)
|
||||
}
|
||||
|
||||
fn from_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let to = to.min(mapping_len);
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(S1I::min_from(I::from(from)));
|
||||
for i in from..to {
|
||||
let start = S1I::min_from(I::from(i));
|
||||
let end = S1I::max_from(I::from(i), source_len) + 1;
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(first) = cursor.next() {
|
||||
f(cursor.fold(count - 1, first, |m, v| if v < m { v } else { m }));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyMin<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self::from_height_source_inner(&format!("{name}_min"), version, source, first_height)
|
||||
}
|
||||
|
||||
fn from_height_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: ComputedVecValue + JsonSchema>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let Some(&first_h) = heights.first() else {
|
||||
return;
|
||||
};
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(first_h.to_usize());
|
||||
for idx in 0..(to - from) {
|
||||
let Some(&cur_h) = heights.get(idx) else {
|
||||
continue;
|
||||
};
|
||||
let first = cur_h.to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
let count = next_first.saturating_sub(first);
|
||||
if count == 0 {
|
||||
continue;
|
||||
}
|
||||
if let Some(first_val) = cursor.next() {
|
||||
f(cursor.fold(count - 1, first_val, |m, v| if v < m { v } else { m }));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
//! Lazy aggregation primitives (finer index → coarser index).
|
||||
//!
|
||||
//! These types implement GROUP BY: map each coarser output index to a range
|
||||
//! in the finer source, then aggregate that range. They implement the vecdb
|
||||
//! ReadableVec trait directly.
|
||||
|
||||
/// Common trait implementations for lazy aggregation types.
|
||||
///
|
||||
/// Provides: Clone, AnyVec, TypedVec, ReadableVec, Traversable.
|
||||
/// The struct must have fields: name, version, source, mapping, for_each_range.
|
||||
macro_rules! impl_lazy_agg {
|
||||
($name:ident) => {
|
||||
impl<I, T, S1I, S2T> Clone for $name<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
version: self.version,
|
||||
source: self.source.clone(),
|
||||
mapping: self.mapping.clone(),
|
||||
for_each_range: self.for_each_range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> vecdb::AnyVec for $name<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn version(&self) -> Version {
|
||||
self.version + self.source.version() + self.mapping.version()
|
||||
}
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
fn index_type_to_string(&self) -> &'static str {
|
||||
I::to_string()
|
||||
}
|
||||
fn len(&self) -> usize {
|
||||
self.mapping.len()
|
||||
}
|
||||
#[inline]
|
||||
fn value_type_to_size_of(&self) -> usize {
|
||||
size_of::<T>()
|
||||
}
|
||||
#[inline]
|
||||
fn value_type_to_string(&self) -> &'static str {
|
||||
vecdb::short_type_name::<T>()
|
||||
}
|
||||
#[inline]
|
||||
fn region_names(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> vecdb::TypedVec for $name<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
type I = I;
|
||||
type T = T;
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> vecdb::ReadableVec<I, T> for $name<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<T>) {
|
||||
let to = to.min(self.mapping.len());
|
||||
if from >= to { return; }
|
||||
buf.reserve(to - from);
|
||||
(self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| buf.push(v));
|
||||
}
|
||||
|
||||
fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(T)) {
|
||||
let to = to.min(self.mapping.len());
|
||||
if from >= to { return; }
|
||||
(self.for_each_range)(from, to, &self.source, &self.mapping, f);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_range_at<B, F: FnMut(B, T) -> B>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.mapping.len());
|
||||
if from >= to { return init; }
|
||||
let mut acc = Some(init);
|
||||
(self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| {
|
||||
acc = Some(f(acc.take().unwrap(), v));
|
||||
});
|
||||
acc.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fold_range_at<B, E, F: FnMut(B, T) -> std::result::Result<B, E>>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> std::result::Result<B, E>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.mapping.len());
|
||||
if from >= to { return Ok(init); }
|
||||
let mut acc: Option<std::result::Result<B, E>> = Some(Ok(init));
|
||||
(self.for_each_range)(from, to, &self.source, &self.mapping, &mut |v| {
|
||||
if let Some(Ok(a)) = acc.take() {
|
||||
acc = Some(f(a, v));
|
||||
}
|
||||
});
|
||||
acc.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_one_at(&self, index: usize) -> Option<T> {
|
||||
if index >= self.mapping.len() {
|
||||
return None;
|
||||
}
|
||||
let mut result = None;
|
||||
(self.for_each_range)(index, index + 1, &self.source, &self.mapping, &mut |v| result = Some(v));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> Traversable for $name<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static,
|
||||
S2T: VecValue,
|
||||
{
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
|
||||
std::iter::once(self as &dyn vecdb::AnyExportableVec)
|
||||
}
|
||||
|
||||
fn to_tree_node(&self) -> brk_types::TreeNode {
|
||||
use vecdb::AnyVec;
|
||||
let index_str = I::to_string();
|
||||
let index = brk_types::Index::try_from(index_str).ok();
|
||||
let indexes = index.into_iter().collect();
|
||||
let leaf = brk_types::MetricLeaf::new(
|
||||
self.name().to_string(),
|
||||
self.value_type_to_string().to_string(),
|
||||
indexes,
|
||||
);
|
||||
let schema =
|
||||
schemars::SchemaGenerator::default().into_root_schema_for::<T>();
|
||||
let schema_json = serde_json::to_value(schema).unwrap_or_default();
|
||||
brk_types::TreeNode::Leaf(brk_types::MetricLeafWithSchema::new(
|
||||
leaf,
|
||||
schema_json,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod average;
|
||||
mod cumulative;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod last;
|
||||
mod max;
|
||||
mod min;
|
||||
mod percentile;
|
||||
mod percentiles;
|
||||
mod sparse_last;
|
||||
mod sum;
|
||||
mod sum_cum;
|
||||
|
||||
pub use average::*;
|
||||
pub use cumulative::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use last::*;
|
||||
pub use max::*;
|
||||
pub use min::*;
|
||||
pub use percentile::*;
|
||||
pub use percentiles::*;
|
||||
pub use sum::*;
|
||||
pub use sum_cum::*;
|
||||
@@ -1,156 +0,0 @@
|
||||
//! Lazy percentile aggregation via const-generic fn pointers.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazyPercentile<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazyPercentile);
|
||||
|
||||
fn select_and_pick<T: PartialOrd + Copy>(values: &mut [T], pct: u8) -> Option<T> {
|
||||
if values.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let idx = (values.len() - 1) * pct as usize / 100;
|
||||
values.select_nth_unstable_by(idx, |a, b| {
|
||||
a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)
|
||||
});
|
||||
Some(values[idx])
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> LazyPercentile<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source<const PCT: u8>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
const PCT: u8,
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let to = to.min(mapping_len);
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(S1I::min_from(I::from(from)));
|
||||
let mut values = Vec::new();
|
||||
for i in from..to {
|
||||
let start = S1I::min_from(I::from(i));
|
||||
let end = S1I::max_from(I::from(i), source_len) + 1;
|
||||
if end <= start {
|
||||
continue;
|
||||
}
|
||||
values.clear();
|
||||
cursor.for_each(end - start, |v| values.push(v));
|
||||
if let Some(v) = select_and_pick(&mut values, PCT) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<PCT, I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyPercentile<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source<const PCT: u8>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<const PCT: u8, I: VecIndex, T: ComputedVecValue + JsonSchema>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let Some(&first_h) = heights.first() else {
|
||||
return;
|
||||
};
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(first_h.to_usize());
|
||||
let mut values = Vec::new();
|
||||
for idx in 0..(to - from) {
|
||||
let Some(&cur_h) = heights.get(idx) else {
|
||||
continue;
|
||||
};
|
||||
let first = cur_h.to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
if next_first <= first {
|
||||
continue;
|
||||
}
|
||||
values.clear();
|
||||
cursor.for_each(next_first - first, |v| values.push(v));
|
||||
if let Some(v) = select_and_pick(&mut values, PCT) {
|
||||
f(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<PCT, I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//! Lazy percentiles composite (pct10, pct25, median, pct75, pct90).
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
use super::LazyPercentile;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyAggPercentiles<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub pct10: LazyPercentile<I, T, S1I, S2T>,
|
||||
pub pct25: LazyPercentile<I, T, S1I, S2T>,
|
||||
pub median: LazyPercentile<I, T, S1I, S2T>,
|
||||
pub pct75: LazyPercentile<I, T, S1I, S2T>,
|
||||
pub pct90: LazyPercentile<I, T, S1I, S2T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> LazyAggPercentiles<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
pct10: LazyPercentile::from_source::<10>(&format!("{name}_pct10"), v, source.clone(), len_source.clone()),
|
||||
pct25: LazyPercentile::from_source::<25>(&format!("{name}_pct25"), v, source.clone(), len_source.clone()),
|
||||
median: LazyPercentile::from_source::<50>(&format!("{name}_median"), v, source.clone(), len_source.clone()),
|
||||
pct75: LazyPercentile::from_source::<75>(&format!("{name}_pct75"), v, source.clone(), len_source.clone()),
|
||||
pct90: LazyPercentile::from_source::<90>(&format!("{name}_pct90"), v, source, len_source),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazyAggPercentiles<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
Self {
|
||||
pct10: LazyPercentile::from_height_source::<10>(&format!("{name}_pct10"), v, source.clone(), first_height.clone()),
|
||||
pct25: LazyPercentile::from_height_source::<25>(&format!("{name}_pct25"), v, source.clone(), first_height.clone()),
|
||||
median: LazyPercentile::from_height_source::<50>(&format!("{name}_median"), v, source.clone(), first_height.clone()),
|
||||
pct75: LazyPercentile::from_height_source::<75>(&format!("{name}_pct75"), v, source.clone(), first_height.clone()),
|
||||
pct90: LazyPercentile::from_height_source::<90>(&format!("{name}_pct90"), v, source, first_height),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
//! Sparse last-value aggregation for time-based periods.
|
||||
//!
|
||||
//! Unlike [`LazyLast`], which skips empty periods, `SparseLast` produces
|
||||
//! `Option<T>` for every period slot: `Some(v)` when blocks exist, `None`
|
||||
//! when a time period contains no blocks. This preserves dense positional
|
||||
//! mapping (slot i = period start + i) required for correct serialization.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, ReadableBoxedVec, VecIndex};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy last-value aggregation that emits `Option<T>` for every time period.
|
||||
///
|
||||
/// For periods containing blocks: `Some(last_value_in_period)`.
|
||||
/// For empty periods (no blocks mined): `None`.
|
||||
pub struct SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
}
|
||||
|
||||
impl<I, T> SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
first_height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height_source(&self) -> &ReadableBoxedVec<Height, T> {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn first_height(&self) -> &ReadableBoxedVec<I, Height> {
|
||||
&self.first_height
|
||||
}
|
||||
|
||||
/// Dense iteration: calls `f` for every period in `[from, to)`,
|
||||
/// including empty ones (with `None`).
|
||||
fn for_each_impl(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
first_height: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(Option<T>),
|
||||
) {
|
||||
let map_end = (to + 1).min(first_height.len());
|
||||
let heights = first_height.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
|
||||
for idx in 0..(to - from) {
|
||||
let current_first = heights[idx].to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
|
||||
// Empty period: no blocks belong to this time slot
|
||||
if next_first == 0 || current_first >= next_first {
|
||||
f(None);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Last height in this period
|
||||
let target = next_first - 1;
|
||||
|
||||
if cursor.position() <= target {
|
||||
cursor.advance(target - cursor.position());
|
||||
match cursor.next() {
|
||||
Some(v) => f(Some(v)),
|
||||
None => f(None),
|
||||
}
|
||||
} else {
|
||||
match source.collect_one_at(target) {
|
||||
Some(v) => f(Some(v)),
|
||||
None => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Clone for SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name.clone(),
|
||||
version: self.version,
|
||||
source: self.source.clone(),
|
||||
first_height: self.first_height.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> vecdb::AnyVec for SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
fn version(&self) -> Version {
|
||||
self.version + self.source.version() + self.first_height.version()
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn index_type_to_string(&self) -> &'static str {
|
||||
I::to_string()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.first_height.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_size_of(&self) -> usize {
|
||||
size_of::<Option<T>>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type_to_string(&self) -> &'static str {
|
||||
vecdb::short_type_name::<T>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn region_names(&self) -> Vec<String> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> vecdb::TypedVec for SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
type I = I;
|
||||
type T = Option<T>;
|
||||
}
|
||||
|
||||
impl<I, T> vecdb::ReadableVec<I, Option<T>> for SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
fn read_into_at(&self, from: usize, to: usize, buf: &mut Vec<Option<T>>) {
|
||||
let to = to.min(self.first_height.len());
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
buf.reserve(to - from);
|
||||
Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| {
|
||||
buf.push(v)
|
||||
});
|
||||
}
|
||||
|
||||
fn for_each_range_dyn_at(&self, from: usize, to: usize, f: &mut dyn FnMut(Option<T>)) {
|
||||
let to = to.min(self.first_height.len());
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
Self::for_each_impl(from, to, &self.source, &self.first_height, f);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_range_at<B, F: FnMut(B, Option<T>) -> B>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.first_height.len());
|
||||
if from >= to {
|
||||
return init;
|
||||
}
|
||||
let mut acc = Some(init);
|
||||
Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| {
|
||||
acc = Some(f(acc.take().unwrap(), v));
|
||||
});
|
||||
acc.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_fold_range_at<B, E, F: FnMut(B, Option<T>) -> std::result::Result<B, E>>(
|
||||
&self,
|
||||
from: usize,
|
||||
to: usize,
|
||||
init: B,
|
||||
mut f: F,
|
||||
) -> std::result::Result<B, E>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let to = to.min(self.first_height.len());
|
||||
if from >= to {
|
||||
return Ok(init);
|
||||
}
|
||||
let mut acc: Option<std::result::Result<B, E>> = Some(Ok(init));
|
||||
Self::for_each_impl(from, to, &self.source, &self.first_height, &mut |v| {
|
||||
if let Some(Ok(a)) = acc.take() {
|
||||
acc = Some(f(a, v));
|
||||
}
|
||||
});
|
||||
acc.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_one_at(&self, index: usize) -> Option<Option<T>> {
|
||||
if index >= self.first_height.len() {
|
||||
return None;
|
||||
}
|
||||
let current_first = self.first_height.collect_one_at(index)?.to_usize();
|
||||
let next_first = self
|
||||
.first_height
|
||||
.collect_one_at(index + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(self.source.len());
|
||||
if next_first == 0 || current_first >= next_first {
|
||||
return Some(None);
|
||||
}
|
||||
Some(self.source.collect_one_at(next_first - 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> Traversable for SparseLast<I, T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn vecdb::AnyExportableVec> {
|
||||
std::iter::once(self as &dyn vecdb::AnyExportableVec)
|
||||
}
|
||||
|
||||
fn to_tree_node(&self) -> brk_types::TreeNode {
|
||||
use vecdb::AnyVec;
|
||||
let index_str = I::to_string();
|
||||
let index = brk_types::Index::try_from(index_str).ok();
|
||||
let indexes = index.into_iter().collect();
|
||||
let leaf = brk_types::MetricLeaf::new(
|
||||
self.name().to_string(),
|
||||
self.value_type_to_string().to_string(),
|
||||
indexes,
|
||||
);
|
||||
let schema = schemars::SchemaGenerator::default().into_root_schema_for::<Option<T>>();
|
||||
let schema_json = serde_json::to_value(schema).unwrap_or_default();
|
||||
brk_types::TreeNode::Leaf(brk_types::MetricLeafWithSchema::new(leaf, schema_json))
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
//! Lazy sum-value aggregation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Cursor, FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
type ForEachRangeFn<S1I, T, I, S2T> =
|
||||
fn(usize, usize, &ReadableBoxedVec<S1I, T>, &ReadableBoxedVec<I, S2T>, &mut dyn FnMut(T));
|
||||
|
||||
pub struct LazySum<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
name: Arc<str>,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
mapping: ReadableBoxedVec<I, S2T>,
|
||||
for_each_range: ForEachRangeFn<S1I, T, I, S2T>,
|
||||
}
|
||||
|
||||
impl_lazy_agg!(LazySum);
|
||||
|
||||
impl<I, T, S1I, S2T> LazySum<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
pub(crate) fn from_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::from_source_inner(&format!("{name}_sum"), version, source, len_source)
|
||||
}
|
||||
|
||||
pub(crate) fn from_source_raw(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self::from_source_inner(name, version, source, len_source)
|
||||
}
|
||||
|
||||
fn from_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
fn for_each_range<
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<S1I, T>,
|
||||
mapping: &ReadableBoxedVec<I, S2T>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let mapping_len = mapping.len();
|
||||
let source_len = source.len();
|
||||
let to = to.min(mapping_len);
|
||||
if from >= to {
|
||||
return;
|
||||
}
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(S1I::min_from(I::from(from)));
|
||||
for i in from..to {
|
||||
let start = S1I::min_from(I::from(i));
|
||||
let end = S1I::max_from(I::from(i), source_len) + 1;
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 || cursor.remaining() == 0 {
|
||||
continue;
|
||||
}
|
||||
f(cursor.fold(count, T::from(0), |s, v| s + v));
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: len_source,
|
||||
for_each_range: for_each_range::<I, T, S1I, S2T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazySum<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_source(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self::from_height_source_inner(&format!("{name}_sum"), version, source, first_height)
|
||||
}
|
||||
|
||||
pub(crate) fn from_height_source_raw(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self::from_height_source_inner(name, version, source, first_height)
|
||||
}
|
||||
|
||||
fn from_height_source_inner(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
fn for_each_range<I: VecIndex, T: ComputedVecValue + JsonSchema>(
|
||||
from: usize,
|
||||
to: usize,
|
||||
source: &ReadableBoxedVec<Height, T>,
|
||||
mapping: &ReadableBoxedVec<I, Height>,
|
||||
f: &mut dyn FnMut(T),
|
||||
) {
|
||||
let map_end = (to + 1).min(mapping.len());
|
||||
let heights = mapping.collect_range_dyn(from, map_end);
|
||||
let source_len = source.len();
|
||||
let Some(&first_h) = heights.first() else {
|
||||
return;
|
||||
};
|
||||
let mut cursor = Cursor::from_dyn(&**source);
|
||||
cursor.advance(first_h.to_usize());
|
||||
for idx in 0..(to - from) {
|
||||
let Some(&cur_h) = heights.get(idx) else {
|
||||
continue;
|
||||
};
|
||||
let first = cur_h.to_usize();
|
||||
let next_first = heights
|
||||
.get(idx + 1)
|
||||
.map(|h| h.to_usize())
|
||||
.unwrap_or(source_len);
|
||||
let count = next_first.saturating_sub(first);
|
||||
if count == 0 || cursor.remaining() == 0 {
|
||||
continue;
|
||||
}
|
||||
f(cursor.fold(count, T::from(0), |s, v| s + v));
|
||||
}
|
||||
}
|
||||
Self {
|
||||
name: Arc::from(name),
|
||||
version: version + VERSION,
|
||||
source,
|
||||
mapping: first_height,
|
||||
for_each_range: for_each_range::<I, T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
//! Lazy sum + cumulative aggregation.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{FromCoarserIndex, ReadableBoxedVec, VecIndex, VecValue};
|
||||
|
||||
use crate::internal::{ComputedVecValue, LazyCumulative, LazySum};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazySumCum<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1I: VecIndex,
|
||||
S2T: VecValue,
|
||||
{
|
||||
#[traversable(flatten)]
|
||||
pub sum: LazySum<I, T, S1I, S2T>,
|
||||
#[traversable(flatten)]
|
||||
pub cumulative: LazyCumulative<I, T, S1I, S2T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1I, S2T> LazySumCum<I, T, S1I, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1I: VecIndex + 'static + FromCoarserIndex<I>,
|
||||
S2T: VecValue,
|
||||
{
|
||||
/// Create from sources without adding _sum suffix to sum vec.
|
||||
pub(crate) fn from_sources_sum_raw(
|
||||
name: &str,
|
||||
version: Version,
|
||||
sum_source: ReadableBoxedVec<S1I, T>,
|
||||
cumulative_source: ReadableBoxedVec<S1I, T>,
|
||||
len_source: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sum: LazySum::from_source_raw(name, version + VERSION, sum_source, len_source.clone()),
|
||||
cumulative: LazyCumulative::from_source(
|
||||
name,
|
||||
version + VERSION,
|
||||
cumulative_source,
|
||||
len_source,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T> LazySumCum<I, T, Height, Height>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
{
|
||||
pub(crate) fn from_height_sources_sum_raw(
|
||||
name: &str,
|
||||
version: Version,
|
||||
sum_source: ReadableBoxedVec<Height, T>,
|
||||
cumulative_source: ReadableBoxedVec<Height, T>,
|
||||
first_height: ReadableBoxedVec<I, Height>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sum: LazySum::from_height_source_raw(
|
||||
name,
|
||||
version + VERSION,
|
||||
sum_source,
|
||||
first_height.clone(),
|
||||
),
|
||||
cumulative: LazyCumulative::from_height_source(
|
||||
name,
|
||||
version + VERSION,
|
||||
cumulative_source,
|
||||
first_height,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
//! Lazy binary transform for Last-only.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{BinaryTransform, ReadableBoxedVec, ReadableCloneableVec, LazyVecFrom2, VecIndex};
|
||||
|
||||
use crate::internal::{ComputedVecValue, LazyLast};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(wrap = "last")]
|
||||
pub struct LazyBinaryTransformLast<I, T, S1T, S2T>(pub LazyVecFrom2<I, T, I, S1T, I, S2T>)
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue;
|
||||
|
||||
impl<I, T, S1T, S2T> LazyBinaryTransformLast<I, T, S1T, S2T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S2T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_lazy_last<
|
||||
F: BinaryTransform<S1T, S2T, T>,
|
||||
S1I: VecIndex + 'static,
|
||||
S2I: VecIndex + 'static,
|
||||
S1S2T,
|
||||
S2S2T,
|
||||
>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: &LazyLast<I, S1T, S1I, S1S2T>,
|
||||
source2: &LazyLast<I, S2T, S2I, S2S2T>,
|
||||
) -> Self
|
||||
where
|
||||
S1S2T: vecdb::VecValue,
|
||||
S2S2T: vecdb::VecValue,
|
||||
{
|
||||
Self(LazyVecFrom2::transformed::<F>(
|
||||
name,
|
||||
version,
|
||||
source1.read_only_boxed_clone(),
|
||||
source2.read_only_boxed_clone(),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn from_vecs<F: BinaryTransform<S1T, S2T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source1: ReadableBoxedVec<I, S1T>,
|
||||
source2: ReadableBoxedVec<I, S2T>,
|
||||
) -> Self {
|
||||
Self(LazyVecFrom2::transformed::<F>(
|
||||
name, version, source1, source2,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
//! Lazy unary transform for Full.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
use super::LazyPercentiles;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyTransformFull<I, T, S1T = T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub average: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub min: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub max: LazyVecFrom1<I, T, I, S1T>,
|
||||
#[traversable(flatten)]
|
||||
pub percentiles: LazyPercentiles<I, T, S1T>,
|
||||
pub sum: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub cumulative: LazyVecFrom1<I, T, I, S1T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyTransformFull<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn from_boxed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
average: ReadableBoxedVec<I, S1T>,
|
||||
min: ReadableBoxedVec<I, S1T>,
|
||||
max: ReadableBoxedVec<I, S1T>,
|
||||
pct10: ReadableBoxedVec<I, S1T>,
|
||||
pct25: ReadableBoxedVec<I, S1T>,
|
||||
median: ReadableBoxedVec<I, S1T>,
|
||||
pct75: ReadableBoxedVec<I, S1T>,
|
||||
pct90: ReadableBoxedVec<I, S1T>,
|
||||
sum: ReadableBoxedVec<I, S1T>,
|
||||
cumulative: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
average: LazyVecFrom1::transformed::<F>(&format!("{name}_average"), version, average),
|
||||
min: LazyVecFrom1::transformed::<F>(&format!("{name}_min"), version, min),
|
||||
max: LazyVecFrom1::transformed::<F>(&format!("{name}_max"), version, max),
|
||||
percentiles: LazyPercentiles::from_boxed::<F>(
|
||||
name, version, pct10, pct25, median, pct75, pct90,
|
||||
),
|
||||
sum: LazyVecFrom1::transformed::<F>(&format!("{name}_sum"), version, sum),
|
||||
cumulative: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_cumulative"),
|
||||
version,
|
||||
cumulative,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//! Lazy unary transform for Last-only - transforms last at one index level.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableBoxedVec, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyTransformLast<I, T, S1T = T>(pub LazyVecFrom1<I, T, I, S1T>)
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue;
|
||||
|
||||
impl<I, T, S1T> LazyTransformLast<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_boxed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
last_source: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self(LazyVecFrom1::transformed::<F>(name, version, last_source))
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
mod binary_last;
|
||||
mod full;
|
||||
mod last;
|
||||
mod percentiles;
|
||||
mod sum_cum;
|
||||
|
||||
pub use binary_last::*;
|
||||
pub use full::*;
|
||||
pub use last::*;
|
||||
pub use percentiles::*;
|
||||
pub use sum_cum::*;
|
||||
@@ -1,48 +0,0 @@
|
||||
//! Lazy unary transform for Percentiles.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyPercentiles<I, T, S1T = T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub pct10: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct25: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub median: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct75: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub pct90: LazyVecFrom1<I, T, I, S1T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyPercentiles<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn from_boxed<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
pct10: ReadableBoxedVec<I, S1T>,
|
||||
pct25: ReadableBoxedVec<I, S1T>,
|
||||
median: ReadableBoxedVec<I, S1T>,
|
||||
pct75: ReadableBoxedVec<I, S1T>,
|
||||
pct90: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
pct10: LazyVecFrom1::transformed::<F>(&format!("{name}_pct10"), version, pct10),
|
||||
pct25: LazyVecFrom1::transformed::<F>(&format!("{name}_pct25"), version, pct25),
|
||||
median: LazyVecFrom1::transformed::<F>(&format!("{name}_median"), version, median),
|
||||
pct75: LazyVecFrom1::transformed::<F>(&format!("{name}_pct75"), version, pct75),
|
||||
pct90: LazyVecFrom1::transformed::<F>(&format!("{name}_pct90"), version, pct90),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//! Lazy unary transform for SumCum.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Version;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{ReadableBoxedVec, LazyVecFrom1, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyTransformSumCum<I, T, S1T = T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
pub sum: LazyVecFrom1<I, T, I, S1T>,
|
||||
pub cumulative: LazyVecFrom1<I, T, I, S1T>,
|
||||
}
|
||||
|
||||
impl<I, T, S1T> LazyTransformSumCum<I, T, S1T>
|
||||
where
|
||||
I: VecIndex,
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_boxed_sum_raw<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
sum_source: ReadableBoxedVec<I, S1T>,
|
||||
cumulative_source: ReadableBoxedVec<I, S1T>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sum: LazyVecFrom1::transformed::<F>(name, version, sum_source),
|
||||
cumulative: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_cumulative"),
|
||||
version,
|
||||
cumulative_source,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,12 @@
|
||||
|
||||
mod group;
|
||||
mod height;
|
||||
mod lazy;
|
||||
mod lazy_transform;
|
||||
mod rolling;
|
||||
mod transform;
|
||||
mod vec;
|
||||
|
||||
pub use group::*;
|
||||
pub use height::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_transform::*;
|
||||
pub use rolling::*;
|
||||
pub use transform::*;
|
||||
pub use vec::*;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//! Block-count-based rolling window starts and distribution — 1h and 24h (actual time-based).
|
||||
//!
|
||||
//! Uses stored height-ago vecs (`height_1h_ago`, `height_24h_ago`) for accurate
|
||||
//! time-based window starts.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Height;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, PcoVec, Rw, StorageMode, Version};
|
||||
|
||||
use crate::internal::{BlockWindows, ComputedVecValue, Distribution, NumericValue};
|
||||
|
||||
/// Rolling window start heights for tx-derived metrics (1h, 24h).
|
||||
pub struct BlockWindowStarts<'a> {
|
||||
pub _1h: &'a EagerVec<PcoVec<Height, Height>>,
|
||||
pub _24h: &'a EagerVec<PcoVec<Height, Height>>,
|
||||
}
|
||||
|
||||
/// 2 rolling window distributions (1h, 24h), each with 8 distribution stat vecs.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct BlockRollingDistribution<T, M: StorageMode = Rw>(
|
||||
pub BlockWindows<Distribution<Height, T, M>>,
|
||||
)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
impl<T> BlockRollingDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(BlockWindows {
|
||||
_1h: Distribution::forced_import(db, &format!("{name}_1h"), version)?,
|
||||
_24h: Distribution::forced_import(db, &format!("{name}_24h"), version)?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! RollingDistribution - 8 distribution stats, each a RollingWindows.
|
||||
//!
|
||||
//! Computes average, min, max, p10, p25, median, p75, p90 rolling windows
|
||||
//! from a single source vec.
|
||||
//! from a single source vec in a single sorted-vec pass per window.
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
@@ -14,15 +14,13 @@ use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, DistributionStats, NumericValue, RollingWindows, WindowStarts},
|
||||
traits::compute_rolling_percentiles_from_starts,
|
||||
traits::compute_rolling_distribution_from_starts,
|
||||
};
|
||||
|
||||
/// 8 distribution stats × 4 windows = 32 stored height vecs, each with 17 index views.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct RollingDistribution<T, M: StorageMode = Rw>(
|
||||
pub DistributionStats<RollingWindows<T, M>>,
|
||||
)
|
||||
pub struct RollingDistribution<T, M: StorageMode = Rw>(pub DistributionStats<RollingWindows<T, M>>)
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema;
|
||||
|
||||
@@ -53,9 +51,10 @@ where
|
||||
|
||||
/// Compute all 8 distribution stats across all 4 windows from a single source.
|
||||
///
|
||||
/// - average: running sum / count (O(n) per window)
|
||||
/// - min/max: deque-based (O(n) amortized per window)
|
||||
/// - p10/p25/median/p75/p90: single-pass sorted vec per window
|
||||
/// Uses a single sorted-vec pass per window that extracts all 8 stats:
|
||||
/// - average: running sum / count
|
||||
/// - min/max: first/last of sorted vec
|
||||
/// - p10/p25/median/p75/p90: percentile interpolation from sorted vec
|
||||
pub(crate) fn compute_distribution(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
@@ -67,24 +66,14 @@ where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
// Average: O(n) per window using running sum
|
||||
self.0
|
||||
.average
|
||||
.compute_rolling_average(max_from, windows, source, exit)?;
|
||||
|
||||
// Min/Max: O(n) amortized per window using deques
|
||||
self.0
|
||||
.min
|
||||
.compute_rolling_min(max_from, windows, source, exit)?;
|
||||
self.0
|
||||
.max
|
||||
.compute_rolling_max(max_from, windows, source, exit)?;
|
||||
|
||||
// Percentiles + median: single-pass per window using sorted vec
|
||||
compute_rolling_percentiles_from_starts(
|
||||
// Single pass per window: all 8 stats extracted from one sorted vec
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from,
|
||||
windows._24h,
|
||||
source,
|
||||
&mut self.0.average._24h.height,
|
||||
&mut self.0.min._24h.height,
|
||||
&mut self.0.max._24h.height,
|
||||
&mut self.0.p10._24h.height,
|
||||
&mut self.0.p25._24h.height,
|
||||
&mut self.0.median._24h.height,
|
||||
@@ -92,10 +81,13 @@ where
|
||||
&mut self.0.p90._24h.height,
|
||||
exit,
|
||||
)?;
|
||||
compute_rolling_percentiles_from_starts(
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from,
|
||||
windows._7d,
|
||||
source,
|
||||
&mut self.0.average._7d.height,
|
||||
&mut self.0.min._7d.height,
|
||||
&mut self.0.max._7d.height,
|
||||
&mut self.0.p10._7d.height,
|
||||
&mut self.0.p25._7d.height,
|
||||
&mut self.0.median._7d.height,
|
||||
@@ -103,10 +95,13 @@ where
|
||||
&mut self.0.p90._7d.height,
|
||||
exit,
|
||||
)?;
|
||||
compute_rolling_percentiles_from_starts(
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from,
|
||||
windows._30d,
|
||||
source,
|
||||
&mut self.0.average._30d.height,
|
||||
&mut self.0.min._30d.height,
|
||||
&mut self.0.max._30d.height,
|
||||
&mut self.0.p10._30d.height,
|
||||
&mut self.0.p25._30d.height,
|
||||
&mut self.0.median._30d.height,
|
||||
@@ -114,10 +109,13 @@ where
|
||||
&mut self.0.p90._30d.height,
|
||||
exit,
|
||||
)?;
|
||||
compute_rolling_percentiles_from_starts(
|
||||
compute_rolling_distribution_from_starts(
|
||||
max_from,
|
||||
windows._1y,
|
||||
source,
|
||||
&mut self.0.average._1y.height,
|
||||
&mut self.0.min._1y.height,
|
||||
&mut self.0.max._1y.height,
|
||||
&mut self.0.p10._1y.height,
|
||||
&mut self.0.p25._1y.height,
|
||||
&mut self.0.median._1y.height,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
mod block_windows;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod value_windows;
|
||||
mod windows;
|
||||
|
||||
pub use block_windows::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use value_windows::*;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! RollingWindows - newtype on Windows with ComputedFromHeightLast per window duration.
|
||||
//!
|
||||
//! Each of the 4 windows (24h, 7d, 30d, 1y) contains a height-level stored vec
|
||||
//! plus all 17 LazyLast index views.
|
||||
//! plus all 17 LazyAggVec index views.
|
||||
|
||||
use std::ops::SubAssign;
|
||||
|
||||
@@ -82,97 +82,4 @@ where
|
||||
.compute_rolling_sum(max_from, windows._1y, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_min<A>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, A>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: vecdb::VecValue + Ord,
|
||||
T: From<A>,
|
||||
{
|
||||
use crate::traits::ComputeRollingMinFromStarts;
|
||||
self.0
|
||||
._24h
|
||||
.height
|
||||
.compute_rolling_min_from_starts(max_from, windows._24h, source, exit)?;
|
||||
self.0
|
||||
._7d
|
||||
.height
|
||||
.compute_rolling_min_from_starts(max_from, windows._7d, source, exit)?;
|
||||
self.0
|
||||
._30d
|
||||
.height
|
||||
.compute_rolling_min_from_starts(max_from, windows._30d, source, exit)?;
|
||||
self.0
|
||||
._1y
|
||||
.height
|
||||
.compute_rolling_min_from_starts(max_from, windows._1y, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_max<A>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, A>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: vecdb::VecValue + Ord,
|
||||
T: From<A>,
|
||||
{
|
||||
use crate::traits::ComputeRollingMaxFromStarts;
|
||||
self.0
|
||||
._24h
|
||||
.height
|
||||
.compute_rolling_max_from_starts(max_from, windows._24h, source, exit)?;
|
||||
self.0
|
||||
._7d
|
||||
.height
|
||||
.compute_rolling_max_from_starts(max_from, windows._7d, source, exit)?;
|
||||
self.0
|
||||
._30d
|
||||
.height
|
||||
.compute_rolling_max_from_starts(max_from, windows._30d, source, exit)?;
|
||||
self.0
|
||||
._1y
|
||||
.height
|
||||
.compute_rolling_max_from_starts(max_from, windows._1y, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rolling_average<A>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, A>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: vecdb::VecValue,
|
||||
f64: From<A> + From<T>,
|
||||
T: From<f64> + Default,
|
||||
{
|
||||
self.0
|
||||
._24h
|
||||
.height
|
||||
.compute_rolling_average(max_from, windows._24h, source, exit)?;
|
||||
self.0
|
||||
._7d
|
||||
.height
|
||||
.compute_rolling_average(max_from, windows._7d, source, exit)?;
|
||||
self.0
|
||||
._30d
|
||||
.height
|
||||
.compute_rolling_average(max_from, windows._30d, source, exit)?;
|
||||
self.0
|
||||
._1y
|
||||
.height
|
||||
.compute_rolling_average(max_from, windows._1y, source, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::{Bitcoin, Dollars, Sats};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// Dollars * Sats -> Dollars (price × sats / 1e8)
|
||||
pub struct PriceTimesSats;
|
||||
|
||||
impl BinaryTransform<Dollars, Sats, Dollars> for PriceTimesSats {
|
||||
#[inline(always)]
|
||||
fn apply(price: Dollars, sats: Sats) -> Dollars {
|
||||
price * Bitcoin::from(sats)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredF32, StoredF32) -> StoredF32 difference (a - b)
|
||||
pub struct DifferenceF32;
|
||||
|
||||
impl BinaryTransform<StoredF32, StoredF32, StoredF32> for DifferenceF32 {
|
||||
#[inline(always)]
|
||||
fn apply(a: StoredF32, b: StoredF32) -> StoredF32 {
|
||||
StoredF32::from(*a - *b)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
use brk_types::Dollars;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
pub struct DollarsMinus;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, Dollars> for DollarsMinus {
|
||||
#[inline(always)]
|
||||
fn apply(lhs: Dollars, rhs: Dollars) -> Dollars {
|
||||
lhs - rhs
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use brk_types::Dollars;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (Dollars, Dollars) -> Dollars: a² / b
|
||||
pub struct DollarsSquaredDivide;
|
||||
|
||||
impl BinaryTransform<Dollars, Dollars, Dollars> for DollarsSquaredDivide {
|
||||
#[inline(always)]
|
||||
fn apply(a: Dollars, b: Dollars) -> Dollars {
|
||||
let af = f64::from(a);
|
||||
let bf = f64::from(b);
|
||||
if bf == 0.0 {
|
||||
Dollars::NAN
|
||||
} else {
|
||||
Dollars::from(af * af / bf)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::{Bitcoin, Dollars, Sats};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// Dollars * Sats -> Dollars/2 (price × sats / 1e8 / 2)
|
||||
pub struct HalfPriceTimesSats;
|
||||
|
||||
impl BinaryTransform<Dollars, Sats, Dollars> for HalfPriceTimesSats {
|
||||
#[inline(always)]
|
||||
fn apply(price: Dollars, sats: Sats) -> Dollars {
|
||||
(price * Bitcoin::from(sats)).halved()
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,13 @@
|
||||
mod block_count_target;
|
||||
mod cents_to_dollars;
|
||||
mod cents_to_sats;
|
||||
mod close_price_times_sats;
|
||||
mod difference_f32;
|
||||
|
||||
mod dollar_halve;
|
||||
mod dollar_identity;
|
||||
mod dollar_minus;
|
||||
mod dollar_plus;
|
||||
mod dollar_times_tenths;
|
||||
mod dollars_squared_divide;
|
||||
mod dollars_to_sats_fract;
|
||||
mod f32_identity;
|
||||
mod half_close_price_times_sats;
|
||||
mod percentage_diff_close_dollars;
|
||||
mod percentage_dollars_f32;
|
||||
mod percentage_dollars_f32_neg;
|
||||
@@ -20,20 +16,17 @@ mod percentage_u32_f32;
|
||||
mod price_times_ratio;
|
||||
mod ratio32;
|
||||
mod ratio64;
|
||||
mod ratio_f32;
|
||||
mod ratio_u64_f32;
|
||||
|
||||
mod return_f32_tenths;
|
||||
mod return_i8;
|
||||
mod return_u16;
|
||||
mod rsi_formula;
|
||||
|
||||
mod sat_halve;
|
||||
mod sat_halve_to_bitcoin;
|
||||
mod sat_identity;
|
||||
mod sat_mask;
|
||||
mod sat_to_bitcoin;
|
||||
mod sats_times_close_price;
|
||||
mod u16_to_years;
|
||||
mod u64_plus;
|
||||
mod volatility_sqrt30;
|
||||
mod volatility_sqrt365;
|
||||
mod volatility_sqrt7;
|
||||
@@ -41,39 +34,32 @@ mod volatility_sqrt7;
|
||||
pub use block_count_target::*;
|
||||
pub use cents_to_dollars::*;
|
||||
pub use cents_to_sats::*;
|
||||
pub use close_price_times_sats::*;
|
||||
pub use difference_f32::*;
|
||||
|
||||
pub use dollar_halve::*;
|
||||
pub use dollar_identity::*;
|
||||
pub use dollar_minus::*;
|
||||
pub use dollar_plus::*;
|
||||
pub use dollar_times_tenths::*;
|
||||
pub use dollars_squared_divide::*;
|
||||
pub use dollars_to_sats_fract::*;
|
||||
pub use f32_identity::*;
|
||||
pub use half_close_price_times_sats::*;
|
||||
pub use percentage_diff_close_dollars::*;
|
||||
pub use percentage_dollars_f32::*;
|
||||
pub use percentage_dollars_f32_neg::*;
|
||||
pub use percentage_sats_f64::*;
|
||||
pub use percentage_u32_f32::*;
|
||||
pub use price_times_ratio::*;
|
||||
pub use ratio_f32::*;
|
||||
pub use ratio_u64_f32::*;
|
||||
|
||||
pub use ratio32::*;
|
||||
pub use ratio64::*;
|
||||
pub use return_f32_tenths::*;
|
||||
pub use return_i8::*;
|
||||
pub use return_u16::*;
|
||||
pub use rsi_formula::*;
|
||||
|
||||
pub use sat_halve::*;
|
||||
pub use sat_halve_to_bitcoin::*;
|
||||
pub use sat_identity::*;
|
||||
pub use sat_mask::*;
|
||||
pub use sat_to_bitcoin::*;
|
||||
pub use sats_times_close_price::*;
|
||||
pub use u16_to_years::*;
|
||||
pub use u64_plus::*;
|
||||
pub use volatility_sqrt7::*;
|
||||
pub use volatility_sqrt30::*;
|
||||
pub use volatility_sqrt365::*;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredF32, StoredF32) -> StoredF32 ratio (a / b)
|
||||
pub struct RatioF32;
|
||||
|
||||
impl BinaryTransform<StoredF32, StoredF32, StoredF32> for RatioF32 {
|
||||
#[inline(always)]
|
||||
fn apply(a: StoredF32, b: StoredF32) -> StoredF32 {
|
||||
if *b == 0.0 {
|
||||
StoredF32::from(0.0)
|
||||
} else {
|
||||
StoredF32::from(*a / *b)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//! (StoredU64, StoredU64) -> StoredF32 ratio
|
||||
|
||||
use brk_types::{StoredF32, StoredU64};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredU64, StoredU64) -> StoredF32 ratio (a/b)
|
||||
pub struct RatioU64F32;
|
||||
|
||||
impl BinaryTransform<StoredU64, StoredU64, StoredF32> for RatioU64F32 {
|
||||
#[inline(always)]
|
||||
fn apply(numerator: StoredU64, denominator: StoredU64) -> StoredF32 {
|
||||
let num: f64 = (*numerator) as f64;
|
||||
let den: f64 = (*denominator) as f64;
|
||||
if den == 0.0 {
|
||||
StoredF32::from(0.0)
|
||||
} else {
|
||||
StoredF32::from(num / den)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
use brk_types::StoredF32;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredF32, StoredF32) -> StoredF32 RSI formula: 100 * a / (a + b)
|
||||
pub struct RsiFormula;
|
||||
|
||||
impl BinaryTransform<StoredF32, StoredF32, StoredF32> for RsiFormula {
|
||||
#[inline(always)]
|
||||
fn apply(average_gain: StoredF32, average_loss: StoredF32) -> StoredF32 {
|
||||
let sum = *average_gain + *average_loss;
|
||||
if sum == 0.0 {
|
||||
StoredF32::from(50.0)
|
||||
} else {
|
||||
StoredF32::from(100.0 * *average_gain / sum)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
use brk_types::{Bitcoin, Dollars, Sats};
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// Sats * Dollars -> Dollars (sats / 1e8 × price)
|
||||
pub struct SatsTimesPrice;
|
||||
|
||||
impl BinaryTransform<Sats, Dollars, Dollars> for SatsTimesPrice {
|
||||
#[inline(always)]
|
||||
fn apply(sats: Sats, price: Dollars) -> Dollars {
|
||||
price * Bitcoin::from(sats)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use brk_types::StoredU64;
|
||||
use vecdb::BinaryTransform;
|
||||
|
||||
/// (StoredU64, StoredU64) -> StoredU64 addition
|
||||
/// Used for computing total_addr_count = addr_count + empty_addr_count
|
||||
pub struct U64Plus;
|
||||
|
||||
impl BinaryTransform<StoredU64, StoredU64, StoredU64> for U64Plus {
|
||||
#[inline(always)]
|
||||
fn apply(lhs: StoredU64, rhs: StoredU64) -> StoredU64 {
|
||||
StoredU64::from(u64::from(lhs) + u64::from(rhs))
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,7 @@ use brk_traversable::Traversable;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, EagerVec, ImportableVec, PcoVec, ReadableBoxedVec, ReadableCloneableVec, Ro, Rw,
|
||||
StorageMode, StoredVec, VecIndex, Version,
|
||||
Database, EagerVec, ImportableVec, PcoVec, Ro, Rw, StorageMode, StoredVec, VecIndex, Version,
|
||||
};
|
||||
|
||||
use crate::internal::ComputedVecValue;
|
||||
@@ -30,10 +29,6 @@ impl<I: VecIndex, T: ComputedVecValue + JsonSchema> CumulativeVec<I, T> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub(crate) fn read_only_boxed_clone(&self) -> ReadableBoxedVec<I, T> {
|
||||
self.0.read_only_boxed_clone()
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> CumulativeVec<I, T, Ro> {
|
||||
CumulativeVec(StoredVec::read_only_clone(&self.0))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ pub struct MaxVec<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> MaxVec<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self(EagerVec::forced_import(db, &format!("{name}_max"), version)?))
|
||||
Ok(Self(EagerVec::forced_import(
|
||||
db,
|
||||
&format!("{name}_max"),
|
||||
version,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> MaxVec<I, T, Ro> {
|
||||
|
||||
@@ -16,7 +16,11 @@ pub struct MinVec<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> MinVec<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self(EagerVec::forced_import(db, &format!("{name}_min"), version)?))
|
||||
Ok(Self(EagerVec::forced_import(
|
||||
db,
|
||||
&format!("{name}_min"),
|
||||
version,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> MinVec<I, T, Ro> {
|
||||
|
||||
@@ -19,8 +19,16 @@ macro_rules! define_percentile_vec {
|
||||
);
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> $name<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self(EagerVec::forced_import(db, &format!("{name}_{}", $suffix), version)?))
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(EagerVec::forced_import(
|
||||
db,
|
||||
&format!("{name}_{}", $suffix),
|
||||
version,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> $name<I, T, Ro> {
|
||||
@@ -30,8 +38,28 @@ macro_rules! define_percentile_vec {
|
||||
};
|
||||
}
|
||||
|
||||
define_percentile_vec!(Pct10Vec, "pct10", "10th percentile in an aggregation period");
|
||||
define_percentile_vec!(Pct25Vec, "pct25", "25th percentile in an aggregation period");
|
||||
define_percentile_vec!(MedianVec, "median", "Median (50th percentile) in an aggregation period");
|
||||
define_percentile_vec!(Pct75Vec, "pct75", "75th percentile in an aggregation period");
|
||||
define_percentile_vec!(Pct90Vec, "pct90", "90th percentile in an aggregation period");
|
||||
define_percentile_vec!(
|
||||
Pct10Vec,
|
||||
"pct10",
|
||||
"10th percentile in an aggregation period"
|
||||
);
|
||||
define_percentile_vec!(
|
||||
Pct25Vec,
|
||||
"pct25",
|
||||
"25th percentile in an aggregation period"
|
||||
);
|
||||
define_percentile_vec!(
|
||||
MedianVec,
|
||||
"median",
|
||||
"Median (50th percentile) in an aggregation period"
|
||||
);
|
||||
define_percentile_vec!(
|
||||
Pct75Vec,
|
||||
"pct75",
|
||||
"75th percentile in an aggregation period"
|
||||
);
|
||||
define_percentile_vec!(
|
||||
Pct90Vec,
|
||||
"pct90",
|
||||
"90th percentile in an aggregation period"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user