sketch_hll_packed_test.nx source
↩ module page · 92 lines · 3076 B
1// sketch_hll_packed_test.nx -- end-to-end test for HLL_6 packed
2// registers. Memory + accuracy parity vs HLL_8.
3
4import "syscalls.nx"
5import "sketch_hll.nx"
6import "sketch_hll_packed.nx"
7import "sketch_types.nx"
8
9func write_i64_le(buf: *u8, value: i64) -> i64 {
10 var i: i64 = 0
11 var v: i64 = value
12 while i < 8 {
13 buf[i] = v & 0xFF
14 v = v >> 8
15 i = i + 1
16 }
17 return 0
18}
19
20func main() -> i64 {
21 // lg_k=10 -> 1024 registers. HLL_8 = 1024 bytes; HLL_6 = 769.
22 let h6: *Hll6 = nx_hll6_alloc(10, 0)
23 if h6 == (0 as *Hll6) { return __syscall(93, 5, 0, 0, 0, 0, 0) }
24
25 // ---- memory stomp ----
26 // HLL_8 register-array size is m bytes; HLL_6 should be
27 // m*6/8 rounded up + 1 guard byte. For m=1024: 768+1 = 769.
28 let mem6: i64 = nx_hll6_memory_bytes(h6)
29 if mem6 != 769 { return __syscall(93, 6, 0, 0, 0, 0, 0) }
30 // Confirm < 25% reduction baseline: 769 < 1024 * 0.80
31 let baseline: i64 = 1024
32 let reduced_threshold: i64 = (baseline * 80) / 100
33 if mem6 >= reduced_threshold {
34 // Should be 75% of HLL_8 size; if we didn't make 80% threshold,
35 // the packing is wrong.
36 return __syscall(93, 7, 0, 0, 0, 0, 0)
37 }
38
39 // ---- accuracy parity ----
40 // Stream same N=5000 keys through HLL_6 and verify estimate
41 // within +-15% of N (same tolerance as HLL_8).
42 let N: i64 = 5000
43 let key: *u8 = sys_mmap(8)
44 var i: i64 = 0
45 while i < N {
46 write_i64_le(key, i)
47 nx_hll6_add(h6, key, 8)
48 i = i + 1
49 }
50 let est: i64 = nx_hll6_estimate(h6)
51 let lo: i64 = (N * 85) / 100
52 let hi: i64 = (N * 115) / 100
53 if est < lo { return __syscall(93, 10, 0, 0, 0, 0, 0) }
54 if est > hi { return __syscall(93, 11, 0, 0, 0, 0, 0) }
55
56 // ---- bit-packing round-trip ----
57 // Set rho=53 at index 100; read it back.
58 let h6b: *Hll6 = nx_hll6_alloc(10, 0)
59 nx_hll6_set(h6b.regs, 100, 53)
60 let got: i64 = nx_hll6_get(h6b.regs, 100)
61 if got != 53 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
62 // Set rho=42 at index 101 (adjacent register); index 100 unchanged.
63 nx_hll6_set(h6b.regs, 101, 42)
64 if nx_hll6_get(h6b.regs, 100) != 53 {
65 return __syscall(93, 21, 0, 0, 0, 0, 0)
66 }
67 if nx_hll6_get(h6b.regs, 101) != 42 {
68 return __syscall(93, 22, 0, 0, 0, 0, 0)
69 }
70 // Clear by overwriting with 0.
71 nx_hll6_set(h6b.regs, 100, 0)
72 if nx_hll6_get(h6b.regs, 100) != 0 {
73 return __syscall(93, 23, 0, 0, 0, 0, 0)
74 }
75 if nx_hll6_get(h6b.regs, 101) != 42 {
76 return __syscall(93, 24, 0, 0, 0, 0, 0)
77 }
78
79 // ---- typed envelope ----
80 let q: *ApproxI64 = nx_hll6_query(h6)
81 if q.envelope_kind != NX_ENV_REL_STDDEV {
82 return __syscall(93, 30, 0, 0, 0, 0, 0)
83 }
84 if q.maturity != NX_MATURITY_REFERENCE_IMPL {
85 return __syscall(93, 31, 0, 0, 0, 0, 0)
86 }
87 if q.adv_safety != NX_ADV_HONEST {
88 return __syscall(93, 32, 0, 0, 0, 0, 0)
89 }
90
91 return 0
92}