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}