code wiki / (root) / nx_supplement.nx

nx_supplement.nx source

↩ module page · 635 lines · 27247 B

1// nx_supplement.nx -- C14.0: S-class unified front-door CLI. 2// 3// Git-style subcommand dispatcher. Single binary, discoverable 4// commands, self-validating. Stays bits-up sovereign. 5// 6// USAGE: 7// nx_supplement # show help 8// nx_supplement help # show help 9// nx_supplement check FILE # single-sample JSON 10// nx_supplement batch C R FILES # multi-sample NDJSON 11// nx_supplement csv R FILES # CSV table for Excel 12// nx_supplement compounds # list all reference compounds 13// nx_supplement explain ID # show details for compound ID 14// nx_supplement selftest # verify install via bundled examples 15// nx_supplement version # show version 16 17import "nx_chem.nx" 18import "nx_chem_molecule.nx" 19import "nx_chem_smiles.nx" 20import "nx_chem_periodic.nx" 21import "nx_chem_valence.nx" 22import "nx_chem_mass.nx" 23import "nx_chem_isotope_pattern.nx" 24import "nx_chem_adulterant_db.nx" 25import "nx_chem_peak_list.nx" 26import "nx_chem_report_json.nx" 27import "nx_chem_report_csv.nx" 28 29func nx_sup_version() -> *u8 { 30 return "nx_supplement 1.0.0 (substrate 2026-05-20)" as *u8 31} 32 33// ================================================================= 34// Strcmp-ish: compare null-terminated strings. Returns 0 if equal. 35// ================================================================= 36func str_eq(a: *u8, b: *u8) -> nx_int { 37 var i: nx_int = 0 38 var done: nx_int = 0 39 var result: nx_int = 1 40 while done == 0 { 41 let ca: nx_int = a[i] as nx_int 42 let cb: nx_int = b[i] as nx_int 43 if ca != cb { result = 0; done = 1 } 44 else { 45 if ca == 0 { done = 1 } 46 else { i = i + 1 } 47 } 48 } 49 return result 50} 51 52func parse_int_arg(s: *u8) -> nx_int { 53 var i: nx_int = 0 54 var v: nx_int = 0 55 var done: nx_int = 0 56 while done == 0 { 57 let c: nx_int = s[i] as nx_int 58 if c == 0 { done = 1 } 59 else { 60 if c < 48 { done = 1 } 61 else { if c > 57 { done = 1 } 62 else { v = v * 10 + (c - 48); i = i + 1 } } 63 } 64 } 65 return v 66} 67 68func is_dash(p: *u8) -> nx_int { 69 if p[0] as nx_int != 45 { return 0 } 70 if p[1] as nx_int != 0 { return 0 } 71 return 1 72} 73 74// ================================================================= 75// HELP -- comprehensive top-level help with copy-paste examples 76// ================================================================= 77func cmd_help() -> i64 { 78 println("nx_supplement -- supplement adulterant detector" as *u8) 79 println("Bits-up sovereign LC-MS adulterant detection with regulatory citations" as *u8) 80 println("" as *u8) 81 println("USAGE" as *u8) 82 println(" nx_supplement <subcommand> [args]" as *u8) 83 println("" as *u8) 84 println("SUBCOMMANDS" as *u8) 85 println(" check FILE Single peak file -> JSON verdict to stdout" as *u8) 86 println(" batch CMP CAL FILES... Multi-sample NDJSON (CMP/CAL accept '-' for none)" as *u8) 87 println(" csv CAL FILES... Multi-sample CSV table for Excel" as *u8) 88 println(" compounds List all reference compounds in the DB" as *u8) 89 println(" explain ID Show full details for compound ID (e.g., 4 = sibutramine)" as *u8) 90 println(" selftest Verify the install by running bundled example data" as *u8) 91 println(" validate Self-test that every DB compound is detected by its own [M+H]+" as *u8) 92 println(" help Show this help" as *u8) 93 println(" version Show version" as *u8) 94 println("" as *u8) 95 println("COMMON LAB EXAMPLES" as *u8) 96 println(" # one sample, no calibration" as *u8) 97 println(" nx_supplement check my_sample.csv" as *u8) 98 println("" as *u8) 99 println(" # batch with RT calibration" as *u8) 100 println(" nx_supplement batch - rt_cal.csv samples/*.csv" as *u8) 101 println("" as *u8) 102 println(" # daily run -> JSON archive + Excel report" as *u8) 103 println(" nx_supplement batch lab_compounds.tsv rt_cal.csv samples/*.csv > today.ndjson" as *u8) 104 println(" nx_supplement csv rt_cal.csv samples/*.csv > today.csv" as *u8) 105 println("" as *u8) 106 println(" # discover the DB" as *u8) 107 println(" nx_supplement compounds" as *u8) 108 println(" nx_supplement explain 4 # = sibutramine details" as *u8) 109 println("" as *u8) 110 println(" # verify the substrate works" as *u8) 111 println(" nx_supplement selftest" as *u8) 112 println("" as *u8) 113 println("EXIT CODES" as *u8) 114 println(" 0 no BANNED ingredients detected (recommendation: ACCEPT)" as *u8) 115 println(" 1 bad arguments / file unreadable" as *u8) 116 println(" N number of BANNED ingredients (for shell branching)" as *u8) 117 println("" as *u8) 118 println("DOCS" as *u8) 119 println(" User guide: nxc2/docs/NISHI_SUPPLEMENT_ADULTERANT_DETECTOR_USER_GUIDE.md" as *u8) 120 println(" Examples: nxc2/examples/supplement-arc/" as *u8) 121 return 0 122} 123 124// ================================================================= 125// VERSION 126// ================================================================= 127func cmd_version() -> i64 { 128 let _w: i64 = println(nx_sup_version()) 129 return 0 130} 131 132// ================================================================= 133// COMPOUNDS -- list all reference compounds in the DB as a table 134// ================================================================= 135func cmd_compounds() -> i64 { 136 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 137 println("Reference compounds in built-in DB:" as *u8) 138 println("" as *u8) 139 println(" ID [M+H]+ REG NAME / citation" as *u8) 140 println(" -- -------- ----------- ----------------------------------" as *u8) 141 var i: nx_int = 0 142 while i < db.n { 143 let e: *AdulterantEntry = ((db.entries as nx_int) + (i * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry 144 let _p1: i64 = print(" " as *u8) 145 if e.id < 10 { 146 let _z: i64 = print(" " as *u8) 147 } 148 let _p2: i64 = print_i64(e.id as i64) 149 let _p3: i64 = print(" " as *u8) 150 let _p4: i64 = print_q4_mass(e.mh_plus_q4) 151 let _p5: i64 = print(" " as *u8) 152 let _p6: i64 = print(nx_chem_regulatory_str(e.regulatory)) 153 // Pad regulatory column to 11 chars 154 let regs: *u8 = nx_chem_regulatory_str(e.regulatory) 155 var reg_len: nx_int = 0 156 while regs[reg_len] as nx_int != 0 { reg_len = reg_len + 1 } 157 var pad: nx_int = 11 - reg_len 158 while pad > 0 { 159 let _sp: i64 = print(" " as *u8) 160 pad = pad - 1 161 } 162 let _p7: i64 = print(" " as *u8) 163 let _p8: i64 = print(e.name_ptr) 164 let _p9: i64 = println("" as *u8) 165 i = i + 1 166 } 167 println("" as *u8) 168 let _t1: i64 = print("Total: " as *u8) 169 let _t2: i64 = print_i64(db.n as i64) 170 let _t3: i64 = println(" compounds" as *u8) 171 println("" as *u8) 172 println("Use 'nx_supplement explain ID' for full details on a compound." as *u8) 173 return 0 174} 175 176// ================================================================= 177// EXPLAIN -- drill into one compound 178// ================================================================= 179func cmd_explain(id_str: *u8) -> i64 { 180 let id: nx_int = parse_int_arg(id_str) 181 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 182 let e: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, id) 183 if (e as nx_int) == 0 { 184 let _e1: i64 = print("nx_supplement explain: no compound with id=" as *u8) 185 let _e2: i64 = print_i64(id as i64) 186 let _e3: i64 = println("" as *u8) 187 println("Use 'nx_supplement compounds' to see the full list." as *u8) 188 return 1 189 } 190 println("==================================================================" as *u8) 191 let _h1: i64 = print(" COMPOUND DETAIL: id=" as *u8) 192 let _h2: i64 = print_i64(e.id as i64) 193 let _h3: i64 = print(" " as *u8) 194 let _h4: i64 = println(e.name_ptr) 195 println("==================================================================" as *u8) 196 let _r1: i64 = print(" Regulatory status : " as *u8) 197 let _r2: i64 = println(nx_chem_regulatory_str(e.regulatory)) 198 let _r3: i64 = print(" Citation : " as *u8) 199 let _r4: i64 = println(e.citation_ptr) 200 println("" as *u8) 201 println(" Analytical chemistry" as *u8) 202 let _a1: i64 = print(" Monoisotopic mass: " as *u8) 203 let _a2: i64 = print_q4_mass(e.monoiso_q4) 204 let _a3: i64 = println(" Da" as *u8) 205 let _a4: i64 = print(" [M+H]+ m/z : " as *u8) 206 let _a5: i64 = print_q4_mass(e.mh_plus_q4) 207 let _a6: i64 = println(" Da (LC-ESI+ target)" as *u8) 208 let _a7: i64 = print(" Heavy atoms : " as *u8) 209 let _a8: i64 = print_i64(e.n_heavy as i64) 210 let _a9: i64 = println("" as *u8) 211 let _b1: i64 = print(" M+1 intensity : " as *u8) 212 let _b2: i64 = print_i64(e.m1_q4 as i64) 213 let _b3: i64 = println("/10000 of M (isotopologue ratio)" as *u8) 214 let _b4: i64 = print(" M+2 intensity : " as *u8) 215 let _b5: i64 = print_i64(e.m2_q4 as i64) 216 let _b6: i64 = println("/10000 of M" as *u8) 217 let _c1: i64 = print(" Halogen sig : " as *u8) 218 if e.halogen_sig == 0 { 219 let _x: i64 = println("0 (no Cl/Br)" as *u8) 220 } 221 if e.halogen_sig == 1 { 222 let _x: i64 = println("1 (single Cl -- expect 3:1 M:M+2 pattern)" as *u8) 223 } 224 if e.halogen_sig == 2 { 225 let _x: i64 = println("2 (single Br -- expect 1:1 M:M+2 pattern)" as *u8) 226 } 227 if e.halogen_sig == 3 { 228 let _x: i64 = println("3 (multi-halogen; binomial expansion needed)" as *u8) 229 } 230 println("" as *u8) 231 if e.ref_rt_q3 > 0 { 232 println(" Retention time (loaded from calibration)" as *u8) 233 let _r5: i64 = print(" Reference RT : " as *u8) 234 let _r6: i64 = print_i64((e.ref_rt_q3 / 1000) as i64) 235 let _r7: i64 = print(" s (tolerance +/- " as *u8) 236 let _r8: i64 = print_i64((e.rt_tol_q3 / 1000) as i64) 237 let _r9: i64 = println(" s)" as *u8) 238 } 239 if e.ref_rt_q3 == 0 { 240 println(" Retention time : uncalibrated (use rt_cal.csv)" as *u8) 241 } 242 println("==================================================================" as *u8) 243 return 0 244} 245 246// ================================================================= 247// CHECK -- mirror of nx_supplement_check 248// ================================================================= 249func cmd_check(path: *u8) -> i64 { 250 let pl: *PeakList = nx_chem_peak_list_read_file(path) 251 if pl.n == 0 { 252 let msg: *u8 = "nx_supplement check: empty / unreadable peak file\n" as *u8 253 var n: i64 = 0 254 while msg[n] != 0 { n = n + 1 } 255 let _w: i64 = sys_write(2, msg, n) 256 return 1 257 } 258 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 259 let buf: *u8 = sys_mmap(32768) 260 let sample_id: *u8 = sys_mmap(256) 261 let _bn: nx_int = nx_chem_basename(path, sample_id) 262 let n_json: nx_int = nx_chem_emit_report_json(buf, pl, db, sample_id, 5) 263 let _w: i64 = sys_write(1, buf, n_json as i64) 264 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A 265 let _w2: i64 = sys_write(1, nl, 1) 266 var n_banned: nx_int = 0 267 var pi: nx_int = 0 268 while pi < pl.n { 269 let p: *PeakObservation = ((pl.peaks as nx_int) + (pi * NX_PEAK_OBS_BYTES)) as *PeakObservation 270 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult 271 let nm: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, p.mz_q4, p.rt_q3, 5, outs, 4) 272 var mi: nx_int = 0 273 while mi < nm { 274 let rr: *MatchResult = ((outs as nx_int) + (mi * NX_MATCH_RESULT_BYTES)) as *MatchResult 275 let ee: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, rr.entry_id) 276 if ee.regulatory == NX_REG_BANNED { n_banned = n_banned + 1 } 277 mi = mi + 1 278 } 279 pi = pi + 1 280 } 281 return n_banned as i64 282} 283 284// ================================================================= 285// VALIDATE -- exhaustive self-validation: every compound in the DB 286// is detected by its own [M+H]+. Gives lab partner Day-1 confidence: 287// "this tool detects every compound it claims to detect". 288// 289// Algorithm: 290// For each entry e in db.entries: 291// 1. Build a synthetic peak at mz = e.mh_plus_q4 292// 2. Look up in db (within 5 ppm) 293// 3. Verify a match returns entry_id == e.id 294// Report per-compound result + overall verdict. 295// ================================================================= 296func cmd_validate() -> i64 { 297 println("Validating: each compound's own [M+H]+ matches itself..." as *u8) 298 println("" as *u8) 299 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 300 var n_pass: nx_int = 0 301 var n_fail: nx_int = 0 302 var i: nx_int = 0 303 while i < db.n { 304 let e: *AdulterantEntry = ((db.entries as nx_int) + (i * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry 305 // Build a single-peak PeakList at this entry's [M+H]+ 306 let pl: *PeakList = nx_chem_peak_list_new(2) 307 let _a: nx_int = nx_chem_peak_list_add(pl, e.mh_plus_q4, 100000, 0) 308 // Lookup at 5 ppm tolerance 309 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult 310 let nm: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, e.mh_plus_q4, 0, 5, outs, 4) 311 var ok: nx_int = 0 312 if nm > 0 { 313 let r0: *MatchResult = ((outs as nx_int) + (0 * NX_MATCH_RESULT_BYTES)) as *MatchResult 314 if r0.entry_id == e.id { ok = 1 } 315 } 316 let _t1: i64 = print(" [") 317 if ok == 1 { 318 let _t2: i64 = print("PASS" as *u8) 319 n_pass = n_pass + 1 320 } 321 if ok == 0 { 322 let _t2f: i64 = print("FAIL" as *u8) 323 n_fail = n_fail + 1 324 } 325 let _t3: i64 = print("] id=" as *u8) 326 if e.id < 10 { let _z: i64 = print(" " as *u8) } 327 let _t4: i64 = print_i64(e.id as i64) 328 let _t5: i64 = print(" [M+H]+ " as *u8) 329 let _t6: i64 = print_q4_mass(e.mh_plus_q4) 330 let _t7: i64 = print(" " as *u8) 331 let _t8: i64 = println(e.name_ptr) 332 i = i + 1 333 } 334 println("" as *u8) 335 let _v1: i64 = print("Result: " as *u8) 336 let _v2: i64 = print_i64(n_pass as i64) 337 let _v3: i64 = print(" PASS, " as *u8) 338 let _v4: i64 = print_i64(n_fail as i64) 339 let _v5: i64 = print(" FAIL (of " as *u8) 340 let _v6: i64 = print_i64(db.n as i64) 341 let _v7: i64 = println(" compounds in DB)" as *u8) 342 if n_fail == 0 { 343 println("" as *u8) 344 println("OK -- substrate detects every reference compound it claims to detect." as *u8) 345 println(" Lab partnership ready: pipe LC-MS peaks through nx_supplement check." as *u8) 346 return 0 347 } 348 return n_fail as i64 349} 350 351// ================================================================= 352// SELFTEST -- run bundled examples + verify expected outcomes 353// ================================================================= 354func cmd_selftest() -> i64 { 355 println("Running selftest on bundled example corpus..." as *u8) 356 println("" as *u8) 357 // Probe for the example dir. Literal paths need nx_chem_path_from_literal 358 // before being passed to syscalls (see litpath quirk; runtime/ 359 // nx_litpath_quirk_test.nx documents the bug class). 360 let try1: *u8 = nx_chem_path_from_literal("examples/supplement-arc/sample_clean.csv" as *u8) 361 let try2: *u8 = nx_chem_path_from_literal("nxc2/examples/supplement-arc/sample_clean.csv" as *u8) 362 let pl_try1: *PeakList = nx_chem_peak_list_read_file(try1) 363 let pl_try2: *PeakList = nx_chem_peak_list_read_file(try2) 364 var prefix: *u8 = 0 as *u8 365 if pl_try1.n > 0 { 366 prefix = nx_chem_path_from_literal("examples/supplement-arc/" as *u8) 367 } 368 if (prefix as nx_int) == 0 { 369 if pl_try2.n > 0 { 370 prefix = nx_chem_path_from_literal("nxc2/examples/supplement-arc/" as *u8) 371 } 372 } 373 if (prefix as nx_int) == 0 { 374 println(" FAIL: cannot find bundled example data" as *u8) 375 println(" Tried: examples/supplement-arc/sample_clean.csv" as *u8) 376 println(" nxc2/examples/supplement-arc/sample_clean.csv" as *u8) 377 println("" as *u8) 378 println(" Run selftest from the nxc2 directory (or the parent of nxc2/)." as *u8) 379 return 1 380 } 381 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 382 var n_pass: nx_int = 0 383 var n_fail: nx_int = 0 384 // Test 1: clean sample -> ACCEPT 385 let path_clean: *u8 = sys_mmap(256) 386 let plen1: nx_int = nx_chem_emit_str(path_clean, 0, prefix) 387 let _x1: nx_int = nx_chem_emit_str(path_clean, plen1, "sample_clean.csv" as *u8) 388 let pl_clean: *PeakList = nx_chem_peak_list_read_file(path_clean) 389 if pl_clean.n == 1 { 390 let _t1: i64 = println(" [PASS] sample_clean.csv: 1 peak parsed (caffeine)" as *u8) 391 n_pass = n_pass + 1 392 } 393 if pl_clean.n != 1 { 394 let _t1f: i64 = println(" [FAIL] sample_clean.csv: expected 1 peak" as *u8) 395 n_fail = n_fail + 1 396 } 397 // Test 2: contaminated sample -> 3 peaks, 2 BANNED + 1 RESTRICTED 398 let path_contam: *u8 = sys_mmap(256) 399 let plen2: nx_int = nx_chem_emit_str(path_contam, 0, prefix) 400 let _x2: nx_int = nx_chem_emit_str(path_contam, plen2, "sample_contaminated.csv" as *u8) 401 let pl_contam: *PeakList = nx_chem_peak_list_read_file(path_contam) 402 if pl_contam.n == 3 { 403 let _t2: i64 = println(" [PASS] sample_contaminated.csv: 3 peaks parsed" as *u8) 404 n_pass = n_pass + 1 405 } 406 if pl_contam.n != 3 { 407 let _t2f: i64 = println(" [FAIL] sample_contaminated.csv: expected 3 peaks" as *u8) 408 n_fail = n_fail + 1 409 } 410 // Test 3: count banned matches in contaminated sample 411 var n_banned_contam: nx_int = 0 412 var ci: nx_int = 0 413 while ci < pl_contam.n { 414 let p: *PeakObservation = ((pl_contam.peaks as nx_int) + (ci * NX_PEAK_OBS_BYTES)) as *PeakObservation 415 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult 416 let nm: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, p.mz_q4, p.rt_q3, 5, outs, 4) 417 var mi: nx_int = 0 418 while mi < nm { 419 let rr: *MatchResult = ((outs as nx_int) + (mi * NX_MATCH_RESULT_BYTES)) as *MatchResult 420 let ee: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, rr.entry_id) 421 if ee.regulatory == NX_REG_BANNED { n_banned_contam = n_banned_contam + 1 } 422 mi = mi + 1 423 } 424 ci = ci + 1 425 } 426 if n_banned_contam == 2 { 427 let _t3: i64 = println(" [PASS] sample_contaminated.csv: 2 BANNED matches (REJECT verdict)" as *u8) 428 n_pass = n_pass + 1 429 } 430 if n_banned_contam != 2 { 431 let _t3f: i64 = print(" [FAIL] sample_contaminated.csv: expected 2 banned, got " as *u8) 432 let _t3f2: i64 = print_i64(n_banned_contam as i64) 433 let _t3f3: i64 = println("" as *u8) 434 n_fail = n_fail + 1 435 } 436 // Test 4: DB has >= 24 compounds 437 if db.n >= 24 { 438 let _t4: i64 = print(" [PASS] built-in compound DB populated (" as *u8) 439 let _t4n: i64 = print_i64(db.n as i64) 440 let _t4e: i64 = println(" compounds)" as *u8) 441 n_pass = n_pass + 1 442 } 443 if db.n < 24 { 444 let _t4f: i64 = println(" [FAIL] expected >= 24 compounds in built-in seed" as *u8) 445 n_fail = n_fail + 1 446 } 447 // Test 5: substrate version 448 let _t5: i64 = print(" [INFO] " as *u8) 449 let _t5n: i64 = println(nx_sup_version()) 450 println("" as *u8) 451 if n_fail == 0 { 452 let _r1: i64 = print("OK -- " as *u8) 453 let _r2: i64 = print_i64(n_pass as i64) 454 let _r3: i64 = println(" of 4 checks passed. Substrate ready for production use." as *u8) 455 return 0 456 } 457 let _f1: i64 = print("FAILED -- " as *u8) 458 let _f2: i64 = print_i64(n_fail as i64) 459 let _f3: i64 = print(" of " as *u8) 460 let _f4: i64 = print_i64((n_pass + n_fail) as i64) 461 let _f5: i64 = println(" checks failed. Substrate may not be properly built." as *u8) 462 return n_fail as i64 463} 464 465// ================================================================= 466// BATCH -- mirror of nx_supplement_batch (3-arg form) 467// ================================================================= 468func process_one_sample_batch(db: *AdulterantDB, peak_path: *u8) -> nx_int { 469 let pl: *PeakList = nx_chem_peak_list_read_file(peak_path) 470 if pl.n == 0 { 471 let err_buf: *u8 = sys_mmap(256) 472 var w: nx_int = 0 473 w = w + nx_chem_emit_str(err_buf, w, "{\"sample_id\":" as *u8) 474 let sid: *u8 = sys_mmap(256) 475 let _b1: nx_int = nx_chem_basename(peak_path, sid) 476 w = w + nx_chem_emit_str_escaped(err_buf, w, sid) 477 w = w + nx_chem_emit_str(err_buf, w, ",\"error\":\"empty or unreadable peak file\"}\n" as *u8) 478 let _w: i64 = sys_write(1, err_buf, w as i64) 479 return 0 480 } 481 let sample_id: *u8 = sys_mmap(256) 482 let _bn: nx_int = nx_chem_basename(peak_path, sample_id) 483 let buf: *u8 = sys_mmap(32768) 484 let n_json: nx_int = nx_chem_emit_report_json(buf, pl, db, sample_id, 5) 485 let _w1: i64 = sys_write(1, buf, n_json as i64) 486 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A 487 let _w2: i64 = sys_write(1, nl, 1) 488 var n_banned: nx_int = 0 489 var pi: nx_int = 0 490 while pi < pl.n { 491 let p: *PeakObservation = ((pl.peaks as nx_int) + (pi * NX_PEAK_OBS_BYTES)) as *PeakObservation 492 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult 493 let nm: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, p.mz_q4, p.rt_q3, 5, outs, 4) 494 var mi: nx_int = 0 495 while mi < nm { 496 let rr: *MatchResult = ((outs as nx_int) + (mi * NX_MATCH_RESULT_BYTES)) as *MatchResult 497 let ee: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, rr.entry_id) 498 if ee.regulatory == NX_REG_BANNED { n_banned = n_banned + 1 } 499 mi = mi + 1 500 } 501 pi = pi + 1 502 } 503 return n_banned 504} 505 506func cmd_batch(argc: i64, argv: *i64, start_idx: i64) -> i64 { 507 if argc - start_idx < 3 { 508 let msg: *u8 = "nx_supplement batch: usage: <compounds-or-dash> <cal-or-dash> <peaks...>\n" as *u8 509 var n: i64 = 0 510 while msg[n] != 0 { n = n + 1 } 511 let _w: i64 = sys_write(2, msg, n) 512 return 1 513 } 514 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 515 let cmp_path: *u8 = argv[start_idx] as *u8 516 if is_dash(cmp_path) == 0 { 517 let _nc: nx_int = nx_chem_load_compounds_from_file(db, cmp_path) 518 } 519 let cal_path: *u8 = argv[start_idx + 1] as *u8 520 if is_dash(cal_path) == 0 { 521 let _nr: nx_int = nx_chem_load_rt_calibration(db, cal_path) 522 } 523 var total_banned: i64 = 0 524 var i: i64 = start_idx + 2 525 while i < argc { 526 let peak_path: *u8 = argv[i] as *u8 527 let b: nx_int = process_one_sample_batch(db, peak_path) 528 total_banned = total_banned + (b as i64) 529 i = i + 1 530 } 531 return total_banned 532} 533 534// ================================================================= 535// CSV -- mirror of nx_supplement_csv 536// ================================================================= 537func process_one_csv_row(db: *AdulterantDB, peak_path: *u8) -> nx_int { 538 let pl: *PeakList = nx_chem_peak_list_read_file(peak_path) 539 if pl.n == 0 { return 0 } 540 let sample_id: *u8 = sys_mmap(256) 541 let _bn: nx_int = nx_chem_basename(peak_path, sample_id) 542 let buf: *u8 = sys_mmap(32768) 543 let n: nx_int = nx_chem_emit_report_csv_rows_only(buf, pl, db, sample_id, 5) 544 let _w: i64 = sys_write(1, buf, n as i64) 545 var n_banned: nx_int = 0 546 var pi: nx_int = 0 547 while pi < pl.n { 548 let p: *PeakObservation = ((pl.peaks as nx_int) + (pi * NX_PEAK_OBS_BYTES)) as *PeakObservation 549 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult 550 let nm: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, p.mz_q4, p.rt_q3, 5, outs, 4) 551 var mi: nx_int = 0 552 while mi < nm { 553 let rr: *MatchResult = ((outs as nx_int) + (mi * NX_MATCH_RESULT_BYTES)) as *MatchResult 554 let ee: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, rr.entry_id) 555 if ee.regulatory == NX_REG_BANNED { n_banned = n_banned + 1 } 556 mi = mi + 1 557 } 558 pi = pi + 1 559 } 560 return n_banned 561} 562 563func cmd_csv(argc: i64, argv: *i64, start_idx: i64) -> i64 { 564 if argc - start_idx < 2 { 565 let msg: *u8 = "nx_supplement csv: usage: <cal-or-dash> <peaks...>\n" as *u8 566 var n: i64 = 0 567 while msg[n] != 0 { n = n + 1 } 568 let _w: i64 = sys_write(2, msg, n) 569 return 1 570 } 571 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 572 let cal_path: *u8 = argv[start_idx] as *u8 573 if is_dash(cal_path) == 0 { 574 let _nr: nx_int = nx_chem_load_rt_calibration(db, cal_path) 575 } 576 let hbuf: *u8 = sys_mmap(256) 577 let nh: nx_int = nx_chem_emit_csv_header(hbuf, 0) 578 let _wh: i64 = sys_write(1, hbuf, nh as i64) 579 var total_banned: i64 = 0 580 var i: i64 = start_idx + 1 581 while i < argc { 582 let peak_path: *u8 = argv[i] as *u8 583 let b: nx_int = process_one_csv_row(db, peak_path) 584 total_banned = total_banned + (b as i64) 585 i = i + 1 586 } 587 return total_banned 588} 589 590// ================================================================= 591// MAIN DISPATCH 592// ================================================================= 593func main(argc: i64, argv: *i64) -> i64 { 594 if argc < 2 { return cmd_help() } 595 let sub: *u8 = argv[1] as *u8 596 if str_eq(sub, "help" as *u8) == 1 { return cmd_help() } 597 if str_eq(sub, "--help" as *u8) == 1 { return cmd_help() } 598 if str_eq(sub, "-h" as *u8) == 1 { return cmd_help() } 599 if str_eq(sub, "version" as *u8) == 1 { return cmd_version() } 600 if str_eq(sub, "--version" as *u8) == 1 { return cmd_version() } 601 if str_eq(sub, "compounds" as *u8) == 1 { return cmd_compounds() } 602 if str_eq(sub, "selftest" as *u8) == 1 { return cmd_selftest() } 603 if str_eq(sub, "validate" as *u8) == 1 { return cmd_validate() } 604 if str_eq(sub, "explain" as *u8) == 1 { 605 if argc < 3 { 606 let msg: *u8 = "nx_supplement explain: usage: explain <id>\n" as *u8 607 var n: i64 = 0 608 while msg[n] != 0 { n = n + 1 } 609 let _w: i64 = sys_write(2, msg, n) 610 return 1 611 } 612 return cmd_explain(argv[2] as *u8) 613 } 614 if str_eq(sub, "check" as *u8) == 1 { 615 if argc < 3 { 616 let msg: *u8 = "nx_supplement check: usage: check <peak-file>\n" as *u8 617 var n: i64 = 0 618 while msg[n] != 0 { n = n + 1 } 619 let _w: i64 = sys_write(2, msg, n) 620 return 1 621 } 622 return cmd_check(argv[2] as *u8) 623 } 624 if str_eq(sub, "batch" as *u8) == 1 { 625 return cmd_batch(argc, argv, 2) 626 } 627 if str_eq(sub, "csv" as *u8) == 1 { 628 return cmd_csv(argc, argv, 2) 629 } 630 let msg: *u8 = "nx_supplement: unknown subcommand. Run 'nx_supplement help' for usage.\n" as *u8 631 var n: i64 = 0 632 while msg[n] != 0 { n = n + 1 } 633 let _w: i64 = sys_write(2, msg, n) 634 return 1 635}