sketch_tdigest_test.nx source
↩ module page · 96 lines · 2970 B
1// sketch_tdigest_test.nx -- T-Digest quantile + tail-tight verification.
2
3import "syscalls.nx"
4import "sketch_tdigest.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 // ---- empty ----
14 let td: *TDigest = nx_tdigest_alloc(100)
15 if td == (0 as *TDigest) { return __syscall(93, 5, 0, 0, 0, 0, 0) }
16 if nx_tdigest_quantile(td, 500) != 0 {
17 return __syscall(93, 10, 0, 0, 0, 0, 0)
18 }
19
20 // ---- single value ----
21 nx_tdigest_add(td, 42)
22 if nx_tdigest_quantile(td, 500) != 42 {
23 return __syscall(93, 20, 0, 0, 0, 0, 0)
24 }
25
26 // ---- streaming uniform 1..2000 ----
27 let td2: *TDigest = nx_tdigest_alloc(100)
28 var i: i64 = 1
29 while i <= 2000 {
30 nx_tdigest_add(td2, i)
31 i = i + 1
32 }
33 // Median should be near 1000 within 3% (we use a conservative
34 // simplified scale; v2 will tighten).
35 let median: i64 = nx_tdigest_quantile(td2, 500)
36 if iabs(median - 1000) > 60 {
37 return __syscall(93, 30, 0, 0, 0, 0, 0)
38 }
39 // p10 ~ 200.
40 let p10: i64 = nx_tdigest_quantile(td2, 100)
41 if iabs(p10 - 200) > 60 {
42 return __syscall(93, 31, 0, 0, 0, 0, 0)
43 }
44 // p90 ~ 1800.
45 let p90: i64 = nx_tdigest_quantile(td2, 900)
46 if iabs(p90 - 1800) > 60 {
47 return __syscall(93, 32, 0, 0, 0, 0, 0)
48 }
49 // p99 ~ 1980. Tail-tight: should be within 25 (rank error ~1%).
50 let p99: i64 = nx_tdigest_quantile(td2, 990)
51 if iabs(p99 - 1980) > 50 {
52 return __syscall(93, 33, 0, 0, 0, 0, 0)
53 }
54 // p1 ~ 20.
55 let p1: i64 = nx_tdigest_quantile(td2, 10)
56 if iabs(p1 - 20) > 50 {
57 return __syscall(93, 34, 0, 0, 0, 0, 0)
58 }
59
60 // ---- centroid count is bounded ----
61 let nc: i64 = nx_tdigest_n_centroids(td2)
62 if nc > 800 { return __syscall(93, 40, 0, 0, 0, 0, 0) }
63 if nc < 5 { return __syscall(93, 41, 0, 0, 0, 0, 0) }
64
65 // ---- typed envelope ----
66 let q: *ApproxI64 = nx_tdigest_query(td2, 500)
67 if q.envelope_kind != NX_ENV_RANK_ERROR {
68 return __syscall(93, 50, 0, 0, 0, 0, 0)
69 }
70 if q.param_a != 10000000 { // 1% for delta=100
71 return __syscall(93, 51, 0, 0, 0, 0, 0)
72 }
73 if q.conf_ppb != 950000000 {
74 return __syscall(93, 52, 0, 0, 0, 0, 0)
75 }
76 if q.maturity != NX_MATURITY_REFERENCE_IMPL {
77 return __syscall(93, 53, 0, 0, 0, 0, 0)
78 }
79 if q.adv_safety != NX_ADV_HONEST {
80 return __syscall(93, 54, 0, 0, 0, 0, 0)
81 }
82
83 // ---- monotonic: increasing p yields non-decreasing quantile ----
84 var prev: i64 = nx_tdigest_quantile(td2, 0)
85 var pp: i64 = 50
86 while pp <= 1000 {
87 let cur: i64 = nx_tdigest_quantile(td2, pp)
88 if cur < prev {
89 return __syscall(93, 60, 0, 0, 0, 0, 0)
90 }
91 prev = cur
92 pp = pp + 50
93 }
94
95 return 0
96}