nx_chem_isotope_pattern_test.nx source
↩ module page · 239 lines · 9869 B
1// nx_chem_isotope_pattern_test.nx -- C2.8b KAT.
2//
3// All M+1/M+2 reference values from textbook + IUPAC 2021 abundances.
4// First-order linear approximation -- KATs use single-halogen molecules
5// where first-order is accurate to within rounding.
6//
7// expect_exit: 0
8// license_tier: ORIGINAL
9
10import "nx_chem.nx"
11import "nx_chem_molecule.nx"
12import "nx_chem_smiles.nx"
13import "nx_chem_valence.nx"
14import "nx_chem_isotope_pattern.nx"
15
16func parse_with_h(src: *u8, n: nx_int) -> *MolGraph {
17 let m: *MolGraph = nx_chem_parse_smiles(src, n)
18 let _f: nx_int = nx_chem_compute_implicit_h(m)
19 return m
20}
21
22// =================================================================
23// A -- water "O"
24// Atoms: 1 O + 2 H (implicit)
25// M+1 = 2*2 (H) + 1*4 (O) = 4 + 4 = 8 (0.08% of M)
26// M+2 = 1*21 (O) = 21 (0.21% of M)
27// =================================================================
28func a_water() -> nx_int {
29 let s: *u8 = sys_mmap(8); s[0] = 0x4F
30 let m: *MolGraph = parse_with_h(s, 1)
31 let m1: nx_int = nx_chem_isotope_m1_q4(m)
32 let m2: nx_int = nx_chem_isotope_m2_q4(m)
33 if m1 != 8 { return 11 }
34 if m2 != 21 { return 12 }
35 return 0
36}
37
38// =================================================================
39// B -- methane "C"
40// 1 C + 4 H. M+1 = 108 + 4*2 = 116 (1.16% of M). M+2 = 0.
41// =================================================================
42func b_methane() -> nx_int {
43 let s: *u8 = sys_mmap(8); s[0] = 0x43
44 let m: *MolGraph = parse_with_h(s, 1)
45 let m1: nx_int = nx_chem_isotope_m1_q4(m)
46 let m2: nx_int = nx_chem_isotope_m2_q4(m)
47 if m1 != 116 { return 21 }
48 if m2 != 0 { return 22 }
49 return 0
50}
51
52// =================================================================
53// C -- ethanol "CCO"
54// 2 C + 6 H + 1 O. M+1 = 2*108 + 6*2 + 1*4 = 216+12+4 = 232 (2.32%)
55// M+2 = 1*21 = 21
56// =================================================================
57func c_ethanol() -> nx_int {
58 let s: *u8 = sys_mmap(8); s[0] = 0x43; s[1] = 0x43; s[2] = 0x4F
59 let m: *MolGraph = parse_with_h(s, 3)
60 let m1: nx_int = nx_chem_isotope_m1_q4(m)
61 let m2: nx_int = nx_chem_isotope_m2_q4(m)
62 if m1 != 232 { return 31 }
63 if m2 != 21 { return 32 }
64 return 0
65}
66
67// =================================================================
68// D -- benzene "c1ccccc1"
69// 6 C + 6 H. M+1 = 6*108 + 6*2 = 648+12 = 660 (6.60%). M+2 = 0.
70// (Each aromatic c gets 1 H from compute_implicit_h)
71// =================================================================
72func d_benzene() -> nx_int {
73 let s: *u8 = sys_mmap(16)
74 s[0] = 0x63; s[1] = 0x31
75 s[2] = 0x63; s[3] = 0x63
76 s[4] = 0x63; s[5] = 0x63
77 s[6] = 0x63; s[7] = 0x31
78 let m: *MolGraph = parse_with_h(s, 8)
79 let m1: nx_int = nx_chem_isotope_m1_q4(m)
80 let m2: nx_int = nx_chem_isotope_m2_q4(m)
81 if m1 != 660 { return 41 }
82 if m2 != 0 { return 42 }
83 return 0
84}
85
86// =================================================================
87// E -- chloromethane "CCl" -> "C[Cl]" tricky, use bracket
88// Atoms: 1 C + 3 H + 1 Cl.
89// Wait -- C with one Cl substituent: valence_bits gets 1 from C-Cl
90// bond; CH3Cl => h_count=3.
91// M+1 = 1*108 + 3*2 + 0 = 114 (1.14% of M)
92// M+2 = 1*3196 (Cl) = 3196 (31.96% of M) -- the classic 3:1 Cl signature!
93// =================================================================
94func e_chloromethane() -> nx_int {
95 let s: *u8 = sys_mmap(16)
96 s[0] = 0x43; s[1] = 0x43; s[2] = 0x6C // "CCl"
97 let m: *MolGraph = parse_with_h(s, 3)
98 let m1: nx_int = nx_chem_isotope_m1_q4(m)
99 let m2: nx_int = nx_chem_isotope_m2_q4(m)
100 if m1 != 114 { return 51 }
101 if m2 != 3196 { return 52 }
102 return 0
103}
104
105// =================================================================
106// F -- bromomethane "CBr"
107// 1 C + 3 H + 1 Br. M+1 = 1*108 + 3*2 = 114. M+2 = 9728 (97.28%) -- the classic 1:1 Br signature!
108// =================================================================
109func f_bromomethane() -> nx_int {
110 let s: *u8 = sys_mmap(16)
111 s[0] = 0x43; s[1] = 0x42; s[2] = 0x72 // "CBr"
112 let m: *MolGraph = parse_with_h(s, 3)
113 let m1: nx_int = nx_chem_isotope_m1_q4(m)
114 let m2: nx_int = nx_chem_isotope_m2_q4(m)
115 if m1 != 114 { return 61 }
116 if m2 != 9728 { return 62 }
117 return 0
118}
119
120// =================================================================
121// G -- methylamine "CN"
122// 1 C + 5 H + 1 N. M+1 = 108 + 5*2 + 37 = 155 (1.55%). M+2 = 0.
123// =================================================================
124func g_methylamine() -> nx_int {
125 let s: *u8 = sys_mmap(8); s[0] = 0x43; s[1] = 0x4E
126 let m: *MolGraph = parse_with_h(s, 2)
127 let m1: nx_int = nx_chem_isotope_m1_q4(m)
128 let m2: nx_int = nx_chem_isotope_m2_q4(m)
129 if m1 != 155 { return 71 }
130 if m2 != 0 { return 72 }
131 return 0
132}
133
134// =================================================================
135// H -- methanethiol "CS"
136// 1 C + 4 H + 1 S. M+1 = 108 + 4*2 + 79 = 195. M+2 = 1*447 = 447 (4.47%)
137// =================================================================
138func h_methanethiol() -> nx_int {
139 let s: *u8 = sys_mmap(8); s[0] = 0x43; s[1] = 0x53
140 let m: *MolGraph = parse_with_h(s, 2)
141 let m1: nx_int = nx_chem_isotope_m1_q4(m)
142 let m2: nx_int = nx_chem_isotope_m2_q4(m)
143 if m1 != 195 { return 81 }
144 if m2 != 447 { return 82 }
145 return 0
146}
147
148// =================================================================
149// I -- halogen signature: water -> 0 (no Cl, no Br)
150// =================================================================
151func i_water_signature() -> nx_int {
152 let s: *u8 = sys_mmap(8); s[0] = 0x4F
153 let m: *MolGraph = parse_with_h(s, 1)
154 if nx_chem_halogen_signature(m) != 0 { return 91 }
155 return 0
156}
157
158// =================================================================
159// J -- chloromethane -> 1 (single Cl, 3:1 signature)
160// =================================================================
161func j_cl_signature() -> nx_int {
162 let s: *u8 = sys_mmap(8); s[0] = 0x43; s[1] = 0x43; s[2] = 0x6C
163 let m: *MolGraph = parse_with_h(s, 3)
164 if nx_chem_halogen_signature(m) != 1 { return 101 }
165 return 0
166}
167
168// =================================================================
169// K -- bromomethane -> 2 (single Br, 1:1 signature)
170// =================================================================
171func k_br_signature() -> nx_int {
172 let s: *u8 = sys_mmap(8); s[0] = 0x43; s[1] = 0x42; s[2] = 0x72
173 let m: *MolGraph = parse_with_h(s, 3)
174 if nx_chem_halogen_signature(m) != 2 { return 111 }
175 return 0
176}
177
178func main() -> nx_exit {
179 println("=== nx_chem_isotope_pattern -- C2.8b KAT: M+1 / M+2 pattern + halogen signatures ===" as *u8)
180
181 let ra: nx_int = a_water()
182 if ra != 0 { println("A water FAIL" as *u8); return ra }
183 println("A water PASS M+1=8/10000 (0.08%) M+2=21/10000 (0.21%)" as *u8)
184
185 let rb: nx_int = b_methane()
186 if rb != 0 { println("B methane FAIL" as *u8); return rb }
187 println("B methane PASS M+1=116/10000 (1.16% -- one C-13 + 4 H-2 contrib)" as *u8)
188
189 let rc: nx_int = c_ethanol()
190 if rc != 0 { println("C ethanol FAIL" as *u8); return rc }
191 println("C ethanol PASS M+1=232 (2.32%) M+2=21" as *u8)
192
193 let rd: nx_int = d_benzene()
194 if rd != 0 { println("D benzene FAIL" as *u8); return rd }
195 println("D benzene PASS M+1=660 (6.60% -- 6 C-13)" as *u8)
196
197 let re: nx_int = e_chloromethane()
198 if re != 0 { println("E chloromethane FAIL" as *u8); return re }
199 println("E chloromethane PASS M+1=114 M+2=3196 (31.96% -- classic Cl 3:1)" as *u8)
200
201 let rf: nx_int = f_bromomethane()
202 if rf != 0 { println("F bromomethane FAIL" as *u8); return rf }
203 println("F bromomethane PASS M+1=114 M+2=9728 (97.28% -- classic Br 1:1)" as *u8)
204
205 let rg: nx_int = g_methylamine()
206 if rg != 0 { println("G methylamine FAIL" as *u8); return rg }
207 println("G methylamine PASS M+1=155 (incl 15-N contribution)" as *u8)
208
209 let rh: nx_int = h_methanethiol()
210 if rh != 0 { println("H methanethiol FAIL" as *u8); return rh }
211 println("H methanethiol PASS M+1=195 M+2=447 (4.47% -- 34-S signature)" as *u8)
212
213 let ri: nx_int = i_water_signature()
214 if ri != 0 { println("I water signature FAIL" as *u8); return ri }
215 println("I water signature PASS 0 = no halogen" as *u8)
216
217 let rj: nx_int = j_cl_signature()
218 if rj != 0 { println("J Cl signature FAIL" as *u8); return rj }
219 println("J Cl signature PASS 1 = single Cl, 3:1 M:M+2 signature" as *u8)
220
221 let rk: nx_int = k_br_signature()
222 if rk != 0 { println("K Br signature FAIL" as *u8); return rk }
223 println("K Br signature PASS 2 = single Br, 1:1 M:M+2 signature" as *u8)
224
225 println("" as *u8)
226 println("=== C2.8b substrate milestone PASS ===" as *u8)
227 println(" isotope_m1_q4 : Q4 fraction-of-M for M+1 peak (first-order linear sum)" as *u8)
228 println(" isotope_m2_q4 : Q4 fraction-of-M for M+2 peak (first-order linear sum)" as *u8)
229 println(" halogen_signature : 0=none, 1=single Cl (3:1), 2=single Br (1:1), 3=multi/mixed" as *u8)
230 println(" Direct supplement-arc adulterant-class identifier from MS spectrum" as *u8)
231 println(" (sibutramine sig=1, 4-bromosalbutamol sig=2, etc.)" as *u8)
232 println(" honest gaps : multi-halogen binomial expansion (C2.8b.1)," as *u8)
233 println(" M+3 / M+4 peaks (C2.8b.2)," as *u8)
234 println(" convolution-style envelope a la BRAIN (C2.8c)" as *u8)
235 println(" composes : C1 AtomicData (z field per atom; iso_abund_ppm reserved for" as *u8)
236 println(" future binomial / convolution implementations)" as *u8)
237 println(" C2.0 MolGraph + C2.3a implicit-H (counts h_count for H total)" as *u8)
238 return 0
239}