rescale_core/drc/
max_hold.rs1pub struct SlidingMax {
12 window_len: usize,
13 values: Vec<f32>,
15 deque: Vec<usize>,
17 deque_head: usize,
18 deque_len: usize,
19 position: usize,
20}
21
22impl SlidingMax {
23 pub fn new(window_len: usize) -> Self {
25 assert!(window_len > 0, "window_len must be greater than 0");
26 Self {
27 window_len,
28 values: vec![0.0; window_len],
29 deque: vec![0; window_len],
30 deque_head: 0,
31 deque_len: 0,
32 position: 0,
33 }
34 }
35
36 pub fn push(&mut self, value: f32) {
38 let index = self.position;
39 self.position += 1;
40 self.values[index % self.window_len] = value;
41
42 self.evict_expired(index);
43 self.evict_smaller_from_back(value);
44
45 let tail = (self.deque_head + self.deque_len) % self.window_len;
46 self.deque[tail] = index;
47 self.deque_len += 1;
48 }
49
50 pub fn max(&self) -> f32 {
52 self.values[self.deque[self.deque_head] % self.window_len]
53 }
54
55 pub fn reset(&mut self) {
57 self.position = 0;
58 self.deque_head = 0;
59 self.deque_len = 0;
60 }
61
62 fn evict_expired(&mut self, newest_index: usize) {
64 while self.deque_len > 0 {
65 let front_index = self.deque[self.deque_head];
66 if newest_index - front_index < self.window_len {
67 break;
68 }
69 self.deque_head = (self.deque_head + 1) % self.window_len;
70 self.deque_len -= 1;
71 }
72 }
73
74 fn evict_smaller_from_back(&mut self, value: f32) {
76 while self.deque_len > 0 {
77 let back_index = self.deque[(self.deque_head + self.deque_len - 1) % self.window_len];
78 if self.values[back_index % self.window_len] > value {
79 break;
80 }
81 self.deque_len -= 1;
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 fn push_all(tracker: &mut SlidingMax, values: &[f32]) -> f32 {
91 for &value in values {
92 tracker.push(value);
93 }
94 tracker.max()
95 }
96
97 #[test]
98 fn tracks_maximum_inside_the_window() {
99 let mut tracker = SlidingMax::new(3);
100
101 assert!((push_all(&mut tracker, &[1.0, 3.0, 2.0]) - 3.0).abs() < 1e-6);
102 tracker.push(0.5);
104 assert!((tracker.max() - 3.0).abs() < 1e-6);
105 tracker.push(0.1);
107 assert!((tracker.max() - 2.0).abs() < 1e-6);
108 tracker.push(0.0);
110 assert!((tracker.max() - 0.5).abs() < 1e-6);
111 tracker.push(0.0);
113 assert!((tracker.max() - 0.1).abs() < 1e-6);
114 }
115
116 #[test]
117 fn maximum_matches_brute_force_for_random_sequence() {
118 let mut tracker = SlidingMax::new(5);
119 let values: Vec<f32> = (0..200)
120 .map(|index| ((index * 37 % 101) as f32 / 101.0).abs())
121 .collect();
122
123 for index in 0..values.len() {
124 tracker.push(values[index]);
125 let window_start = (index + 1).saturating_sub(5);
126 let expected = values[window_start..=index]
127 .iter()
128 .fold(0.0_f32, |acc, &value| acc.max(value));
129 assert!(
130 (tracker.max() - expected).abs() < 1e-6,
131 "index {index}: {} vs {expected}",
132 tracker.max()
133 );
134 }
135 }
136
137 #[test]
138 fn reset_restarts_the_window() {
139 let mut tracker = SlidingMax::new(2);
140 tracker.push(1.0);
141 tracker.push(0.0);
142 tracker.reset();
143 tracker.push(0.25);
144 assert!((tracker.max() - 0.25).abs() < 1e-6);
145 }
146}