nx_chem_adulterant_db_test.nx source
↩ module page · 468 lines · 23850 B
1// nx_chem_adulterant_db_test.nx -- C4.0 KAT + END-TO-END lab demo.
2//
3// The KAT validates that the seed DB parses correctly, m/z values
4// match published reference, and lookup-by-m/z returns expected
5// matches. The demo at the end prints a realistic lab-report output
6// for a synthetic 4-peak LC-MS observation.
7//
8// expect_exit: 0
9// license_tier: ORIGINAL
10
11import "nx_chem.nx"
12import "nx_chem_molecule.nx"
13import "nx_chem_smiles.nx"
14import "nx_chem_periodic.nx"
15import "nx_chem_valence.nx"
16import "nx_chem_mass.nx"
17import "nx_chem_isotope_pattern.nx"
18import "nx_chem_adulterant_db.nx"
19import "nx_chem_peak_list.nx"
20
21// =================================================================
22// A -- seed DB must populate 24 entries
23// =================================================================
24func a_seed_count() -> nx_int {
25 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
26 if db.n != 33 { return 11 }
27 return 0
28}
29
30// =================================================================
31// B -- sibutramine entry (id=4) must have halogen signature 1 (Cl)
32// and be flagged BANNED
33// =================================================================
34func b_sibutramine_signature() -> nx_int {
35 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
36 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
37 if e.id != 4 { return 21 }
38 if e.halogen_sig != 1 { return 22 }
39 if e.regulatory != NX_REG_BANNED { return 23 }
40 return 0
41}
42
43// =================================================================
44// C -- caffeine entry (id=5) must have NO halogen signature and be
45// flagged APPROVED (baseline)
46// =================================================================
47func c_caffeine_signature() -> nx_int {
48 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
49 let e: *AdulterantEntry = ((db.entries as nx_int) + (5 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
50 if e.id != 5 { return 31 }
51 if e.halogen_sig != 0 { return 32 }
52 if e.regulatory != NX_REG_APPROVED { return 33 }
53 return 0
54}
55
56// =================================================================
57// D -- lookup observed peak at sibutramine [M+H]+ within 5 ppm
58// should return exactly 1 match = id 4 (sibutramine).
59// Sibutramine C17H26ClN [M+H]+ = 280.18 m/z; compute from our DB.
60// =================================================================
61func d_lookup_sibutramine() -> nx_int {
62 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
63 // Get sibutramine's own [M+H]+ from DB and query at that exact m/z
64 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
65 let target: nx_int = e.mh_plus_q4
66 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
67 let n: nx_int = nx_chem_adulterant_db_lookup_mh_plus(db, target, 5, out, 4)
68 if n != 1 { return 41 }
69 let r0: *MatchResult = ((out as nx_int) + (0 * NX_MATCH_RESULT_BYTES)) as *MatchResult
70 if r0.entry_id != 4 { return 42 }
71 return 0
72}
73
74// =================================================================
75// E -- lookup at a wildly off-mass peak should return 0 matches
76// =================================================================
77func e_lookup_no_match() -> nx_int {
78 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
79 let target: nx_int = 9999999 // 999.9999 Da -- nothing in DB matches
80 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
81 let n: nx_int = nx_chem_adulterant_db_lookup_mh_plus(db, target, 5, out, 4)
82 if n != 0 { return 51 }
83 return 0
84}
85
86// =================================================================
87// F -- 5 ppm tolerance is non-zero: a 3 ppm offset peak SHOULD still match
88// =================================================================
89func f_lookup_3ppm_offset() -> nx_int {
90 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
91 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
92 // Add 3 ppm offset: target = e.mh_plus_q4 * 1000003 / 1000000
93 let target: nx_int = e.mh_plus_q4 + ((e.mh_plus_q4 * 3) / 1000000)
94 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
95 let n: nx_int = nx_chem_adulterant_db_lookup_mh_plus(db, target, 5, out, 4)
96 if n != 1 { return 61 }
97 return 0
98}
99
100// =================================================================
101// G -- at 5 ppm tolerance, a 10 ppm offset should NOT match
102// =================================================================
103func g_lookup_10ppm_offset() -> nx_int {
104 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
105 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
106 let target: nx_int = e.mh_plus_q4 + ((e.mh_plus_q4 * 10) / 1000000)
107 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
108 let n: nx_int = nx_chem_adulterant_db_lookup_mh_plus(db, target, 5, out, 4)
109 if n != 0 { return 71 }
110 return 0
111}
112
113// =================================================================
114// I -- C7.0 RT filter: when ref_rt unset (0), lookup_with_rt is
115// mass-only and behaves identically to lookup_mh_plus
116// =================================================================
117func i_rt_lookup_no_ref_rt() -> nx_int {
118 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
119 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
120 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
121 let n: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, e.mh_plus_q4, 1000000, 5, out, 4)
122 if n != 1 { return 111 }
123 let r0: *MatchResult = ((out as nx_int) + (0 * NX_MATCH_RESULT_BYTES)) as *MatchResult
124 if r0.entry_id != 4 { return 112 }
125 return 0
126}
127
128// =================================================================
129// J -- C7.0 set ref_rt, then off-rt query within mass tolerance
130// should be REJECTED by the RT filter
131// =================================================================
132func j_rt_off_rejected() -> nx_int {
133 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
134 // Set ref_rt for sibutramine to 740 sec (740000 ms) ± 30 sec
135 let _s: nx_int = nx_chem_adulterant_db_set_rt(db, 4, 740000, 30000)
136 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
137 // Query at sibutramine mass + RT 1000s (260s off from ref 740s -- way outside tol)
138 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
139 let n: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, e.mh_plus_q4, 1000000, 5, out, 4)
140 if n != 0 { return 121 }
141 return 0
142}
143
144// =================================================================
145// K -- C7.0 set ref_rt, then on-rt query within mass + RT tolerance
146// should MATCH
147// =================================================================
148func k_rt_on_matches() -> nx_int {
149 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
150 let _s: nx_int = nx_chem_adulterant_db_set_rt(db, 4, 740000, 30000)
151 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
152 // Query at ref RT 750s = 10s offset (within ±30s tol)
153 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
154 let n: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, e.mh_plus_q4, 750000, 5, out, 4)
155 if n != 1 { return 131 }
156 return 0
157}
158
159// =================================================================
160// L -- C7.0 RT filter is bypassed if observed RT is 0 (caller didn't
161// provide RT info), even when ref_rt is set
162// =================================================================
163func l_rt_zero_observed_bypasses() -> nx_int {
164 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
165 let _s: nx_int = nx_chem_adulterant_db_set_rt(db, 4, 740000, 30000)
166 let e: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
167 let out: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
168 let n: nx_int = nx_chem_adulterant_db_lookup_with_rt(db, e.mh_plus_q4, 0, 5, out, 4)
169 if n != 1 { return 141 }
170 return 0
171}
172
173// =================================================================
174// H -- 8 entries' m/z values should all be distinct
175// (sanity check the seed data doesn't have accidental collisions)
176// =================================================================
177func h_distinct_mz() -> nx_int {
178 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
179 var i: nx_int = 0
180 while i < db.n {
181 let ei: *AdulterantEntry = ((db.entries as nx_int) + (i * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
182 var j: nx_int = i + 1
183 while j < db.n {
184 let ej: *AdulterantEntry = ((db.entries as nx_int) + (j * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
185 if ei.mh_plus_q4 == ej.mh_plus_q4 {
186 // Allow same m/z only if the SMILES happen to be the same
187 // formula by coincidence; flag for review.
188 return 81
189 }
190 j = j + 1
191 }
192 i = i + 1
193 }
194 return 0
195}
196
197// =================================================================
198// O -- C4.3 load compounds from disk file (DB extensibility)
199// =================================================================
200func o_load_compounds() -> nx_int {
201 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
202 let initial_count: nx_int = db.n
203 // Build CSV-like text with two compounds
204 // "100|test1|CCO|1|test approved\n101|test2|CC(N)C|3|test banned\n"
205 let src: *u8 = sys_mmap(256)
206 src[0] = 0x31; src[1] = 0x30; src[2] = 0x30; src[3] = 0x7C // "100|"
207 src[4] = 0x74; src[5] = 0x65; src[6] = 0x73; src[7] = 0x74; src[8] = 0x31; src[9] = 0x7C // "test1|"
208 src[10] = 0x43; src[11] = 0x43; src[12] = 0x4F; src[13] = 0x7C // "CCO|"
209 src[14] = 0x31; src[15] = 0x7C // "1|"
210 src[16] = 0x74; src[17] = 0x65; src[18] = 0x73; src[19] = 0x74; src[20] = 0x20; src[21] = 0x61; src[22] = 0x70; src[23] = 0x70; src[24] = 0x72 // "test appr"
211 src[25] = 0x0A
212 src[26] = 0x31; src[27] = 0x30; src[28] = 0x31; src[29] = 0x7C // "101|"
213 src[30] = 0x74; src[31] = 0x65; src[32] = 0x73; src[33] = 0x74; src[34] = 0x32; src[35] = 0x7C // "test2|"
214 src[36] = 0x43; src[37] = 0x43; src[38] = 0x28; src[39] = 0x4E; src[40] = 0x29; src[41] = 0x43; src[42] = 0x7C // "CC(N)C|"
215 src[43] = 0x33; src[44] = 0x7C // "3|"
216 src[45] = 0x74; src[46] = 0x65; src[47] = 0x73; src[48] = 0x74; src[49] = 0x20; src[50] = 0x62; src[51] = 0x61; src[52] = 0x6E // "test ban"
217 src[53] = 0x0A
218 let n_src: nx_int = 54
219 let path: *u8 = sys_mmap(64)
220 path[0] = 0x2F; path[1] = 0x74; path[2] = 0x6D; path[3] = 0x70
221 path[4] = 0x2F; path[5] = 0x6E; path[6] = 0x78; path[7] = 0x5F
222 path[8] = 0x6C; path[9] = 0x6F; path[10] = 0x61; path[11] = 0x64
223 path[12] = 0x5F; path[13] = 0x6B; path[14] = 0x61; path[15] = 0x74
224 path[16] = 0x2E; path[17] = 0x74; path[18] = 0x73; path[19] = 0x76
225 path[20] = 0
226 let nw: nx_int = nx_chem_write_file(path, src, n_src)
227 if nw != n_src { return 171 }
228 let n_loaded: nx_int = nx_chem_load_compounds_from_file(db, path)
229 if n_loaded != 2 { return 172 }
230 if db.n != initial_count + 2 { return 173 }
231 // Lookup test1 by id 100
232 let e1: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, 100)
233 if (e1 as nx_int) == 0 { return 174 }
234 if e1.regulatory != NX_REG_APPROVED { return 175 }
235 if e1.name_ptr[0] as nx_int != 0x74 { return 176 } // 't'
236 if e1.name_ptr[4] as nx_int != 0x31 { return 177 } // '1'
237 // Lookup test2 by id 101
238 let e2: *AdulterantEntry = nx_chem_adulterant_entry_by_id(db, 101)
239 if (e2 as nx_int) == 0 { return 178 }
240 if e2.regulatory != NX_REG_BANNED { return 179 }
241 return 0
242}
243
244// =================================================================
245// N -- C10.0 multi-halogen entries: clenbuterol (2 Cl -> sig 3),
246// 2C-B (1 Br -> sig 2), salbutamol (no halogen -> sig 0)
247// =================================================================
248func n_new_halogen_sigs() -> nx_int {
249 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
250 let e_salb: *AdulterantEntry = ((db.entries as nx_int) + (21 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
251 if e_salb.halogen_sig != 0 { return 161 }
252 let e_clen: *AdulterantEntry = ((db.entries as nx_int) + (22 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
253 if e_clen.halogen_sig != 3 { return 162 } // 2 Cl -> multi-halogen sig
254 let e_2cb: *AdulterantEntry = ((db.entries as nx_int) + (23 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
255 if e_2cb.halogen_sig != 2 { return 163 } // 1 Br -> Br 1:1 sig
256 return 0
257}
258
259// =================================================================
260// M -- C7.1 load RT calibration from CSV file
261// =================================================================
262func m_load_rt_calibration() -> nx_int {
263 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
264 // Build CSV text in memory
265 // "# RT calibration\n4 740.0 30.0\n1 45.0 15.0\n"
266 let src: *u8 = sys_mmap(128)
267 src[0] = 0x23; src[1] = 0x20; src[2] = 0x52; src[3] = 0x54
268 src[4] = 0x0A
269 src[5] = 0x34 // '4'
270 src[6] = 0x20
271 src[7] = 0x37; src[8] = 0x34; src[9] = 0x30; src[10] = 0x2E; src[11] = 0x30
272 src[12] = 0x20
273 src[13] = 0x33; src[14] = 0x30; src[15] = 0x2E; src[16] = 0x30
274 src[17] = 0x0A
275 src[18] = 0x31 // '1'
276 src[19] = 0x20
277 src[20] = 0x34; src[21] = 0x35; src[22] = 0x2E; src[23] = 0x30
278 src[24] = 0x20
279 src[25] = 0x31; src[26] = 0x35; src[27] = 0x2E; src[28] = 0x30
280 src[29] = 0x0A
281 let n_src: nx_int = 30
282 // Write to disk
283 let path: *u8 = sys_mmap(64)
284 path[0] = 0x2F; path[1] = 0x74; path[2] = 0x6D; path[3] = 0x70
285 path[4] = 0x2F; path[5] = 0x6E; path[6] = 0x78; path[7] = 0x5F
286 path[8] = 0x72; path[9] = 0x74; path[10] = 0x5F
287 path[11] = 0x63; path[12] = 0x61; path[13] = 0x6C; path[14] = 0x2E
288 path[15] = 0x63; path[16] = 0x73; path[17] = 0x76
289 path[18] = 0
290 let nw: nx_int = nx_chem_write_file(path, src, n_src)
291 if nw != n_src { return 151 }
292 // Load calibration
293 let n_loaded: nx_int = nx_chem_load_rt_calibration(db, path)
294 if n_loaded != 2 { return 152 }
295 // Verify sibutramine (id 4) has ref_rt = 740000 ms (740 sec)
296 let e4: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
297 if e4.ref_rt_q3 != 740000 { return 153 }
298 if e4.rt_tol_q3 != 30000 { return 154 }
299 // Verify DMAA (id 1) has ref_rt = 45000 ms (45 sec)
300 let e1: *AdulterantEntry = ((db.entries as nx_int) + (1 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
301 if e1.ref_rt_q3 != 45000 { return 155 }
302 if e1.rt_tol_q3 != 15000 { return 156 }
303 return 0
304}
305
306// =================================================================
307// LAB-FACING END-TO-END DEMO:
308// Simulate a lab analyst feeding in 4 LC-MS peaks from a suspect
309// supplement and producing a verdict report.
310// =================================================================
311func run_lab_demo() -> nx_int {
312 println("" as *u8)
313 println("==================================================================" as *u8)
314 println(" SUPPLEMENT ADULTERANT DETECTION REPORT (LC-MS pipeline demo)" as *u8)
315 println("==================================================================" as *u8)
316 println(" Sample : SUSPECT-2026-001 (pre-workout powder)" as *u8)
317 println(" Method : LC-ESI(+)-MS, 5 ppm tolerance" as *u8)
318 println(" Substrate : nx_chem bits-up sovereign analytical-chem stack" as *u8)
319 println(" Pipeline : C2.1 SMILES -> C2.8a [M+H]+ -> 5ppm DB lookup" as *u8)
320 println("" as *u8)
321 println(" Observed peaks:" as *u8)
322 let db: *AdulterantDB = nx_chem_adulterant_db_seed()
323 // 4 synthetic peaks: use DB-computed m/z to ensure exact match
324 let e_sibutramine: *AdulterantEntry = ((db.entries as nx_int) + (4 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
325 let e_dmaa: *AdulterantEntry = ((db.entries as nx_int) + (1 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
326 let e_caffeine: *AdulterantEntry = ((db.entries as nx_int) + (5 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
327 let e_ephedrine: *AdulterantEntry = ((db.entries as nx_int) + (0 * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
328 let peaks: *nx_int = (sys_mmap((4 * 8) as i64)) as *nx_int
329 peaks[0] = e_sibutramine.mh_plus_q4 + 5 // small noise offset
330 peaks[1] = e_dmaa.mh_plus_q4
331 peaks[2] = e_caffeine.mh_plus_q4
332 peaks[3] = e_ephedrine.mh_plus_q4
333 var n_banned: nx_int = 0
334 var n_restricted: nx_int = 0
335 var n_unknown: nx_int = 0
336 var pk: nx_int = 0
337 while pk < 4 {
338 let mz: nx_int = peaks[pk]
339 let outs: *MatchResult = (sys_mmap((4 * NX_MATCH_RESULT_BYTES) as i64)) as *MatchResult
340 let nm: nx_int = nx_chem_adulterant_db_lookup_mh_plus(db, mz, 5, outs, 4)
341 if nm == 0 {
342 let _u1: i64 = print(" m/z " as *u8)
343 let _u2: i64 = print_q4_mass(mz)
344 let _u3: i64 = println(" -> NO MATCH (unidentified)" as *u8)
345 n_unknown = n_unknown + 1
346 }
347 if nm > 0 {
348 var mi: nx_int = 0
349 while mi < nm {
350 let rr: *MatchResult = ((outs as nx_int) + (mi * NX_MATCH_RESULT_BYTES)) as *MatchResult
351 let _r: nx_int = nx_chem_adulterant_report_match(mz, db, rr)
352 let _cite_pad: i64 = print(" cite: " as *u8)
353 let _cite: i64 = println(nx_chem_adulterant_citation(rr.entry_id))
354 let ee: *AdulterantEntry = ((db.entries as nx_int) + (rr.entry_id * NX_ADULTERANT_ENTRY_BYTES)) as *AdulterantEntry
355 if ee.regulatory == NX_REG_BANNED { n_banned = n_banned + 1 }
356 if ee.regulatory == NX_REG_RESTRICTED { n_restricted = n_restricted + 1 }
357 mi = mi + 1
358 }
359 }
360 pk = pk + 1
361 }
362 println("" as *u8)
363 println(" VERDICT:" as *u8)
364 let _v1: i64 = print(" Banned ingredients detected : " as *u8)
365 let _v2: i64 = print_i64(n_banned as i64)
366 let _v3: i64 = println("" as *u8)
367 let _v4: i64 = print(" Restricted ingredients : " as *u8)
368 let _v5: i64 = print_i64(n_restricted as i64)
369 let _v6: i64 = println("" as *u8)
370 let _v7: i64 = print(" Unidentified peaks : " as *u8)
371 let _v8: i64 = print_i64(n_unknown as i64)
372 let _v9: i64 = println("" as *u8)
373 if n_banned > 0 {
374 println(" *** RECOMMENDATION: REJECT -- contains banned ingredient(s) ***" as *u8)
375 }
376 if n_banned == 0 {
377 if n_restricted > 0 {
378 println(" RECOMMENDATION: review label claim vs restricted ingredient list" as *u8)
379 }
380 if n_restricted == 0 {
381 println(" RECOMMENDATION: no banned/restricted ingredients in matched peaks" as *u8)
382 }
383 }
384 println("==================================================================" as *u8)
385 return 0
386}
387
388func main() -> nx_exit {
389 println("=== nx_chem_adulterant_db -- C4.0 KAT: seed DB + lookup pipeline ===" as *u8)
390
391 let ra: nx_int = a_seed_count()
392 if ra != 0 { println("A seed_count FAIL" as *u8); return ra }
393 println("A seed_count PASS 33 reference adulterants populated" as *u8)
394
395 let rb: nx_int = b_sibutramine_signature()
396 if rb != 0 { println("B sibutramine_signature FAIL" as *u8); return rb }
397 println("B sibutramine_sig PASS id=4 has Cl halogen sig + BANNED status" as *u8)
398
399 let rc: nx_int = c_caffeine_signature()
400 if rc != 0 { println("C caffeine_signature FAIL" as *u8); return rc }
401 println("C caffeine_sig PASS id=5 has no halogen sig + APPROVED status" as *u8)
402
403 let rd: nx_int = d_lookup_sibutramine()
404 if rd != 0 { println("D lookup_sibutramine FAIL" as *u8); return rd }
405 println("D lookup_sibutramine PASS exact m/z lookup returns sibutramine match" as *u8)
406
407 let re: nx_int = e_lookup_no_match()
408 if re != 0 { println("E lookup_no_match FAIL" as *u8); return re }
409 println("E lookup_no_match PASS off-mass m/z returns zero matches" as *u8)
410
411 let rf: nx_int = f_lookup_3ppm_offset()
412 if rf != 0 { println("F lookup_3ppm_offset FAIL" as *u8); return rf }
413 println("F lookup_3ppm_offset PASS 3 ppm offset within 5 ppm tolerance" as *u8)
414
415 let rg: nx_int = g_lookup_10ppm_offset()
416 if rg != 0 { println("G lookup_10ppm_offset FAIL" as *u8); return rg }
417 println("G lookup_10ppm_offset PASS 10 ppm offset OUTSIDE 5 ppm tolerance (correctly rejected)" as *u8)
418
419 let rh: nx_int = h_distinct_mz()
420 if rh != 0 { println("H distinct_mz FAIL (seed data has m/z collision)" as *u8); return rh }
421 println("H distinct_mz PASS no accidental m/z collisions in seed" as *u8)
422
423 let ri: nx_int = i_rt_lookup_no_ref_rt()
424 if ri != 0 { println("I rt_lookup_no_ref_rt FAIL" as *u8); return ri }
425 println("I rt_no_ref_rt PASS RT filter no-op when ref_rt unset (mass-only)" as *u8)
426
427 let rj: nx_int = j_rt_off_rejected()
428 if rj != 0 { println("J rt_off_rejected FAIL" as *u8); return rj }
429 println("J rt_off_rejected PASS off-RT same-mass peak correctly rejected" as *u8)
430
431 let rk: nx_int = k_rt_on_matches()
432 if rk != 0 { println("K rt_on_matches FAIL" as *u8); return rk }
433 println("K rt_on_matches PASS on-RT (within tolerance) matches" as *u8)
434
435 let rl: nx_int = l_rt_zero_observed_bypasses()
436 if rl != 0 { println("L rt_zero_observed FAIL" as *u8); return rl }
437 println("L rt_zero_observed PASS obs_rt=0 bypasses RT filter (graceful)" as *u8)
438
439 let rm: nx_int = m_load_rt_calibration()
440 if rm != 0 { println("M load_rt_calibration FAIL" as *u8); return rm }
441 println("M load_rt_calibration PASS CSV file -> ref_rt + tol populated in DB" as *u8)
442
443 let rn: nx_int = n_new_halogen_sigs()
444 if rn != 0 { println("N new_halogen_sigs FAIL" as *u8); return rn }
445 println("N new_halogen_sigs PASS salbutamol=0 clenbuterol=3(multi-Cl) 2C-B=2(Br)" as *u8)
446
447 let ro: nx_int = o_load_compounds()
448 if ro != 0 { println("O load_compounds FAIL" as *u8); return ro }
449 println("O load_compounds PASS pipe-separated TSV -> 2 compounds loaded into DB" as *u8)
450
451 let _demo: nx_int = run_lab_demo()
452
453 println("" as *u8)
454 println("=== C4.0 substrate milestone PASS ===" as *u8)
455 println(" AdulterantDB struct + 8 reference compounds seeded via SMILES parsing" as *u8)
456 println(" Lookup primitive: 5 ppm window m/z -> matching reference entries" as *u8)
457 println(" End-to-end demo: 4-peak synthetic LC-MS -> verdict report with REJECT" as *u8)
458 println(" recommendation when banned ingredients detected" as *u8)
459 println(" FIRST USABLE MILESTONE: a lab analyst could read this output and act" as *u8)
460 println("" as *u8)
461 println(" Composes: C2.1 SMILES parse, C2.3a implicit-H, C2.8a [M+H]+, C2.8b" as *u8)
462 println(" halogen sig + isotope pattern. End-to-end SMILES -> verdict." as *u8)
463 println("" as *u8)
464 println(" Honest gaps: persistence (memory-only), retention-time matching not yet," as *u8)
465 println(" complex-SMILES adulterants (sildenafil, tadalafil) deferred" as *u8)
466 println(" until parser handles their aromatic+saturated mixed rings." as *u8)
467 return 0
468}