computer: fixes

This commit is contained in:
nym21
2026-03-08 23:36:38 +01:00
parent bb2458c765
commit c2240c7a60
11 changed files with 194 additions and 275 deletions
@@ -1,6 +1,4 @@
use brk_cohort::{
compute_profitability_boundaries, Filter, PROFITABILITY_RANGE_COUNT,
};
use brk_cohort::{Filter, PROFITABILITY_RANGE_COUNT, compute_profitability_boundaries};
use brk_types::{Cents, CentsCompact, Sats};
use crate::{
@@ -60,7 +58,6 @@ pub(super) struct CostBasisFenwick {
// to a flat bucket index across two tiers.
// ---------------------------------------------------------------------------
/// Map rounded dollars to a flat bucket index.
/// Prices >= $1M are clamped to the last bucket.
#[inline]
@@ -185,13 +182,6 @@ impl CostBasisFenwick {
self.initialized = true;
}
/// Reset to uninitialized empty state.
pub(super) fn reset(&mut self) {
self.tree.reset();
self.totals = CostBasisNode::default();
self.initialized = false;
}
// -----------------------------------------------------------------------
// Percentile queries
// -----------------------------------------------------------------------
@@ -268,9 +258,9 @@ impl CostBasisFenwick {
self.tree
.batch_kth(&usd_targets, &usd_field, &mut usd_buckets);
for i in 0..PERCENTILES_LEN {
(0..PERCENTILES_LEN).for_each(|i| {
result.usd_prices[i] = bucket_to_cents(usd_buckets[i]);
}
});
}
result
@@ -330,114 +320,3 @@ pub(super) struct PercentileResult {
pub min_price: Cents,
pub max_price: Cents,
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
#[test]
fn bucket_round_trip() {
// Low prices: exact dollar precision
let price = CentsCompact::new(5000_00); // $5000
let bucket = price_to_bucket(price);
let back = bucket_to_cents(bucket);
assert_eq!(u64::from(back), 5000 * 100);
// High price: $90,000 → rounded to $90,000 (already 5 digits)
let price = CentsCompact::new(90_000_00);
let bucket = price_to_bucket(price);
let back = bucket_to_cents(bucket);
assert_eq!(u64::from(back), 90_000 * 100);
// Tier 1: $123,456 → rounded to $123,460
let price = CentsCompact::new(123_456_00);
let bucket = price_to_bucket(price);
let back = bucket_to_cents(bucket);
assert_eq!(u64::from(back), 123_460 * 100);
// Overflow: $2,000,000 → clamped to $1,000,000
let price = CentsCompact::new(2_000_000_00);
let bucket = price_to_bucket(price);
assert_eq!(bucket, TREE_SIZE - 1);
assert_eq!(u64::from(bucket_to_cents(bucket)), 1_000_000 * 100);
}
#[test]
fn bucket_edge_cases() {
// $0
assert_eq!(price_to_bucket(CentsCompact::new(0)), 0);
assert_eq!(u64::from(bucket_to_cents(0)), 0);
// $1
let bucket = price_to_bucket(CentsCompact::new(100));
assert_eq!(bucket, 1);
// Max CentsCompact
let bucket = price_to_bucket(CentsCompact::MAX);
assert!(bucket < TREE_SIZE);
}
#[test]
fn bulk_init_and_percentiles() {
let mut fenwick = CostBasisFenwick::new();
// Create a simple BTreeMap: 100 sats at $10,000, 100 sats at $50,000
let mut map = BTreeMap::new();
map.insert(CentsCompact::new(10_000_00), Sats::from(100u64));
map.insert(CentsCompact::new(50_000_00), Sats::from(100u64));
fenwick.bulk_init(std::iter::once((&map, true)));
assert!(fenwick.is_initialized());
let result = fenwick.percentiles_all();
// Median (50th percentile) should be at $10,000 (first 100 sats)
// since target = 200 * 50/100 = 100, and first 100 sats are at $10,000
assert_eq!(u64::from(result.sat_prices[9]), 10_000 * 100); // index 9 = 50th percentile
// Min should be $10,000, max should be $50,000
assert_eq!(u64::from(result.min_price), 10_000 * 100);
assert_eq!(u64::from(result.max_price), 50_000 * 100);
}
#[test]
fn apply_delta_updates_totals() {
let mut fenwick = CostBasisFenwick::new();
fenwick.initialized = true;
let price = CentsCompact::new(10_000_00);
fenwick.apply_delta(price, &PendingDelta { inc: Sats::from(500u64), dec: Sats::ZERO }, true);
assert_eq!(fenwick.totals.all_sats, 500);
assert_eq!(fenwick.totals.sth_sats, 500);
fenwick.apply_delta(price, &PendingDelta { inc: Sats::ZERO, dec: Sats::from(200u64) }, true);
assert_eq!(fenwick.totals.all_sats, 300);
assert_eq!(fenwick.totals.sth_sats, 300);
// Non-STH delta
fenwick.apply_delta(price, &PendingDelta { inc: Sats::from(100u64), dec: Sats::ZERO }, false);
assert_eq!(fenwick.totals.all_sats, 400);
assert_eq!(fenwick.totals.sth_sats, 300);
}
#[test]
fn profitability_ranges_sum_to_total() {
let mut fenwick = CostBasisFenwick::new();
let mut map = BTreeMap::new();
// Spread sats across different prices
map.insert(CentsCompact::new(1_000_00), Sats::from(1000u64));
map.insert(CentsCompact::new(10_000_00), Sats::from(2000u64));
map.insert(CentsCompact::new(50_000_00), Sats::from(3000u64));
map.insert(CentsCompact::new(100_000_00), Sats::from(4000u64));
fenwick.bulk_init(std::iter::once((&map, false)));
let spot = Cents::from(50_000u64 * 100);
let prof = fenwick.profitability(spot);
let total_sats: u64 = prof.iter().map(|(s, _)| s).sum();
assert_eq!(total_sats, 10_000);
}
}
@@ -117,7 +117,7 @@ impl CoreCohortMetrics {
self.realized
.compute_rest_part1(blocks, starting_indexes, exit)?;
self.unrealized.compute_rest(starting_indexes, exit)?;
self.unrealized.compute_rest(prices, starting_indexes, exit)?;
Ok(())
}
@@ -1,25 +1,16 @@
use brk_cohort::Filter;
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use brk_types::{Indexes, Version};
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
use crate::distribution::metrics::unrealized::UnrealizedMinimal;
use crate::{blocks, prices};
use crate::internal::ValueFromHeight;
use crate::distribution::{
metrics::{ActivityCore, ImportConfig, OutputsMetrics, RealizedMinimal, SupplyMetrics},
state::UnrealizedState,
use crate::distribution::metrics::{
ActivityCore, ImportConfig, OutputsMetrics, RealizedMinimal, SupplyMetrics,
};
/// Minimal unrealized metrics: supply in profit/loss only.
#[derive(Traversable)]
pub struct MinimalUnrealized<M: StorageMode = Rw> {
pub supply_in_profit: ValueFromHeight<M>,
pub supply_in_loss: ValueFromHeight<M>,
}
/// MinimalCohortMetrics: supply, outputs, sent+ema, realized cap/price/mvrv/profit/loss,
/// supply in profit/loss.
///
@@ -33,71 +24,7 @@ pub struct MinimalCohortMetrics<M: StorageMode = Rw> {
pub outputs: Box<OutputsMetrics<M>>,
pub activity: Box<ActivityCore<M>>,
pub realized: Box<RealizedMinimal<M>>,
pub unrealized: Box<MinimalUnrealized<M>>,
}
impl MinimalUnrealized {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
supply_in_profit: cfg.import("supply_in_profit", Version::ZERO)?,
supply_in_loss: cfg.import("supply_in_loss", Version::ZERO)?,
})
}
pub(crate) fn min_stateful_height_len(&self) -> usize {
self.supply_in_profit
.sats
.height
.len()
.min(self.supply_in_loss.sats.height.len())
}
pub(crate) fn truncate_push(
&mut self,
height: Height,
state: &UnrealizedState,
) -> Result<()> {
self.supply_in_profit
.sats
.height
.truncate_push(height, state.supply_in_profit)?;
self.supply_in_loss
.sats
.height
.truncate_push(height, state.supply_in_loss)?;
Ok(())
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
]
}
pub(crate) fn compute_from_sources(
&mut self,
starting_indexes: &Indexes,
others: &[&Self],
exit: &Exit,
) -> Result<()> {
sum_others!(self, starting_indexes, others, exit; supply_in_profit.base.sats.height);
sum_others!(self, starting_indexes, others, exit; supply_in_loss.base.sats.height);
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.supply_in_profit.compute(prices, max_from, exit)?;
self.supply_in_loss.compute(prices, max_from, exit)?;
Ok(())
}
pub unrealized: Box<UnrealizedMinimal<M>>,
}
impl MinimalCohortMetrics {
@@ -108,7 +35,7 @@ impl MinimalCohortMetrics {
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
activity: Box::new(ActivityCore::forced_import(cfg)?),
realized: Box::new(RealizedMinimal::forced_import(cfg)?),
unrealized: Box::new(MinimalUnrealized::forced_import(cfg)?),
unrealized: Box::new(UnrealizedMinimal::forced_import(cfg)?),
})
}
@@ -150,12 +77,18 @@ impl MinimalCohortMetrics {
)?;
self.outputs.compute_from_stateful(
starting_indexes,
&others.iter().map(|v| v.outputs.as_ref()).collect::<Vec<_>>(),
&others
.iter()
.map(|v| v.outputs.as_ref())
.collect::<Vec<_>>(),
exit,
)?;
self.activity.compute_from_stateful(
starting_indexes,
&others.iter().map(|v| v.activity.as_ref()).collect::<Vec<_>>(),
&others
.iter()
.map(|v| v.activity.as_ref())
.collect::<Vec<_>>(),
exit,
)?;
self.realized.compute_from_stateful(
@@ -184,12 +117,10 @@ impl MinimalCohortMetrics {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.supply
.compute(prices, starting_indexes.height, exit)?;
self.supply.compute(prices, starting_indexes.height, exit)?;
self.supply
.compute_rest_part1(blocks, starting_indexes, exit)?;
self.outputs
.compute_rest(blocks, starting_indexes, exit)?;
self.outputs.compute_rest(blocks, starting_indexes, exit)?;
self.activity
.compute_rest_part1(blocks, starting_indexes, exit)?;
self.realized
@@ -4,9 +4,7 @@ use brk_types::{CentsSats, CentsSquaredSats, Height, Indexes, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
use crate::distribution::state::UnrealizedState;
use crate::distribution::metrics::ImportConfig;
use crate::{distribution::{metrics::ImportConfig, state::UnrealizedState}, prices};
use super::UnrealizedCore;
@@ -163,10 +161,11 @@ impl UnrealizedBase {
pub(crate) fn compute_rest(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.core.compute_rest(starting_indexes, exit)?;
self.core.compute_rest(prices, starting_indexes, exit)?;
Ok(())
}
}
@@ -1,24 +1,28 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Cents, CentsSigned, Height, Indexes, Version};
use derive_more::{Deref, DerefMut};
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, Rw, StorageMode, WritableVec};
use crate::{
distribution::state::UnrealizedState,
distribution::{
metrics::{ImportConfig, unrealized::UnrealizedMinimal},
state::UnrealizedState,
},
internal::{
CentsSubtractToCentsSigned, FiatFromHeight, LazyFromHeight, NegCentsUnsignedToDollars,
ValueFromHeight,
},
prices,
};
use brk_types::Dollars;
use crate::distribution::metrics::ImportConfig;
#[derive(Traversable)]
#[derive(Deref, DerefMut, Traversable)]
pub struct UnrealizedCore<M: StorageMode = Rw> {
pub supply_in_profit: ValueFromHeight<M>,
pub supply_in_loss: ValueFromHeight<M>,
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub minimal: UnrealizedMinimal<M>,
pub unrealized_profit: FiatFromHeight<Cents, M>,
pub unrealized_loss: FiatFromHeight<Cents, M>,
@@ -31,8 +35,8 @@ pub struct UnrealizedCore<M: StorageMode = Rw> {
impl UnrealizedCore {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
let v0 = Version::ZERO;
let supply_in_profit = cfg.import("supply_in_profit", v0)?;
let supply_in_loss = cfg.import("supply_in_loss", v0)?;
let minimal = UnrealizedMinimal::forced_import(cfg)?;
let unrealized_profit = cfg.import("unrealized_profit", v0)?;
let unrealized_loss: FiatFromHeight<Cents> = cfg.import("unrealized_loss", v0)?;
@@ -47,8 +51,7 @@ impl UnrealizedCore {
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", v0)?;
Ok(Self {
supply_in_profit,
supply_in_loss,
minimal,
unrealized_profit,
unrealized_loss,
neg_unrealized_loss,
@@ -57,11 +60,8 @@ impl UnrealizedCore {
}
pub(crate) fn min_stateful_height_len(&self) -> usize {
self.supply_in_profit
.sats
.height
.len()
.min(self.supply_in_loss.sats.height.len())
self.minimal
.min_stateful_height_len()
.min(self.unrealized_profit.cents.height.len())
.min(self.unrealized_loss.cents.height.len())
}
@@ -71,14 +71,7 @@ impl UnrealizedCore {
height: Height,
height_state: &UnrealizedState,
) -> Result<()> {
self.supply_in_profit
.sats
.height
.truncate_push(height, height_state.supply_in_profit)?;
self.supply_in_loss
.sats
.height
.truncate_push(height, height_state.supply_in_loss)?;
self.minimal.truncate_push(height, height_state)?;
self.unrealized_profit
.cents
.height
@@ -92,14 +85,10 @@ impl UnrealizedCore {
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
&mut self.unrealized_profit.cents.height,
&mut self.unrealized_loss.cents.height,
]
let mut vecs = self.minimal.collect_vecs_mut();
vecs.push(&mut self.unrealized_profit.cents.height);
vecs.push(&mut self.unrealized_loss.cents.height);
vecs
}
pub(crate) fn compute_from_stateful(
@@ -108,8 +97,10 @@ impl UnrealizedCore {
others: &[&Self],
exit: &Exit,
) -> Result<()> {
sum_others!(self, starting_indexes, others, exit; supply_in_profit.sats.height);
sum_others!(self, starting_indexes, others, exit; supply_in_loss.sats.height);
let minimal_refs: Vec<&UnrealizedMinimal> = others.iter().map(|o| &o.minimal).collect();
self.minimal
.compute_from_sources(starting_indexes, &minimal_refs, exit)?;
sum_others!(self, starting_indexes, others, exit; unrealized_profit.cents.height);
sum_others!(self, starting_indexes, others, exit; unrealized_loss.cents.height);
@@ -119,9 +110,13 @@ impl UnrealizedCore {
/// Compute derived metrics from stored values.
pub(crate) fn compute_rest(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.minimal
.compute_rest(prices, starting_indexes.height, exit)?;
self.net_unrealized_pnl
.cents
.height
@@ -84,7 +84,7 @@ impl UnrealizedFull {
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.inner.compute_rest(starting_indexes, exit)?;
self.inner.compute_rest(prices, starting_indexes, exit)?;
self.gross_pnl.cents.height.compute_add(
starting_indexes.height,
@@ -0,0 +1,77 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Height, Indexes, Version};
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
use crate::prices;
use crate::internal::ValueFromHeight;
use crate::distribution::{metrics::ImportConfig, state::UnrealizedState};
/// Minimal unrealized metrics: supply in profit/loss only.
#[derive(Traversable)]
pub struct UnrealizedMinimal<M: StorageMode = Rw> {
pub supply_in_profit: ValueFromHeight<M>,
pub supply_in_loss: ValueFromHeight<M>,
}
impl UnrealizedMinimal {
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
Ok(Self {
supply_in_profit: cfg.import("supply_in_profit", Version::ZERO)?,
supply_in_loss: cfg.import("supply_in_loss", Version::ZERO)?,
})
}
pub(crate) fn min_stateful_height_len(&self) -> usize {
self.supply_in_profit
.sats
.height
.len()
.min(self.supply_in_loss.sats.height.len())
}
pub(crate) fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
self.supply_in_profit
.sats
.height
.truncate_push(height, state.supply_in_profit)?;
self.supply_in_loss
.sats
.height
.truncate_push(height, state.supply_in_loss)?;
Ok(())
}
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
vec![
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
]
}
pub(crate) fn compute_from_sources(
&mut self,
starting_indexes: &Indexes,
others: &[&Self],
exit: &Exit,
) -> Result<()> {
sum_others!(self, starting_indexes, others, exit; supply_in_profit.base.sats.height);
sum_others!(self, starting_indexes, others, exit; supply_in_loss.base.sats.height);
Ok(())
}
pub(crate) fn compute_rest(
&mut self,
prices: &prices::Vecs,
max_from: Height,
exit: &Exit,
) -> Result<()> {
self.supply_in_profit.compute(prices, max_from, exit)?;
self.supply_in_loss.compute(prices, max_from, exit)?;
Ok(())
}
}
@@ -1,10 +1,12 @@
mod base;
mod core;
mod full;
mod minimal;
pub use base::UnrealizedBase;
pub use self::core::UnrealizedCore;
pub use base::UnrealizedBase;
pub use full::UnrealizedFull;
pub use minimal::UnrealizedMinimal;
use brk_error::Result;
use brk_types::{Height, Indexes};
@@ -17,36 +19,75 @@ pub trait UnrealizedLike: Send + Sync {
fn as_base_mut(&mut self) -> &mut UnrealizedBase;
fn min_stateful_height_len(&self) -> usize;
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()>;
fn compute_rest(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
fn compute_net_sentiment_height(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
fn compute_rest(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()>;
fn compute_net_sentiment_height(
&mut self,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()>;
}
impl UnrealizedLike for UnrealizedBase {
fn as_base(&self) -> &UnrealizedBase { self }
fn as_base_mut(&mut self) -> &mut UnrealizedBase { self }
fn min_stateful_height_len(&self) -> usize { self.min_stateful_height_len() }
fn as_base(&self) -> &UnrealizedBase {
self
}
fn as_base_mut(&mut self) -> &mut UnrealizedBase {
self
}
fn min_stateful_height_len(&self) -> usize {
self.min_stateful_height_len()
}
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
self.truncate_push(height, state)
}
fn compute_rest(&mut self, _prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
self.compute_rest(starting_indexes, exit)
fn compute_rest(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rest(prices, starting_indexes, exit)
}
fn compute_net_sentiment_height(&mut self, _starting_indexes: &Indexes, _exit: &Exit) -> Result<()> {
fn compute_net_sentiment_height(
&mut self,
_starting_indexes: &Indexes,
_exit: &Exit,
) -> Result<()> {
Ok(())
}
}
impl UnrealizedLike for UnrealizedFull {
fn as_base(&self) -> &UnrealizedBase { &self.inner }
fn as_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.inner }
fn min_stateful_height_len(&self) -> usize { self.inner.min_stateful_height_len() }
fn as_base(&self) -> &UnrealizedBase {
&self.inner
}
fn as_base_mut(&mut self) -> &mut UnrealizedBase {
&mut self.inner
}
fn min_stateful_height_len(&self) -> usize {
self.inner.min_stateful_height_len()
}
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
self.truncate_push_all(height, state)
}
fn compute_rest(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
fn compute_rest(
&mut self,
prices: &prices::Vecs,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_rest_all(prices, starting_indexes, exit)
}
fn compute_net_sentiment_height(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
fn compute_net_sentiment_height(
&mut self,
starting_indexes: &Indexes,
exit: &Exit,
) -> Result<()> {
self.compute_net_sentiment_height(starting_indexes, exit)
}
}
@@ -29,11 +29,6 @@ impl<N: FenwickNode> FenwickTree<N> {
}
}
#[inline]
pub fn size(&self) -> usize {
self.size
}
pub fn reset(&mut self) {
self.tree.fill(N::default());
}
@@ -73,7 +68,7 @@ impl<N: FenwickNode> FenwickTree<N> {
debug_assert!(self.size > 0);
let mut pos = 0usize;
let mut remaining = k;
let mut bit = 1usize << (usize::BITS - 1 - self.size.leading_zeros() as u32);
let mut bit = 1usize << (usize::BITS - 1 - self.size.leading_zeros());
while bit > 0 {
let next = pos + bit;
if next <= self.size {
@@ -105,7 +100,7 @@ impl<N: FenwickNode> FenwickTree<N> {
out.fill(0);
// Copy targets so we can subtract in-place
let mut remaining: smallvec::SmallVec<[V; 24]> = sorted_targets.into();
let mut bit = 1usize << (usize::BITS - 1 - self.size.leading_zeros() as u32);
let mut bit = 1usize << (usize::BITS - 1 - self.size.leading_zeros());
while bit > 0 {
for i in 0..k {
let next = out[i] + bit;
@@ -12,4 +12,3 @@ pub(crate) use expanding_percentiles::*;
pub(crate) use fenwick::*;
pub(crate) use sliding_distribution::*;
pub(crate) use sliding_median::*;
pub(crate) use sliding_window::*;
@@ -5,7 +5,7 @@ use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{BasisPointsSigned32, Height, Version};
use schemars::JsonSchema;
use vecdb::{AnyVec, Database, Exit, ReadableVec, Rw, StorageMode};
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
use crate::{
indexes,
@@ -152,8 +152,11 @@ where
source: &impl ReadableVec<Height, S>,
exit: &Exit,
) -> Result<()> {
let changes: [&mut FiatFromHeight<C>; 3] =
[&mut self.change_24h, &mut self.change_1w, &mut self.change_1y];
let changes: [&mut FiatFromHeight<C>; 3] = [
&mut self.change_24h,
&mut self.change_1w,
&mut self.change_1y,
];
let rates = [&mut self.rate_24h, &mut self.rate_1w, &mut self.rate_1y];
let starts = [windows._24h, windows._1w, windows._1y];