nx_chem_molecule_test.nx source
↩ module page · 279 lines · 11527 B
1// nx_chem_molecule_test.nx -- C2.0 KAT for MolGraph struct + builder.
2//
3// All molecules constructed by hand (no SMILES parser yet -- that's
4// C2.1). Verifies: struct layout, atom + bond adders, bounded
5// lookups, error-code path on overflow, count-by-Z helper, and the
6// convenience builders for the canonical reference molecules.
7//
8// expect_exit: 0
9//
10// license_tier: ORIGINAL
11
12import "nx_chem_molecule.nx"
13
14// ============================================================
15// A -- new + empty graph defaults.
16// ============================================================
17func a_new_empty() -> nx_int {
18 let m: *MolGraph = nx_chem_mol_new(8, 8)
19 if m.n_atoms != 0 { return 11 }
20 if m.n_bonds != 0 { return 12 }
21 if m.capacity_atoms != 8 { return 13 }
22 if m.capacity_bonds != 8 { return 14 }
23 if m.is_valid != 1 { return 15 }
24 if m.err_code != NX_MOL_OK { return 16 }
25 let null_a: *Atom = nx_chem_mol_atom(m, 0)
26 if (null_a as nx_int) != 0 { return 17 } // empty graph: lookup must return null
27 let null_b: *Bond = nx_chem_mol_bond(m, 0)
28 if (null_b as nx_int) != 0 { return 18 }
29 return 0
30}
31
32// ============================================================
33// B -- atom_add appends + lookup returns the same atom.
34// ============================================================
35func b_atom_add() -> nx_int {
36 let m: *MolGraph = nx_chem_mol_new(4, 4)
37 let i: nx_int = nx_chem_mol_atom_add(m, 8)
38 if i != 0 { return 21 }
39 if m.n_atoms != 1 { return 22 }
40 let a: *Atom = nx_chem_mol_atom(m, 0)
41 if (a as nx_int) == 0 { return 23 }
42 if a.z != 8 { return 24 }
43 if a.charge != 0 { return 25 }
44 if a.isotope != 0 { return 26 }
45 if a.h_count != -1 { return 27 } // implicit by default
46 if a.aromaticity != 0 { return 28 }
47 if a.stereo != NX_STEREO_NONE { return 29 }
48 // out-of-range lookup must return null
49 let bad: *Atom = nx_chem_mol_atom(m, 5)
50 if (bad as nx_int) != 0 { return 30 }
51 let neg: *Atom = nx_chem_mol_atom(m, -1)
52 if (neg as nx_int) != 0 { return 31 }
53 return 0
54}
55
56// ============================================================
57// C -- bond_add appends + endpoint validation.
58// ============================================================
59func c_bond_add() -> nx_int {
60 let m: *MolGraph = nx_chem_mol_new(4, 4)
61 let c1: nx_int = nx_chem_mol_atom_add(m, 6)
62 let c2: nx_int = nx_chem_mol_atom_add(m, 6)
63 let bi: nx_int = nx_chem_mol_bond_add(m, c1, c2, NX_BOND_SINGLE)
64 if bi != 0 { return 41 }
65 if m.n_bonds != 1 { return 42 }
66 let b: *Bond = nx_chem_mol_bond(m, 0)
67 if (b as nx_int) == 0 { return 43 }
68 if b.a != c1 { return 44 }
69 if b.b != c2 { return 45 }
70 if b.order != NX_BOND_SINGLE { return 46 }
71 if b.in_ring != 0 { return 47 }
72 return 0
73}
74
75// ============================================================
76// D -- atom-overflow returns -1 + sets structured error.
77// ============================================================
78func d_atom_overflow() -> nx_int {
79 let m: *MolGraph = nx_chem_mol_new(2, 2)
80 let _ok1: nx_int = nx_chem_mol_atom_add(m, 6)
81 let _ok2: nx_int = nx_chem_mol_atom_add(m, 6)
82 let fail: nx_int = nx_chem_mol_atom_add(m, 6)
83 if fail != -1 { return 51 }
84 if m.is_valid != 0 { return 52 }
85 if m.err_code != NX_MOL_ERR_ATOM_OVERFLOW { return 53 }
86 return 0
87}
88
89// ============================================================
90// E -- bond-add rejects invalid atom index with structured error.
91// ============================================================
92func e_bond_bad_atom() -> nx_int {
93 let m: *MolGraph = nx_chem_mol_new(4, 4)
94 let _c1: nx_int = nx_chem_mol_atom_add(m, 6)
95 // try to bond atom 0 to atom 5 (which doesn't exist)
96 let fail: nx_int = nx_chem_mol_bond_add(m, 0, 5, NX_BOND_SINGLE)
97 if fail != -1 { return 61 }
98 if m.is_valid != 0 { return 62 }
99 if m.err_code != NX_MOL_ERR_BAD_ATOM_INDEX { return 63 }
100 return 0
101}
102
103// ============================================================
104// F -- bond-add rejects invalid bond order.
105// ============================================================
106func f_bond_bad_order() -> nx_int {
107 let m: *MolGraph = nx_chem_mol_new(4, 4)
108 let c1: nx_int = nx_chem_mol_atom_add(m, 6)
109 let c2: nx_int = nx_chem_mol_atom_add(m, 6)
110 let fail: nx_int = nx_chem_mol_bond_add(m, c1, c2, 99)
111 if fail != -1 { return 71 }
112 if m.err_code != NX_MOL_ERR_BAD_BOND_ORDER { return 72 }
113 return 0
114}
115
116// ============================================================
117// G -- water builder: single O atom, 0 bonds.
118// ============================================================
119func g_water() -> nx_int {
120 let m: *MolGraph = nx_chem_mol_water()
121 if m.n_atoms != 1 { return 81 }
122 if m.n_bonds != 0 { return 82 }
123 let o: *Atom = nx_chem_mol_atom(m, 0)
124 if o.z != 8 { return 83 }
125 if nx_chem_mol_count_z(m, 8) != 1 { return 84 }
126 if nx_chem_mol_count_z(m, 6) != 0 { return 85 }
127 return 0
128}
129
130// ============================================================
131// H -- methane builder: single C atom.
132// ============================================================
133func h_methane() -> nx_int {
134 let m: *MolGraph = nx_chem_mol_methane()
135 if m.n_atoms != 1 { return 91 }
136 let c: *Atom = nx_chem_mol_atom(m, 0)
137 if c.z != 6 { return 92 }
138 return 0
139}
140
141// ============================================================
142// I -- ethanol builder: C-C-O, 2 single bonds.
143// ============================================================
144func i_ethanol() -> nx_int {
145 let m: *MolGraph = nx_chem_mol_ethanol()
146 if m.n_atoms != 3 { return 101 }
147 if m.n_bonds != 2 { return 102 }
148 if nx_chem_mol_count_z(m, 6) != 2 { return 103 } // 2 carbons
149 if nx_chem_mol_count_z(m, 8) != 1 { return 104 } // 1 oxygen
150 let b0: *Bond = nx_chem_mol_bond(m, 0)
151 if b0.order != NX_BOND_SINGLE { return 105 }
152 if b0.a != 0 { return 106 } // C-C
153 if b0.b != 1 { return 107 }
154 let b1: *Bond = nx_chem_mol_bond(m, 1)
155 if b1.order != NX_BOND_SINGLE { return 108 }
156 if b1.a != 1 { return 109 } // C-O
157 if b1.b != 2 { return 110 }
158 return 0
159}
160
161// ============================================================
162// J -- ethene builder: C=C.
163// ============================================================
164func j_ethene() -> nx_int {
165 let m: *MolGraph = nx_chem_mol_ethene()
166 if m.n_atoms != 2 { return 121 }
167 if m.n_bonds != 1 { return 122 }
168 let b: *Bond = nx_chem_mol_bond(m, 0)
169 if b.order != NX_BOND_DOUBLE { return 123 }
170 return 0
171}
172
173// ============================================================
174// K -- ethyne builder: C#C.
175// ============================================================
176func k_ethyne() -> nx_int {
177 let m: *MolGraph = nx_chem_mol_ethyne()
178 if m.n_atoms != 2 { return 131 }
179 if m.n_bonds != 1 { return 132 }
180 let b: *Bond = nx_chem_mol_bond(m, 0)
181 if b.order != NX_BOND_TRIPLE { return 133 }
182 return 0
183}
184
185// ============================================================
186// L -- benzene builder: 6 aromatic carbons in a ring.
187// Verifies 6-aromaticity-tuple (E3 EXCEED axis) is lit for every
188// supported model on every atom + every bond + ring perception flag.
189// ============================================================
190func l_benzene() -> nx_int {
191 let m: *MolGraph = nx_chem_mol_benzene()
192 if m.n_atoms != 6 { return 141 }
193 if m.n_bonds != 6 { return 142 }
194 if nx_chem_mol_count_z(m, 6) != 6 { return 143 }
195 let all_arom: nx_int = NX_AROM_DAYLIGHT | NX_AROM_OPENEYE | NX_AROM_RDKIT | NX_AROM_MDL | NX_AROM_TRIPOS | NX_AROM_MMFF
196 // every atom: aromaticity bits all set + in_ring
197 var i: nx_int = 0
198 while i < 6 {
199 let a: *Atom = nx_chem_mol_atom(m, i)
200 if a.aromaticity != all_arom { return 150 + i }
201 if a.in_ring != 1 { return 160 + i }
202 if a.z != 6 { return 170 + i }
203 i = i + 1
204 }
205 // every bond: order=aromatic + aromaticity bits all set + in_ring
206 var j: nx_int = 0
207 while j < 6 {
208 let b: *Bond = nx_chem_mol_bond(m, j)
209 if b.order != NX_BOND_AROMATIC { return 180 + j }
210 if b.aromaticity != all_arom { return 190 + j }
211 if b.in_ring != 1 { return 200 + j }
212 j = j + 1
213 }
214 // ring closure: bond 5 connects atom 5 back to atom 0
215 let b5: *Bond = nx_chem_mol_bond(m, 5)
216 if b5.a != 5 { return 211 }
217 if b5.b != 0 { return 212 }
218 return 0
219}
220
221func main() -> nx_exit {
222 println("=== nx_chem_molecule -- C2.0 KAT: MolGraph struct + builders ===" as *u8)
223
224 let ra: nx_int = a_new_empty()
225 if ra != 0 { println("A new_empty FAIL" as *u8); return ra }
226 println("A new_empty PASS fresh graph: 0 atoms / 0 bonds / valid / null lookups" as *u8)
227
228 let rb: nx_int = b_atom_add()
229 if rb != 0 { println("B atom_add FAIL" as *u8); return rb }
230 println("B atom_add PASS atom_add returns index 0; lookup returns matching atom; bounds reject" as *u8)
231
232 let rc: nx_int = c_bond_add()
233 if rc != 0 { println("C bond_add FAIL" as *u8); return rc }
234 println("C bond_add PASS bond_add appends + records endpoints + order" as *u8)
235
236 let rd: nx_int = d_atom_overflow()
237 if rd != 0 { println("D atom_overflow FAIL" as *u8); return rd }
238 println("D atom_overflow PASS capacity exceeded -> -1 + is_valid=0 + structured err_code" as *u8)
239
240 let re: nx_int = e_bond_bad_atom()
241 if re != 0 { println("E bond_bad_atom FAIL" as *u8); return re }
242 println("E bond_bad_atom PASS bond_add rejects out-of-range atom index with structured err" as *u8)
243
244 let rf: nx_int = f_bond_bad_order()
245 if rf != 0 { println("F bond_bad_order FAIL" as *u8); return rf }
246 println("F bond_bad_order PASS bond_add rejects invalid bond order with structured err" as *u8)
247
248 let rg: nx_int = g_water()
249 if rg != 0 { println("G water FAIL" as *u8); return rg }
250 println("G water PASS H2O = 1 O atom (H-suppressed); count_z(O)=1, count_z(C)=0" as *u8)
251
252 let rh: nx_int = h_methane()
253 if rh != 0 { println("H methane FAIL" as *u8); return rh }
254 println("H methane PASS CH4 = 1 C atom (H-suppressed)" as *u8)
255
256 let ri: nx_int = i_ethanol()
257 if ri != 0 { println("I ethanol FAIL" as *u8); return ri }
258 println("I ethanol PASS CCO = C-C-O; 2 single bonds; 2C + 1O" as *u8)
259
260 let rj: nx_int = j_ethene()
261 if rj != 0 { println("J ethene FAIL" as *u8); return rj }
262 println("J ethene PASS C=C; 1 double bond" as *u8)
263
264 let rk: nx_int = k_ethyne()
265 if rk != 0 { println("K ethyne FAIL" as *u8); return rk }
266 println("K ethyne PASS C#C; 1 triple bond" as *u8)
267
268 let rl: nx_int = l_benzene()
269 if rl != 0 { println("L benzene FAIL" as *u8); return rl }
270 println("L benzene PASS c1ccccc1: 6C + 6 aromatic bonds; 6-aromaticity-tuple lit on every atom+bond; ring perception flagged" as *u8)
271
272 println("" as *u8)
273 println("=== C2.0 substrate milestone PASS ===" as *u8)
274 println(" MolGraph : Atom + Bond + graph container with capacity-checked appends" as *u8)
275 println(" EXCEED axes : E3 (6-aromaticity-tuple slot; lit for benzene) + E4 (per-bond stereo slot)" as *u8)
276 println(" forward-compat : isotope / charge / radical / stereo / map_num / in_ring slots present" as *u8)
277 println(" next : C2.1 -- nx_chem_smiles parser (organic subset + bracket atoms basic)" as *u8)
278 return 0
279}