code wiki / (root) / sketch_tdigest_test.nx

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}