code wiki / (root) / sketch_cusum_test.nx

sketch_cusum_test.nx source

↩ module page · 121 lines · 3710 B

1// sketch_cusum_test.nx -- CUSUM change-point detector verification. 2 3import "syscalls.nx" 4import "sketch_cusum.nx" 5import "sketch_types.nx" 6 7func main() -> i64 { 8 // ---- alloc ---- 9 let c: *Cusum = nx_cusum_alloc(100, 5, 50) // k=100, delta=5, h=50 10 if c == (0 as *Cusum) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 11 // Reject delta < 0. 12 if nx_cusum_alloc(100, -1, 50) != (0 as *Cusum) { 13 return __syscall(93, 6, 0, 0, 0, 0, 0) 14 } 15 // Reject h <= 0. 16 if nx_cusum_alloc(100, 5, 0) != (0 as *Cusum) { 17 return __syscall(93, 7, 0, 0, 0, 0, 0) 18 } 19 20 // ---- stable stream around k=100: no alarm ---- 21 var i: i64 = 0 22 while i < 50 { 23 // Alternating +/-3 jitter around 100, well within slack delta=5. 24 let v: i64 = 100 + (i % 2) * 3 - (i % 2) * (1 - (i % 2)) 25 nx_cusum_add(c, 100 + (i & 1) * 2 - 1) // +/-1 jitter 26 i = i + 1 27 } 28 if nx_cusum_verdict(c) != NX_CUSUM_NORMAL { 29 return __syscall(93, 10, 0, 0, 0, 0, 0) 30 } 31 if nx_cusum_n(c) != 50 { 32 return __syscall(93, 11, 0, 0, 0, 0, 0) 33 } 34 if nx_cusum_n_alarms(c) != 0 { 35 return __syscall(93, 12, 0, 0, 0, 0, 0) 36 } 37 38 // ---- UPWARD shift: alarm fires ---- 39 let c2: *Cusum = nx_cusum_alloc(100, 2, 30) // k=100, delta=2, h=30 40 // Baseline stable. 41 i = 0 42 while i < 20 { 43 nx_cusum_add(c2, 100) 44 i = i + 1 45 } 46 // Now jump: insert 15 of value 120 (way above baseline). 47 i = 0 48 while i < 15 { 49 nx_cusum_add(c2, 120) 50 i = i + 1 51 } 52 // s_pos += (120 - 100 - 2) = 18 per step. After 2 steps: 36 > h=30. 53 if nx_cusum_verdict(c2) != NX_CUSUM_SHIFT_UP { 54 return __syscall(93, 20, 0, 0, 0, 0, 0) 55 } 56 // step() returns verdict AND resets. 57 let v_up: i64 = nx_cusum_step(c2) 58 if v_up != NX_CUSUM_SHIFT_UP { 59 return __syscall(93, 21, 0, 0, 0, 0, 0) 60 } 61 // After step, s_pos and s_neg are 0; n_alarms = 1. 62 if nx_cusum_s_pos(c2) != 0 { 63 return __syscall(93, 22, 0, 0, 0, 0, 0) 64 } 65 if nx_cusum_n_alarms(c2) != 1 { 66 return __syscall(93, 23, 0, 0, 0, 0, 0) 67 } 68 if nx_cusum_verdict(c2) != NX_CUSUM_NORMAL { 69 return __syscall(93, 24, 0, 0, 0, 0, 0) 70 } 71 72 // ---- DOWNWARD shift ---- 73 let c3: *Cusum = nx_cusum_alloc(100, 2, 30) 74 i = 0 75 while i < 20 { 76 nx_cusum_add(c3, 100) 77 i = i + 1 78 } 79 i = 0 80 while i < 15 { 81 nx_cusum_add(c3, 80) // 20 below k 82 i = i + 1 83 } 84 if nx_cusum_verdict(c3) != NX_CUSUM_SHIFT_DOWN { 85 return __syscall(93, 30, 0, 0, 0, 0, 0) 86 } 87 88 // ---- small drift within delta slack: no alarm ---- 89 let c4: *Cusum = nx_cusum_alloc(100, 10, 30) // generous slack 90 i = 0 91 while i < 100 { 92 nx_cusum_add(c4, 105) // drift of 5, within slack 10 93 i = i + 1 94 } 95 if nx_cusum_verdict(c4) != NX_CUSUM_NORMAL { 96 return __syscall(93, 40, 0, 0, 0, 0, 0) 97 } 98 99 // ---- typed envelope ---- 100 let q: *ApproxI64 = nx_cusum_query(c2) 101 if q.envelope_kind != NX_ENV_ABS { 102 return __syscall(93, 50, 0, 0, 0, 0, 0) 103 } 104 if q.param_a != 0 { // exact verdict 105 return __syscall(93, 51, 0, 0, 0, 0, 0) 106 } 107 if q.maturity != NX_MATURITY_PRODUCTION { 108 return __syscall(93, 52, 0, 0, 0, 0, 0) 109 } 110 111 // ---- reset ---- 112 nx_cusum_reset(c2) 113 if nx_cusum_s_pos(c2) != 0 { 114 return __syscall(93, 60, 0, 0, 0, 0, 0) 115 } 116 if nx_cusum_s_neg(c2) != 0 { 117 return __syscall(93, 61, 0, 0, 0, 0, 0) 118 } 119 120 return 0 121}