code wiki / (root) / nx_calc_solve.nx

nx_calc_solve.nx source

↩ module page · 84 lines · 3357 B

1// nx_calc_solve.nx -- symbolic Solve for linear and quadratic. 2// 3// Per the comparison commit's Mathematica LOSE_BIG: ship Solve. 4// First cut: linear (a*x + b = 0) and quadratic (a*x^2 + b*x + c = 0). 5// General polynomial Solve up to degree 4 + numerical Newton iter 6// for higher are named follow-ups. 7 8// nx_safety_envelope: 9// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 10// sil_target: SIL1 11// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 12// verdict: NOT_YET_EVALUATED 13 14import "nx_calc.nx" 15 16// Result of a Solve call. kind = 1 (one real root), 2 (two real 17// roots), 3 (no real roots / complex), 4 (degenerate / infinite), 18// 5 (unsupported equation form). 19struct SolveResult { 20 kind: nx_int, 21 root1: *Term, 22 root2: *Term, 23 discriminant: *Term, 24} 25const NX_SOLVE_BYTES: nx_int = 32 26 27const NX_SOLVE_LINEAR_ONE: nx_int = 1 28const NX_SOLVE_QUADRATIC_TWO: nx_int = 2 29const NX_SOLVE_NO_REAL: nx_int = 3 30const NX_SOLVE_DEGENERATE: nx_int = 4 31const NX_SOLVE_UNSUPPORTED: nx_int = 5 32 33func nx_solve_result_new() -> *SolveResult { 34 let r: *SolveResult = (sys_mmap(NX_SOLVE_BYTES as i64)) as *SolveResult 35 r.kind = NX_SOLVE_UNSUPPORTED 36 r.root1 = 0 as *Term 37 r.root2 = 0 as *Term 38 r.discriminant = 0 as *Term 39 return r 40} 41 42// Linear: a*x + b = 0 -> x = -b/a 43// a, b are *Term coefficients (constants or symbolic). 44// Caller checks a != 0 (otherwise SOLVE_DEGENERATE). 45func nx_calc_solve_linear(a: *Term, b: *Term) -> *SolveResult { 46 let r: *SolveResult = nx_solve_result_new() 47 // x = -b / a, encoded as (-1 * b) / a 48 let neg_b: *Term = nx_calc_mul(nx_calc_const_int(-1), b) 49 let root: *Term = nx_calc_bin(NX_CALC_SYM_DIV, neg_b, a) 50 r.kind = NX_SOLVE_LINEAR_ONE 51 r.root1 = root 52 return r 53} 54 55// Quadratic: a*x^2 + b*x + c = 0 56// discriminant D = b*b - 4*a*c 57// x = (-b +/- sqrt(D)) / (2a) 58// If D > 0 -> two real roots; D == 0 -> one repeated; D < 0 -> complex. 59// Since we don't have sqrt as a Term constructor yet, we encode the 60// discriminant symbolically + return root forms with sqrt(D) as a 61// generic POW(D, 1/2). Numeric callers can evaluate. 62func nx_calc_solve_quadratic(a: *Term, b: *Term, c: *Term) -> *SolveResult { 63 let r: *SolveResult = nx_solve_result_new() 64 // D = b*b - 4*a*c 65 let bb: *Term = nx_calc_mul(b, b) 66 let four: *Term = nx_calc_const_int(4) 67 let four_ac: *Term = nx_calc_mul(four, nx_calc_mul(a, c)) 68 let disc: *Term = nx_calc_sub(bb, four_ac) 69 r.discriminant = disc 70 // sqrt(D) encoded as POW(D, 1/2). We use DIV(1,2) for 1/2 since 71 // we don't have a rational literal -- caller knows the convention. 72 let half: *Term = nx_calc_bin(NX_CALC_SYM_DIV, nx_calc_const_int(1), nx_calc_const_int(2)) 73 let sqrt_d: *Term = nx_calc_pow(disc, half) 74 let neg_b: *Term = nx_calc_mul(nx_calc_const_int(-1), b) 75 let two_a: *Term = nx_calc_mul(nx_calc_const_int(2), a) 76 let root_plus: *Term = nx_calc_bin(NX_CALC_SYM_DIV, 77 nx_calc_add(neg_b, sqrt_d), two_a) 78 let root_minus: *Term = nx_calc_bin(NX_CALC_SYM_DIV, 79 nx_calc_sub(neg_b, sqrt_d), two_a) 80 r.kind = NX_SOLVE_QUADRATIC_TWO 81 r.root1 = root_plus 82 r.root2 = root_minus 83 return r 84}