code wiki / (root) / sketch_tdigest_v2_test.nx

sketch_tdigest_v2_test.nx source

↩ module page · 123 lines · 4297 B

1// sketch_tdigest_v2_test.nx -- V2 (Dunning k1 arcsin scale) verification. 2 3import "syscalls.nx" 4import "sketch_tdigest_v2.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 rejection ---- 14 let bad_small: *TDigestV2 = nx_tdv2_alloc(5) 15 if bad_small != (0 as *TDigestV2) { return __syscall(93, 1, 0, 0, 0, 0, 0) } 16 let bad_huge: *TDigestV2 = nx_tdv2_alloc(999) 17 if bad_huge != (0 as *TDigestV2) { return __syscall(93, 2, 0, 0, 0, 0, 0) } 18 19 // ---- trig constants for delta=100 ---- 20 // Taylor in Q14: x = 102944/100 = 1029 21 // x² = 1029·1029/16384 = 64 22 // x³ = 64·1029/16384 = 4 23 // x⁴ = 64·64/16384 = 0 24 // sin = 1029 - 4/6 = 1029 25 // cos = 16384 - 32 + 0 = 16352 → one_minus_cos = 32 26 let td: *TDigestV2 = nx_tdv2_alloc(100) 27 if td == (0 as *TDigestV2) { return __syscall(93, 3, 0, 0, 0, 0, 0) } 28 if nx_tdv2_sin_q14(td) != 1029 { 29 return __syscall(93, 4, 0, 0, 0, 0, 0) 30 } 31 if nx_tdv2_one_minus_cos_q14(td) != 32 { 32 return __syscall(93, 5, 0, 0, 0, 0, 0) 33 } 34 35 // ---- empty + single-value ---- 36 if nx_tdv2_quantile(td, 500) != 0 { return __syscall(93, 10, 0, 0, 0, 0, 0) } 37 nx_tdv2_add(td, 42) 38 if nx_tdv2_quantile(td, 500) != 42 { 39 return __syscall(93, 11, 0, 0, 0, 0, 0) 40 } 41 42 // ---- stream 1..2000 / delta=100 ---- 43 let td2: *TDigestV2 = nx_tdv2_alloc(100) 44 var i: i64 = 1 45 while i <= 2000 { 46 nx_tdv2_add(td2, i) 47 i = i + 1 48 } 49 if td2.total_weight != 2000 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 50 // Median ~ 1000. V2 global rank-err 1.5% at delta=100 → abs 51 // err <= 30, allow +- 40 margin. 52 let median: i64 = nx_tdv2_quantile(td2, 500) 53 if iabs(median - 1000) > 40 { 54 return __syscall(93, 21, 0, 0, 0, 0, 0) 55 } 56 // p10 = 200, p90 = 1800 57 let p10: i64 = nx_tdv2_quantile(td2, 100) 58 if iabs(p10 - 200) > 40 { 59 return __syscall(93, 22, 0, 0, 0, 0, 0) 60 } 61 let p90: i64 = nx_tdv2_quantile(td2, 900) 62 if iabs(p90 - 1800) > 40 { 63 return __syscall(93, 23, 0, 0, 0, 0, 0) 64 } 65 // p99 = 1980; tail bound looser for V2; tolerate +- 80. 66 let p99: i64 = nx_tdv2_quantile(td2, 990) 67 if iabs(p99 - 1980) > 80 { 68 return __syscall(93, 24, 0, 0, 0, 0, 0) 69 } 70 71 // ---- memory-efficiency claim: V2 < 200 centroids at delta=100 ---- 72 // (V1 typically uses 200-400 centroids on same stream; V2's 73 // looser tail capacity allows fewer centroids overall.) 74 let n_cent: i64 = nx_tdv2_n_centroids(td2) 75 if n_cent < 1 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 76 if n_cent > 200 { return __syscall(93, 31, 0, 0, 0, 0, 0) } 77 78 // ---- typed envelope ---- 79 let q: *ApproxI64 = nx_tdv2_query(td2, 500) 80 if q.envelope_kind != NX_ENV_RANK_ERROR { 81 return __syscall(93, 40, 0, 0, 0, 0, 0) 82 } 83 if q.conf_ppb != 950000000 { 84 return __syscall(93, 41, 0, 0, 0, 0, 0) 85 } 86 if q.maturity != NX_MATURITY_REFERENCE_IMPL { 87 return __syscall(93, 42, 0, 0, 0, 0, 0) 88 } 89 if q.adv_safety != NX_ADV_HONEST { 90 return __syscall(93, 43, 0, 0, 0, 0, 0) 91 } 92 // delta=100 → 1.5% rank err → 15_000_000 ppb 93 if q.param_a != 15000000 { 94 return __syscall(93, 44, 0, 0, 0, 0, 0) 95 } 96 97 // ---- delta=50 trig sanity ---- 98 // x = 102944/50 = 2058. x² = 2058·2058/16384 = 258. 99 // x³ = 258·2058/16384 = 32. x⁴ = 258·258/16384 = 4. 100 // sin = 2058 - 32/6 = 2058 - 5 = 2053 101 // cos = 16384 - 129 + 0 = 16255 → one_minus_cos = 129 102 let td50: *TDigestV2 = nx_tdv2_alloc(50) 103 if nx_tdv2_sin_q14(td50) != 2053 { 104 return __syscall(93, 50, 0, 0, 0, 0, 0) 105 } 106 if nx_tdv2_one_minus_cos_q14(td50) != 129 { 107 return __syscall(93, 51, 0, 0, 0, 0, 0) 108 } 109 110 // ---- delta=200 trig sanity ---- 111 // x = 102944/200 = 514. x² = 514·514/16384 = 16. 112 // x³ = 16·514/16384 = 0. x⁴ ~ 0. 113 // sin = 514. cos = 16384 - 8 = 16376. one_minus_cos = 8. 114 let td200: *TDigestV2 = nx_tdv2_alloc(200) 115 if nx_tdv2_sin_q14(td200) != 514 { 116 return __syscall(93, 60, 0, 0, 0, 0, 0) 117 } 118 if nx_tdv2_one_minus_cos_q14(td200) != 8 { 119 return __syscall(93, 61, 0, 0, 0, 0, 0) 120 } 121 122 return 0 123}