From c97c18595c59d8c34b18805d2c747efbedc800c1 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 3 Jun 2026 21:00:23 +0200 Subject: [PATCH 01/14] introduce `MadScaler`, a robust, median based scaler. --- Cargo.lock | 53 +++++- Cargo.toml | 1 + src/sliding_windows/mad_scaler.rs | 263 ++++++++++++++++++++++++++++++ src/sliding_windows/mod.rs | 2 + 4 files changed, 314 insertions(+), 5 deletions(-) create mode 100644 src/sliding_windows/mad_scaler.rs diff --git a/Cargo.lock b/Cargo.lock index d819f4e..af5ee66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -722,6 +722,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", + "rand 0.8.6", + "serde", +] + [[package]] name = "page_size" version = "0.6.0" @@ -871,6 +882,16 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core 0.6.4", + "serde", +] + [[package]] name = "rand" version = "0.9.2" @@ -878,7 +899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -888,7 +909,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "serde", ] [[package]] @@ -907,7 +937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand", + "rand 0.9.2", ] [[package]] @@ -1069,9 +1099,10 @@ dependencies = [ "getset", "num", "plotters", - "rand", + "rand 0.9.2", "round", "time_series_generator", + "watermill", ] [[package]] @@ -1112,7 +1143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3098b86531bc0f07e6455f381717a3e1f48e457d8d03cb01a284abc40617964b" dependencies = [ "num-traits", - "rand", + "rand 0.9.2", "rand_distr", ] @@ -1208,6 +1239,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "watermill" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "674ae43b249a45b79db22c99564f348da46f16ef243c605db082d0cbe088669a" +dependencies = [ + "num", + "ordered-float", + "serde", + "serde_json", +] + [[package]] name = "web-sys" version = "0.3.85" diff --git a/Cargo.toml b/Cargo.toml index 9a3d1cd..011b795 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ exclude = ["img/"] [dependencies] getset = "0.1" num = "0.4" +watermill = "0.1.2" [dev-dependencies] time_series_generator = "0.4.1" diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs new file mode 100644 index 0000000..041aa13 --- /dev/null +++ b/src/sliding_windows/mad_scaler.rs @@ -0,0 +1,263 @@ +//! Robust MAD-based (mean absolute difference) scaler over a sliding window. +//! +//! Computes `(x - median) / (1.4826 * MAD)` where `median` and `MAD` +//! (median absolute deviation) are derived from the *previous* sliding +//! window. The constant 1.4826 makes the estimator consistent with the +//! standard deviation for normally distributed data. +//! +//! Unlike `ZScoreStandardization`, the MAD scaler is resistant to outliers +//! and works well for skewed distributions. + +use std::{ + num::NonZeroUsize, + ops::{ + AddAssign, + SubAssign, + }, +}; + +use num::{ + Float, + FromPrimitive, +}; +use watermill::sorted_window::SortedWindow; + +use crate::View; + +/// Robust MAD-based scaler over a sliding window. +/// +/// Computes `(x - median) / (1.4826 * MAD)` using only *past* values so +/// that the transformation does not leak information about the current +/// observation. +/// +/// No output is emitted until the sliding window is fully filled. +/// During warm‑up `last()` returns `None`. +pub struct MadScaler { + view: V, + /// Sliding window length (for warm-up tracking). + window_len: NonZeroUsize, + /// Sliding window storing the sorted values. + sorted: SortedWindow, + /// Cached median of the window (recomputed when the sliding median changes). + cached_median: T, + /// Cached MAD (recomputed when the sliding median changes). + cached_mad: T, + /// Whether the caches are stale and need recomputation. + cache_stale: bool, + /// Most recent output of the scaler. + out: Option, + /// Running count of values pushed into the sorted window (capped at window_len). + count: usize, +} + +/// MAD consistency constant: ~1.4826 makes MAD ≈ σ for normally distributed data. +const MAD_SCALE: f64 = 1.4826; + +impl MadScaler +where + V: View, + T: Float + FromPrimitive + AddAssign + SubAssign, +{ + /// Create a new `MadScaler` with a chained `View` and a given sliding + /// window length. + #[inline] + pub fn new(view: V, window_len: NonZeroUsize) -> Self { + Self { + view, + window_len, + sorted: SortedWindow::new(window_len.get()), + cached_median: T::zero(), + cached_mad: T::zero(), + cache_stale: true, + out: None, + count: 0, + } + } + + /// The sliding window length. + #[inline(always)] + pub fn window_len(&self) -> NonZeroUsize { + self.window_len + } + + /// Recompute the cached median and MAD from the sorted window. + fn recompute_cache(&mut self) { + let n = self.sorted.len(); + if n == 0 { + self.cached_median = T::zero(); + self.cached_mad = T::zero(); + self.cache_stale = false; + return; + } + + // --- median --- + let median = if n % 2 == 0 { + let a = self.sorted[n / 2 - 1]; + let b = self.sorted[n / 2]; + (a + b) / (T::one() + T::one()) + } else { + self.sorted[n / 2] + }; + self.cached_median = median; + + // --- median absolute deviation --- + // Collect absolute deviations, sort them. + let mut abs_devs: Vec = Vec::with_capacity(n); + for i in 0..n { + let diff = self.sorted[i] - median; + abs_devs.push(if diff < T::zero() { -diff } else { diff }); + } + abs_devs.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + let mad = if n % 2 == 0 { + (abs_devs[n / 2 - 1] + abs_devs[n / 2]) / (T::one() + T::one()) + } else { + abs_devs[n / 2] + }; + self.cached_mad = mad; + + self.cache_stale = false; + } +} + +impl View for MadScaler +where + V: View, + T: Float + FromPrimitive + AddAssign + SubAssign, +{ + fn update(&mut self, val: T) { + debug_assert!(val.is_finite(), "value must be finite"); + self.view.update(val); + let Some(val) = self.view.last() else { + return; + }; + debug_assert!(val.is_finite(), "value must be finite"); + + // Only emit output once the window is full (count >= window_len). + // At that point `sorted` contains exactly `window_len` past values. + if self.count >= self.window_len.get() { + // The window (`sorted`) holds the *previous* window of values. + // Normalize `val` against those. + if self.cache_stale { + self.recompute_cache(); + } + + self.out = { + let scale_const = T::from(MAD_SCALE).expect("convert"); + if self.cached_mad <= T::zero() { + // No dispersion → all values identical. + Some(T::zero()) + } else { + let diff = val - self.cached_median; + Some(diff / (scale_const * self.cached_mad)) + } + }; + } else { + self.out = None; + } + + // Slide window: push current value into the sorted window. + // This makes it part of the *next* normalization's reference. + let prev_len = self.sorted.len(); + self.sorted.push_back(val); + // Only mark stale if the median may have changed. + // SortedWindow insertion/removal may change the median, so always invalidate. + if self.sorted.len() != prev_len { + // A value was evicted (full window) → cached median/MAD always stale. + self.cache_stale = true; + } else { + // A value was pushed without eviction (warm-up) → cache is stale too. + self.cache_stale = true; + } + + self.count = (self.count + 1).min(self.window_len.get()); + } + + #[inline] + fn last(&self) -> Option { + self.out + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + plot::plot_values, + pure_functions::Echo, + test_data::TEST_DATA, + }; + + #[test] + fn mad_scaler_plot() { + let mut ms = MadScaler::new(Echo::new(), NonZeroUsize::new(16).unwrap()); + let mut out: Vec = Vec::with_capacity(TEST_DATA.len()); + for v in &TEST_DATA { + ms.update(*v); + if let Some(val) = ms.last() { + out.push(val); + } + } + let filename = "img/mad_scaler.png"; + plot_values(out, filename).unwrap(); + } + + #[test] + fn mad_scaler_no_lookahead() { + // Window = 3. Warm up with [10, 10, 10], then spike 100. + // The spike must be normalized against [10, 10, 10], + // where median = 10, MAD = 0 → output 0. + let mut ms = MadScaler::new(Echo::new(), NonZeroUsize::new(3).unwrap()); + + ms.update(10.0); + assert!(ms.last().is_none(), "warm-up 1"); + ms.update(10.0); + assert!(ms.last().is_none(), "warm-up 2"); + ms.update(10.0); + assert!(ms.last().is_none(), "warm-up 3"); + + // 4th value: first real output. Window = [10, 10, 10]. + ms.update(100.0); + let got = ms.last().unwrap(); + assert!( + (got - 0.0).abs() < 1e-12, + "spike must not leak into its own reference window, got {got}, expected 0.0" + ); + + // Now window = [10, 10, 100]. Next 10 is normalized against it. + // median of [10, 10, 100] = 10. + // absolute deviations: [0, 0, 90] → sorted [0, 0, 90] → median MAD = 0. + // → output = 0. + ms.update(10.0); + let got = ms.last().unwrap(); + assert!( + (got - 0.0).abs() < 1e-12, + "after spike entered window (MAD=0), got {got}, expected 0.0" + ); + } + + #[test] + fn mad_scaler_disjoint_clean_and_outlier() { + // Values: 5 clean (1,2,3,4,5) then outlier (100). + // Window = 5. + let mut ms = MadScaler::new(Echo::new(), NonZeroUsize::new(5).unwrap()); + + // Warm-up: no output for first 5. + for v in [1.0, 2.0, 3.0, 4.0, 5.0] { + ms.update(v); + assert!(ms.last().is_none(), "warm-up"); + } + + // 6th: 100 normalized against [1,2,3,4,5]. + // median of [1,2,3,4,5] = 3. + // abs devs: [2,1,0,1,2] → sorted [0,1,1,2,2] → MAD = 1. + // → output = (100 - 3) / (1.4826 * 1) ≈ 65.42 + ms.update(100.0); + let got = ms.last().unwrap(); + let expected = (100.0 - 3.0) / (1.4826); + assert!( + (got - expected).abs() < 1e-6, + "outlier scaling: got {got}, expected ~{expected}" + ); + } +} diff --git a/src/sliding_windows/mod.rs b/src/sliding_windows/mod.rs index d9690c8..5e80cc7 100644 --- a/src/sliding_windows/mod.rs +++ b/src/sliding_windows/mod.rs @@ -12,6 +12,7 @@ mod ema; mod lag; mod laguerre_filter; mod laguerre_rsi; +mod mad_scaler; mod max; mod min; mod min_max_normalizer; @@ -40,6 +41,7 @@ pub use ema::Ema; pub use lag::Lag; pub use laguerre_filter::LaguerreFilter; pub use laguerre_rsi::LaguerreRSI; +pub use mad_scaler::MadScaler; pub use max::Max; pub use min::Min; pub use min_max_normalizer::MinMaxNormalizer; From 36f2fe65e940de3b42e19f46ad03b287861bcbd5 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 3 Jun 2026 21:30:41 +0200 Subject: [PATCH 02/14] include benchmark for `MadScaler` --- Cargo.toml | 4 ++ benches/mad_scaler.rs | 100 ++++++++++++++++++++++++++++++ src/sliding_windows/mad_scaler.rs | 2 +- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 benches/mad_scaler.rs diff --git a/Cargo.toml b/Cargo.toml index 011b795..0d142d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,6 +71,10 @@ harness = false name = "laguerre_rsi" harness = false +[[bench]] +name = "mad_scaler" +harness = false + [[bench]] name = "my_rsi" harness = false diff --git a/benches/mad_scaler.rs b/benches/mad_scaler.rs new file mode 100644 index 0000000..4cc206a --- /dev/null +++ b/benches/mad_scaler.rs @@ -0,0 +1,100 @@ +use std::{ + hint::black_box, + num::NonZeroUsize, +}; + +use criterion::{ + Criterion, + criterion_group, + criterion_main, +}; +use rand::{ + Rng, + SeedableRng, + rngs::SmallRng, +}; +use sliding_features::{ + View, + pure_functions::Echo, + sliding_windows::MadScaler, +}; + +const N: usize = 100_000; +const WINDOW_LEN: usize = 1024; + +fn standard_normal(rng: &mut SmallRng) -> f64 { + // Box-Muller transform. Clamp u1 away from zero so ln(u1) is finite. + let u1 = rng.random::().max(f64::MIN_POSITIVE); + let u2 = rng.random::(); + (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos() +} + +fn realistic_market_prices_f64() -> Vec { + let mut rng = SmallRng::seed_from_u64(0); + let mut price: f64 = 100.0; + let mut volatility: f64 = 0.012; + + (0..N) + .map(|i| { + // Slowly varying volatility with clustering after large shocks. + volatility = 0.985 * volatility + 0.015 * 0.012; + let shock = standard_normal(&mut rng); + if shock.abs() > 2.5 { + volatility = (volatility * 1.35).min(0.08); + } + + // Occasional gap/jump events, as seen in real market data. + let jump = if rng.random::() < 0.002 { + standard_normal(&mut rng) * 0.06 + } else { + 0.0 + }; + + // Mild trend plus a daily-ish seasonality component. + let drift = 0.00002; + let seasonality = 0.0015 * ((i as f64 / 390.0) * std::f64::consts::TAU).sin(); + let log_return = drift + seasonality + volatility * shock + jump; + + price *= log_return.exp(); + price + }) + .collect() +} + +fn realistic_market_prices_f32() -> Vec { + realistic_market_prices_f64() + .into_iter() + .map(|v| v as f32) + .collect() +} + +fn criterion_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("mad_scaler_100k"); + + group.bench_function("realistic_market_prices_f64", |b| { + let vals = realistic_market_prices_f64(); + b.iter(|| { + let mut view = + MadScaler::::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); + for v in vals.iter() { + view.update(*v); + let _ = black_box(view.last()); + } + }) + }); + + group.bench_function("realistic_market_prices_f32", |b| { + let vals = realistic_market_prices_f32(); + b.iter(|| { + let mut view = + MadScaler::::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); + for v in vals.iter() { + view.update(*v); + let _ = black_box(view.last()); + } + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index 041aa13..21bbfa8 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -1,4 +1,4 @@ -//! Robust MAD-based (mean absolute difference) scaler over a sliding window. +//! Robust MAD-based (mean absolute deviation) scaler over a sliding window. //! //! Computes `(x - median) / (1.4826 * MAD)` where `median` and `MAD` //! (median absolute deviation) are derived from the *previous* sliding From 0457386a991d047af8846378ae09dd96eedef711 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 3 Jun 2026 21:41:30 +0200 Subject: [PATCH 03/14] Baseline MadScaler bench test compile/run smoke Result: {"status":"keep","cargo_bench_wall":0.9,"bench_test_s":0.9} --- autoresearch.jsonl | 1 + 1 file changed, 1 insertion(+) create mode 100644 autoresearch.jsonl diff --git a/autoresearch.jsonl b/autoresearch.jsonl new file mode 100644 index 0000000..a3ecf4d --- /dev/null +++ b/autoresearch.jsonl @@ -0,0 +1 @@ +{"type":"config","name":"MadScaler implementation performance","metricName":"cargo_bench_wall","metricUnit":"s","bestDirection":"lower"} From db3e5a3ae38f0e9893216b53649ffe2ef7d2bdf9 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 3 Jun 2026 21:42:54 +0200 Subject: [PATCH 04/14] Baseline Criterion MadScaler realistic f64 Result: {"status":"keep","f64_realistic_ms":378.35,"f64_mean_ms":378.35,"f32_mean_ms":379} --- autoresearch.jsonl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autoresearch.jsonl b/autoresearch.jsonl index a3ecf4d..1e04450 100644 --- a/autoresearch.jsonl +++ b/autoresearch.jsonl @@ -1 +1,3 @@ {"type":"config","name":"MadScaler implementation performance","metricName":"cargo_bench_wall","metricUnit":"s","bestDirection":"lower"} +{"run":1,"commit":"0457386","metric":0.9,"metrics":{"bench_test_s":0.9},"status":"keep","description":"Baseline MadScaler bench test compile/run smoke","timestamp":1780515690117,"segment":0,"confidence":null,"asi":{"hypothesis":"baseline for evaluating MadScaler implementation changes using cargo bench --bench mad_scaler -- --test"}} +{"type":"config","name":"MadScaler Criterion benchmark optimization","metricName":"f64_realistic_ms","metricUnit":"ms","bestDirection":"lower"} From e8c01ca1095032d0ab9bc1aa3d2258cb7a071d22 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Wed, 3 Jun 2026 22:36:42 +0200 Subject: [PATCH 05/14] bench: iter some window lengths --- autoresearch.jsonl | 3 --- benches/mad_scaler.rs | 60 ++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 26 deletions(-) delete mode 100644 autoresearch.jsonl diff --git a/autoresearch.jsonl b/autoresearch.jsonl deleted file mode 100644 index 1e04450..0000000 --- a/autoresearch.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"type":"config","name":"MadScaler implementation performance","metricName":"cargo_bench_wall","metricUnit":"s","bestDirection":"lower"} -{"run":1,"commit":"0457386","metric":0.9,"metrics":{"bench_test_s":0.9},"status":"keep","description":"Baseline MadScaler bench test compile/run smoke","timestamp":1780515690117,"segment":0,"confidence":null,"asi":{"hypothesis":"baseline for evaluating MadScaler implementation changes using cargo bench --bench mad_scaler -- --test"}} -{"type":"config","name":"MadScaler Criterion benchmark optimization","metricName":"f64_realistic_ms","metricUnit":"ms","bestDirection":"lower"} diff --git a/benches/mad_scaler.rs b/benches/mad_scaler.rs index 4cc206a..eb0a02a 100644 --- a/benches/mad_scaler.rs +++ b/benches/mad_scaler.rs @@ -5,6 +5,7 @@ use std::{ use criterion::{ Criterion, + Throughput, criterion_group, criterion_main, }; @@ -20,7 +21,7 @@ use sliding_features::{ }; const N: usize = 100_000; -const WINDOW_LEN: usize = 1024; +const WINDOW_LENS: &[usize] = &[1024, 8192]; fn standard_normal(rng: &mut SmallRng) -> f64 { // Box-Muller transform. Clamp u1 away from zero so ln(u1) is finite. @@ -70,30 +71,43 @@ fn realistic_market_prices_f32() -> Vec { fn criterion_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("mad_scaler_100k"); + group.throughput(Throughput::Elements(N as u64)); - group.bench_function("realistic_market_prices_f64", |b| { - let vals = realistic_market_prices_f64(); - b.iter(|| { - let mut view = - MadScaler::::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); - for v in vals.iter() { - view.update(*v); - let _ = black_box(view.last()); - } - }) - }); + for &window_len in WINDOW_LENS { + group.bench_function( + format!("realistic_market_prices_f64/window_len_{window_len}"), + |b| { + let vals = realistic_market_prices_f64(); + b.iter(|| { + let mut view = MadScaler::::new( + Echo::new(), + NonZeroUsize::new(window_len).unwrap(), + ); + for v in vals.iter() { + view.update(*v); + let _ = black_box(view.last()); + } + }) + }, + ); - group.bench_function("realistic_market_prices_f32", |b| { - let vals = realistic_market_prices_f32(); - b.iter(|| { - let mut view = - MadScaler::::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); - for v in vals.iter() { - view.update(*v); - let _ = black_box(view.last()); - } - }) - }); + group.bench_function( + format!("realistic_market_prices_f32/window_len_{window_len}"), + |b| { + let vals = realistic_market_prices_f32(); + b.iter(|| { + let mut view = MadScaler::::new( + Echo::new(), + NonZeroUsize::new(window_len).unwrap(), + ); + for v in vals.iter() { + view.update(*v); + let _ = black_box(view.last()); + } + }) + }, + ); + } } criterion_group!(benches, criterion_benchmark); From 4a5b51ce7bf49967b42a286057235e397db80283 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 01:07:43 +0200 Subject: [PATCH 06/14] mad_scaler: new tests with naive median and MAD computation for comparison --- Cargo.lock | 17 +++++++++ Cargo.toml | 2 ++ img/trend_flex.png | Bin 94131 -> 94131 bytes src/sliding_windows/mad_scaler.rs | 57 +++++++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5ee66..61c3aa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "ballpark" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774d962b50f4c48f992d781f993fd00dbc0a64e648a7de7ef7125df37cd71327" + [[package]] name = "bitflags" version = "1.3.2" @@ -1000,6 +1006,15 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +[[package]] +name = "romu" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb6d76270085d48d0c65f0a78d1255905a6bbe3ff4737b7c591b12eecd87b62" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "round" version = "0.1.2" @@ -1095,11 +1110,13 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" name = "sliding_features" version = "9.0.0" dependencies = [ + "ballpark", "criterion", "getset", "num", "plotters", "rand 0.9.2", + "romu", "round", "time_series_generator", "watermill", diff --git a/Cargo.toml b/Cargo.toml index 0d142d4..2fae595 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,10 @@ num = "0.4" watermill = "0.1.2" [dev-dependencies] +ballpark = "1" time_series_generator = "0.4.1" rand = "0.9" +romu = "0.8" round = "0.1" plotters = "0.3" criterion = "0.8" diff --git a/img/trend_flex.png b/img/trend_flex.png index b02620d5dd3348b2a7eb8d061989b8e875eb5541..7ead302f2a3c778af8e9f9d3a7a236e6d7966e1e 100644 GIT binary patch literal 94131 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�!0iuu;OecaV{=?U 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 LB>E%n7}@^?c6@Ok literal 94131 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 FGcOK&J5i$h$CFh{_0 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 AmjD0& diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index 21bbfa8..a938aa7 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -1,8 +1,10 @@ -//! Robust MAD-based (mean absolute deviation) scaler over a sliding window. +//! Robust MAD-based (median absolute deviation) scaler over a sliding window. //! //! Computes `(x - median) / (1.4826 * MAD)` where `median` and `MAD` //! (median absolute deviation) are derived from the *previous* sliding -//! window. The constant 1.4826 makes the estimator consistent with the +//! window. +//! `MAD = median((x - median(X)).abs())` +//! The constant 1.4826 makes the estimator consistent with the //! standard deviation for normally distributed data. //! //! Unlike `ZScoreStandardization`, the MAD scaler is resistant to outliers @@ -104,8 +106,8 @@ where // Collect absolute deviations, sort them. let mut abs_devs: Vec = Vec::with_capacity(n); for i in 0..n { - let diff = self.sorted[i] - median; - abs_devs.push(if diff < T::zero() { -diff } else { diff }); + let diff = (self.sorted[i] - median).abs(); + abs_devs.push(diff); } abs_devs.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -181,6 +183,8 @@ where #[cfg(test)] mod tests { + use ballpark::assert_approx_eq; + use super::*; use crate::{ plot::plot_values, @@ -260,4 +264,49 @@ mod tests { "outlier scaling: got {got}, expected ~{expected}" ); } + + fn median(vals: &mut [f64]) -> f64 { + vals.sort_by(f64::total_cmp); + let n = vals.len(); + if n.is_multiple_of(2) { + let a = vals[n / 2 - 1]; + let b = vals[n / 2]; + (a + b) / 2.0 + } else { + vals[n / 2] + } + } + + fn mad_scaled(vals: &mut [f64], buf: &mut [f64], current: f64) -> f64 { + assert_eq!(vals.len(), buf.len()); + let m = median(vals); + buf.iter_mut().for_each(|v| *v = (*v - m).abs()); + let mad = median(buf); + (current - m) / mad + } + + #[test] + fn mad_scaler() { + const WINDOW_LEN: usize = 5; + + let r = romu::Rng::from_seed_with_64bit(0); + let vals = Vec::from_iter((0..100).map(|_| r.f64())); + let mut buf = vec![0.0; WINDOW_LEN]; + + let mut ms = MadScaler::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); + + // warmup + for v in vals.iter().take(WINDOW_LEN) { + ms.update(*v); + assert!(ms.last().is_none()) + } + + for (i, v) in vals.iter().enumerate().skip(WINDOW_LEN + 1) { + ms.update(*v); + let start = i - WINDOW_LEN - 1; + let end = i - 1; + let expected = mad_scaled(&mut vals[start..end].to_vec(), &mut buf, *v); + assert_approx_eq!(ms.last().expect("is warm"), expected); + } + } } From 6c5f13c43f3935a68189507a34c98be0ff4d8d3e Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 11:00:53 +0100 Subject: [PATCH 07/14] mad_scaler: remove `cache_stale` as its useless. --- src/sliding_windows/mad_scaler.rs | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index a938aa7..5188104 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -44,14 +44,27 @@ pub struct MadScaler { cached_median: T, /// Cached MAD (recomputed when the sliding median changes). cached_mad: T, - /// Whether the caches are stale and need recomputation. - cache_stale: bool, /// Most recent output of the scaler. out: Option, /// Running count of values pushed into the sorted window (capped at window_len). count: usize, } +impl std::fmt::Debug for MadScaler +where + F: Float + FromPrimitive + AddAssign + SubAssign + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MadScaler") + .field("window_len", &self.window_len) + .field("cached_median", &self.cached_median) + .field("cached_mad", &self.cached_mad) + .field("out", &self.out) + .field("count", &self.count) + .finish() + } +} + /// MAD consistency constant: ~1.4826 makes MAD ≈ σ for normally distributed data. const MAD_SCALE: f64 = 1.4826; @@ -70,7 +83,6 @@ where sorted: SortedWindow::new(window_len.get()), cached_median: T::zero(), cached_mad: T::zero(), - cache_stale: true, out: None, count: 0, } @@ -88,7 +100,6 @@ where if n == 0 { self.cached_median = T::zero(); self.cached_mad = T::zero(); - self.cache_stale = false; return; } @@ -117,8 +128,6 @@ where abs_devs[n / 2] }; self.cached_mad = mad; - - self.cache_stale = false; } } @@ -140,9 +149,7 @@ where if self.count >= self.window_len.get() { // The window (`sorted`) holds the *previous* window of values. // Normalize `val` against those. - if self.cache_stale { - self.recompute_cache(); - } + self.recompute_cache(); self.out = { let scale_const = T::from(MAD_SCALE).expect("convert"); @@ -160,17 +167,7 @@ where // Slide window: push current value into the sorted window. // This makes it part of the *next* normalization's reference. - let prev_len = self.sorted.len(); self.sorted.push_back(val); - // Only mark stale if the median may have changed. - // SortedWindow insertion/removal may change the median, so always invalidate. - if self.sorted.len() != prev_len { - // A value was evicted (full window) → cached median/MAD always stale. - self.cache_stale = true; - } else { - // A value was pushed without eviction (warm-up) → cache is stale too. - self.cache_stale = true; - } self.count = (self.count + 1).min(self.window_len.get()); } @@ -291,6 +288,7 @@ mod tests { let r = romu::Rng::from_seed_with_64bit(0); let vals = Vec::from_iter((0..100).map(|_| r.f64())); + dbg!(&vals); let mut buf = vec![0.0; WINDOW_LEN]; let mut ms = MadScaler::new(Echo::new(), NonZeroUsize::new(WINDOW_LEN).unwrap()); @@ -302,6 +300,8 @@ mod tests { } for (i, v) in vals.iter().enumerate().skip(WINDOW_LEN + 1) { + dbg!(&v); + dbg!(&ms); ms.update(*v); let start = i - WINDOW_LEN - 1; let end = i - 1; From 2480cafa553036059a45cbc234a17cda5be845a2 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 11:45:38 +0100 Subject: [PATCH 08/14] mad_scaler: avoid allocations in the hot-path, improve perf by 17% --- src/sliding_windows/mad_scaler.rs | 51 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index 5188104..e80b674 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -48,6 +48,8 @@ pub struct MadScaler { out: Option, /// Running count of values pushed into the sorted window (capped at window_len). count: usize, + /// Buffer for `(x - m).abs()` computations to avoid allocation in the hot-path. + buf: Vec, } impl std::fmt::Debug for MadScaler @@ -68,10 +70,10 @@ where /// MAD consistency constant: ~1.4826 makes MAD ≈ σ for normally distributed data. const MAD_SCALE: f64 = 1.4826; -impl MadScaler +impl MadScaler where - V: View, - T: Float + FromPrimitive + AddAssign + SubAssign, + V: View, + F: Float + FromPrimitive + AddAssign + SubAssign, { /// Create a new `MadScaler` with a chained `View` and a given sliding /// window length. @@ -81,10 +83,11 @@ where view, window_len, sorted: SortedWindow::new(window_len.get()), - cached_median: T::zero(), - cached_mad: T::zero(), + cached_median: F::zero(), + cached_mad: F::zero(), out: None, count: 0, + buf: vec![F::zero(); window_len.get()], } } @@ -98,16 +101,16 @@ where fn recompute_cache(&mut self) { let n = self.sorted.len(); if n == 0 { - self.cached_median = T::zero(); - self.cached_mad = T::zero(); + self.cached_median = F::zero(); + self.cached_mad = F::zero(); return; } // --- median --- - let median = if n % 2 == 0 { + let median = if n.is_power_of_two() { let a = self.sorted[n / 2 - 1]; let b = self.sorted[n / 2]; - (a + b) / (T::one() + T::one()) + (a + b) / (F::one() + F::one()) } else { self.sorted[n / 2] }; @@ -115,17 +118,17 @@ where // --- median absolute deviation --- // Collect absolute deviations, sort them. - let mut abs_devs: Vec = Vec::with_capacity(n); - for i in 0..n { + assert_eq!(n, self.buf.len()); + for (i, b) in self.buf.iter_mut().enumerate() { let diff = (self.sorted[i] - median).abs(); - abs_devs.push(diff); + *b = diff; } - abs_devs.sort_by(|a, b| a.partial_cmp(b).unwrap()); + self.buf.sort_by(|a, b| a.partial_cmp(b).unwrap()); - let mad = if n % 2 == 0 { - (abs_devs[n / 2 - 1] + abs_devs[n / 2]) / (T::one() + T::one()) + let mad = if n.is_power_of_two() { + (self.buf[n / 2 - 1] + self.buf[n / 2]) / (F::one() + F::one()) } else { - abs_devs[n / 2] + self.buf[n / 2] }; self.cached_mad = mad; } @@ -151,15 +154,13 @@ where // Normalize `val` against those. self.recompute_cache(); - self.out = { - let scale_const = T::from(MAD_SCALE).expect("convert"); - if self.cached_mad <= T::zero() { - // No dispersion → all values identical. - Some(T::zero()) - } else { - let diff = val - self.cached_median; - Some(diff / (scale_const * self.cached_mad)) - } + let scale_const = T::from(MAD_SCALE).expect("convert"); + self.out = if self.cached_mad <= T::zero() { + // No dispersion → all values identical. + Some(T::zero()) + } else { + let diff = val - self.cached_median; + Some(diff / (scale_const * self.cached_mad)) }; } else { self.out = None; From b6cdf871762fb0cbf572a71ea3e70f16c84c35ce Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 11:56:37 +0100 Subject: [PATCH 09/14] mad_scaler: extract helper `median_from_sorted` --- src/sliding_windows/mad_scaler.rs | 34 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index e80b674..98b4160 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -125,15 +125,23 @@ where } self.buf.sort_by(|a, b| a.partial_cmp(b).unwrap()); - let mad = if n.is_power_of_two() { - (self.buf[n / 2 - 1] + self.buf[n / 2]) / (F::one() + F::one()) - } else { - self.buf[n / 2] - }; + let mad = median_from_sorted(&self.buf); self.cached_mad = mad; } } +#[inline(always)] +fn median_from_sorted(vals: &[F]) -> F { + let n = vals.len(); + if n.is_power_of_two() { + let a = vals[n / 2 - 1]; + let b = vals[n / 2]; + (a + b) / (F::one() + F::one()) + } else { + vals[n / n] + } +} + impl View for MadScaler where V: View, @@ -277,9 +285,15 @@ mod tests { fn mad_scaled(vals: &mut [f64], buf: &mut [f64], current: f64) -> f64 { assert_eq!(vals.len(), buf.len()); + assert_ne!(*vals.last().unwrap(), current); + let m = median(vals); - buf.iter_mut().for_each(|v| *v = (*v - m).abs()); + dbg!(&m); + buf.iter_mut() + .zip(vals) + .for_each(|(b, v)| *b = (*v - m).abs()); let mad = median(buf); + dbg!(&mad); (current - m) / mad } @@ -288,7 +302,7 @@ mod tests { const WINDOW_LEN: usize = 5; let r = romu::Rng::from_seed_with_64bit(0); - let vals = Vec::from_iter((0..100).map(|_| r.f64())); + let vals = Vec::from_iter((0..25).map(|_| r.f64())); dbg!(&vals); let mut buf = vec![0.0; WINDOW_LEN]; @@ -301,12 +315,14 @@ mod tests { } for (i, v) in vals.iter().enumerate().skip(WINDOW_LEN + 1) { + ms.update(*v); dbg!(&v); dbg!(&ms); - ms.update(*v); let start = i - WINDOW_LEN - 1; let end = i - 1; - let expected = mad_scaled(&mut vals[start..end].to_vec(), &mut buf, *v); + let mut window = vals[start..end].to_vec(); + assert_eq!(window.len(), WINDOW_LEN); + let expected = mad_scaled(&mut window, &mut buf, *v); assert_approx_eq!(ms.last().expect("is warm"), expected); } } From 133f71930d4ceb1d215df0054dbac6e97170d064 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 12:22:54 +0100 Subject: [PATCH 10/14] mad_scaler: fix tests. --- Cargo.toml | 2 +- src/sliding_windows/mad_scaler.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2fae595..346512f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["edition2024"] [package] name = "sliding_features" -version = "9.0.0" +version = "9.1.0" authors = ["MathisWellmann "] edition = "2024" license-file = "LICENSE" diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index 98b4160..2927348 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -138,7 +138,7 @@ fn median_from_sorted(vals: &[F]) -> F { let b = vals[n / 2]; (a + b) / (F::one() + F::one()) } else { - vals[n / n] + vals[n / 2] } } @@ -294,7 +294,7 @@ mod tests { .for_each(|(b, v)| *b = (*v - m).abs()); let mad = median(buf); dbg!(&mad); - (current - m) / mad + (current - m) / (MAD_SCALE * mad) } #[test] @@ -314,12 +314,12 @@ mod tests { assert!(ms.last().is_none()) } - for (i, v) in vals.iter().enumerate().skip(WINDOW_LEN + 1) { + for (i, v) in vals.iter().enumerate().skip(WINDOW_LEN) { ms.update(*v); dbg!(&v); dbg!(&ms); - let start = i - WINDOW_LEN - 1; - let end = i - 1; + let start = i - WINDOW_LEN; + let end = i; let mut window = vals[start..end].to_vec(); assert_eq!(window.len(), WINDOW_LEN); let expected = mad_scaled(&mut window, &mut buf, *v); From 5d14771ec2a8812675b812dbbaf3eda08068641d Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 12:33:56 +0100 Subject: [PATCH 11/14] CI: include `yamlfmt` step. --- .github/workflows/ci.yml | 57 +++++++++++++--------------------------- flake.nix | 1 + 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fa6725..3389002 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,64 +1,43 @@ name: "Rust checks" - on: pull_request: - branches: [ main ] - + branches: [main] permissions: contents: read actions: read - +env: + RUSTFLAGS: "-D warnings" # Make all warnings deny. jobs: check: # Target self-hosted runner by label - runs-on: [ nixos ] - + runs-on: [nixos] # SECURITY: Require manual approval for external PRs if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - steps: - name: checkout-code uses: actions/checkout@v4 - + - name: nix-flake-check + run: nix flake check + - name: yamlfmt + run: niix develop --command yamlfmt -lint . + - name: nix-deadnix + run: nix develop --command deadnix + - name: nix-statix + run: nix develop --command statix check + - name: nix-alejandra + run: nix develop --command alejandra --check . + - name: taplo-fmt + run: nix develop --command taplo fmt --check + - name: cargo-fmt + run: nix develop --command cargo fmt --check - name: cargo-check - env: - RUSTFLAGS: "-D warnings" # Make all warnings deny. run: nix develop --command cargo check - - name: cargo-test - env: - RUSTFLAGS: "-D warnings" # Make all warnings deny. run: nix develop --command cargo test - # TODO: make clippy work # - name: cargo-clippy - # env: - # RUSTFLAGS: "-D warnings" # Make all warnings deny. # run: nix develop --command cargo clippy - - name: cargo-bench run: nix develop --command cargo bench --no-run # Just to make sure it compiles - - - name: cargo-fmt - run: nix develop --command cargo fmt --check - - name: cargo-doc run: nix develop --command cargo doc - - - name: nix-flake-check - run: nix flake check - - - name: nix-deadnix - run: nix develop --command deadnix - - - name: nix-statix - run: nix develop --command statix check - - - name: nix-alejandra - run: nix develop --command alejandra --check . - - # TODO: enable hongdown - # - name: markdown-formatting - # run: nix develop --command hongdown --check . - - diff --git a/flake.nix b/flake.nix index 76664d7..924e4d1 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ cmake ]; tools = with pkgs; [ + yamlfmt taplo cargo-semver-checks cargo_upgrades From 89d988ed945f8d648e592f5c2fff3bc063921b3a Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 12:33:56 +0100 Subject: [PATCH 12/14] deps: bump `rand` to `0.10` mad_scaler --- .github/workflows/ci.yml | 2 +- Cargo.lock | 484 ++++++++++++++++++------ Cargo.toml | 2 +- benches/alma.rs | 2 +- benches/center_of_gravity.rs | 2 +- benches/correlation_trend_indicator.rs | 2 +- benches/cumulative.rs | 2 +- benches/cyber_cycle.rs | 2 +- benches/ehlers_fisher_transform.rs | 2 +- benches/ema.rs | 2 +- benches/hl_normalizer.rs | 2 +- benches/laguerre_filter.rs | 2 +- benches/laguerre_rsi.rs | 2 +- benches/mad_scaler.rs | 2 +- benches/my_rsi.rs | 2 +- benches/noise_elimination_technology.rs | 2 +- benches/polarized_fractal_efficiency.rs | 2 +- benches/re_flex.rs | 2 +- benches/roc.rs | 2 +- benches/roofing_filter.rs | 2 +- benches/rsi.rs | 2 +- benches/sma.rs | 2 +- benches/super_smoother.rs | 2 +- benches/trend_flex.rs | 2 +- benches/vsct.rs | 2 +- benches/vst.rs | 2 +- benches/welford_online.rs | 2 +- src/sliding_windows/alma.rs | 2 +- src/sliding_windows/laguerre_filter.rs | 2 +- src/sliding_windows/mad_scaler.rs | 2 +- src/sliding_windows/sma.rs | 2 +- 31 files changed, 405 insertions(+), 139 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3389002..f244af8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: nix-flake-check run: nix flake check - name: yamlfmt - run: niix develop --command yamlfmt -lint . + run: nix develop --command yamlfmt -lint . - name: nix-deadnix run: nix develop --command deadnix - name: nix-statix diff --git a/Cargo.lock b/Cargo.lock index 61c3aa5..4bff427 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,15 +43,21 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "ballpark" @@ -67,15 +73,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" [[package]] name = "bumpalo" -version = "3.20.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c81d250916401487680ed13b8b675660281dcfc3ab0121fe44c94bcab9eae2fb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytemuck" @@ -97,9 +103,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.56" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "shlex", @@ -111,11 +117,22 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -153,18 +170,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.59" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.59" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstyle", "clap_lex", @@ -172,9 +189,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "color_quant" @@ -234,6 +251,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -332,9 +358,9 @@ dependencies = [ [[package]] name = "dlib" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ "libloading", ] @@ -353,9 +379,15 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fdeflate" @@ -388,13 +420,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "font-kit" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.12.1", "byteorder", "core-foundation", "core-graphics", @@ -451,6 +489,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -470,8 +532,22 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", + "wasip3", ] [[package]] @@ -507,6 +583,27 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -531,6 +628,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "image" version = "0.24.9" @@ -545,6 +648,18 @@ dependencies = [ "png", ] +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + [[package]] name = "itertools" version = "0.13.0" @@ -556,9 +671,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jpeg-decoder" @@ -568,10 +683,12 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -582,11 +699,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.182" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -606,25 +729,24 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.11.0", "libc", ] [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "miniz_oxide" @@ -712,9 +834,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" @@ -761,18 +883,24 @@ dependencies = [ [[package]] name = "pathfinder_simd" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" dependencies = [ "rustc_version", ] +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plotters" @@ -834,12 +962,13 @@ dependencies = [ ] [[package]] -name = "ppv-lite86" -version = "0.2.21" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "zerocopy", + "proc-macro2", + "syn", ] [[package]] @@ -875,9 +1004,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -888,6 +1017,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.8.6" @@ -900,22 +1035,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -929,28 +1055,25 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.5" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_distr" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +checksum = "4d431c2703ccf129de4d45253c03f49ebb22b97d6ad79ee3ecfc7e3f4862c1d8" dependencies = [ "num-traits", - "rand 0.9.2", + "rand 0.10.1", ] [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -1002,9 +1125,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "romu" @@ -1047,9 +1170,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1083,9 +1206,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -1096,26 +1219,32 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "sliding_features" -version = "9.0.0" +version = "9.1.0" dependencies = [ "ballpark", "criterion", "getset", "num", "plotters", - "rand 0.9.2", + "rand 0.10.1", "romu", "round", "time_series_generator", @@ -1124,9 +1253,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.116" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1155,12 +1284,12 @@ dependencies = [ [[package]] name = "time_series_generator" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3098b86531bc0f07e6455f381717a3e1f48e457d8d03cb01a284abc40617964b" +checksum = "d0882209b46f9f4a88a984a9445325665877167f509363feed83a0b2e0cc51bf" dependencies = [ "num-traits", - "rand 0.9.2", + "rand 0.10.1", "rand_distr", ] @@ -1186,6 +1315,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "walkdir" version = "2.5.0" @@ -1204,18 +1339,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -1226,9 +1370,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1236,9 +1380,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -1249,13 +1393,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.12.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "watermill" version = "0.1.2" @@ -1270,9 +1448,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -1397,12 +1575,100 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.12.1", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "yeslogic-fontconfig-sys" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503a066b4c037c440169d995b869046827dbc71263f6e8f3be6d77d4f3229dbd" +checksum = "1d8b8abf912b9a29ff112e1671c97c33636903d13a69712037190e6805af4f76" dependencies = [ "dlib", "once_cell", @@ -1411,18 +1677,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 346512f..475b5e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ watermill = "0.1.2" [dev-dependencies] ballpark = "1" time_series_generator = "0.4.1" -rand = "0.9" +rand = "0.10" romu = "0.8" round = "0.1" plotters = "0.3" diff --git a/benches/alma.rs b/benches/alma.rs index 7469088..16e74eb 100644 --- a/benches/alma.rs +++ b/benches/alma.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/center_of_gravity.rs b/benches/center_of_gravity.rs index c39aafc..61c604e 100644 --- a/benches/center_of_gravity.rs +++ b/benches/center_of_gravity.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/correlation_trend_indicator.rs b/benches/correlation_trend_indicator.rs index 981949c..15e1572 100644 --- a/benches/correlation_trend_indicator.rs +++ b/benches/correlation_trend_indicator.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/cumulative.rs b/benches/cumulative.rs index f97ad42..e77d94a 100644 --- a/benches/cumulative.rs +++ b/benches/cumulative.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/cyber_cycle.rs b/benches/cyber_cycle.rs index 9a28a29..073de48 100644 --- a/benches/cyber_cycle.rs +++ b/benches/cyber_cycle.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/ehlers_fisher_transform.rs b/benches/ehlers_fisher_transform.rs index 4106ca5..94eeb4c 100644 --- a/benches/ehlers_fisher_transform.rs +++ b/benches/ehlers_fisher_transform.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, SeedableRng, rng, rngs::SmallRng, diff --git a/benches/ema.rs b/benches/ema.rs index 1f16b85..f4d5c8c 100644 --- a/benches/ema.rs +++ b/benches/ema.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/hl_normalizer.rs b/benches/hl_normalizer.rs index 53eb746..0527287 100644 --- a/benches/hl_normalizer.rs +++ b/benches/hl_normalizer.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/laguerre_filter.rs b/benches/laguerre_filter.rs index 700b885..d8a95ed 100644 --- a/benches/laguerre_filter.rs +++ b/benches/laguerre_filter.rs @@ -6,7 +6,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/laguerre_rsi.rs b/benches/laguerre_rsi.rs index f46fd7f..e01d873 100644 --- a/benches/laguerre_rsi.rs +++ b/benches/laguerre_rsi.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/mad_scaler.rs b/benches/mad_scaler.rs index eb0a02a..f81895d 100644 --- a/benches/mad_scaler.rs +++ b/benches/mad_scaler.rs @@ -10,7 +10,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, SeedableRng, rngs::SmallRng, }; diff --git a/benches/my_rsi.rs b/benches/my_rsi.rs index 66c8c09..81fed2f 100644 --- a/benches/my_rsi.rs +++ b/benches/my_rsi.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/noise_elimination_technology.rs b/benches/noise_elimination_technology.rs index 896f1e0..5af7f46 100644 --- a/benches/noise_elimination_technology.rs +++ b/benches/noise_elimination_technology.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/polarized_fractal_efficiency.rs b/benches/polarized_fractal_efficiency.rs index abc8831..53278b2 100644 --- a/benches/polarized_fractal_efficiency.rs +++ b/benches/polarized_fractal_efficiency.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/re_flex.rs b/benches/re_flex.rs index 0a4f78c..44582cb 100644 --- a/benches/re_flex.rs +++ b/benches/re_flex.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/roc.rs b/benches/roc.rs index 2a66d7f..de59ffb 100644 --- a/benches/roc.rs +++ b/benches/roc.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/roofing_filter.rs b/benches/roofing_filter.rs index 1dfde6f..9f44432 100644 --- a/benches/roofing_filter.rs +++ b/benches/roofing_filter.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/rsi.rs b/benches/rsi.rs index 0d5d629..4c6d0b4 100644 --- a/benches/rsi.rs +++ b/benches/rsi.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/sma.rs b/benches/sma.rs index 9a075f9..6062a40 100644 --- a/benches/sma.rs +++ b/benches/sma.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/super_smoother.rs b/benches/super_smoother.rs index f236ab3..d3a3857 100644 --- a/benches/super_smoother.rs +++ b/benches/super_smoother.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/trend_flex.rs b/benches/trend_flex.rs index 22583ea..802f530 100644 --- a/benches/trend_flex.rs +++ b/benches/trend_flex.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/vsct.rs b/benches/vsct.rs index d66b239..8229d88 100644 --- a/benches/vsct.rs +++ b/benches/vsct.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/vst.rs b/benches/vst.rs index 8e797fb..0a3fc23 100644 --- a/benches/vst.rs +++ b/benches/vst.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/benches/welford_online.rs b/benches/welford_online.rs index a7dff41..ee051b2 100644 --- a/benches/welford_online.rs +++ b/benches/welford_online.rs @@ -9,7 +9,7 @@ use criterion::{ criterion_main, }; use rand::{ - Rng, + RngExt, rng, }; use sliding_features::{ diff --git a/src/sliding_windows/alma.rs b/src/sliding_windows/alma.rs index 8b1f694..3eb3451 100644 --- a/src/sliding_windows/alma.rs +++ b/src/sliding_windows/alma.rs @@ -108,7 +108,7 @@ where #[cfg(test)] mod tests { use rand::{ - Rng, + RngExt, rng, }; diff --git a/src/sliding_windows/laguerre_filter.rs b/src/sliding_windows/laguerre_filter.rs index dbb73dd..3250d48 100644 --- a/src/sliding_windows/laguerre_filter.rs +++ b/src/sliding_windows/laguerre_filter.rs @@ -100,7 +100,7 @@ where #[cfg(test)] mod tests { use rand::{ - Rng, + RngExt, rng, }; diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index 2927348..ced1c8e 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -133,7 +133,7 @@ where #[inline(always)] fn median_from_sorted(vals: &[F]) -> F { let n = vals.len(); - if n.is_power_of_two() { + if n % 2 == 0 { let a = vals[n / 2 - 1]; let b = vals[n / 2]; (a + b) / (F::one() + F::one()) diff --git a/src/sliding_windows/sma.rs b/src/sliding_windows/sma.rs index bb96967..63a9c40 100644 --- a/src/sliding_windows/sma.rs +++ b/src/sliding_windows/sma.rs @@ -72,7 +72,7 @@ where #[cfg(test)] mod tests { use rand::{ - Rng, + RngExt, rng, }; From 217e3b532e9652662ebd6713da9261c2852fd1c3 Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 14:22:57 +0100 Subject: [PATCH 13/14] benchmark for more window lengths in `mad_scaler.rs` --- benches/mad_scaler.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/benches/mad_scaler.rs b/benches/mad_scaler.rs index f81895d..1e00604 100644 --- a/benches/mad_scaler.rs +++ b/benches/mad_scaler.rs @@ -4,6 +4,7 @@ use std::{ }; use criterion::{ + BenchmarkId, Criterion, Throughput, criterion_group, @@ -21,7 +22,7 @@ use sliding_features::{ }; const N: usize = 100_000; -const WINDOW_LENS: &[usize] = &[1024, 8192]; +const WINDOW_LENS: &[usize] = &[128, 256, 512, 1024, 2048, 4092, 8192]; fn standard_normal(rng: &mut SmallRng) -> f64 { // Box-Muller transform. Clamp u1 away from zero so ln(u1) is finite. @@ -75,7 +76,7 @@ fn criterion_benchmark(c: &mut Criterion) { for &window_len in WINDOW_LENS { group.bench_function( - format!("realistic_market_prices_f64/window_len_{window_len}"), + BenchmarkId::new("realistic_market_prices_f64", window_len), |b| { let vals = realistic_market_prices_f64(); b.iter(|| { @@ -92,7 +93,7 @@ fn criterion_benchmark(c: &mut Criterion) { ); group.bench_function( - format!("realistic_market_prices_f32/window_len_{window_len}"), + BenchmarkId::new("realistic_market_prices_f32", window_len), |b| { let vals = realistic_market_prices_f32(); b.iter(|| { From f52c919377e64794bd1c33b4fe59e76e301845fc Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Thu, 4 Jun 2026 12:33:56 +0100 Subject: [PATCH 14/14] mad_scaler: use `O(w)` merge strategy improving on previous `O(w * log(w))` sorting based implementation. Runtime improvements of > 6%, up to +35% on 4096 window length. --- benches/mad_scaler.rs | 2 +- img/trend_flex.png | Bin 94131 -> 94131 bytes src/lib.rs | 6 +-- src/sliding_windows/mad_scaler.rs | 82 ++++++++++++++++++++++-------- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/benches/mad_scaler.rs b/benches/mad_scaler.rs index 1e00604..ad7502c 100644 --- a/benches/mad_scaler.rs +++ b/benches/mad_scaler.rs @@ -22,7 +22,7 @@ use sliding_features::{ }; const N: usize = 100_000; -const WINDOW_LENS: &[usize] = &[128, 256, 512, 1024, 2048, 4092, 8192]; +const WINDOW_LENS: &[usize] = &[128, 256, 512, 1024, 2048, 4096, 8192]; fn standard_normal(rng: &mut SmallRng) -> f64 { // Box-Muller transform. Clamp u1 away from zero so ln(u1) is finite. diff --git a/img/trend_flex.png b/img/trend_flex.png index 7ead302f2a3c778af8e9f9d3a7a236e6d7966e1e..e950d8d6e9d793e2840b154deb1d467202185f85 100644 GIT binary patch literal 94131 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 FGcOK&J5i$h$CFh{_0 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 H=g9s8K~xB^ literal 94131 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�!0iuu;OecaV{=?U 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 LB>E%n7}@^?c6@Ok diff --git a/src/lib.rs b/src/lib.rs index 549e8e1..618c5b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,4 @@ -#![deny( - missing_docs, - rustdoc::missing_crate_level_docs, - unused_crate_dependencies -)] +#![deny(missing_docs, rustdoc::missing_crate_level_docs)] #![warn(clippy::all)] #![doc = include_str!("../README.md")] diff --git a/src/sliding_windows/mad_scaler.rs b/src/sliding_windows/mad_scaler.rs index ced1c8e..eb249e9 100644 --- a/src/sliding_windows/mad_scaler.rs +++ b/src/sliding_windows/mad_scaler.rs @@ -48,8 +48,8 @@ pub struct MadScaler { out: Option, /// Running count of values pushed into the sorted window (capped at window_len). count: usize, - /// Buffer for `(x - m).abs()` computations to avoid allocation in the hot-path. - buf: Vec, + /// Pre-allocated buffer for merging absolute deviations in the hot-path. + mad_buf: Vec, } impl std::fmt::Debug for MadScaler @@ -87,7 +87,7 @@ where cached_mad: F::zero(), out: None, count: 0, - buf: vec![F::zero(); window_len.get()], + mad_buf: vec![F::zero(); window_len.get()], } } @@ -97,17 +97,20 @@ where self.window_len } - /// Recompute the cached median and MAD from the sorted window. + /// Recompute median and MAD from the sorted window. + /// + /// Because `self.sorted` is already sorted, the absolute deviations + /// `|s[i] - median|` form two monotonic sequences radiating outward + /// from the median: one increasing to the left, one to the right. + /// We merge these two "tails" in **O(w)** instead of paying the + /// **O(w log w)** cost of a full sort. fn recompute_cache(&mut self) { let n = self.sorted.len(); - if n == 0 { - self.cached_median = F::zero(); - self.cached_mad = F::zero(); - return; - } + debug_assert!(n > 0, "recompute_cache called on empty window"); + debug_assert_eq!(n, self.mad_buf.len()); - // --- median --- - let median = if n.is_power_of_two() { + // --- median (from already-sorted window) --- + let median = if n % 2 == 0 { let a = self.sorted[n / 2 - 1]; let b = self.sorted[n / 2]; (a + b) / (F::one() + F::one()) @@ -116,17 +119,56 @@ where }; self.cached_median = median; - // --- median absolute deviation --- - // Collect absolute deviations, sort them. - assert_eq!(n, self.buf.len()); - for (i, b) in self.buf.iter_mut().enumerate() { - let diff = (self.sorted[i] - median).abs(); - *b = diff; + // --- median absolute deviation via O(w) merge --- + // Walk the sorted window outward from the median, merging the + // monotonically-increasing left and right absolute deviations. + let mad_buf = &mut self.mad_buf; + let mid = n / 2; + let mut out_idx = 0usize; + + // For odd n, the median element itself has deviation 0 — emit it first. + if n % 2 == 1 { + mad_buf[0] = F::zero(); + out_idx = 1; + } + + // left walks down from mid-1 to 0, right walks up from mid (even) or mid+1 (odd). + let mut left: isize = mid.checked_sub(1).map_or(-1, |v| v as isize); + let mut right: usize = if n % 2 == 0 { mid } else { mid + 1 }; + + loop { + let l_valid = left >= 0; + let r_valid = right < n; + match (l_valid, r_valid) { + (true, true) => { + let l_abs = (self.sorted[left as usize] - median).abs(); + let r_abs = (self.sorted[right] - median).abs(); + if l_abs <= r_abs { + mad_buf[out_idx] = l_abs; + out_idx += 1; + left -= 1; + } else { + mad_buf[out_idx] = r_abs; + out_idx += 1; + right += 1; + } + } + (true, false) => { + mad_buf[out_idx] = (self.sorted[left as usize] - median).abs(); + out_idx += 1; + left -= 1; + } + (false, true) => { + mad_buf[out_idx] = (self.sorted[right] - median).abs(); + out_idx += 1; + right += 1; + } + (false, false) => break, + } } - self.buf.sort_by(|a, b| a.partial_cmp(b).unwrap()); + debug_assert_eq!(out_idx, n, "merge must fill entire buffer"); - let mad = median_from_sorted(&self.buf); - self.cached_mad = mad; + self.cached_mad = median_from_sorted(mad_buf); } }