sketch_geomean_test.nx source
↩ module page · 93 lines · 3070 B
1// sketch_geomean_test.nx -- geometric mean via log-space accumulation tests.
2
3import "syscalls.nx"
4import "sketch_geomean.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 g: *Geomean = nx_gm_alloc()
15 if g == (0 as *Geomean) { return __syscall(93, 5, 0, 0, 0, 0, 0) }
16 if nx_gm_value(g) != 0 { return __syscall(93, 6, 0, 0, 0, 0, 0) }
17
18 // ---- single value: geomean = value (approximate) ----
19 nx_gm_add(g, 100)
20 // floor(log_2(100)) = 6. 2^6 = 64 (under-estimate due to floor).
21 // We accept floor-log behavior; v2 can use mantissa interpolation.
22 let v_single: i64 = nx_gm_value(g)
23 if v_single < 50 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
24 if v_single > 100 { return __syscall(93, 11, 0, 0, 0, 0, 0) }
25
26 // ---- powers of 2: floor-log is exact ----
27 let g2: *Geomean = nx_gm_alloc()
28 nx_gm_add(g2, 8) // log_2(8) = 3
29 nx_gm_add(g2, 32) // log_2(32) = 5
30 // mean log = (3 + 5) / 2 = 4. 2^4 = 16.
31 if nx_gm_value(g2) != 16 {
32 return __syscall(93, 20, 0, 0, 0, 0, 0)
33 }
34
35 // ---- four powers-of-2: 2, 4, 8, 16 ----
36 let g3: *Geomean = nx_gm_alloc()
37 nx_gm_add(g3, 2) // log=1
38 nx_gm_add(g3, 4) // log=2
39 nx_gm_add(g3, 8) // log=3
40 nx_gm_add(g3, 16) // log=4
41 // mean log = (1+2+3+4)/4 = 10/4 = 2.5. geomean = 2^2.5 ≈ 5.66.
42 // With linear approx: 2^2.5 = 2^2 * (1+0.5) = 4 * 1.5 = 6.
43 let v3: i64 = nx_gm_value(g3)
44 if iabs(v3 - 6) > 1 {
45 return __syscall(93, 30, 0, 0, 0, 0, 0)
46 }
47
48 // ---- non-positive values rejected ----
49 if nx_gm_add(g, 0) != -1 {
50 return __syscall(93, 40, 0, 0, 0, 0, 0)
51 }
52 if nx_gm_add(g, -5) != -1 {
53 return __syscall(93, 41, 0, 0, 0, 0, 0)
54 }
55 if nx_gm_count_positive(g) != 1 { // unchanged from initial add
56 return __syscall(93, 42, 0, 0, 0, 0, 0)
57 }
58
59 // ---- log_mean_ppm query (log-domain access) ----
60 let lm: i64 = nx_gm_log_mean_ppm(g3)
61 // (1+2+3+4)/4 = 2.5 -> 2_500_000 PPM.
62 if iabs(lm - 2500000) > 1 {
63 return __syscall(93, 50, 0, 0, 0, 0, 0)
64 }
65
66 // ---- typed envelope ----
67 let q: *ApproxI64 = nx_gm_query(g3)
68 if q.envelope_kind != NX_ENV_REL_STDDEV {
69 return __syscall(93, 60, 0, 0, 0, 0, 0)
70 }
71 if q.maturity != NX_MATURITY_REFERENCE_IMPL {
72 return __syscall(93, 61, 0, 0, 0, 0, 0)
73 }
74
75 // ---- merge ----
76 let g4a: *Geomean = nx_gm_alloc()
77 let g4b: *Geomean = nx_gm_alloc()
78 nx_gm_add(g4a, 2)
79 nx_gm_add(g4a, 4)
80 nx_gm_add(g4b, 8)
81 nx_gm_add(g4b, 16)
82 let merged: *Geomean = nx_gm_merge(g4a, g4b)
83 if nx_gm_count(merged) != 4 {
84 return __syscall(93, 70, 0, 0, 0, 0, 0)
85 }
86 // Same data as g3; same result.
87 let v_merged: i64 = nx_gm_value(merged)
88 if v_merged != v3 {
89 return __syscall(93, 71, 0, 0, 0, 0, 0)
90 }
91
92 return 0
93}