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}