code wiki / _hdl_build / nx_survey_stats_gate.nx

nx_survey_stats_gate.nx source

↩ module page · 266 lines · 13618 B

1import "nx_gate_gn.nx" 2// nx_survey_stats_gate.nx -- GATE for the survey insights engine. Every statistic is checked against a 3// HAND-COMPUTED known answer, and each carries an ADVERSARIAL neg-control: a fabricated "significant" 4// result, a spurious correlation, and an out-of-table degrees-of-freedom must all be REJECTED. Ballots are 5// built directly (canon_encode of a_<qid>/usec fields = exactly what se_ballots yields) so tests are 6// deterministic and socket-free. Exits 0 iff ALL pass. license_tier: ORIGINAL expect_exit: 0 7import "nx_syscalls.nx" 8import "nx_survey_stats.nx" 9 10func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 11func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 { 12 if got == want { st[0] = st[0] + 1; gp(" PASS " as *u8); gp(name); gp("\n" as *u8) } 13 else { st[1] = st[1] + 1; gp(" FAIL " as *u8); gp(name); gp(" got=" as *u8); gn(got); gp(" want=" as *u8); gn(want); gp("\n" as *u8) } 14 return 0 15} 16func seti(a: *i64, i: i64, v: i64) -> i64 { a[i] = v; return 0 } 17// build ONE ballot record from parallel key/val ptr arrays -> returns len, ptr into outp[0] 18func mkballot(keys: *i64, vals: *i64, nf: i64, outp: *i64) -> i64 { 19 let rec: *u8 = sys_mmap(8192) 20 let rl: i64 = canon_encode(keys, vals, nf, rec) 21 outp[0] = rec as i64 22 return rl 23} 24// append k copies of a ballot (2 numeric a_-fields) to the population arrays; advances nbp[0] 25func addk2(vout: *i64, lout: *i64, nbp: *i64, ka: *u8, va: i64, kb: *u8, vb: i64, k: i64) -> i64 { 26 let vas: *u8 = sys_mmap(16) 27 se_catn(vas, 0, va) 28 let vbs: *u8 = sys_mmap(16) 29 se_catn(vbs, 0, vb) 30 let keys: *i64 = sys_mmap(8 * 4) as *i64 31 let vals: *i64 = sys_mmap(8 * 4) as *i64 32 keys[0] = ka as i64 33 vals[0] = vas as i64 34 keys[1] = kb as i64 35 vals[1] = vbs as i64 36 var i: i64 = 0 37 while i < k { 38 let op: *i64 = sys_mmap(16) as *i64 39 let rl: i64 = mkballot(keys, vals, 2, op) 40 let n: i64 = nbp[0] 41 vout[n] = op[0] 42 lout[n] = rl 43 nbp[0] = n + 1 44 i = i + 1 45 } 46 return 0 47} 48 49func main() -> i64 { 50 let st: *i64 = sys_mmap(16) as *i64 51 st[0] = 0 52 st[1] = 0 53 gp("=== nx_survey_stats_gate (descriptive/NPS/top-box/CI/cross-tab/chi-square/correlation/segment/trend/triage/themes; KATs + adversary) ===\n" as *u8) 54 55 // ---- G1 isqrt ---- 56 chk("G1a isqrt(0)=0" as *u8, st_isqrt(0), 0, st) 57 chk("G1b isqrt(2)=1" as *u8, st_isqrt(2), 1, st) 58 chk("G1c isqrt(15)=3" as *u8, st_isqrt(15), 3, st) 59 chk("G1d isqrt(16)=4" as *u8, st_isqrt(16), 4, st) 60 chk("G1e isqrt(9604)=98" as *u8, st_isqrt(9604), 98, st) 61 chk("G1f isqrt(1000000)=1000" as *u8, st_isqrt(1000000), 1000, st) 62 63 // ---- G2 descriptive on [2,4,4,4,5,5,7,9] ---- 64 let xs: *i64 = sys_mmap(8 * 16) as *i64 65 seti(xs,0,2); seti(xs,1,4); seti(xs,2,4); seti(xs,3,4); seti(xs,4,5); seti(xs,5,5); seti(xs,6,7); seti(xs,7,9) 66 let d: *i64 = sys_mmap(8 * 8) as *i64 67 st_desc(xs, 8, d) 68 chk("G2a n=8" as *u8, d[0], 8, st) 69 chk("G2b min=2" as *u8, d[1], 2, st) 70 chk("G2c max=9" as *u8, d[2], 9, st) 71 chk("G2d mean=5.000" as *u8, d[3], 5000, st) 72 chk("G2e median=4.500" as *u8, d[4], 4500, st) 73 chk("G2f stddev=2.000" as *u8, d[5], 2000, st) 74 chk("G2g mode=4" as *u8, d[6], 4, st) 75 76 // ---- G3 NPS on [10,10,10,10,8,8,6,0], prom>=9 det<=6 -> 25 ---- 77 let ns: *i64 = sys_mmap(8 * 16) as *i64 78 seti(ns,0,10); seti(ns,1,10); seti(ns,2,10); seti(ns,3,10); seti(ns,4,8); seti(ns,5,8); seti(ns,6,6); seti(ns,7,0) 79 let no: *i64 = sys_mmap(8 * 8) as *i64 80 st_nps(ns, 8, 9, 6, no) 81 chk("G3a NPS=25" as *u8, no[0], 25, st) 82 chk("G3b promoters=4" as *u8, no[1], 4, st) 83 chk("G3c passives=2" as *u8, no[2], 2, st) 84 chk("G3d detractors=2" as *u8, no[3], 2, st) 85 86 // ---- G4 top-box + CI margin ---- 87 let tb: *i64 = sys_mmap(8 * 8) as *i64 88 seti(tb,0,10); seti(tb,1,10); seti(tb,2,9); seti(tb,3,8); seti(tb,4,7); seti(tb,5,6) 89 chk("G4a top-2-box(max10)=500 permille" as *u8, st_topbox(tb, 6, 10, 2), 500, st) 90 chk("G4b CI margin(50,100)=98 permille (9.8pct)" as *u8, st_ci_margin(50, 100), 98, st) 91 chk("G4c CI margin(50,1000)=13 permille" as *u8, st_ci_margin(50, 1000), 13, st) 92 chk("G4d CI margin(0,100)=0 (degenerate)" as *u8, st_ci_margin(0, 100), 0, st) 93 chk("G4e CI margin(100,100)=0 (degenerate)" as *u8, st_ci_margin(100, 100), 0, st) 94 95 // ---- G5 chi-square (direct tables) ---- 96 let tbl: *i64 = sys_mmap(8 * 16) as *i64 97 seti(tbl,0,10); seti(tbl,1,20); seti(tbl,2,20); seti(tbl,3,10) 98 let co: *i64 = sys_mmap(8 * 4) as *i64 99 let sig: i64 = st_chisq(tbl, 2, 2, co) 100 chk("G5a chi-square permille=6664" as *u8, co[0], 6664, st) 101 chk("G5b df=1" as *u8, co[1], 1, st) 102 chk("G5c SIGNIFICANT at .05 (6664>=3841)" as *u8, sig, 1, st) 103 // ADVERSARY: independent table -> chi 0, NOT significant 104 seti(tbl,0,15); seti(tbl,1,15); seti(tbl,2,15); seti(tbl,3,15) 105 let sig2: i64 = st_chisq(tbl, 2, 2, co) 106 chk("G5d ADVERSARY independent -> chi=0" as *u8, co[0], 0, st) 107 chk("G5e ADVERSARY independent -> NOT significant" as *u8, sig2, 0, st) 108 // ADVERSARY: a visible imbalance that is NOT significant at n=40 109 seti(tbl,0,8); seti(tbl,1,12); seti(tbl,2,12); seti(tbl,3,8) 110 let sig3: i64 = st_chisq(tbl, 2, 2, co) 111 chk("G5f ADVERSARY small-n imbalance chi=1600" as *u8, co[0], 1600, st) 112 chk("G5g ADVERSARY small-n -> NOT significant (1600<3841)" as *u8, sig3, 0, st) 113 // ADVERSARY: df past the critical table must FAIL CLOSED (never falsely significant) 114 chk("G5h crit(3)=7815" as *u8, st_crit05_pm(3), 7815, st) 115 chk("G5i crit(11) fails closed (huge sentinel)" as *u8, st_crit05_pm(11), 9223372036854775807, st) 116 117 // ---- G6 Pearson correlation (direct) ---- 118 let cx: *i64 = sys_mmap(8 * 8) as *i64 119 let cy: *i64 = sys_mmap(8 * 8) as *i64 120 seti(cx,0,1); seti(cx,1,2); seti(cx,2,3) 121 seti(cy,0,2); seti(cy,1,4); seti(cy,2,6) 122 chk("G6a perfect +corr=1000" as *u8, st_corr(cx, cy, 3), 1000, st) 123 seti(cy,0,6); seti(cy,1,4); seti(cy,2,2) 124 chk("G6b perfect -corr=-1000" as *u8, st_corr(cx, cy, 3), 0 - 1000, st) 125 // ADVERSARY: no real linear relationship -> exactly 0, not a spurious driver 126 let ax: *i64 = sys_mmap(8 * 8) as *i64 127 let ay: *i64 = sys_mmap(8 * 8) as *i64 128 seti(ax,0,1); seti(ax,1,2); seti(ax,2,3); seti(ax,3,4) 129 seti(ay,0,3); seti(ay,1,1); seti(ay,2,4); seti(ay,3,2) 130 chk("G6c ADVERSARY uncorrelated=0 (no false driver)" as *u8, st_corr(ax, ay, 4), 0, st) 131 132 // ---- build a welfare population for the ballot-consuming stats ---- 133 // fields: a_house (0 stable / 1 risk / 2 crisis), a_work (0 employed / 1 unemployed) 134 let vout: *i64 = sys_mmap(8 * 200) as *i64 135 let lout: *i64 = sys_mmap(8 * 200) as *i64 136 let nbp: *i64 = sys_mmap(16) as *i64 137 nbp[0] = 0 138 addk2(vout, lout, nbp, "a_house" as *u8, 0, "a_work" as *u8, 0, 10) 139 addk2(vout, lout, nbp, "a_house" as *u8, 0, "a_work" as *u8, 1, 20) 140 addk2(vout, lout, nbp, "a_house" as *u8, 1, "a_work" as *u8, 0, 20) 141 addk2(vout, lout, nbp, "a_house" as *u8, 1, "a_work" as *u8, 1, 10) 142 let nb: i64 = nbp[0] 143 chk("G7a population built (60 ballots)" as *u8, nb, 60, st) 144 // cross-tab house x work -> [[10,20],[20,10]] 145 let ct: *i64 = sys_mmap(8 * 16) as *i64 146 let grand: i64 = st_xtab(vout, lout, nb, "house" as *u8, "work" as *u8, 2, 2, ct) 147 var ctok: i64 = 0 148 if grand == 60 { if ct[0] == 10 { if ct[1] == 20 { if ct[2] == 20 { if ct[3] == 10 { ctok = 1 } } } } } 149 chk("G7b cross-tab counts [[10,20],[20,10]] grand 60" as *u8, ctok, 1, st) 150 let cs: i64 = st_chisq(ct, 2, 2, co) 151 chk("G7c chi on the real cross-tab = 6664" as *u8, co[0], 6664, st) 152 chk("G7d cross-tab association SIGNIFICANT" as *u8, cs, 1, st) 153 154 // ---- G8 segment means: food-security by housing (the killer insight) ---- 155 let vo2: *i64 = sys_mmap(8 * 64) as *i64 156 let lo2: *i64 = sys_mmap(8 * 64) as *i64 157 let np2: *i64 = sys_mmap(16) as *i64 158 np2[0] = 0 159 // house=0: food 8,7,9 ; house=1: food 3,2,4 ; house=2: food 1,1 160 addk2(vo2, lo2, np2, "a_house" as *u8, 0, "a_food" as *u8, 8, 1) 161 addk2(vo2, lo2, np2, "a_house" as *u8, 0, "a_food" as *u8, 7, 1) 162 addk2(vo2, lo2, np2, "a_house" as *u8, 0, "a_food" as *u8, 9, 1) 163 addk2(vo2, lo2, np2, "a_house" as *u8, 1, "a_food" as *u8, 3, 1) 164 addk2(vo2, lo2, np2, "a_house" as *u8, 1, "a_food" as *u8, 2, 1) 165 addk2(vo2, lo2, np2, "a_house" as *u8, 1, "a_food" as *u8, 4, 1) 166 addk2(vo2, lo2, np2, "a_house" as *u8, 2, "a_food" as *u8, 1, 1) 167 addk2(vo2, lo2, np2, "a_house" as *u8, 2, "a_food" as *u8, 1, 1) 168 let onn: *i64 = sys_mmap(8 * 4) as *i64 169 let omp: *i64 = sys_mmap(8 * 4) as *i64 170 st_segment_means(vo2, lo2, np2[0], "house" as *u8, "food" as *u8, 3, onn, omp) 171 var segok: i64 = 0 172 if onn[0] == 3 { if omp[0] == 8000 { if onn[1] == 3 { if omp[1] == 3000 { if onn[2] == 2 { if omp[2] == 1000 { segok = 1 } } } } } } 173 chk("G8 segment means: food 8.0 stable / 3.0 risk / 1.0 crisis" as *u8, segok, 1, st) 174 175 // ---- G9 key-driver correlation end-to-end via ballots (food vs overall) ---- 176 let vo3: *i64 = sys_mmap(8 * 32) as *i64 177 let lo3: *i64 = sys_mmap(8 * 32) as *i64 178 let np3: *i64 = sys_mmap(16) as *i64 179 np3[0] = 0 180 addk2(vo3, lo3, np3, "a_food" as *u8, 1, "a_overall" as *u8, 2, 1) 181 addk2(vo3, lo3, np3, "a_food" as *u8, 2, "a_overall" as *u8, 4, 1) 182 addk2(vo3, lo3, np3, "a_food" as *u8, 3, "a_overall" as *u8, 6, 1) 183 let px: *i64 = sys_mmap(8 * 8) as *i64 184 let py: *i64 = sys_mmap(8 * 8) as *i64 185 let mp: i64 = st_collect_pairs(vo3, lo3, np3[0], "food" as *u8, "overall" as *u8, px, py) 186 chk("G9a paired collection m=3" as *u8, mp, 3, st) 187 chk("G9b food->overall driver corr=1000" as *u8, st_corr(px, py, mp), 1000, st) 188 189 // ---- G10 trend split on real timestamps ---- 190 let vo4: *i64 = sys_mmap(8 * 32) as *i64 191 let lo4: *i64 = sys_mmap(8 * 32) as *i64 192 let np4: *i64 = sys_mmap(16) as *i64 193 np4[0] = 0 194 // before (usec 1000): food 4,6 ; after (usec 3000): food 8,8 ; split at 2000 195 addk2(vo4, lo4, np4, "a_food" as *u8, 4, "usec" as *u8, 1000, 1) 196 addk2(vo4, lo4, np4, "a_food" as *u8, 6, "usec" as *u8, 1000, 1) 197 addk2(vo4, lo4, np4, "a_food" as *u8, 8, "usec" as *u8, 3000, 1) 198 addk2(vo4, lo4, np4, "a_food" as *u8, 8, "usec" as *u8, 3000, 1) 199 let tr: *i64 = sys_mmap(8 * 8) as *i64 200 st_trend_split(vo4, lo4, np4[0], "food" as *u8, 2000, tr) 201 var trok: i64 = 0 202 if tr[0] == 2 { if tr[1] == 5000 { if tr[2] == 2 { if tr[3] == 8000 { if tr[4] == 3000 { trok = 1 } } } } } 203 chk("G10 trend: before 5.0 -> after 8.0, delta +3.0" as *u8, trok, 1, st) 204 205 // ---- G11 triage rollup (composes nx_survey sv_triage/sv_band) ---- 206 let vo5: *i64 = sys_mmap(8 * 32) as *i64 207 let lo5: *i64 = sys_mmap(8 * 32) as *i64 208 let np5: *i64 = sys_mmap(16) as *i64 209 np5[0] = 0 210 // weights a_r1=30 a_r2=25 ; thresholds strained>=20 crisis>=40 211 // both flags -> 55 crisis ; one flag -> 30 strained ; none -> 0 stable 212 addk2(vo5, lo5, np5, "a_r1" as *u8, 1, "a_r2" as *u8, 1, 2) 213 addk2(vo5, lo5, np5, "a_r1" as *u8, 1, "a_r2" as *u8, 0, 3) 214 addk2(vo5, lo5, np5, "a_r1" as *u8, 0, "a_r2" as *u8, 0, 1) 215 let wq: *i64 = sys_mmap(8 * 4) as *i64 216 let wt: *i64 = sys_mmap(8 * 4) as *i64 217 wq[0] = "a_r1" as *u8 as i64 218 wt[0] = 30 219 wq[1] = "a_r2" as *u8 as i64 220 wt[1] = 25 221 let tro: *i64 = sys_mmap(8 * 4) as *i64 222 st_triage_rollup(vo5, lo5, np5[0], wq, wt, 2, 20, 40, tro) 223 var trgok: i64 = 0 224 if tro[0] == 1 { if tro[1] == 3 { if tro[2] == 2 { trgok = 1 } } } 225 chk("G11 triage rollup: 1 stable, 3 strained, 2 crisis" as *u8, trgok, 1, st) 226 227 // ---- G12 open-text themes (sovereign frequency) ---- 228 let vo6: *i64 = sys_mmap(8 * 32) as *i64 229 let lo6: *i64 = sys_mmap(8 * 32) as *i64 230 let np6: *i64 = sys_mmap(16) as *i64 231 np6[0] = 0 232 addk2(vo6, lo6, np6, "a_help" as *u8, 0, "a_txt" as *u8, 0, 0) 233 // build 4 text ballots directly (single a_help field) 234 let tk: *i64 = sys_mmap(8 * 2) as *i64 235 let tv: *i64 = sys_mmap(8 * 2) as *i64 236 tk[0] = "a_help" as *u8 as i64 237 tv[0] = "need steady work" as *u8 as i64 238 let bp: *i64 = sys_mmap(16) as *i64 239 var rl: i64 = mkballot(tk, tv, 1, bp) 240 vo6[0] = bp[0]; lo6[0] = rl 241 tv[0] = "need work now" as *u8 as i64 242 let bp2: *i64 = sys_mmap(16) as *i64 243 rl = mkballot(tk, tv, 1, bp2) 244 vo6[1] = bp2[0]; lo6[1] = rl 245 tv[0] = "housing help please" as *u8 as i64 246 let bp3: *i64 = sys_mmap(16) as *i64 247 rl = mkballot(tk, tv, 1, bp3) 248 vo6[2] = bp3[0]; lo6[2] = rl 249 tv[0] = "the the work" as *u8 as i64 250 let bp4: *i64 = sys_mmap(16) as *i64 251 rl = mkballot(tk, tv, 1, bp4) 252 vo6[3] = bp4[0]; lo6[3] = rl 253 chk("G12a theme 'work' freq=3" as *u8, st_term_freq(vo6, lo6, 4, "help" as *u8, "work" as *u8), 3, st) 254 chk("G12b theme 'need' freq=2" as *u8, st_term_freq(vo6, lo6, 4, "help" as *u8, "need" as *u8), 2, st) 255 chk("G12c stopword 'the' filtered -> freq=0" as *u8, st_term_freq(vo6, lo6, 4, "help" as *u8, "the" as *u8), 0, st) 256 chk("G12d absent term freq=0" as *u8, st_term_freq(vo6, lo6, 4, "help" as *u8, "zebra" as *u8), 0, st) 257 258 gp("nx_survey_stats_gate: PASS=" as *u8) 259 gn(st[0]) 260 gp(" FAIL=" as *u8) 261 gn(st[1]) 262 gp("\n" as *u8) 263 if st[1] == 0 { gp("VERDICT: verdict=GREEN (survey insights: descriptive/NPS/CI/cross-tab/chi-square/correlation/segment/trend/triage/themes -- KAT-exact, adversary-guarded)\n" as *u8); return 0 } 264 gp("VERDICT: RED\n" as *u8) 265 return 1 266}