1use std::{collections::VecDeque, num::NonZeroUsize};
2
3#[derive(Debug)]
6pub struct RollingMax<T> {
7 deq: VecDeque<T>,
11 expires: VecDeque<usize>,
12
13 ct: usize,
14 cap: usize,
15}
16
17impl<T> RollingMax<T>
18where
19 T: PartialOrd,
20{
21 #[must_use]
22 pub fn new(capacity: NonZeroUsize) -> Self {
23 let cap: usize = capacity.into();
24 Self {
25 deq: VecDeque::with_capacity(cap),
26 expires: VecDeque::with_capacity(cap),
27 cap,
28 ct: 0,
29 }
30 }
31
32 pub fn push(&mut self, entry: T) {
33 self.ct = self.ct.wrapping_add(1);
34
35 while self
36 .expires
37 .front()
38 .is_some_and(|&exp| self.ct.wrapping_sub(exp) <= self.cap)
39 {
40 self.deq.pop_front();
41 self.expires.pop_front();
42 }
43
44 while self.deq.back().is_some_and(|tail| tail <= &entry) {
45 self.deq.pop_back();
46 self.expires.pop_back();
47 }
48
49 self.deq.push_back(entry);
50 self.expires.push_back(self.ct.wrapping_add(self.cap));
51 }
52
53 #[must_use]
54 pub fn max(&self) -> Option<&T> {
55 self.deq.front()
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 fn nz(n: usize) -> NonZeroUsize {
64 NonZeroUsize::new(n).unwrap()
65 }
66
67 fn maxes<T: PartialOrd + Copy>(vals: &[T], cap: usize) -> Vec<T> {
69 let mut rm = RollingMax::new(nz(cap));
70 vals.iter()
71 .map(|&v| {
72 rm.push(v);
73 *rm.max().unwrap()
74 })
75 .collect()
76 }
77
78 #[test]
80 fn max_on_empty_is_none() {
81 let rm: RollingMax<i32> = RollingMax::new(nz(3));
82 assert_eq!(rm.max(), None);
83 }
84
85 #[test]
87 fn single_push_yields_some() {
88 let mut rm = RollingMax::new(nz(5));
89 rm.push(42i32);
90 assert_eq!(rm.max(), Some(&42));
91 }
92
93 #[test]
96 fn window_of_one() {
97 assert_eq!(maxes(&[3, 1, 4, 1, 5], 1), vec![3, 1, 4, 1, 5]);
98 }
99
100 #[test]
103 fn window_larger_than_input() {
104 assert_eq!(maxes(&[2, 4, 1], 10), vec![2, 4, 4]);
105 }
106
107 #[test]
110 fn window_equals_input_length() {
111 assert_eq!(maxes(&[1, 3, 2, 5, 4], 5), vec![1, 3, 3, 5, 5]);
112 }
113
114 #[test]
117 fn sliding_window_canonical() {
118 assert_eq!(maxes(&[1, 3, 1, 2, 0, 5], 3), vec![1, 3, 3, 3, 2, 5]);
119 }
120
121 #[test]
124 fn strictly_increasing() {
125 assert_eq!(maxes(&[1, 2, 3, 4, 5], 3), vec![1, 2, 3, 4, 5]);
126 }
127
128 #[test]
131 fn strictly_decreasing() {
132 assert_eq!(maxes(&[5, 4, 3, 2, 1], 3), vec![5, 5, 5, 4, 3]);
133 }
134
135 #[test]
138 fn all_equal() {
139 assert_eq!(maxes(&[7i32; 6], 3), vec![7; 6]);
140 }
141
142 #[test]
144 fn negative_values() {
145 assert_eq!(maxes(&[-3, -1, -4, -1, -5], 2), vec![-3, -1, -1, -1, -1]);
146 }
147
148 #[test]
150 fn float_values() {
151 assert_eq!(
152 maxes(&[1.0f64, 3.0, 2.0, 5.0, 4.0], 2),
153 vec![1.0, 3.0, 3.0, 5.0, 5.0]
154 );
155 }
156
157 #[test]
160 fn max_expires_at_exact_boundary() {
161 let mut rm = RollingMax::new(nz(3));
162 rm.push(99i32);
163 rm.push(1);
164 rm.push(1);
165 assert_eq!(rm.max(), Some(&99)); rm.push(1);
167 assert_eq!(rm.max(), Some(&1)); }
169
170 #[test]
174 fn expiry_counter_wrapping() {
175 let cap = 3;
176 let mut rm = RollingMax {
177 deq: VecDeque::with_capacity(cap),
178 expires: VecDeque::with_capacity(cap),
179 cap: 3,
180 ct: usize::MAX - 3,
181 };
182
183 rm.push(10); rm.push(5); rm.push(8); assert_eq!(rm.max(), Some(&10)); rm.push(6); assert_eq!(rm.max(), Some(&8));
190
191 rm.push(7); assert_eq!(rm.max(), Some(&8));
193
194 rm.push(9); assert_eq!(rm.max(), Some(&9));
196 }
197}