code wiki / (root) / sketch_stream_stats_test.nx

sketch_stream_stats_test.nx source

↩ module page · 127 lines · 5384 B

1// sketch_stream_stats_test.nx -- streaming mean / variance / stddev / merge. 2 3import "syscalls.nx" 4import "sketch_stream_stats.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 s: *StreamStats = nx_stats_alloc() 15 if s == (0 as *StreamStats) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 16 if s.count != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) } 17 if nx_stats_mean(s) != 0 { return __syscall(93, 7, 0, 0, 0, 0, 0) } 18 if nx_stats_variance(s) != 0 { return __syscall(93, 8, 0, 0, 0, 0, 0) } 19 20 // ---- single value ---- 21 nx_stats_add(s, 42) 22 if nx_stats_mean(s) != 42 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 23 if nx_stats_variance(s) != 0 { return __syscall(93, 11, 0, 0, 0, 0, 0) } 24 if nx_stats_min(s) != 42 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 25 if nx_stats_max(s) != 42 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 26 27 // ---- two values: known mean + variance ---- 28 // [42, 58]: mean = 50, variance = ((42-50)² + (58-50)²)/2 = (64+64)/2 = 64. 29 // stddev = 8. 30 nx_stats_add(s, 58) 31 if nx_stats_mean(s) != 50 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 32 if nx_stats_variance(s) != 64 { return __syscall(93, 21, 0, 0, 0, 0, 0) } 33 if nx_stats_stddev(s) != 8 { return __syscall(93, 22, 0, 0, 0, 0, 0) } 34 if nx_stats_min(s) != 42 { return __syscall(93, 23, 0, 0, 0, 0, 0) } 35 if nx_stats_max(s) != 58 { return __syscall(93, 24, 0, 0, 0, 0, 0) } 36 37 // ---- streaming 1..100 ---- 38 // Sum: 5050. Sum_sq: 338350. 39 // mean = 5050 / 100 = 50. variance = 338350/100 - 50² = 3383.5 - 2500 = 833. 40 // Integer arithmetic: e_sq = 338350/100 = 3383 (truncated). m² = 2500. 41 // variance reported = 883. stddev = isqrt(883) = 29. 42 let s2: *StreamStats = nx_stats_alloc() 43 var i: i64 = 1 44 while i <= 100 { 45 nx_stats_add(s2, i) 46 i = i + 1 47 } 48 if nx_stats_count(s2) != 100 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 49 if nx_stats_mean(s2) != 50 { return __syscall(93, 31, 0, 0, 0, 0, 0) } 50 // True variance 833.25 -> integer 883 due to truncation in e_sq. 51 let v: i64 = nx_stats_variance(s2) 52 if iabs(v - 833) > 50 { return __syscall(93, 32, 0, 0, 0, 0, 0) } 53 // stddev around 28-29. 54 let sd: i64 = nx_stats_stddev(s2) 55 if iabs(sd - 28) > 3 { return __syscall(93, 33, 0, 0, 0, 0, 0) } 56 if nx_stats_min(s2) != 1 { return __syscall(93, 34, 0, 0, 0, 0, 0) } 57 if nx_stats_max(s2) != 100 { return __syscall(93, 35, 0, 0, 0, 0, 0) } 58 59 // ---- merge ---- 60 let s3a: *StreamStats = nx_stats_alloc() 61 let s3b: *StreamStats = nx_stats_alloc() 62 i = 1 63 while i <= 50 { 64 nx_stats_add(s3a, i) 65 i = i + 1 66 } 67 i = 51 68 while i <= 100 { 69 nx_stats_add(s3b, i) 70 i = i + 1 71 } 72 let merged: *StreamStats = nx_stats_merge(s3a, s3b) 73 if nx_stats_count(merged) != 100 { return __syscall(93, 40, 0, 0, 0, 0, 0) } 74 if nx_stats_mean(merged) != 50 { return __syscall(93, 41, 0, 0, 0, 0, 0) } 75 if nx_stats_min(merged) != 1 { return __syscall(93, 42, 0, 0, 0, 0, 0) } 76 if nx_stats_max(merged) != 100 { return __syscall(93, 43, 0, 0, 0, 0, 0) } 77 // Merged should match s2 (which got the same items inline). 78 let v_merged: i64 = nx_stats_variance(merged) 79 if v_merged != v { return __syscall(93, 44, 0, 0, 0, 0, 0) } 80 81 // ---- isqrt edge cases ---- 82 if nx_stats_isqrt(0) != 0 { return __syscall(93, 50, 0, 0, 0, 0, 0) } 83 if nx_stats_isqrt(1) != 1 { return __syscall(93, 51, 0, 0, 0, 0, 0) } 84 if nx_stats_isqrt(4) != 2 { return __syscall(93, 52, 0, 0, 0, 0, 0) } 85 if nx_stats_isqrt(15) != 3 { return __syscall(93, 53, 0, 0, 0, 0, 0) } 86 if nx_stats_isqrt(16) != 4 { return __syscall(93, 54, 0, 0, 0, 0, 0) } 87 if nx_stats_isqrt(10000) != 100 { return __syscall(93, 55, 0, 0, 0, 0, 0) } 88 if nx_stats_isqrt(1000000) != 1000 { return __syscall(93, 56, 0, 0, 0, 0, 0) } 89 if nx_stats_isqrt(9999) != 99 { return __syscall(93, 57, 0, 0, 0, 0, 0) } 90 91 // ---- overflow safety ---- 92 // value = 2^30 should be rejected (v² overflows). 93 let big_v: i64 = 0x40000000 // 2^30 94 let s_overflow: *StreamStats = nx_stats_alloc() 95 if nx_stats_safe_p(s_overflow, big_v) != 0 { 96 return __syscall(93, 60, 0, 0, 0, 0, 0) 97 } 98 // value = 1_000_000 should be safe initially. 99 if nx_stats_safe_p(s_overflow, 1000000) != 1 { 100 return __syscall(93, 61, 0, 0, 0, 0, 0) 101 } 102 if nx_stats_add(s_overflow, big_v) != -1 { 103 return __syscall(93, 62, 0, 0, 0, 0, 0) 104 } 105 // State unchanged after failed add. 106 if s_overflow.count != 0 { 107 return __syscall(93, 63, 0, 0, 0, 0, 0) 108 } 109 110 // ---- typed envelope ---- 111 let q: *ApproxI64 = nx_stats_query_mean(s2) 112 if q.envelope_kind != NX_ENV_ABS { 113 return __syscall(93, 70, 0, 0, 0, 0, 0) 114 } 115 if q.param_a != 0 { // EXACT under overflow budget 116 return __syscall(93, 71, 0, 0, 0, 0, 0) 117 } 118 if q.conf_ppb != 1000000000 { 119 return __syscall(93, 72, 0, 0, 0, 0, 0) 120 } 121 // First Production-tier maturity in the sketch suite! 122 if q.maturity != NX_MATURITY_PRODUCTION { 123 return __syscall(93, 73, 0, 0, 0, 0, 0) 124 } 125 126 return 0 127}