code wiki / (root) / nx_chem_adulterant_charge_test.nx

nx_chem_adulterant_charge_test.nx source

↩ module page · 131 lines · 7883 B

1// nx_chem_adulterant_charge_test.nx -- gate for the seq651 fix: charge-state 2// screening in nx_chem_adulterant_db. 3// 4// The decisive test is T4/T5: take a REAL catalog entry, synthesise the peak 5// an electrospray source would actually produce for it at z=2 and z=3, and 6// show the OLD single-charge lookup returns ZERO while the new charge-aware 7// lookup identifies it. That is the defect reproduced and then closed, not 8// a claim that it was. 9// expect_exit: 0 license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_chem.nx" 12import "nx_chem_molecule.nx" 13import "nx_chem_periodic.nx" 14import "nx_chem_mass.nx" 15import "nx_chem_adulterant_db.nx" 16 17func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 18func t_putn(v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0 } 19 20// The m/z an ESI source gives for a neutral mass M at charge z: 21// (M + z*proton) / z 22func esi_mz(mh_plus_q4: i64, z: i64) -> i64 { 23 let neutral: i64 = mh_plus_q4 - NX_PROTON_MASS_Q4 24 let protons: i64 = z * NX_PROTON_MASS_Q4 25 let tot: i64 = neutral + protons 26 return tot / z 27} 28 29func main() -> i64 { 30 var pass: i64 = 0 31 var total: i64 = 0 32 33 let db: *AdulterantDB = nx_chem_adulterant_db_seed() 34 let out: *MatchResult = (sys_mmap(NX_MATCH_RESULT_BYTES * 32)) as *MatchResult 35 36 // --- T1 the seed DB is populated and entry 0 has a real m/z --- 37 total = total + 1 38 let e0: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, 0) 39 let mh0: i64 = e0.mh_plus_q4 40 t_puts("T1 seed entries=" as *u8); t_putn(db.n); t_puts(" entry0 [M+H]+ Q4=" as *u8); t_putn(mh0); t_puts(": " as *u8) 41 if db.n > 0 { if mh0 > 0 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 42 43 // --- T2 the change of variable is EXACT: translate back and forth --- 44 total = total + 1 45 let mz2: i64 = esi_mz(mh0, 2) 46 let mz3: i64 = esi_mz(mh0, 3) 47 let back2: i64 = nx_chem_adulterant_mz_to_mh_plus(mz2, 2) 48 let back3: i64 = nx_chem_adulterant_mz_to_mh_plus(mz3, 3) 49 t_puts("T2 z=2 peak " as *u8); t_putn(mz2); t_puts(" -> " as *u8); t_putn(back2); t_puts(" ; z=3 peak " as *u8); t_putn(mz3); t_puts(" -> " as *u8); t_putn(back3); t_puts(" want " as *u8); t_putn(mh0); t_puts(" (+/-3): " as *u8) 50 var d2: i64 = back2 - mh0 51 if d2 < 0 { d2 = 0 - d2 } 52 var d3: i64 = back3 - mh0 53 if d3 < 0 { d3 = 0 - d3 } 54 if d2 <= 3 { if d3 <= 3 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 55 56 // --- T3 singly-charged peaks still match (no regression) --- 57 total = total + 1 58 let n1_old: i64 = nx_chem_adulterant_db_lookup_with_rt(db, mh0, 0, 20, out, 32) 59 let n1_new: i64 = nx_chem_adulterant_db_lookup_charges(db, mh0, 0, 20, 4, out, 32) 60 t_puts("T3 [M+H]+ peak: old lookup=" as *u8); t_putn(n1_old); t_puts(" charge-aware=" as *u8); t_putn(n1_new); t_puts(" (both >=1): " as *u8) 61 if n1_old >= 1 { if n1_new >= 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 62 63 // --- T4 THE DEFECT, REPRODUCED: a z=2 peak is INVISIBLE to the old path --- 64 total = total + 1 65 let n2_old: i64 = nx_chem_adulterant_db_lookup_with_rt(db, mz2, 0, 20, out, 32) 66 let n2_new: i64 = nx_chem_adulterant_db_lookup_charges(db, mz2, 0, 20, 4, out, 32) 67 t_puts("T4 z=2 peak: old lookup=" as *u8); t_putn(n2_old); t_puts(" (blind) charge-aware=" as *u8); t_putn(n2_new); t_puts(" (finds it): " as *u8) 68 if n2_old == 0 { if n2_new >= 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 69 70 // --- T5 same at z=3, and it resolves to the RIGHT compound --- 71 total = total + 1 72 let n3_old: i64 = nx_chem_adulterant_db_lookup_with_rt(db, mz3, 0, 20, out, 32) 73 let n3_new: i64 = nx_chem_adulterant_db_lookup_charges(db, mz3, 0, 20, 4, out, 32) 74 var id3: i64 = 0 - 1 75 if n3_new >= 1 { id3 = out.entry_id } 76 t_puts("T5 z=3 peak: old=" as *u8); t_putn(n3_old); t_puts(" charge-aware=" as *u8); t_putn(n3_new); t_puts(" identified entry_id=" as *u8); t_putn(id3); t_puts(" want 0/>=1/0: " as *u8) 77 var ok5: i64 = 1 78 if n3_old != 0 { ok5 = 0 } 79 if n3_new < 1 { ok5 = 0 } 80 if id3 != 0 { ok5 = 0 } 81 if ok5 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 82 83 // --- T6 ppm tolerance SURVIVES the translation --- 84 // A peak 10 ppm off at z=2 must still match at a 20 ppm window, and must 85 // NOT match at 5 ppm. If the translation broke the ratio, one of these 86 // would flip. 87 total = total + 1 88 let off: i64 = mz2 / 100000 89 let near2: i64 = mz2 + off 90 let loose: i64 = nx_chem_adulterant_db_lookup_charges(db, near2, 0, 20, 4, out, 32) 91 let tight: i64 = nx_chem_adulterant_db_lookup_charges(db, near2, 0, 5, 4, out, 32) 92 t_puts("T6 z=2 peak +10ppm: at 20ppm window=" as *u8); t_putn(loose); t_puts(" at 5ppm window=" as *u8); t_putn(tight); t_puts(" want >=1/0: " as *u8) 93 if loose >= 1 { if tight == 0 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 94 95 // --- T7 max_charge=0 degrades to old behaviour, never to nothing --- 96 total = total + 1 97 let z0: i64 = nx_chem_adulterant_db_lookup_charges(db, mh0, 0, 20, 0, out, 32) 98 let zneg: i64 = nx_chem_adulterant_db_lookup_charges(db, mh0, 0, 20, 0 - 5, out, 32) 99 t_puts("T7 max_charge 0/-5 on a [M+H]+ peak = " as *u8); t_putn(z0); t_puts("/" as *u8); t_putn(zneg); t_puts(" (both >=1, degrade to z=1): " as *u8) 100 if z0 >= 1 { if zneg >= 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } } else { t_puts("FAIL\n" as *u8) } 101 102 // --- T8 non-physical inputs refuse rather than fabricate --- 103 total = total + 1 104 let bad_z: i64 = nx_chem_adulterant_mz_to_mh_plus(mz2, 0) 105 let bad_mz: i64 = nx_chem_adulterant_mz_to_mh_plus(0, 2) 106 let bad_neg: i64 = nx_chem_adulterant_mz_to_mh_plus(0 - 100, 2) 107 t_puts("T8 z=0 -> " as *u8); t_putn(bad_z); t_puts(" mz=0 -> " as *u8); t_putn(bad_mz); t_puts(" mz<0 -> " as *u8); t_putn(bad_neg); t_puts(" (all 0): " as *u8) 108 var ok8: i64 = 1 109 if bad_z != 0 { ok8 = 0 } 110 if bad_mz != 0 { ok8 = 0 } 111 if bad_neg != 0 { ok8 = 0 } 112 if ok8 == 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 113 114 // --- T9 an unrelated peak matches at NO charge state (neg control) --- 115 // Guards against the change of variable turning the screen into a sieve 116 // that hits on anything. 117 total = total + 1 118 let junk: i64 = nx_chem_adulterant_db_lookup_charges(db, 3333333, 0, 20, 4, out, 32) 119 t_puts("T9 unrelated peak m/z 333.33 across z=1..4 matches=" as *u8); t_putn(junk); t_puts(" want 0: " as *u8) 120 if junk == 0 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 121 122 // --- T10 the output cap is respected (no overrun past max_out) --- 123 total = total + 1 124 let capped: i64 = nx_chem_adulterant_db_lookup_charges(db, mh0, 0, 20, 4, out, 1) 125 t_puts("T10 max_out=1 returns " as *u8); t_putn(capped); t_puts(" (<=1): " as *u8) 126 if capped <= 1 { pass = pass + 1; t_puts("PASS\n" as *u8) } else { t_puts("FAIL\n" as *u8) } 127 128 t_puts("ADULTERANT-CHARGE-GATE passed " as *u8); t_putn(pass); t_puts("/" as *u8); t_putn(total) 129 if pass == total { t_puts(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 130 t_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1 131}