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}