code wiki / (root) / sketch_linear_counter_test.nx

sketch_linear_counter_test.nx source

↩ module page · 158 lines · 4572 B

1// sketch_linear_counter_test.nx -- Whang-Vander Zanden 1990 cardinality. 2 3import "syscalls.nx" 4import "sketch_linear_counter.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 ---- 14 let lc: *LinearCounter = nx_lc_alloc(8192, 42) 15 if lc == (0 as *LinearCounter) { return __syscall(93, 5, 0, 0, 0, 0, 0) } 16 // Reject non-power-of-2 + too-small. 17 if nx_lc_alloc(100, 1) != (0 as *LinearCounter) { 18 return __syscall(93, 6, 0, 0, 0, 0, 0) 19 } 20 if nx_lc_alloc(32, 1) != (0 as *LinearCounter) { 21 return __syscall(93, 7, 0, 0, 0, 0, 0) 22 } 23 24 // ---- empty ---- 25 if nx_lc_zeros(lc) != 8192 { 26 return __syscall(93, 10, 0, 0, 0, 0, 0) 27 } 28 if nx_lc_estimate(lc) != 0 { 29 return __syscall(93, 11, 0, 0, 0, 0, 0) 30 } 31 32 // ---- small cardinality: LC's sweet spot ---- 33 // Insert 100 distinct values into m=8192 bits. 34 // Expected zeros ~ 8192 * e^(-100/8192) ~ 8192 * 0.988 ~ 8093. 35 // estimate = -8192 * ln(8093/8192) = -8192 * (-0.01207) ~ 99. 36 let buf: *u8 = sys_mmap(8) 37 var i: i64 = 0 38 while i < 100 { 39 buf[0] = (i ) & 0xFF 40 buf[1] = (i >> 8 ) & 0xFF 41 buf[2] = 0xA1 42 buf[3] = 0 43 buf[4] = 0 44 buf[5] = 0 45 buf[6] = 0 46 buf[7] = 0 47 nx_lc_add(lc, buf, 8) 48 i = i + 1 49 } 50 let est_small: i64 = nx_lc_estimate(lc) 51 // Allow +/-20% since our ln lookup is coarse. 52 if iabs(est_small - 100) > 50 { 53 return __syscall(93, 20, 0, 0, 0, 0, 0) 54 } 55 56 // ---- medium cardinality ---- 57 let lc2: *LinearCounter = nx_lc_alloc(8192, 42) 58 i = 0 59 while i < 1000 { 60 buf[0] = (i ) & 0xFF 61 buf[1] = (i >> 8 ) & 0xFF 62 buf[2] = 0xB1 63 buf[3] = 0 64 buf[4] = 0 65 buf[5] = 0 66 buf[6] = 0 67 buf[7] = 0 68 nx_lc_add(lc2, buf, 8) 69 i = i + 1 70 } 71 let est_med: i64 = nx_lc_estimate(lc2) 72 // True = 1000. Allow +/-30% margin. 73 if iabs(est_med - 1000) > 300 { 74 return __syscall(93, 30, 0, 0, 0, 0, 0) 75 } 76 77 // ---- saturated bitmap returns m (ceiling) ---- 78 let lc3: *LinearCounter = nx_lc_alloc(64, 42) // tiny, easily saturates 79 i = 0 80 while i < 1000 { 81 buf[0] = (i ) & 0xFF 82 buf[1] = (i >> 8 ) & 0xFF 83 buf[2] = 0xC1 84 buf[3] = 0 85 buf[4] = 0 86 buf[5] = 0 87 buf[6] = 0 88 buf[7] = 0 89 nx_lc_add(lc3, buf, 8) 90 i = i + 1 91 } 92 // With 1000 inserts into 64 bits, almost certainly all set. 93 let z3: i64 = nx_lc_zeros(lc3) 94 if z3 == 0 { 95 // Saturated: estimate should return m as ceiling, not blow up. 96 if nx_lc_estimate(lc3) != 64 { 97 return __syscall(93, 40, 0, 0, 0, 0, 0) 98 } 99 } 100 101 // ---- typed envelope ---- 102 let q: *ApproxI64 = nx_lc_query(lc2) 103 if q.envelope_kind != NX_ENV_REL_STDDEV { 104 return __syscall(93, 50, 0, 0, 0, 0, 0) 105 } 106 if q.maturity != NX_MATURITY_REFERENCE_IMPL { 107 return __syscall(93, 51, 0, 0, 0, 0, 0) 108 } 109 if q.adv_safety != NX_ADV_HONEST { 110 return __syscall(93, 52, 0, 0, 0, 0, 0) 111 } 112 113 // ---- merge ---- 114 let m_a: *LinearCounter = nx_lc_alloc(8192, 99) 115 let m_b: *LinearCounter = nx_lc_alloc(8192, 99) 116 i = 0 117 while i < 200 { 118 buf[0] = (i ) & 0xFF 119 buf[1] = (i >> 8 ) & 0xFF 120 buf[2] = 0xD1 121 buf[3] = 0 122 buf[4] = 0 123 buf[5] = 0 124 buf[6] = 0 125 buf[7] = 0 126 nx_lc_add(m_a, buf, 8) 127 i = i + 1 128 } 129 i = 100 130 while i < 300 { 131 buf[0] = (i ) & 0xFF 132 buf[1] = (i >> 8 ) & 0xFF 133 buf[2] = 0xD1 134 buf[3] = 0 135 buf[4] = 0 136 buf[5] = 0 137 buf[6] = 0 138 buf[7] = 0 139 nx_lc_add(m_b, buf, 8) 140 i = i + 1 141 } 142 let merged: *LinearCounter = nx_lc_merge(m_a, m_b) 143 if merged == (0 as *LinearCounter) { 144 return __syscall(93, 60, 0, 0, 0, 0, 0) 145 } 146 // Union: distinct values 0..299 = 300 total. 147 let est_merge: i64 = nx_lc_estimate(merged) 148 if iabs(est_merge - 300) > 100 { 149 return __syscall(93, 61, 0, 0, 0, 0, 0) 150 } 151 // Mismatched m -> NULL. 152 let m_c: *LinearCounter = nx_lc_alloc(16384, 99) 153 if nx_lc_merge(m_a, m_c) != (0 as *LinearCounter) { 154 return __syscall(93, 62, 0, 0, 0, 0, 0) 155 } 156 157 return 0 158}