@@ -19,6 +19,7 @@ pub mod api {
19
19
// 2 * pi * SCALE
20
20
const TWO_PI_SCALED : u64 = 6_283_185 ;
21
21
22
+ #[ derive( Clone ) ]
22
23
pub struct FilterConfig {
23
24
pub output_range : std:: ops:: Range < u64 > ,
24
25
pub k : u64 ,
@@ -71,6 +72,8 @@ pub mod api {
71
72
72
73
/// Approximates `base^alpha` rounded to nearest integer using
73
74
/// 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.
74
77
#[ inline]
75
78
pub fn interpolate ( base : u64 , t : u64 ) -> u64 {
76
79
let scale = SCALE . get ( ) ;
@@ -84,4 +87,272 @@ pub mod api {
84
87
}
85
88
}
86
89
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