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}