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}