nx_analyze.nx source
↩ module page · 529 lines · 24415 B
1// nx_analyze.nx -- THE ANALYST FRONT DOOR. Everything else in this arc is a library with no runnable
2// main: an agent could run the gates but could not actually analyse a dataset over MCP. This is the one
3// registered, cap-gated, verb-driven CLI that makes the whole stack callable -- by a human, an agent, or a
4// workflow step -- and it answers in STRUCTURED JSON so the caller parses fields, not prose.
5//
6// DESIGN, against the operator's checklist:
7// MCP / API : one organ, one tool_allowlist row, driven entirely through the sovereign mgmt API.
8// AGENTIC : output is a single JSON object; every honesty flag (significant, family_significant,
9// truncated, degenerate, survives) is a machine field a planner can branch on.
10// WORKFLOW : verbs are orthogonal primitives -- `sig`/`ci` are pure and cheap for fan-out, `report`
11// is the composed pipeline. A workflow calls exactly the piece it needs.
12// COMPOSED : it owns no statistics of its own; it wires nx_dataframe -> nx_analyst_multi ->
13// nx_analyst_infer (significance / Bonferroni / CI / partial) over nx_analyst_store's
14// sharded loader. One seam, the pieces stay single-responsibility.
15// SCALE : reads REAL seg_store data across key shards <keybase>:0/:1/... past the 256 version
16// window, and DECLARES loaded-row count + truncation in the envelope rather than quietly
17// analysing a slice.
18//
19// VERBS
20// nx_analyze sig <r_permille> <n>
21// nx_analyze ci <r_permille> <n> [alpha_permille=50]
22// nx_analyze report <prefix> <keybase> <target_idx> <nmax> <maxshards> <name0> <name1> ...
23// license_tier: ORIGINAL No hardware writes (Rule 26).
24import "nx_syscalls.nx"
25import "_hdl_build/nx_analyst_store.nx"
26import "nx_analyst_causal.nx"
27import "nx_analyst_insight.nx"
28const AZ_MAGIC_2048: i64 = 2048
29
30const AZ_MAXCOL: i64 = 32
31const AZ_OUT: i64 = 65536
32const AZ_ALPHA_05: i64 = 50
33const AZ_EXIT_USAGE: i64 = 2
34
35func az_err(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 }
36func az_vlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
37func az_streq(a: *u8, b: *u8) -> i64 {
38 var i: i64 = 0
39 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
40 if b[i] != (0 as u8) { return 0 }
41 return 1
42}
43func az_atoi(s: *u8) -> i64 {
44 var i: i64 = 0
45 var neg: i64 = 0
46 if s[0] == (45 as u8) { neg = 1; i = 1 }
47 var v: i64 = 0
48 var go: i64 = 1
49 while go == 1 {
50 let c: i64 = s[i] as i64
51 if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } }
52 }
53 if neg == 1 { return 0 - v }
54 return v
55}
56// JSON emit helpers on a shared (out,o) cursor
57func az_raw(out: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } return o }
58func az_num(out: *u8, o: i64, v: i64) -> i64 {
59 var oo: i64 = o
60 var m: i64 = v
61 if m < 0 { out[oo] = 45 as u8; oo = oo + 1; m = 0 - m }
62 let t: *u8 = sys_mmap(24)
63 var k: i64 = 0
64 if m == 0 { t[0] = 48 as u8; k = 1 }
65 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
66 var i: i64 = k - 1
67 while i >= 0 { out[oo] = t[i]; oo = oo + 1; i = i - 1 }
68 return oo
69}
70// a JSON string literal, quoted and escaped (\" \\ and control bytes)
71func az_str(out: *u8, o: i64, s: *u8) -> i64 {
72 out[o] = 34 as u8
73 var oo: i64 = o + 1
74 var i: i64 = 0
75 while s[i] != (0 as u8) {
76 let c: i64 = s[i] as i64
77 if c == 34 { out[oo] = 92 as u8; oo = oo + 1; out[oo] = 34 as u8; oo = oo + 1 } else {
78 if c == 92 { out[oo] = 92 as u8; oo = oo + 1; out[oo] = 92 as u8; oo = oo + 1 } else {
79 if c < 32 { out[oo] = 32 as u8; oo = oo + 1 } else {
80 out[oo] = s[i]; oo = oo + 1 } } }
81 i = i + 1
82 }
83 out[oo] = 34 as u8
84 return oo + 1
85}
86
87// ---- verb: sig -- pure significance of a correlation, JSON --------------------------------------
88func az_sig(rp: i64, n: i64, out: *u8) -> i64 {
89 var o: i64 = 0
90 o = az_raw(out, o, "{\"tool\":\"nx_analyze\",\"verb\":\"sig\",\"r_permille\":" as *u8)
91 o = az_num(out, o, rp)
92 o = az_raw(out, o, ",\"n\":" as *u8)
93 o = az_num(out, o, n)
94 let s: i64 = ai_r_significant(rp, n)
95 o = az_raw(out, o, ",\"significant\":" as *u8)
96 if s == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
97 o = az_raw(out, o, ",\"testable\":" as *u8)
98 if s < 0 { o = az_raw(out, o, "false" as *u8) } else { o = az_raw(out, o, "true" as *u8) }
99 let need: i64 = ai_min_n(rp)
100 o = az_raw(out, o, ",\"min_n_for_significance\":" as *u8)
101 o = az_num(out, o, need)
102 o = az_raw(out, o, "}\n" as *u8)
103 out[o] = 0 as u8
104 return o
105}
106
107// ---- verb: ci -- confidence interval, JSON -----------------------------------------------------
108func az_ci(rp: i64, n: i64, alpha: i64, out: *u8) -> i64 {
109 var o: i64 = 0
110 o = az_raw(out, o, "{\"tool\":\"nx_analyze\",\"verb\":\"ci\",\"r_permille\":" as *u8)
111 o = az_num(out, o, rp)
112 o = az_raw(out, o, ",\"n\":" as *u8)
113 o = az_num(out, o, n)
114 o = az_raw(out, o, ",\"alpha_permille\":" as *u8)
115 o = az_num(out, o, alpha)
116 let lo: *i64 = sys_mmap(8) as *i64
117 let hi: *i64 = sys_mmap(8) as *i64
118 if ai_r_ci(rp, n, alpha, lo, hi) == 1 {
119 o = az_raw(out, o, ",\"ok\":true,\"ci_lo_permille\":" as *u8)
120 o = az_num(out, o, lo[0])
121 o = az_raw(out, o, ",\"ci_hi_permille\":" as *u8)
122 o = az_num(out, o, hi[0])
123 } else {
124 o = az_raw(out, o, ",\"ok\":false,\"reason\":\"n<4 or no quantile\"" as *u8)
125 }
126 o = az_raw(out, o, "}\n" as *u8)
127 out[o] = 0 as u8
128 return o
129}
130
131// ---- verb: report -- the composed pipeline over stored data at scale, JSON ---------------------
132func az_report(prefix: *u8, keybase: *u8, tidx: i64, nmax: i64, maxsh: i64, names: *i64, ncol: i64, out: *u8) -> i64 {
133 var o: i64 = 0
134 if ncol <= 0 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"no columns\"}\n" as *u8); out[o] = 0 as u8; return o }
135 if ncol > AZ_MAXCOL { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"too many columns\"}\n" as *u8); out[o] = 0 as u8; return o }
136 if tidx < 0 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"bad target\"}\n" as *u8); out[o] = 0 as u8; return o }
137 if tidx >= ncol { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"bad target\"}\n" as *u8); out[o] = 0 as u8; return o }
138
139 // load every field across shards; a shared flag captures any truncation so the envelope is honest
140 let cols: *i64 = sys_mmap(8 * ncol) as *i64
141 let fl: *i64 = sys_mmap(8) as *i64
142 fl[0] = 0
143 var rows: i64 = 0
144 var f: i64 = 0
145 while f < ncol {
146 let col: *i64 = sys_mmap(8 * nmax) as *i64
147 rows = asr_load_field_sharded(prefix, keybase, f, col, nmax, maxsh, fl)
148 cols[f] = col as i64
149 f = f + 1
150 }
151 let tcol: *i64 = cols[tidx] as *i64
152
153 o = az_raw(out, o, "{\"tool\":\"nx_analyze\",\"verb\":\"report\",\"ok\":true,\"rows\":" as *u8)
154 o = az_num(out, o, rows)
155 o = az_raw(out, o, ",\"truncated\":" as *u8)
156 if fl[0] == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
157 o = az_raw(out, o, ",\"target\":" as *u8)
158 o = az_str(out, o, names[tidx] as *u8)
159 o = az_raw(out, o, ",\"ncol\":" as *u8)
160 o = az_num(out, o, ncol)
161
162 // candidate columns = every column except the target; family = the count actually tested
163 let cand: *i64 = sys_mmap(8 * ncol) as *i64
164 let cand_names: *i64 = sys_mmap(8 * ncol) as *i64
165 var nc: i64 = 0
166 var c: i64 = 0
167 while c < ncol {
168 if c != tidx { cand[nc] = cols[c]; cand_names[nc] = names[c]; nc = nc + 1 }
169 c = c + 1
170 }
171
172 o = az_raw(out, o, ",\"relationships\":[" as *u8)
173 let cls: *u8 = sys_mmap(128)
174 let lo: *i64 = sys_mmap(8) as *i64
175 let hi: *i64 = sys_mmap(8) as *i64
176 var i: i64 = 0
177 while i < nc {
178 if i > 0 { o = az_raw(out, o, "," as *u8) }
179 let colc: *i64 = cand[i] as *i64
180 let r: i64 = am_pearson_milli(tcol, colc, rows)
181 var ar: i64 = r
182 if ar < 0 { ar = 0 - ar }
183 o = az_raw(out, o, "{\"name\":" as *u8)
184 o = az_str(out, o, cand_names[i] as *u8)
185 if ar > 1000 {
186 // |r|>1000 is the degenerate sentinel, not a correlation
187 o = az_raw(out, o, ",\"degenerate\":true}" as *u8)
188 } else {
189 o = az_raw(out, o, ",\"r_permille\":" as *u8)
190 o = az_num(out, o, r)
191 let sg: i64 = ai_r_significant(r, rows)
192 let fsg: i64 = ai_r_sig_bonferroni(r, rows, nc)
193 o = az_raw(out, o, ",\"significant\":" as *u8)
194 if sg == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
195 o = az_raw(out, o, ",\"family_significant\":" as *u8)
196 if fsg == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
197 if ai_r_ci(r, rows, AZ_ALPHA_05, lo, hi) == 1 {
198 o = az_raw(out, o, ",\"ci_lo\":" as *u8)
199 o = az_num(out, o, lo[0])
200 o = az_raw(out, o, ",\"ci_hi\":" as *u8)
201 o = az_num(out, o, hi[0])
202 }
203 am_class(0 as *u8, r, cls)
204 o = az_raw(out, o, ",\"class\":" as *u8)
205 o = az_str(out, o, cls)
206 o = az_raw(out, o, "}" as *u8)
207 }
208 i = i + 1
209 }
210 o = az_raw(out, o, "]" as *u8)
211
212 // strongest FAMILY-significant association (Bonferroni over nc) + confound check
213 var best: i64 = 0 - 1
214 var bestabs: i64 = 0 - 1
215 i = 0
216 while i < nc {
217 let colc: *i64 = cand[i] as *i64
218 let r: i64 = am_pearson_milli(tcol, colc, rows)
219 var ar: i64 = r
220 if ar < 0 { ar = 0 - ar }
221 if ar <= 1000 { if ar > bestabs { if ai_r_sig_bonferroni(r, rows, nc) == 1 { bestabs = ar; best = i } } }
222 i = i + 1
223 }
224 o = az_raw(out, o, ",\"strongest\":" as *u8)
225 if best < 0 {
226 o = az_raw(out, o, "null,\"strongest_note\":\"no association survives family-wise .05 over " as *u8)
227 o = az_num(out, o, nc)
228 o = az_raw(out, o, " candidates\"" as *u8)
229 } else {
230 let br: i64 = am_pearson_milli(tcol, cand[best] as *i64, rows)
231 o = az_raw(out, o, "{\"name\":" as *u8)
232 o = az_str(out, o, cand_names[best] as *u8)
233 o = az_raw(out, o, ",\"r_permille\":" as *u8)
234 o = az_num(out, o, br)
235 // confound: does it survive the control that weakens it most?
236 let zi: *i64 = sys_mmap(8) as *i64
237 let pr: *i64 = sys_mmap(8) as *i64
238 if ai_confound_scan(tcol, cand, nc, rows, best, zi, pr) == 1 {
239 o = az_raw(out, o, ",\"control\":" as *u8)
240 o = az_str(out, o, cand_names[zi[0]] as *u8)
241 o = az_raw(out, o, ",\"partial_permille\":" as *u8)
242 o = az_num(out, o, pr[0])
243 o = az_raw(out, o, ",\"survives_control\":" as *u8)
244 if ai_r_significant(pr[0], rows - 1) == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
245 }
246 o = az_raw(out, o, "}" as *u8)
247 }
248 // causal ceiling: nothing declared through this surface, so the honest answer is association-only
249 o = az_raw(out, o, ",\"causal\":\"association-only\",\"causal_note\":\"declare roles via nx_analyst_causal to license a causal reading; a correlation cannot tell a confounder from a mediator from a collider\"}\n" as *u8)
250 out[o] = 0 as u8
251 return o
252}
253
254// ---- verb: mine -- no-target insight scan over stored data, JSON (F1023) -----------------------
255func az_kind_name(k: i64) -> *u8 {
256 if k == 1 { return "relationship" as *u8 }
257 if k == 2 { return "nonlinear_monotone" as *u8 }
258 if k == 3 { return "constant" as *u8 }
259 if k == 4 { return "id_like" as *u8 }
260 if k == 5 { return "skewed" as *u8 }
261 if k == 6 { return "outliers" as *u8 }
262 return "unknown" as *u8
263}
264func az_mine(prefix: *u8, keybase: *u8, nmax: i64, maxsh: i64, names: *i64, ncol: i64, out: *u8) -> i64 {
265 var o: i64 = 0
266 if ncol < 2 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"mine needs >=2 columns\"}\n" as *u8); out[o] = 0 as u8; return o }
267 if ncol > AZ_MAXCOL { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"too many columns\"}\n" as *u8); out[o] = 0 as u8; return o }
268 let cols: *i64 = sys_mmap(8 * ncol) as *i64
269 let fl: *i64 = sys_mmap(8) as *i64
270 fl[0] = 0
271 var rows: i64 = 0
272 var f: i64 = 0
273 while f < ncol {
274 let col: *i64 = sys_mmap(8 * nmax) as *i64
275 rows = asr_load_field_sharded(prefix, keybase, f, col, nmax, maxsh, fl)
276 cols[f] = col as i64
277 f = f + 1
278 }
279 let kind: *i64 = sys_mmap(8 * AZ_MAGIC_2048) as *i64
280 let fa: *i64 = sys_mmap(8 * AZ_MAGIC_2048) as *i64
281 let fb: *i64 = sys_mmap(8 * AZ_MAGIC_2048) as *i64
282 let fscore: *i64 = sys_mmap(8 * AZ_MAGIC_2048) as *i64
283 let ft: *i64 = sys_mmap(8 * AZ_MAGIC_2048) as *i64
284 let meta: *i64 = sys_mmap(8 * 4) as *i64
285 let nf: i64 = ai_mine_scan(cols, names, ncol, rows, kind, fa, fb, fscore, ft, meta)
286 let maxf: i64 = ins_conf("max_findings" as *u8, 12)
287
288 o = az_raw(out, o, "{\"tool\":\"nx_analyze\",\"verb\":\"mine\",\"ok\":true,\"rows\":" as *u8)
289 o = az_num(out, o, rows)
290 o = az_raw(out, o, ",\"truncated\":" as *u8)
291 if fl[0] == 1 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
292 o = az_raw(out, o, ",\"ncol\":" as *u8)
293 o = az_num(out, o, ncol)
294 o = az_raw(out, o, ",\"hypotheses_tested\":" as *u8)
295 o = az_num(out, o, meta[0])
296 o = az_raw(out, o, ",\"per_test_alpha_permille\":" as *u8)
297 o = az_num(out, o, meta[2])
298 o = az_raw(out, o, ",\"pairs_surviving\":" as *u8)
299 o = az_num(out, o, meta[1])
300 // family-wise level unavailable => certify nothing pairwise, and SAY so as a machine field
301 o = az_raw(out, o, ",\"pairwise_certifiable\":" as *u8)
302 if meta[2] > 0 { o = az_raw(out, o, "true" as *u8) } else { o = az_raw(out, o, "false" as *u8) }
303 o = az_raw(out, o, ",\"findings\":[" as *u8)
304 var shown: i64 = 0
305 var i: i64 = 0
306 while i < nf {
307 if shown < maxf {
308 if shown > 0 { o = az_raw(out, o, "," as *u8) }
309 shown = shown + 1
310 o = az_raw(out, o, "{\"kind\":" as *u8)
311 o = az_str(out, o, az_kind_name(kind[i]))
312 if kind[i] <= 2 {
313 o = az_raw(out, o, ",\"a\":" as *u8)
314 o = az_str(out, o, names[fa[i]] as *u8)
315 o = az_raw(out, o, ",\"b\":" as *u8)
316 o = az_str(out, o, names[fb[i]] as *u8)
317 o = az_raw(out, o, ",\"stat_permille\":" as *u8)
318 o = az_num(out, o, ft[i])
319 } else {
320 o = az_raw(out, o, ",\"col\":" as *u8)
321 o = az_str(out, o, names[fa[i]] as *u8)
322 o = az_raw(out, o, ",\"value\":" as *u8)
323 o = az_num(out, o, ft[i])
324 }
325 o = az_raw(out, o, ",\"score\":" as *u8)
326 o = az_num(out, o, fscore[i])
327 o = az_raw(out, o, "}" as *u8)
328 }
329 i = i + 1
330 }
331 o = az_raw(out, o, "]" as *u8)
332 if nf > shown {
333 o = az_raw(out, o, ",\"findings_truncated\":" as *u8)
334 o = az_num(out, o, nf - shown)
335 }
336 o = az_raw(out, o, "}\n" as *u8)
337 out[o] = 0 as u8
338 return o
339}
340
341// ---- verb: ask -- deterministic NL intent router (F1006), NO LLM ------------------------------
342// Recognises the QUESTION TYPE from keywords and routes to sig/ci/report/mine. Numbers for sig/ci are
343// pulled from the question; for report/mine the dataset is passed structured and the TARGET column is
344// resolved by matching a column name named in the question (else the last column, the usual outcome
345// convention). Honestly PARTIAL: intent+parameters are deterministic; understanding arbitrary phrasing
346// (a real NL model) is the named gap. Numbers are PERMILLE: "994 over 300" = r=994/1000, n=300.
347func az_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
348func az_has_ci(hay: *u8, needle: *u8) -> i64 {
349 var hn: i64 = 0
350 while hay[hn] != (0 as u8) { hn = hn + 1 }
351 var nl: i64 = 0
352 while needle[nl] != (0 as u8) { nl = nl + 1 }
353 if nl == 0 { return 0 }
354 var i: i64 = 0
355 while i + nl <= hn {
356 var j: i64 = 0
357 var eq: i64 = 1
358 while j < nl { if az_lc(hay[i+j] as i64) != az_lc(needle[j] as i64) { eq = 0 } j = j + 1 }
359 if eq == 1 { return 1 }
360 i = i + 1
361 }
362 return 0
363}
364// extract up to `want` signed integers from s into nums; returns count found
365func az_nums(s: *u8, nums: *i64, want: i64) -> i64 {
366 var found: i64 = 0
367 var i: i64 = 0
368 while s[i] != (0 as u8) {
369 let c: i64 = s[i] as i64
370 var isdig: i64 = 0
371 if c >= 48 { if c <= 57 { isdig = 1 } }
372 if isdig == 1 { if found < want {
373 var neg: i64 = 0
374 if i > 0 { if s[i-1] == (45 as u8) { neg = 1 } }
375 var v: i64 = 0
376 var go: i64 = 1
377 while go == 1 {
378 let d: i64 = s[i] as i64
379 if d >= 48 { if d <= 57 { v = v * 10 + (d - 48); i = i + 1 } else { go = 0 } } else { go = 0 }
380 }
381 if neg == 1 { v = 0 - v }
382 nums[found] = v
383 found = found + 1
384 i = i - 1
385 } }
386 i = i + 1
387 }
388 return found
389}
390// resolve a target column: first names[] entry whose text appears in the question; else the last column.
391func az_resolve_target(question: *u8, names: *i64, ncol: i64) -> i64 {
392 var i: i64 = 0
393 while i < ncol {
394 if az_has_ci(question, names[i] as *u8) == 1 { return i }
395 i = i + 1
396 }
397 return ncol - 1
398}
399// copy the routed JSON from tmp into out at o, dropping a single trailing newline; return new o
400func az_embed(out: *u8, o: i64, tmp: *u8, tn: i64) -> i64 {
401 var end: i64 = tn
402 if end > 0 { if tmp[end-1] == (10 as u8) { end = end - 1 } }
403 var k: i64 = 0
404 while k < end { out[o] = tmp[k]; o = o + 1; k = k + 1 }
405 return o
406}
407func az_ask(question: *u8, dsargs: *i64, nds: i64, out: *u8) -> i64 {
408 var o: i64 = 0
409 // intent detection (order matters: "confidence interval" is CI, not SIG)
410 var intent: *u8 = "report" as *u8
411 if az_has_ci(question, "interval" as *u8) == 1 { intent = "ci" as *u8 } else {
412 if az_has_ci(question, "confidence" as *u8) == 1 { intent = "ci" as *u8 } else {
413 if az_has_ci(question, "significan" as *u8) == 1 { intent = "sig" as *u8 } else {
414 if az_has_ci(question, "noise" as *u8) == 1 { intent = "sig" as *u8 } else {
415 if az_has_ci(question, "distinguish" as *u8) == 1 { intent = "sig" as *u8 } else {
416 if az_has_ci(question, "interesting" as *u8) == 1 { intent = "mine" as *u8 } else {
417 if az_has_ci(question, "explore" as *u8) == 1 { intent = "mine" as *u8 } else {
418 if az_has_ci(question, "anomal" as *u8) == 1 { intent = "mine" as *u8 } else {
419 if az_has_ci(question, "surprising" as *u8) == 1 { intent = "mine" as *u8 } else {
420 if az_has_ci(question, "insight" as *u8) == 1 { intent = "mine" as *u8 } } } } } } } } } }
421
422 o = az_raw(out, o, "{\"tool\":\"nx_analyze\",\"verb\":\"ask\",\"intent\":\"" as *u8)
423 o = az_raw(out, o, intent)
424 o = az_raw(out, o, "\",\"question\":" as *u8)
425 o = az_str(out, o, question)
426 o = az_raw(out, o, ",\"result\":" as *u8)
427
428 let tmp: *u8 = sys_mmap(AZ_OUT)
429 // route
430 if az_streq(intent, "sig" as *u8) == 1 {
431 let nums: *i64 = sys_mmap(8 * 2) as *i64
432 if az_nums(question, nums, 2) < 2 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"need r and n in the question (permille)\"}}\n" as *u8); out[o] = 0 as u8; return o }
433 let tn: i64 = az_sig(nums[0], nums[1], tmp)
434 o = az_embed(out, o, tmp, tn)
435 } else {
436 if az_streq(intent, "ci" as *u8) == 1 {
437 let nums: *i64 = sys_mmap(8 * 2) as *i64
438 if az_nums(question, nums, 2) < 2 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"need r and n in the question (permille)\"}}\n" as *u8); out[o] = 0 as u8; return o }
439 let tn: i64 = az_ci(nums[0], nums[1], AZ_ALPHA_05, tmp)
440 o = az_embed(out, o, tmp, tn)
441 } else {
442 // report / mine both need the structured dataset: prefix keybase nmax maxshards name...
443 if nds < 5 { o = az_raw(out, o, "{\"ok\":false,\"reason\":\"need dataset: <prefix> <keybase> <nmax> <maxshards> <name>...\"}}\n" as *u8); out[o] = 0 as u8; return o }
444 let prefix: *u8 = dsargs[0] as *u8
445 let keybase: *u8 = dsargs[1] as *u8
446 let nmax: i64 = az_atoi(dsargs[2] as *u8)
447 let maxsh: i64 = az_atoi(dsargs[3] as *u8)
448 let names: *i64 = (((dsargs as i64) + 4 * 8) as *i64)
449 let ncol: i64 = nds - 4
450 if az_streq(intent, "mine" as *u8) == 1 {
451 let tn: i64 = az_mine(prefix, keybase, nmax, maxsh, names, ncol, tmp)
452 o = az_embed(out, o, tmp, tn)
453 } else {
454 let tidx: i64 = az_resolve_target(question, names, ncol)
455 let tn: i64 = az_report(prefix, keybase, tidx, nmax, maxsh, names, ncol, tmp)
456 o = az_embed(out, o, tmp, tn)
457 }
458 } }
459 o = az_raw(out, o, "}\n" as *u8)
460 out[o] = 0 as u8
461 return o
462}
463
464func main(argc: i64, argv: *i64) -> i64 {
465 if argc < 2 {
466 az_err("usage: nx_analyze {sig <r> <n> | ci <r> <n> [alpha] | report <prefix> <keybase> <target_idx> <nmax> <maxshards> <name>...}\n" as *u8)
467 sys_exit(AZ_EXIT_USAGE)
468 return AZ_EXIT_USAGE
469 }
470 let verb: *u8 = argv[1] as *u8
471 let out: *u8 = sys_mmap(AZ_OUT)
472
473 if az_streq(verb, "sig" as *u8) == 1 {
474 if argc < 4 { az_err("usage: nx_analyze sig <r_permille> <n>\n" as *u8); sys_exit(AZ_EXIT_USAGE); return AZ_EXIT_USAGE }
475 let n: i64 = az_sig(az_atoi(argv[2] as *u8), az_atoi(argv[3] as *u8), out)
476 sys_write(1, out, n)
477 return 0
478 }
479 if az_streq(verb, "ci" as *u8) == 1 {
480 if argc < 4 { az_err("usage: nx_analyze ci <r_permille> <n> [alpha_permille]\n" as *u8); sys_exit(AZ_EXIT_USAGE); return AZ_EXIT_USAGE }
481 var alpha: i64 = AZ_ALPHA_05
482 if argc >= 5 { alpha = az_atoi(argv[4] as *u8) }
483 let n: i64 = az_ci(az_atoi(argv[2] as *u8), az_atoi(argv[3] as *u8), alpha, out)
484 sys_write(1, out, n)
485 return 0
486 }
487 if az_streq(verb, "ask" as *u8) == 1 {
488 if argc < 3 { az_err("usage: nx_analyze ask <question> [<prefix> <keybase> <nmax> <maxshards> <name>...]
489" as *u8); sys_exit(AZ_EXIT_USAGE); return AZ_EXIT_USAGE }
490 let question: *u8 = argv[2] as *u8
491 let dsargs: *i64 = (((argv as i64) + 3 * 8) as *i64)
492 let nds: i64 = argc - 3
493 let n: i64 = az_ask(question, dsargs, nds, out)
494 sys_write(1, out, n)
495 return 0
496 }
497 if az_streq(verb, "mine" as *u8) == 1 {
498 if argc < 8 { az_err("usage: nx_analyze mine <prefix> <keybase> <nmax> <maxshards> <name>...\n" as *u8); sys_exit(AZ_EXIT_USAGE); return AZ_EXIT_USAGE }
499 let prefix: *u8 = argv[2] as *u8
500 let keybase: *u8 = argv[3] as *u8
501 let nmax: i64 = az_atoi(argv[4] as *u8)
502 let maxsh: i64 = az_atoi(argv[5] as *u8)
503 let ncol: i64 = argc - 6
504 let names: *i64 = sys_mmap(8 * ncol) as *i64
505 var k: i64 = 0
506 while k < ncol { names[k] = argv[6 + k]; k = k + 1 }
507 let n: i64 = az_mine(prefix, keybase, nmax, maxsh, names, ncol, out)
508 sys_write(1, out, n)
509 return 0
510 }
511 if az_streq(verb, "report" as *u8) == 1 {
512 if argc < 8 { az_err("usage: nx_analyze report <prefix> <keybase> <target_idx> <nmax> <maxshards> <name>...\n" as *u8); sys_exit(AZ_EXIT_USAGE); return AZ_EXIT_USAGE }
513 let prefix: *u8 = argv[2] as *u8
514 let keybase: *u8 = argv[3] as *u8
515 let tidx: i64 = az_atoi(argv[4] as *u8)
516 let nmax: i64 = az_atoi(argv[5] as *u8)
517 let maxsh: i64 = az_atoi(argv[6] as *u8)
518 let ncol: i64 = argc - 7
519 let names: *i64 = sys_mmap(8 * ncol) as *i64
520 var k: i64 = 0
521 while k < ncol { names[k] = argv[7 + k]; k = k + 1 }
522 let n: i64 = az_report(prefix, keybase, tidx, nmax, maxsh, names, ncol, out)
523 sys_write(1, out, n)
524 return 0
525 }
526 az_err("nx_analyze: unknown verb\n" as *u8)
527 sys_exit(AZ_EXIT_USAGE)
528 return AZ_EXIT_USAGE
529}