use brk_cohort::{ByAddrType, zip2_by_addr_type};
use brk_error::Result;
use brk_indexer::Lengths;
use brk_traversable::Traversable;
use brk_types::{PartsPerMillion32, StoredF32, StoredU32, StoredU64, Version};
use rayon::prelude::*;
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
use crate::{
indexes, inputs,
internal::{
CachedBlockCountReader, CachedWindowStartVec, CountPerBlockRollingAverage,
LazyPercentCumulativeRolling, PerBlockCumulativeRolling, PerBlockRollingAverage, RatioU64,
Windows, WithAddrTypes,
},
outputs,
};
use super::state::AddrTypeToAddrEventCount;
#[derive(Clone, Traversable)]
pub struct AddrEventShares {
pub all: LazyPercentCumulativeRolling<PartsPerMillion32>,
#[traversable(flatten)]
pub by_addr_type: ByAddrType<LazyPercentCumulativeRolling<PartsPerMillion32>>,
}
impl AddrEventShares {
fn new(
name: &str,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &Windows<&CachedWindowStartVec>,
all: LazyPercentCumulativeRolling<PartsPerMillion32>,
numerators: &ByAddrType<PerBlockCumulativeRolling<StoredU64>>,
denominators: &ByAddrType<CachedBlockCountReader>,
) -> Result<Self> {
let by_addr_type = zip2_by_addr_type(
numerators,
denominators,
|type_name, numerator, denominator| {
Ok(LazyPercentCumulativeRolling::from_cached_block_count(
&format!("{type_name}_{name}"),
version,
&numerator.cumulative.height,
denominator.clone(),
cached_starts,
indexes,
))
},
)?;
Ok(Self { all, by_addr_type })
}
}
#[derive(Traversable)]
pub struct AddrEventsVecs<M: StorageMode = Rw> {
pub output_to_reused_addr_count: WithAddrTypes<PerBlockCumulativeRolling<StoredU64, M>>,
pub output_to_reused_addr_share: AddrEventShares,
pub spendable_output_to_reused_addr_share: LazyPercentCumulativeRolling<PartsPerMillion32>,
pub input_from_reused_addr_count: WithAddrTypes<PerBlockCumulativeRolling<StoredU64, M>>,
pub input_from_reused_addr_share: AddrEventShares,
pub active_reused_addr_count: CountPerBlockRollingAverage<M>,
pub active_reused_addr_share: PerBlockRollingAverage<StoredF32, StoredF32, M>,
}
impl AddrEventsVecs {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
cached_starts: &Windows<&CachedWindowStartVec>,
outputs_by_type: &outputs::ByTypeVecs,
inputs_by_type: &inputs::ByTypeVecs,
) -> Result<Self> {
let import_count = |name: &str| {
WithAddrTypes::<PerBlockCumulativeRolling<StoredU64>>::forced_import(
db,
name,
version,
indexes,
cached_starts,
)
};
let output_to_reused_addr_count = import_count(&format!("output_to_{name}_addr_count"))?;
let output_share_name = format!("output_to_{name}_addr_share");
let output_denominators = outputs_by_type.output_count.cached_addr_type_counts();
let output_to_reused_addr_share = AddrEventShares::new(
&output_share_name,
version,
indexes,
cached_starts,
outputs_by_type.output_count.lazy_share(
&output_share_name,
version,
&output_to_reused_addr_count.all.cumulative.height,
cached_starts,
indexes,
),
&output_to_reused_addr_count.by_addr_type,
&output_denominators,
)?;
let spendable_share_name = format!("spendable_output_to_{name}_addr_share");
let spendable_output_to_reused_addr_share =
LazyPercentCumulativeRolling::from_cumulative_ratio::<
StoredU64,
StoredU64,
RatioU64<PartsPerMillion32>,
>(
&spendable_share_name,
version,
&output_to_reused_addr_count.all.cumulative.height,
outputs_by_type.spendable_output_count.cached_cumulative(),
cached_starts,
indexes,
);
let input_from_reused_addr_count = import_count(&format!("input_from_{name}_addr_count"))?;
let input_share_name = format!("input_from_{name}_addr_share");
let input_denominators = inputs_by_type.input_count.cached_addr_type_counts();
let input_from_reused_addr_share = AddrEventShares::new(
&input_share_name,
version,
indexes,
cached_starts,
inputs_by_type.input_count.lazy_share(
&input_share_name,
version,
&input_from_reused_addr_count.all.cumulative.height,
cached_starts,
indexes,
),
&input_from_reused_addr_count.by_addr_type,
&input_denominators,
)?;
let active_reused_addr_count = CountPerBlockRollingAverage::forced_import(
db,
&format!("active_{name}_addr_count"),
version,
indexes,
cached_starts,
)?;
let active_reused_addr_share = PerBlockRollingAverage::forced_import(
db,
&format!("active_{name}_addr_share"),
version,
indexes,
cached_starts,
)?;
Ok(Self {
output_to_reused_addr_count,
output_to_reused_addr_share,
spendable_output_to_reused_addr_share,
input_from_reused_addr_count,
input_from_reused_addr_share,
active_reused_addr_count,
active_reused_addr_share,
})
}
pub(crate) fn min_stateful_len(&self) -> usize {
self.output_to_reused_addr_count
.min_stateful_len()
.min(self.input_from_reused_addr_count.min_stateful_len())
.min(self.active_reused_addr_count.min_stateful_len())
.min(self.active_reused_addr_share.block.len())
}
pub(crate) fn par_iter_height_mut(
&mut self,
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
self.output_to_reused_addr_count
.par_iter_height_mut()
.chain(self.input_from_reused_addr_count.par_iter_height_mut())
.chain([
self.active_reused_addr_count.stored_mut(),
&mut self.active_reused_addr_share.block as &mut dyn AnyStoredVec,
])
}
pub(crate) fn reset_height(&mut self) -> Result<()> {
self.output_to_reused_addr_count.reset_height()?;
self.input_from_reused_addr_count.reset_height()?;
self.active_reused_addr_count.reset()?;
self.active_reused_addr_share.block.reset()?;
Ok(())
}
#[inline(always)]
pub(crate) fn push_height(
&mut self,
uses: &AddrTypeToAddrEventCount,
spends: &AddrTypeToAddrEventCount,
active_addr_count: u32,
active_reused_addr_count: u32,
) {
self.output_to_reused_addr_count
.push_height(uses.sum(), uses.values().copied());
self.input_from_reused_addr_count
.push_height(spends.sum(), spends.values().copied());
self.active_reused_addr_count
.push_block(StoredU32::from(active_reused_addr_count));
let share = if active_addr_count > 0 {
100.0 * (active_reused_addr_count as f32 / active_addr_count as f32)
} else {
0.0
};
self.active_reused_addr_share
.block
.push(StoredF32::from(share));
}
pub(crate) fn compute_rest(&mut self, starting_lengths: &Lengths, exit: &Exit) -> Result<()> {
self.active_reused_addr_share
.compute_rest(starting_lengths.height, exit)?;
Ok(())
}
}