code wiki / (root) / nx_analyze.nx

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}