code wiki / _hdl_build / nx_research_chem_parse_gate.nx
nx_research_chem_parse_gate.nx source
↩ module page · 172 lines · 10088 B
1// nx_research_chem_parse_gate.nx -- broaden the chemistry discoverer from hardcoded element-matrices to a real
2// general capability: PARSE arbitrary chemical formula strings (incl. parentheses like Ca(OH)2) into element
3// counts, build the element-count matrix, and DISCOVER the balancing coefficients (integer nullspace) by search,
4// verified by atom conservation. So "balance this reaction" works for ANY reaction typed as text, not three
5// baked-in matrices. Pure integer, deterministic. GREEN iff 6/6. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7
8func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
9func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-");m=0-m} let t:*u8=sys_mmap(24); 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; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1}; sys_write(1,o,k); return 0 }
10func g_row(id: *u8, ok: i64, pass: *i64) -> i64 { g_w(" "); g_w(id); g_w(": "); if ok==1 { g_w("OK\n"); pass[0]=pass[0]+1 } else { g_w("FAIL\n") } return 0 }
11
12func gcd(a: i64, b: i64) -> i64 { var x: i64=a; var y: i64=b; if x<0{x=0-x} if y<0{y=0-y} while y!=0 { let t: i64=x%y; x=y; y=t } return x }
13func gcd_vec(x: *i64, n: i64) -> i64 { var g: i64=x[0]; var i: i64=1; while i<n { g=gcd(g,x[i]); i=i+1 } return g }
14func balanced_ok(A: *i64, E: i64, S: i64, x: *i64) -> i64 {
15 var e: i64=0
16 while e<E { var sum: i64=0; var c: i64=0; while c<S { sum=sum+A[e*S+c]*x[c]; c=c+1 } if sum!=0 { return 0 } e=e+1 }
17 return 1
18}
19func balance(A: *i64, E: i64, S: i64, B: i64, xout: *i64) -> i64 {
20 let x: *i64 = sys_mmap(8*16) as *i64
21 var s: i64=0; while s<S { x[s]=1; s=s+1 }
22 var done: i64=0
23 while done==0 {
24 if balanced_ok(A,E,S,x)==1 {
25 var g: i64=x[0]; var i: i64=1; while i<S { g=gcd(g,x[i]); i=i+1 }
26 i=0; while i<S { xout[i]=x[i]/g; i=i+1 }
27 return 1
28 }
29 var p: i64=0; var carry: i64=1
30 while p<S { if carry==1 { x[p]=x[p]+1; if x[p]>B { x[p]=1 } else { carry=0 } } p=p+1 }
31 if carry==1 { done=1 }
32 }
33 return 0
34}
35func veq(a: *i64, b: *i64, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i] { return 0 } i=i+1 } return 1 }
36
37// ---- the chemical-formula parser ----
38func is_lower(c: i64) -> i64 { if c>=97 { if c<=122 { return 1 } } return 0 }
39func is_digit(c: i64) -> i64 { if c>=48 { if c<=57 { return 1 } } return 0 }
40func read_symbol(s: *u8, pos: *i64) -> i64 { // uppercase + optional one lowercase -> packed int
41 let c1: i64 = s[pos[0]] as i64; pos[0]=pos[0]+1
42 var packed: i64 = c1*256
43 let c2: i64 = s[pos[0]] as i64
44 if is_lower(c2)==1 { packed=c1*256+c2; pos[0]=pos[0]+1 }
45 return packed
46}
47func read_number(s: *u8, pos: *i64) -> i64 { // optional integer, default 1
48 var have: i64=0; var n: i64=0
49 while is_digit(s[pos[0]] as i64)==1 { n=n*10+((s[pos[0]] as i64)-48); pos[0]=pos[0]+1; have=1 }
50 if have==0 { return 1 }
51 return n
52}
53func elem_index(etab: *i64, nel: *i64, sym: i64) -> i64 {
54 var i: i64=0; while i<nel[0] { if etab[i]==sym { return i } i=i+1 }
55 let idx: i64=nel[0]; etab[idx]=sym; nel[0]=nel[0]+1; return idx
56}
57// parse from *pos until ')' or '\0', accumulating count*scale into counts[] (indexed by shared element table).
58func parse_seg(s: *u8, pos: *i64, etab: *i64, nel: *i64, counts: *i64, scale: i64) -> i64 {
59 while (s[pos[0]] as i64) != 0 {
60 let ch: i64 = s[pos[0]] as i64
61 if ch==41 { pos[0]=pos[0]+1; return 0 }
62 if ch==40 {
63 pos[0]=pos[0]+1
64 let tmp: *i64 = sys_mmap(8*16) as *i64; var z: i64=0; while z<16 { tmp[z]=0; z=z+1 }
65 parse_seg(s, pos, etab, nel, tmp, 1)
66 let num: i64 = read_number(s, pos)
67 var e: i64=0; while e<nel[0] { counts[e]=counts[e]+tmp[e]*num*scale; e=e+1 }
68 } else {
69 let sym: i64 = read_symbol(s, pos)
70 let idx: i64 = elem_index(etab, nel, sym)
71 let num: i64 = read_number(s, pos)
72 counts[idx]=counts[idx]+num*scale
73 }
74 }
75 return 0
76}
77func parse_one(s: *u8, etab: *i64, nel: *i64, counts: *i64) -> i64 {
78 var z: i64=0; while z<16 { counts[z]=0; z=z+1 }
79 let pos: *i64 = sys_mmap(8) as *i64; pos[0]=0
80 parse_seg(s, pos, etab, nel, counts, 1)
81 return 0
82}
83// parse all species (shared element table) -> element-count matrix A -> balance.
84func balance_reaction(strs: *i64, signs: *i64, S: i64, xout: *i64, B: i64) -> i64 {
85 let etab: *i64=sys_mmap(8*16) as *i64; let nel: *i64=sys_mmap(8) as *i64; nel[0]=0
86 let counts: *i64=sys_mmap(8*16*8) as *i64 // counts[sp*16 + e]
87 var sp: i64=0
88 while sp<S {
89 let cs: *i64 = ((counts as i64) + sp*16*8) as *i64
90 parse_one((strs[sp] as *u8), etab, nel, cs)
91 sp=sp+1
92 }
93 let A: *i64=sys_mmap(8*16*8) as *i64
94 var e: i64=0
95 while e<nel[0] { var s2: i64=0; while s2<S { let cs2: *i64=((counts as i64)+s2*16*8) as *i64; A[e*S+s2]=signs[s2]*cs2[e]; s2=s2+1 } e=e+1 }
96 return balance(A, nel[0], S, B, xout)
97}
98func conserves_reaction(strs: *i64, signs: *i64, S: i64, x: *i64) -> i64 {
99 let etab: *i64=sys_mmap(8*16) as *i64; let nel: *i64=sys_mmap(8) as *i64; nel[0]=0
100 let counts: *i64=sys_mmap(8*16*8) as *i64
101 var sp: i64=0
102 while sp<S { let cs: *i64=((counts as i64)+sp*16*8) as *i64; parse_one((strs[sp] as *u8), etab, nel, cs); sp=sp+1 }
103 let A: *i64=sys_mmap(8*16*8) as *i64
104 var e: i64=0
105 while e<nel[0] { var s2: i64=0; while s2<S { let cs2: *i64=((counts as i64)+s2*16*8) as *i64; A[e*S+s2]=signs[s2]*cs2[e]; s2=s2+1 } e=e+1 }
106 return balanced_ok(A, nel[0], S, x)
107}
108
109func main() -> i64 {
110 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0
111 g_w("=== NX-RESEARCH-CHEM-PARSE GATE (parse arbitrary formulas -> balance any reaction by search) ===\n")
112
113 // ---- PARSE KAT ----
114 let etab: *i64=sys_mmap(8*16) as *i64; let nel: *i64=sys_mmap(8) as *i64; nel[0]=0
115 let cc: *i64=sys_mmap(8*16) as *i64
116 parse_one("C3H8" as *u8, etab, nel, cc)
117 let symC: i64=67*256; let symH: i64=72*256
118 var c3h8_ok: i64=0
119 if cc[elem_index(etab,nel,symC)]==3 { if cc[elem_index(etab,nel,symH)]==8 { c3h8_ok=1 } }
120 // parentheses: Ca(OH)2 -> Ca1 O2 H2
121 let etab2: *i64=sys_mmap(8*16) as *i64; let nel2: *i64=sys_mmap(8) as *i64; nel2[0]=0
122 let cc2: *i64=sys_mmap(8*16) as *i64
123 parse_one("Ca(OH)2" as *u8, etab2, nel2, cc2)
124 let symCa: i64=67*256+97; let symO: i64=79*256; let symH2: i64=72*256
125 var caoh_ok: i64=0
126 if cc2[elem_index(etab2,nel2,symCa)]==1 { if cc2[elem_index(etab2,nel2,symO)]==2 { if cc2[elem_index(etab2,nel2,symH2)]==2 { caoh_ok=1 } } }
127 var parse_ok: i64=0; if c3h8_ok==1 { if caoh_ok==1 { parse_ok=1 } }
128
129 // ---- R1: C3H8 + O2 -> CO2 + H2O ----
130 let r1: *i64=sys_mmap(8*8) as *i64; r1[0]=("C3H8" as *u8) as i64; r1[1]=("O2" as *u8) as i64; r1[2]=("CO2" as *u8) as i64; r1[3]=("H2O" as *u8) as i64
131 let g1: *i64=sys_mmap(8*8) as *i64; g1[0]=1; g1[1]=1; g1[2]=0-1; g1[3]=0-1
132 let x1: *i64=sys_mmap(8*8) as *i64; let f1: i64=balance_reaction(r1,g1,4,x1,8)
133 let e1: *i64=sys_mmap(8*8) as *i64; e1[0]=1; e1[1]=5; e1[2]=3; e1[3]=4
134
135 // ---- R2: Al + O2 -> Al2O3 ----
136 let r2: *i64=sys_mmap(8*8) as *i64; r2[0]=("Al" as *u8) as i64; r2[1]=("O2" as *u8) as i64; r2[2]=("Al2O3" as *u8) as i64
137 let g2: *i64=sys_mmap(8*8) as *i64; g2[0]=1; g2[1]=1; g2[2]=0-1
138 let x2: *i64=sys_mmap(8*8) as *i64; let f2: i64=balance_reaction(r2,g2,3,x2,8)
139 let e2: *i64=sys_mmap(8*8) as *i64; e2[0]=4; e2[1]=3; e2[2]=2
140
141 // ---- R3 (parentheses): Ca(OH)2 + HCl -> CaCl2 + H2O ----
142 let r3: *i64=sys_mmap(8*8) as *i64; r3[0]=("Ca(OH)2" as *u8) as i64; r3[1]=("HCl" as *u8) as i64; r3[2]=("CaCl2" as *u8) as i64; r3[3]=("H2O" as *u8) as i64
143 let g3: *i64=sys_mmap(8*8) as *i64; g3[0]=1; g3[1]=1; g3[2]=0-1; g3[3]=0-1
144 let x3: *i64=sys_mmap(8*8) as *i64; let f3: i64=balance_reaction(r3,g3,4,x3,8)
145 let e3: *i64=sys_mmap(8*8) as *i64; e3[0]=1; e3[1]=2; e3[2]=1; e3[3]=2
146
147 let m1: i64=veq(x1,e1,4); let m2: i64=veq(x2,e2,3); let m3: i64=veq(x3,e3,4)
148 let cons: i64 = conserves_reaction(r1,g1,4,x1)*conserves_reaction(r2,g2,3,x2)*conserves_reaction(r3,g3,4,x3)
149 // liar-kill: wrong coeffs for R1
150 let wrong: i64=sys_mmap(8*8) as *i64; wrong[0]=1; wrong[1]=5; wrong[2]=3; wrong[3]=3
151 let bad: i64=(conserves_reaction(r1,g1,4,wrong)==0) as i64
152
153 g_w(" parse C3H8 -> C="); g_n(cc[elem_index(etab,nel,symC)]); g_w(" H="); g_n(cc[elem_index(etab,nel,symH)]); g_w("\n")
154 g_w(" parse Ca(OH)2 -> Ca="); g_n(cc2[elem_index(etab2,nel2,symCa)]); g_w(" O="); g_n(cc2[elem_index(etab2,nel2,symO)]); g_w(" H="); g_n(cc2[elem_index(etab2,nel2,symH2)]); g_w("\n")
155 g_w(" R1 C3H8+O2->CO2+H2O: "); g_n(x1[0]);g_w(",");g_n(x1[1]);g_w(",");g_n(x1[2]);g_w(",");g_n(x1[3]); g_w(" (1,5,3,4)\n")
156 g_w(" R2 Al+O2->Al2O3: "); g_n(x2[0]);g_w(",");g_n(x2[1]);g_w(",");g_n(x2[2]); g_w(" (4,3,2)\n")
157 g_w(" R3 Ca(OH)2+HCl->CaCl2+H2O: "); g_n(x3[0]);g_w(",");g_n(x3[1]);g_w(",");g_n(x3[2]);g_w(",");g_n(x3[3]); g_w(" (1,2,1,2)\n")
158
159 g_row("PARSER: reads C3H8 (C3,H8) AND Ca(OH)2 with parentheses (Ca1,O2,H2) correctly" as *u8, parse_ok, pass)
160 var d1: i64=0; if f1==1 { if m1==1 { d1=1 } }
161 g_row("DISCOVERY (from text) R1: balances 'C3H8 + O2 -> CO2 + H2O' to (1,5,3,4)" as *u8, d1, pass)
162 var d2: i64=0; if f2==1 { if m2==1 { d2=1 } }
163 g_row("DISCOVERY (from text) R2: balances 'Al + O2 -> Al2O3' to (4,3,2)" as *u8, d2, pass)
164 var d3: i64=0; if f3==1 { if m3==1 { d3=1 } }
165 g_row("DISCOVERY (parentheses) R3: balances 'Ca(OH)2 + HCl -> CaCl2 + H2O' to (1,2,1,2)" as *u8, d3, pass)
166 g_row("CONSERVATION: independent atom-balance verifier confirms all 3 parsed+discovered reactions" as *u8, (cons==1) as i64, pass)
167 g_row("LIAR-KILL: a wrong coefficient set is REJECTED by the conservation verifier" as *u8, bad, pass)
168
169 g_w("RESEARCH-CHEM-PARSE-GATE rows=6 pass="); g_n(pass[0])
170 if pass[0]==6 { g_w(" verdict=GREEN\n"); sys_exit(0); return 0 }
171 g_w(" verdict=RED\n"); sys_exit(1); return 1
172}