Skip to main content

stats_claw/streaming/
mod.rs

1//! Online (streaming) estimators that run in bounded memory.
2//!
3//! Each estimator consumes one value at a time through `update` and reports its
4//! current estimate through an accessor, holding only a fixed-size summary of the
5//! stream seen so far. State size is a compile-time constant — independent of how
6//! many values have been consumed — so an arbitrarily long stream is processed
7//! without memory growth.
8//!
9//! * [`RunningMoments`] — Welford's online mean and variance.
10//! * [`P2Quantile`] — the P² streaming-quantile estimator (Jain & Chlamtac 1985).
11
12mod moments;
13mod p2;
14
15pub use moments::RunningMoments;
16pub use p2::P2Quantile;
17
18/// Converts a stream count to `f64` for use as a divisor.
19///
20/// Shared by the streaming estimators so the count→float conversion is done one
21/// way: through a lossless `u32` window, never an `as` cast (banned in `src/`).
22///
23/// # Arguments
24///
25/// * `n` — a stream count. Counts beyond `u32::MAX` clamp to `u32::MAX`, far past
26///   any realistic stream where `f64` already loses integer precision (`> 2^53`).
27///
28/// # Returns
29///
30/// `n` represented as an `f64`.
31pub(crate) fn count_to_f64(n: u64) -> f64 {
32    f64::from(u32::try_from(n).unwrap_or(u32::MAX))
33}
34
35/// Converts a 1-based, possibly-negative marker position to `f64`.
36///
37/// Shared by [`P2Quantile`]'s parabolic/linear updates. Goes through a lossless
38/// `u32` magnitude window so the conversion never uses an `as` cast.
39///
40/// # Arguments
41///
42/// * `n` — a marker position; its magnitude clamps to `u32::MAX` (far beyond the
43///   five P² markers' realistic positions).
44///
45/// # Returns
46///
47/// `n` as an `f64`, sign preserved.
48pub(crate) fn position_as_f64(n: i64) -> f64 {
49    let mag = f64::from(u32::try_from(n.unsigned_abs()).unwrap_or(u32::MAX));
50    if n < 0 { -mag } else { mag }
51}