code wiki / (root) / sketch_ewma_test.nx

sketch_ewma_test.nx source

↩ module page · 97 lines · 3376 B

1// sketch_ewma_test.nx -- EWMA streaming smoother verification. 2 3import "syscalls.nx" 4import "sketch_ewma.nx" 5import "sketch_types.nx" 6 7func iabs(x: i64) -> i64 { 8 if x < 0 { return -x } 9 return x 10} 11 12func main() -> i64 { 13 // ---- alloc + empty ---- 14 let e: *Ewma = nx_ewma_alloc(100000) // alpha=0.1 15 if e == (0 as *Ewma) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 16 if e.has_data != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 17 if nx_ewma_value(e) != 0 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 18 // Reject alpha out of range. 19 if nx_ewma_alloc(0) != (0 as *Ewma) { return __syscall(93, 8, 0, 0, 0, 0, 0) } 20 if nx_ewma_alloc(1000001) != (0 as *Ewma) { return __syscall(93, 9, 0, 0, 0, 0, 0) } 21 22 // ---- first sample warms up to that value ---- 23 nx_ewma_add(e, 100) 24 if nx_ewma_value(e) != 100 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 25 if nx_ewma_count(e) != 1 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 26 27 // ---- second sample: x_new = 0.1*200 + 0.9*100 = 110 ---- 28 nx_ewma_add(e, 200) 29 if nx_ewma_value(e) != 110 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 30 if nx_ewma_count(e) != 2 { return __syscall(93, 21, 0, 0, 0, 0, 0) } 31 32 // ---- converge toward target ---- 33 // Continue feeding 200; EWMA should approach 200. 34 var i: i64 = 0 35 while i < 100 { 36 nx_ewma_add(e, 200) 37 i = i + 1 38 } 39 // After ~100 steps with alpha=0.1, value should be very close to 200. 40 let v: i64 = nx_ewma_value(e) 41 if iabs(v - 200) > 5 { 42 return __syscall(93, 30, 0, 0, 0, 0, 0) 43 } 44 45 // ---- spike rejection: a single outlier moves value only by alpha ---- 46 let e2: *Ewma = nx_ewma_alloc(10000) // alpha=0.01 (heavy smoothing) 47 nx_ewma_add(e2, 1000) 48 nx_ewma_add(e2, 100000) // huge spike 49 // x_new = 0.01 * 100000 + 0.99 * 1000 = 1000 + 990 = 1990. 50 let v2: i64 = nx_ewma_value(e2) 51 if iabs(v2 - 1990) > 1 { 52 return __syscall(93, 40, 0, 0, 0, 0, 0) 53 } 54 55 // ---- effective window ---- 56 if nx_ewma_effective_window(e) != 10 { // 1e6 / 100000 = 10 57 return __syscall(93, 50, 0, 0, 0, 0, 0) 58 } 59 if nx_ewma_effective_window(e2) != 100 { // 1e6 / 10000 = 100 60 return __syscall(93, 51, 0, 0, 0, 0, 0) 61 } 62 63 // ---- alpha=1.0 (no smoothing): value tracks current sample ---- 64 let e3: *Ewma = nx_ewma_alloc(1000000) 65 nx_ewma_add(e3, 42) 66 nx_ewma_add(e3, 99) 67 nx_ewma_add(e3, 17) 68 if nx_ewma_value(e3) != 17 { 69 return __syscall(93, 60, 0, 0, 0, 0, 0) 70 } 71 72 // ---- typed envelope ---- 73 let q: *ApproxI64 = nx_ewma_query(e) 74 if q.envelope_kind != NX_ENV_ABS { 75 return __syscall(93, 70, 0, 0, 0, 0, 0) 76 } 77 if q.param_a != 1 { 78 return __syscall(93, 71, 0, 0, 0, 0, 0) 79 } 80 if q.maturity != NX_MATURITY_PRODUCTION { 81 return __syscall(93, 72, 0, 0, 0, 0, 0) 82 } 83 84 // ---- overflow safety ---- 85 let e_big: *Ewma = nx_ewma_alloc(500000) 86 if nx_ewma_safe_p(4000000000000) != 0 { 87 return __syscall(93, 80, 0, 0, 0, 0, 0) 88 } 89 if nx_ewma_safe_p(1000000) != 1 { 90 return __syscall(93, 81, 0, 0, 0, 0, 0) 91 } 92 if nx_ewma_add(e_big, 4000000000000) != -1 { 93 return __syscall(93, 82, 0, 0, 0, 0, 0) 94 } 95 96 return 0 97}