Skip to content

Commit 320c570

Browse files
committed
add tests for low-pass-filter
1 parent 76856ac commit 320c570

File tree

1 file changed

+272
-1
lines changed

1 file changed

+272
-1
lines changed

low-pass-filter/src/lib.rs

Lines changed: 272 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod api {
1919
// 2 * pi * SCALE
2020
const TWO_PI_SCALED: u64 = 6_283_185;
2121

22+
#[derive(Clone)]
2223
pub struct FilterConfig {
2324
pub output_range: std::ops::Range<u64>,
2425
pub k: u64,
@@ -71,6 +72,8 @@ pub mod api {
7172

7273
/// Approximates `base^alpha` rounded to nearest integer using
7374
/// integer-only linear interpolation between `base^1` and `base^2`.
75+
///
76+
/// Note: This function is most accurate when `base` is small e.g. < ~25.
7477
#[inline]
7578
pub fn interpolate(base: u64, t: u64) -> u64 {
7679
let scale = SCALE.get();
@@ -84,4 +87,272 @@ pub mod api {
8487
}
8588
}
8689

87-
//TODO: greg: add tests for this file
90+
#[cfg(test)]
91+
mod tests {
92+
use super::api::*;
93+
94+
#[test]
95+
fn test_compute_k_zero_tc() {
96+
// When time constant is 0, K should be 0
97+
assert_eq!(compute_k(100, 0), 0);
98+
assert_eq!(compute_k(1000, 0), 0);
99+
assert_eq!(compute_k(u64::MAX, 0), 0);
100+
}
101+
102+
#[test]
103+
fn test_compute_k_zero_fs() {
104+
// When sample frequency is 0, K should be 0
105+
assert_eq!(compute_k(0, 100), 0);
106+
assert_eq!(compute_k(0, 1000), 0);
107+
assert_eq!(compute_k(0, u64::MAX), 0);
108+
}
109+
110+
#[test]
111+
fn test_compute_k_large_values() {
112+
// K should never exceed SCALE
113+
let k = compute_k(u64::MAX, 1);
114+
assert!(k <= SCALE.get());
115+
116+
let k = compute_k(1000000, 1);
117+
assert!(k <= SCALE.get());
118+
119+
let k = compute_k(u64::MAX / 2, u64::MAX / 4);
120+
assert!(k <= SCALE.get());
121+
122+
let k = compute_k(500000000, 1000000000);
123+
assert!(k <= SCALE.get());
124+
}
125+
126+
#[test]
127+
fn test_compute_k_normal_cases() {
128+
// Test some normal cases
129+
let k1 = compute_k(100, 1000);
130+
assert_eq!(k1, 385869);
131+
132+
let k2 = compute_k(1000, 100);
133+
assert_eq!(k2, 984333);
134+
assert!(k2 > k1);
135+
136+
let k3 = compute_k(1000, 1000);
137+
assert_eq!(k3, 862697);
138+
}
139+
140+
#[test]
141+
fn test_filter_alpha_k_zero() {
142+
// When K=0, output should equal previous value
143+
let config = FilterConfig {
144+
output_range: 0..1000000,
145+
k: 0,
146+
};
147+
148+
assert_eq!(filter_alpha(100, 500, config.clone()), 100);
149+
assert_eq!(filter_alpha(0, 1000000, config.clone()), 0);
150+
assert_eq!(filter_alpha(999999, 0, config), 999999);
151+
}
152+
153+
#[test]
154+
fn test_filter_alpha_k_max() {
155+
// When K=SCALE, output should equal target value (clamped to range)
156+
let config = FilterConfig {
157+
output_range: 0..1000000,
158+
k: SCALE.get(),
159+
};
160+
161+
assert_eq!(filter_alpha(100, 500, config.clone()), 500);
162+
assert_eq!(filter_alpha(0, 1000000, config), 1000000);
163+
164+
// Test clamping - target outside range
165+
let config = FilterConfig {
166+
output_range: 100..900,
167+
k: SCALE.get(),
168+
};
169+
assert_eq!(filter_alpha(200, 50, config.clone()), 100);
170+
assert_eq!(filter_alpha(200, 1000, config), 900);
171+
}
172+
173+
#[test]
174+
fn test_filter_alpha_clamping() {
175+
// Test output range clamping
176+
let config = FilterConfig {
177+
output_range: 100..900,
178+
k: SCALE.get() / 2,
179+
};
180+
181+
// Result would be (500000 * 50 + 500000 * 950) / 1000000 = 500
182+
// This should be within range
183+
let result = filter_alpha(950, 50, config);
184+
assert_eq!(result, 500);
185+
186+
// Test extreme clamping
187+
let config_narrow = FilterConfig {
188+
output_range: 500..501,
189+
k: SCALE.get() / 4,
190+
};
191+
let result = filter_alpha(0, 1000000, config_narrow);
192+
assert_eq!(result, 501);
193+
}
194+
195+
#[test]
196+
fn test_filter_alpha_overflow_protection() {
197+
// Test with large values that might cause overflow
198+
let config = FilterConfig {
199+
output_range: 0..u64::MAX,
200+
k: SCALE.get() / 2,
201+
};
202+
203+
let result = filter_alpha(u64::MAX / 2, u64::MAX / 2, config.clone());
204+
assert_eq!(result, 18446744073709);
205+
206+
let result2 = filter_alpha(u64::MAX - 1000, u64::MAX - 2000, config);
207+
assert_eq!(result2, 18446744073709);
208+
}
209+
210+
#[test]
211+
fn test_filter_alpha_mathematical_correctness() {
212+
// Test the formula: (k * target + (scale - k) * prev) / scale
213+
let config = FilterConfig {
214+
output_range: 0..u64::MAX,
215+
k: SCALE.get() / 4, // 25%
216+
};
217+
218+
let prev = 800;
219+
let target = 400;
220+
let result = filter_alpha(prev, target, config);
221+
222+
assert_eq!(result, 700);
223+
224+
// Test with k at 60%
225+
let config = FilterConfig {
226+
output_range: 0..u64::MAX,
227+
k: SCALE.get() * 60 / 100, // 60%
228+
};
229+
230+
let prev = 111111;
231+
let target = 222222;
232+
let result = filter_alpha(prev, target, config);
233+
234+
assert_eq!(result, 177777);
235+
}
236+
237+
#[test]
238+
fn test_interpolate_t_zero() {
239+
// When t=0, should return base
240+
assert_eq!(interpolate(100, 0), 100);
241+
assert_eq!(interpolate(0, 0), 0);
242+
assert_eq!(interpolate(1000000, 0), 1000000);
243+
}
244+
245+
#[test]
246+
fn test_interpolate_t_max() {
247+
// When t=SCALE, should return base^2
248+
let base = 100;
249+
let result = interpolate(base, SCALE.get());
250+
assert_eq!(result, base * base);
251+
252+
let base2 = 1000;
253+
let result = interpolate(base2, SCALE.get());
254+
assert_eq!(result, base2 * base2);
255+
}
256+
257+
#[test]
258+
fn test_interpolate_values() {
259+
let t_10 = SCALE.get() / 10; // 10%
260+
let t_50 = SCALE.get() / 2; // 50%
261+
let t_75 = SCALE.get() * 3 / 4; // 75%
262+
263+
let base = 3;
264+
let result = interpolate(base, t_10);
265+
assert_eq!(result, 4);
266+
267+
let result = interpolate(base, t_50);
268+
assert_eq!(result, 6);
269+
270+
let result = interpolate(base, t_75);
271+
assert_eq!(result, 8);
272+
273+
let base = 15;
274+
let result = interpolate(base, t_10);
275+
assert_eq!(result, 36);
276+
277+
let result = interpolate(base, t_50);
278+
assert_eq!(result, 120);
279+
280+
let result = interpolate(base, t_75);
281+
assert_eq!(result, 173);
282+
283+
let base = 24;
284+
let result = interpolate(base, t_10);
285+
assert_eq!(result, 79);
286+
287+
let result = interpolate(base, t_50);
288+
assert_eq!(result, 300);
289+
290+
let result = interpolate(base, t_75);
291+
assert_eq!(result, 438);
292+
}
293+
294+
#[test]
295+
fn test_interpolate_large_base() {
296+
let base = 1000000000000000000;
297+
let result = interpolate(base, SCALE.get() / 2);
298+
assert!(result >= base);
299+
assert!(result < base * base);
300+
}
301+
302+
#[test]
303+
fn test_interpolate_edge_cases() {
304+
// Test with base = 1
305+
assert_eq!(interpolate(1, 0), 1);
306+
assert_eq!(interpolate(1, SCALE.get()), 1);
307+
assert_eq!(interpolate(1, SCALE.get() / 2), 1);
308+
309+
// Test with base = 0
310+
assert_eq!(interpolate(0, 0), 0);
311+
assert_eq!(interpolate(0, SCALE.get()), 0);
312+
assert_eq!(interpolate(0, SCALE.get() / 2), 0);
313+
}
314+
315+
#[test]
316+
fn test_interpolate_rounding() {
317+
// Test rounding behavior with values that result in fractional parts
318+
let base = 3;
319+
let t = SCALE.get() / 3;
320+
let result = interpolate(base, t);
321+
322+
assert!(result >= 3);
323+
assert!(result <= 9);
324+
}
325+
326+
#[test]
327+
fn test_scale_constant() {
328+
assert_eq!(SCALE.get(), 1_000_000);
329+
assert!(SCALE.get() > 0);
330+
}
331+
332+
#[test]
333+
fn test_integration_filter_and_interpolate() {
334+
// Test using filtered alpha with interpolate
335+
// Alpha range is [SCALE, 2*SCALE] as used in push_active_set
336+
let alpha_min = SCALE.get();
337+
let alpha_max = 2 * SCALE.get();
338+
339+
let config = FilterConfig {
340+
output_range: alpha_min..alpha_max,
341+
k: SCALE.get() / 10, // 10%
342+
};
343+
344+
let prev_alpha = alpha_min + SCALE.get() / 4; // 1.25 * SCALE
345+
let target_alpha = alpha_min + SCALE.get() / 2; // 1.5 * SCALE
346+
let filtered_alpha = filter_alpha(prev_alpha, target_alpha, config);
347+
348+
let t = filtered_alpha.saturating_sub(alpha_min);
349+
let base = 2;
350+
let result = interpolate(base, t);
351+
352+
assert!(result >= base);
353+
assert!(result <= base * base);
354+
355+
assert!(filtered_alpha >= alpha_min);
356+
assert!(filtered_alpha <= alpha_max);
357+
}
358+
}

0 commit comments

Comments
 (0)