sketch_zscore_test.nx source
↩ module page · 104 lines · 3129 B
1// sketch_zscore_test.nx -- z-score anomaly detector verification.
2
3import "syscalls.nx"
4import "sketch_zscore.nx"
5import "sketch_stream_stats.nx"
6import "sketch_types.nx"
7
8func iabs(x: i64) -> i64 {
9 if x < 0 { return -x }
10 return x
11}
12
13func main() -> i64 {
14 // ---- alloc ----
15 let z: *ZScore = nx_zs_alloc(2000000, 30) // threshold 2.0
16 if z == (0 as *ZScore) { return __syscall(93, 5, 0, 0, 0, 0, 0) }
17 // Reject threshold <= 0.
18 if nx_zs_alloc(0, 30) != (0 as *ZScore) {
19 return __syscall(93, 6, 0, 0, 0, 0, 0)
20 }
21 if nx_zs_alloc(-1, 30) != (0 as *ZScore) {
22 return __syscall(93, 7, 0, 0, 0, 0, 0)
23 }
24
25 // ---- warm-up: no alarms before min_count ----
26 var i: i64 = 0
27 while i < 10 {
28 let v: i64 = nx_zs_step(z, 10000) // way above expected baseline
29 if v != NX_ZS_VERDICT_NORMAL {
30 return __syscall(93, 10, 0, 0, 0, 0, 0) // false positive in warm-up
31 }
32 i = i + 1
33 }
34 if nx_zs_n_alarms(z) != 0 {
35 return __syscall(93, 11, 0, 0, 0, 0, 0)
36 }
37
38 // ---- past warm-up: stable baseline at ~100, then spike ----
39 let z2: *ZScore = nx_zs_alloc(2000000, 20)
40 // Feed 30 samples near 100.
41 i = 0
42 while i < 30 {
43 // Jitter +/-3.
44 var v: i64 = 100
45 if (i & 1) == 0 { v = 103 }
46 if (i & 1) == 1 { v = 97 }
47 nx_zs_step(z2, v)
48 i = i + 1
49 }
50 // Now feed extreme outlier: x = 1000.
51 let verdict_spike: i64 = nx_zs_verdict(z2, 1000)
52 if verdict_spike != NX_ZS_VERDICT_HIGH {
53 return __syscall(93, 20, 0, 0, 0, 0, 0)
54 }
55 // Negative outlier.
56 let verdict_drop: i64 = nx_zs_verdict(z2, -500)
57 if verdict_drop != NX_ZS_VERDICT_LOW {
58 return __syscall(93, 21, 0, 0, 0, 0, 0)
59 }
60
61 // ---- normal sample within baseline: NORMAL ----
62 let verdict_normal: i64 = nx_zs_verdict(z2, 102)
63 if verdict_normal != NX_ZS_VERDICT_NORMAL {
64 return __syscall(93, 30, 0, 0, 0, 0, 0)
65 }
66
67 // ---- step pipeline tracks alarms ----
68 let z3: *ZScore = nx_zs_alloc(2000000, 20)
69 i = 0
70 while i < 30 {
71 // Stable baseline at 100.
72 if (i & 1) == 0 { nx_zs_step(z3, 102) }
73 if (i & 1) == 1 { nx_zs_step(z3, 98) }
74 i = i + 1
75 }
76 // 3 outliers.
77 nx_zs_step(z3, 5000)
78 nx_zs_step(z3, -5000)
79 nx_zs_step(z3, 3000)
80 if nx_zs_n_alarms(z3) != 3 {
81 return __syscall(93, 40, 0, 0, 0, 0, 0)
82 }
83
84 // ---- typed envelope ----
85 let q: *ApproxI64 = nx_zs_query(z2, 1000)
86 if q.envelope_kind != NX_ENV_ABS {
87 return __syscall(93, 50, 0, 0, 0, 0, 0)
88 }
89 if q.maturity != NX_MATURITY_PRODUCTION {
90 return __syscall(93, 51, 0, 0, 0, 0, 0)
91 }
92
93 // ---- query basic stats accessors ----
94 if nx_zs_count(z2) < 30 {
95 return __syscall(93, 60, 0, 0, 0, 0, 0)
96 }
97 // Mean should be near 100.
98 let mn: i64 = nx_zs_mean(z2)
99 if iabs(mn - 100) > 5 {
100 return __syscall(93, 61, 0, 0, 0, 0, 0)
101 }
102
103 return 0
104}