@descix/egpt-math-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +224 -0
- package/dist/egpt-math-sdk.browser.js +5 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.js +985 -0
- package/dist/notebooks/arithmetic-basics.html +166 -0
- package/dist/notebooks/complex-twiddle.html +148 -0
- package/dist/notebooks/double-slit-rosetta-qft.html +154 -0
- package/dist/notebooks/egpt-fft-test.html +202 -0
- package/dist/notebooks/egpt-matrix-test.html +241 -0
- package/dist/notebooks/egpt-polynomial-test.html +703 -0
- package/dist/notebooks/egpt-prime-composite-test.html +561 -0
- package/dist/notebooks/egpt-test-suite.html +1387 -0
- package/dist/notebooks/egpt-topology-test.html +410 -0
- package/dist/notebooks/factorization-basics.html +131 -0
- package/dist/notebooks/fft.html +155 -0
- package/dist/notebooks/frqtl-qft-benchmark.html +207 -0
- package/dist/notebooks/gallery-atomic-model.html +51 -0
- package/dist/notebooks/gallery-big-bang.html +62 -0
- package/dist/notebooks/gallery-blackbody.html +52 -0
- package/dist/notebooks/gallery-circuit-sat.html +104 -0
- package/dist/notebooks/gallery-collision-matrix-3d.html +51 -0
- package/dist/notebooks/gallery-double-slit.html +53 -0
- package/dist/notebooks/gallery-gun-chamber.html +62 -0
- package/dist/notebooks/gallery-particle-walk-3d.html +53 -0
- package/dist/notebooks/gallery-wave-interference.html +52 -0
- package/dist/notebooks/getting-started-hello-frqtl.html +53 -0
- package/dist/notebooks/iso-canonical-chain.html +203 -0
- package/dist/notebooks/iso-entropy-log.html +127 -0
- package/dist/notebooks/iso-fft-polynomial.html +136 -0
- package/dist/notebooks/iso-matrix-four-views.html +131 -0
- package/dist/notebooks/iso-number-theory-four-views.html +99 -0
- package/dist/notebooks/iso-pi-log-fraction-loop.html +140 -0
- package/dist/notebooks/matrix.html +202 -0
- package/dist/notebooks/notebooks.json +240 -0
- package/dist/notebooks/polynomials.html +201 -0
- package/dist/notebooks/prime-composite.html +268 -0
- package/dist/notebooks/rational-core.html +179 -0
- package/dist/notebooks/reference-egpt-fft-test.html +187 -0
- package/dist/notebooks/reference-order-finder-test.html +196 -0
- package/dist/notebooks/reference-prime-atom-polynomial-test.html +542 -0
- package/dist/notebooks/reference-solve-polynomial-system-test.html +471 -0
- package/dist/notebooks/scaled-vectors.html +112 -0
- package/dist/notebooks/stats.html +206 -0
- package/dist/notebooks/transcendentals.html +195 -0
- package/dist/notebooks/view-vendored-d3.html +78 -0
- package/dist/vendor/d3-array/src/array.js +4 -0
- package/dist/vendor/d3-array/src/ascending.js +3 -0
- package/dist/vendor/d3-array/src/bin.js +125 -0
- package/dist/vendor/d3-array/src/bisect.js +9 -0
- package/dist/vendor/d3-array/src/bisector.js +56 -0
- package/dist/vendor/d3-array/src/blur.js +115 -0
- package/dist/vendor/d3-array/src/constant.js +3 -0
- package/dist/vendor/d3-array/src/count.js +18 -0
- package/dist/vendor/d3-array/src/cross.js +33 -0
- package/dist/vendor/d3-array/src/cumsum.js +6 -0
- package/dist/vendor/d3-array/src/descending.js +7 -0
- package/dist/vendor/d3-array/src/deviation.js +6 -0
- package/dist/vendor/d3-array/src/difference.js +11 -0
- package/dist/vendor/d3-array/src/disjoint.js +15 -0
- package/dist/vendor/d3-array/src/every.js +10 -0
- package/dist/vendor/d3-array/src/extent.js +29 -0
- package/dist/vendor/d3-array/src/filter.js +11 -0
- package/dist/vendor/d3-array/src/fsum.js +69 -0
- package/dist/vendor/d3-array/src/greatest.js +29 -0
- package/dist/vendor/d3-array/src/greatestIndex.js +19 -0
- package/dist/vendor/d3-array/src/group.js +65 -0
- package/dist/vendor/d3-array/src/groupSort.js +10 -0
- package/dist/vendor/d3-array/src/identity.js +3 -0
- package/dist/vendor/d3-array/src/index.js +57 -0
- package/dist/vendor/d3-array/src/intersection.js +19 -0
- package/dist/vendor/d3-array/src/least.js +29 -0
- package/dist/vendor/d3-array/src/leastIndex.js +19 -0
- package/dist/vendor/d3-array/src/map.js +5 -0
- package/dist/vendor/d3-array/src/max.js +20 -0
- package/dist/vendor/d3-array/src/maxIndex.js +22 -0
- package/dist/vendor/d3-array/src/mean.js +19 -0
- package/dist/vendor/d3-array/src/median.js +9 -0
- package/dist/vendor/d3-array/src/merge.js +9 -0
- package/dist/vendor/d3-array/src/min.js +20 -0
- package/dist/vendor/d3-array/src/minIndex.js +22 -0
- package/dist/vendor/d3-array/src/mode.js +28 -0
- package/dist/vendor/d3-array/src/nice.js +18 -0
- package/dist/vendor/d3-array/src/number.js +20 -0
- package/dist/vendor/d3-array/src/pairs.js +15 -0
- package/dist/vendor/d3-array/src/permute.js +3 -0
- package/dist/vendor/d3-array/src/quantile.js +47 -0
- package/dist/vendor/d3-array/src/quickselect.js +53 -0
- package/dist/vendor/d3-array/src/range.js +13 -0
- package/dist/vendor/d3-array/src/rank.js +24 -0
- package/dist/vendor/d3-array/src/reduce.js +14 -0
- package/dist/vendor/d3-array/src/reverse.js +4 -0
- package/dist/vendor/d3-array/src/scan.js +6 -0
- package/dist/vendor/d3-array/src/shuffle.js +13 -0
- package/dist/vendor/d3-array/src/some.js +10 -0
- package/dist/vendor/d3-array/src/sort.js +39 -0
- package/dist/vendor/d3-array/src/subset.js +5 -0
- package/dist/vendor/d3-array/src/sum.js +18 -0
- package/dist/vendor/d3-array/src/superset.js +19 -0
- package/dist/vendor/d3-array/src/threshold/freedmanDiaconis.js +7 -0
- package/dist/vendor/d3-array/src/threshold/scott.js +7 -0
- package/dist/vendor/d3-array/src/threshold/sturges.js +5 -0
- package/dist/vendor/d3-array/src/ticks.js +55 -0
- package/dist/vendor/d3-array/src/transpose.js +15 -0
- package/dist/vendor/d3-array/src/union.js +11 -0
- package/dist/vendor/d3-array/src/variance.js +25 -0
- package/dist/vendor/d3-array/src/zip.js +5 -0
- package/dist/vendor/internmap/src/index.js +61 -0
- package/dist/vendor/manifest.json +11 -0
- package/dist/wasm/egpt_math_sdk_wasm.d.ts +214 -0
- package/dist/wasm/egpt_math_sdk_wasm.js +1120 -0
- package/dist/wasm/egpt_math_sdk_wasm_bg.wasm +0 -0
- package/dist/wasm/egpt_math_sdk_wasm_bg.wasm.d.ts +66 -0
- package/dist/wasm/manifest.json +5 -0
- package/package.json +53 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="Solving Polynomial Systems — fractional roots, GCD, and shared-factor detection">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# Solving Polynomial Systems in EGPT Canonical Space
|
|
5
|
+
|
|
6
|
+
This notebook ports `SolvePolynomialSystemTest.js` — the canonical test suite for
|
|
7
|
+
`SolvePolynomialSystem.js`.
|
|
8
|
+
|
|
9
|
+
The library operates entirely in **rational (PPF) arithmetic** via `EGPTReal` and
|
|
10
|
+
`EGPTPolynomial`. No floating-point, no approximate GCD — every root, every remainder,
|
|
11
|
+
every shared factor is detected to the last bit.
|
|
12
|
+
|
|
13
|
+
Four stages are covered:
|
|
14
|
+
|
|
15
|
+
| Stage | What is tested |
|
|
16
|
+
|-------|---------------|
|
|
17
|
+
| 1 | Single polynomial with fractional coefficients — root evaluation and exact division |
|
|
18
|
+
| 2 | System of two polynomials — GCD finds the shared root |
|
|
19
|
+
| 3 | Minimal-polynomial elimination — express √2 and √3 by integer-coefficient polynomials |
|
|
20
|
+
| 4 | `EGPTPolynomial.gcd` / `shareCommonFactor` — the polynomial-chain API |
|
|
21
|
+
|
|
22
|
+
Plus a set of **edge cases** covering zero polynomials, roots at zero, repeated roots,
|
|
23
|
+
large rational denominators, negative-rational canonicalization, and algebraic laws.
|
|
24
|
+
|
|
25
|
+
*(All arithmetic goes through `caps.math`. No URL imports.)*
|
|
26
|
+
</cell>
|
|
27
|
+
|
|
28
|
+
<cell type="markdown">
|
|
29
|
+
## Setup — helpers and test harness
|
|
30
|
+
|
|
31
|
+
`EGPTReal.fromBigInt` / `EGPTReal.fromRational` are the two constructors used throughout.
|
|
32
|
+
The SDK also exports standalone `polyGCD` and `monicNormalize` (the Euclidean GCD walk and
|
|
33
|
+
monic normalization) — these replace the local helpers from the source file.
|
|
34
|
+
|
|
35
|
+
The `suite` binding is consumed by every phase cell.
|
|
36
|
+
</cell>
|
|
37
|
+
|
|
38
|
+
<cell type="js" lane="math" id="setup" out="suite">
|
|
39
|
+
const { math } = caps;
|
|
40
|
+
const { EGPTReal, EGPTPolynomial, polyGCD, monicNormalize } = math;
|
|
41
|
+
|
|
42
|
+
const ZERO = EGPTReal.fromBigInt(0n);
|
|
43
|
+
const ONE = EGPTReal.fromBigInt(1n);
|
|
44
|
+
|
|
45
|
+
// Convenience constructors
|
|
46
|
+
const frac = (n, d) => EGPTReal.fromRational(BigInt(n), BigInt(d));
|
|
47
|
+
const intN = (n) => EGPTReal.fromBigInt(BigInt(n));
|
|
48
|
+
const isZero = (en) => en.equals(ZERO);
|
|
49
|
+
|
|
50
|
+
// Minimal test harness
|
|
51
|
+
let passed = 0;
|
|
52
|
+
let failed = 0;
|
|
53
|
+
const failures = [];
|
|
54
|
+
|
|
55
|
+
function test(name, fn) {
|
|
56
|
+
try {
|
|
57
|
+
fn();
|
|
58
|
+
passed++;
|
|
59
|
+
console.log(' ok — ' + name);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
failed++;
|
|
62
|
+
failures.push({ name, err });
|
|
63
|
+
console.log(' FAIL — ' + name);
|
|
64
|
+
console.log(' ' + err.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function assert(cond, msg) {
|
|
69
|
+
if (!cond) throw new Error(msg || 'assertion failed');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function assertEqPoly(actual, expected, msg) {
|
|
73
|
+
if (!EGPTPolynomial.equals(actual, expected)) {
|
|
74
|
+
const show = (p) => '[' + p.map(c => c.breakSymbolicToString()).join(', ') + ']';
|
|
75
|
+
throw new Error((msg || 'polynomial mismatch') + '\n actual = ' + show(actual) + '\n expected = ' + show(expected));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function assertZero(en, msg) {
|
|
80
|
+
if (!isZero(en)) throw new Error((msg || 'expected zero') + ': got ' + en.breakSymbolicToString());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// `polyGCD` and `monicNormalize` come from the SDK — no local re-implementation needed.
|
|
84
|
+
// They are identical to the local helpers in the source file.
|
|
85
|
+
|
|
86
|
+
return { suite: { test, assert, assertEqPoly, assertZero, frac, intN, ZERO, ONE, isZero, polyGCD, monicNormalize, getResults: () => ({ passed, failed, failures }) } };
|
|
87
|
+
</cell>
|
|
88
|
+
|
|
89
|
+
<cell type="markdown">
|
|
90
|
+
## Stage 1 — Single polynomial with fractional coefficients
|
|
91
|
+
|
|
92
|
+
Consider `f(x) = x² − (7/6)x + 1/3`. In coefficient-vector form (constant term first):
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
f = [1/3, −7/6, 1]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Its roots are `x = 1/2` and `x = 2/3` — both rational, discoverable by exact evaluation and exact polynomial division with zero remainder.
|
|
99
|
+
</cell>
|
|
100
|
+
|
|
101
|
+
<cell type="js" lane="math" id="stage1" in="suite" out="s1">
|
|
102
|
+
const { math } = caps;
|
|
103
|
+
const { EGPTPolynomial } = math;
|
|
104
|
+
const { test, assert, assertEqPoly, assertZero, frac, intN, ZERO, ONE, isZero } = inputs.suite;
|
|
105
|
+
|
|
106
|
+
console.log('\n[Stage 1] single polynomial, fractional coefficients');
|
|
107
|
+
|
|
108
|
+
// f(x) = x² − (7/6)x + 1/3 (coefficients stored constant-term first)
|
|
109
|
+
const f = [ frac(1, 3), frac(-7, 6), ONE ];
|
|
110
|
+
|
|
111
|
+
test('f(1/2) is exactly zero', () => {
|
|
112
|
+
assertZero(EGPTPolynomial.evaluateAt(f, frac(1, 2)), 'f(1/2)');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('f(2/3) is exactly zero', () => {
|
|
116
|
+
assertZero(EGPTPolynomial.evaluateAt(f, frac(2, 3)), 'f(2/3)');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('f / (x − 1/2) has zero remainder and leaves the other root', () => {
|
|
120
|
+
const { quotient, remainder } = EGPTPolynomial.divide(f, [frac(-1, 2), ONE]);
|
|
121
|
+
assert(remainder.every(isZero), 'non-zero remainder');
|
|
122
|
+
assertZero(EGPTPolynomial.evaluateAt(quotient, frac(2, 3)), 'quotient(2/3)');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('f / (x − 1/2) quotient is exactly x − 2/3', () => {
|
|
126
|
+
const { quotient } = EGPTPolynomial.divide(f, [frac(-1, 2), ONE]);
|
|
127
|
+
assertEqPoly(quotient, [frac(-2, 3), ONE], 'quotient should be x − 2/3');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('f(1) equals the sum of coefficients (= 1/3 − 7/6 + 1 = 1/6)', () => {
|
|
131
|
+
const v = EGPTPolynomial.evaluateAt(f, ONE);
|
|
132
|
+
assert(v.equals(frac(1, 6)), 'got ' + v.breakSymbolicToString() + ', expected 1/6');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('f(0) equals the constant term 1/3', () => {
|
|
136
|
+
const v = EGPTPolynomial.evaluateAt(f, ZERO);
|
|
137
|
+
assert(v.equals(frac(1, 3)), 'got ' + v.breakSymbolicToString() + ', expected 1/3');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return { s1: inputs.suite.getResults() };
|
|
141
|
+
</cell>
|
|
142
|
+
|
|
143
|
+
<cell type="markdown">
|
|
144
|
+
## Stage 2 — System of polynomials: polynomial GCD finds the shared root
|
|
145
|
+
|
|
146
|
+
Two polynomials sharing exactly one root:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
f1(x) = x² − (7/6)x + 1/3 roots: 1/2, 2/3
|
|
150
|
+
f2(x) = x² − (5/6)x + 1/6 roots: 1/2, 1/3
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The Euclidean algorithm on polynomial remainder sequences (`polyGCD`) isolates the common
|
|
154
|
+
factor `(x − 1/2)` in exact rational arithmetic — no numeric threshold, no tolerance.
|
|
155
|
+
|
|
156
|
+
Additional GCD identities are checked: `gcd(f, f) = f`, coprime polynomials, divisor-of
|
|
157
|
+
relationships, and a three-polynomial chained GCD.
|
|
158
|
+
</cell>
|
|
159
|
+
|
|
160
|
+
<cell type="js" lane="math" id="stage2" in="suite" out="s2">
|
|
161
|
+
const { math } = caps;
|
|
162
|
+
const { EGPTPolynomial } = math;
|
|
163
|
+
const { test, assert, assertEqPoly, assertZero, frac, intN, ONE, isZero, polyGCD, monicNormalize } = inputs.suite;
|
|
164
|
+
|
|
165
|
+
console.log('\n[Stage 2] system of polynomials via polynomial GCD');
|
|
166
|
+
|
|
167
|
+
const f1 = [ frac(1, 3), frac(-7, 6), ONE ]; // roots 1/2, 2/3
|
|
168
|
+
const f2 = [ frac(1, 6), frac(-5, 6), ONE ]; // roots 1/2, 1/3
|
|
169
|
+
|
|
170
|
+
test('gcd(f1, f2) is monic x − 1/2', () => {
|
|
171
|
+
const g = monicNormalize(polyGCD(f1, f2));
|
|
172
|
+
assertEqPoly(g, [frac(-1, 2), ONE], 'gcd should be x − 1/2');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('gcd vanishes at the shared root 1/2', () => {
|
|
176
|
+
const g = monicNormalize(polyGCD(f1, f2));
|
|
177
|
+
assertZero(EGPTPolynomial.evaluateAt(g, frac(1, 2)));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('gcd(f, f) = f (monic)', () => {
|
|
181
|
+
const g = monicNormalize(polyGCD(f1, f1));
|
|
182
|
+
assertEqPoly(g, f1, 'gcd(f1, f1) should equal f1');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('coprime polynomials have constant gcd (degree 0, nonzero)', () => {
|
|
186
|
+
// (x − 1) and (x − 2) share no root
|
|
187
|
+
const a = [intN(-1), ONE];
|
|
188
|
+
const b = [intN(-2), ONE];
|
|
189
|
+
const g = polyGCD(a, b);
|
|
190
|
+
assert(EGPTPolynomial.degree(g) === 0, 'coprime gcd should be degree 0, got ' + EGPTPolynomial.degree(g));
|
|
191
|
+
assert(!isZero(g[0]), 'coprime gcd constant must be nonzero');
|
|
192
|
+
const gm = monicNormalize(g);
|
|
193
|
+
assertEqPoly(gm, [ONE], 'monic coprime gcd should be [1]');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('when f2 divides f1, gcd(f1, f2) = f2 (monic)', () => {
|
|
197
|
+
// f1 = (x−1)(x−2) = x² − 3x + 2, f2 = (x−1)
|
|
198
|
+
const a = [intN(2), intN(-3), ONE];
|
|
199
|
+
const b = [intN(-1), ONE];
|
|
200
|
+
const g = monicNormalize(polyGCD(a, b));
|
|
201
|
+
assertEqPoly(g, b, 'gcd should equal the divisor factor');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('two shared roots produce a degree-2 gcd', () => {
|
|
205
|
+
// f1 = (x−1)(x−2)(x−3), f2 = (x−1)(x−2)(x−4)
|
|
206
|
+
// Shared: (x−1)(x−2) = x² − 3x + 2
|
|
207
|
+
const p1 = [intN(-6), intN(11), intN(-6), ONE];
|
|
208
|
+
const p2 = [intN(-8), intN(14), intN(-7), ONE];
|
|
209
|
+
const g = monicNormalize(polyGCD(p1, p2));
|
|
210
|
+
assertEqPoly(g, [intN(2), intN(-3), ONE], 'gcd should be x² − 3x + 2');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('three-polynomial system: chained gcd finds the common root', () => {
|
|
214
|
+
// All share (x − 1): (x−1)(x−2), (x−1)(x−3), (x−1)(x−5)
|
|
215
|
+
const a = [intN(2), intN(-3), ONE];
|
|
216
|
+
const b = [intN(3), intN(-4), ONE];
|
|
217
|
+
const c = [intN(5), intN(-6), ONE];
|
|
218
|
+
const g = monicNormalize(polyGCD(polyGCD(a, b), c));
|
|
219
|
+
assertEqPoly(g, [intN(-1), ONE], 'chained gcd should be x − 1');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return { s2: inputs.suite.getResults() };
|
|
223
|
+
</cell>
|
|
224
|
+
|
|
225
|
+
<cell type="markdown">
|
|
226
|
+
## Stage 3 — Minimal-polynomial elimination for irrationals
|
|
227
|
+
|
|
228
|
+
To work with `x² − √k · x − 1 = 0` (which has irrational coefficients), we eliminate `√k`
|
|
229
|
+
by conjugation. Multiplying `(x² − 1 − √k · x)(x² − 1 + √k · x)` gives:
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
p(x) = (x² − 1)² − k · x²
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
This is a degree-4 polynomial in `x` with **integer coefficients only** — the irrationality
|
|
236
|
+
is gone. For `k = 2`: `x⁴ − 4x² + 1`. For `k = 3`: `x⁴ − 5x² + 1`.
|
|
237
|
+
|
|
238
|
+
All arithmetic uses `EGPTPolynomial.multiply` and `EGPTPolynomial.subtract` on integer `EGPTReal` coefficients.
|
|
239
|
+
</cell>
|
|
240
|
+
|
|
241
|
+
<cell type="js" lane="math" id="stage3" in="suite" out="s3">
|
|
242
|
+
const { math } = caps;
|
|
243
|
+
const { EGPTPolynomial } = math;
|
|
244
|
+
const { test, assertEqPoly, intN, ZERO, ONE } = inputs.suite;
|
|
245
|
+
|
|
246
|
+
console.log('\n[Stage 3] minimal-polynomial elimination of irrationals');
|
|
247
|
+
|
|
248
|
+
function eliminateSquareRoot(kBigInt) {
|
|
249
|
+
// For g(x) = x² − √k · x − 1 with α² = k, the elimination yields
|
|
250
|
+
// p(x) = (x² − 1)² − k·x²
|
|
251
|
+
// which is a polynomial with integer coefficients in x alone.
|
|
252
|
+
const u = [intN(-1), ZERO, ONE]; // x² − 1
|
|
253
|
+
const u2 = EGPTPolynomial.multiply(u, u); // (x² − 1)²
|
|
254
|
+
const kxSq = [ZERO, ZERO, EGPTReal.fromBigInt(kBigInt)];
|
|
255
|
+
return EGPTPolynomial.subtract(u2, kxSq);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// EGPTReal must be in scope for eliminateSquareRoot
|
|
259
|
+
const { EGPTReal } = math;
|
|
260
|
+
|
|
261
|
+
test('√2 elimination yields x⁴ − 4x² + 1', () => {
|
|
262
|
+
const p = eliminateSquareRoot(2n);
|
|
263
|
+
assertEqPoly(p, [ONE, ZERO, intN(-4), ZERO, ONE], 'minimal poly for √2 case');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('√3 elimination yields x⁴ − 5x² + 1', () => {
|
|
267
|
+
const p = eliminateSquareRoot(3n);
|
|
268
|
+
assertEqPoly(p, [ONE, ZERO, intN(-5), ZERO, ONE], 'minimal poly for √3 case');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('minimal polynomial has no fractional coefficients (structural)', () => {
|
|
272
|
+
const p = eliminateSquareRoot(2n);
|
|
273
|
+
// Every coefficient must equal its integer rebuild — confirms no hidden fractions.
|
|
274
|
+
const rebuilt = [intN(1), intN(0), intN(-4), intN(0), intN(1)];
|
|
275
|
+
assertEqPoly(p, rebuilt, 'integer rebuild must match');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return { s3: inputs.suite.getResults() };
|
|
279
|
+
</cell>
|
|
280
|
+
|
|
281
|
+
<cell type="markdown">
|
|
282
|
+
## Edge cases — structural integrity of canonical-space operations
|
|
283
|
+
|
|
284
|
+
These tests cover the boundary conditions flagged during design:
|
|
285
|
+
|
|
286
|
+
- Subtraction `a − a` collapses to the zero polynomial
|
|
287
|
+
- `evaluateAt(p, 0)` returns the constant term
|
|
288
|
+
- A polynomial with a root at zero (`x`) evaluates to zero at zero and to `c` at `c`
|
|
289
|
+
- Repeated roots: `(x − 2)²` vanishes at 2 and divides cleanly by `(x − 2)`
|
|
290
|
+
- Large rational denominators (denominator = 10¹⁸) — no floating-point loss
|
|
291
|
+
- Negative-rational canonicalization: `fromRational(-1, 2)` equals `fromRational(1, -2)`
|
|
292
|
+
- Distributive law: `a·(b + c) = a·b + a·c`
|
|
293
|
+
- Multiply by the zero polynomial collapses to `[0]`
|
|
294
|
+
- Addition commutes: `a + b = b + a`
|
|
295
|
+
- Degree is consistent after arithmetic
|
|
296
|
+
</cell>
|
|
297
|
+
|
|
298
|
+
<cell type="js" lane="math" id="edge-cases" in="suite" out="ec">
|
|
299
|
+
const { math } = caps;
|
|
300
|
+
const { EGPTReal, EGPTPolynomial } = math;
|
|
301
|
+
const { test, assert, assertEqPoly, assertZero, frac, intN, ZERO, ONE, isZero } = inputs.suite;
|
|
302
|
+
|
|
303
|
+
console.log('\n[Edge cases] structural integrity of canonical-space ops');
|
|
304
|
+
|
|
305
|
+
test('a − a collapses to the zero polynomial', () => {
|
|
306
|
+
const a = [frac(1, 3), frac(-7, 6), ONE];
|
|
307
|
+
const r = EGPTPolynomial.subtract(a, a);
|
|
308
|
+
assert(r.every(isZero), 'subtract should be all-zero, got ' + r.map(x => x.breakSymbolicToString()));
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('evaluate at 0 returns the constant term (even for higher-degree poly)', () => {
|
|
312
|
+
const p = [intN(7), intN(-3), intN(5), intN(-9)]; // 7 − 3x + 5x² − 9x³
|
|
313
|
+
const v = EGPTPolynomial.evaluateAt(p, ZERO);
|
|
314
|
+
assert(v.equals(intN(7)), 'got ' + v.breakSymbolicToString() + ', expected 7');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('root at zero: x vanishes at 0 and equals c at c', () => {
|
|
318
|
+
const x = [ZERO, ONE]; // polynomial = x
|
|
319
|
+
assertZero(EGPTPolynomial.evaluateAt(x, ZERO));
|
|
320
|
+
const c = EGPTPolynomial.evaluateAt(x, frac(5, 7));
|
|
321
|
+
assert(c.equals(frac(5, 7)), 'got ' + c.breakSymbolicToString() + ', expected 5/7');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test('repeated root: (x − 2)² vanishes at 2 and divides cleanly by (x − 2)', () => {
|
|
325
|
+
// (x − 2)² = x² − 4x + 4
|
|
326
|
+
const p = [intN(4), intN(-4), ONE];
|
|
327
|
+
assertZero(EGPTPolynomial.evaluateAt(p, intN(2)), '(x−2)² at x=2');
|
|
328
|
+
const { quotient, remainder } = EGPTPolynomial.divide(p, [intN(-2), ONE]);
|
|
329
|
+
assert(remainder.every(isZero), 'remainder should be zero');
|
|
330
|
+
assertEqPoly(quotient, [intN(-2), ONE], 'quotient should be (x − 2)');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('large rational denominator: x − 1/10^18 vanishes at 1/10^18', () => {
|
|
334
|
+
const tiny = EGPTReal.fromRational(1n, 10n ** 18n);
|
|
335
|
+
const p = [EGPTReal.fromRational(-1n, 10n ** 18n), ONE];
|
|
336
|
+
assertZero(EGPTPolynomial.evaluateAt(p, tiny), 'large-denom root');
|
|
337
|
+
// At 2 * tiny the value should be exactly tiny — not floating-point zero.
|
|
338
|
+
const two_tiny = EGPTReal.fromRational(2n, 10n ** 18n);
|
|
339
|
+
const v = EGPTPolynomial.evaluateAt(p, two_tiny);
|
|
340
|
+
assert(v.equals(tiny), 'got ' + v.breakSymbolicToString() + ', expected ' + tiny.breakSymbolicToString());
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('negative rational canonicalization: -1/2 equals 1/-2', () => {
|
|
344
|
+
const a = EGPTReal.fromRational(-1n, 2n);
|
|
345
|
+
const b = EGPTReal.fromRational(1n, -2n);
|
|
346
|
+
assert(a.equals(b), 'fromRational(-1,2) should equal fromRational(1,-2): ' + a.breakSymbolicToString() + ' vs ' + b.breakSymbolicToString());
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('distributivity: a·(b + c) = a·b + a·c', () => {
|
|
350
|
+
const a = [ONE, ONE]; // x + 1
|
|
351
|
+
const b = [intN(2), ONE]; // x + 2
|
|
352
|
+
const c = [intN(3), ONE]; // x + 3
|
|
353
|
+
const lhs = EGPTPolynomial.multiply(a, EGPTPolynomial.add(b, c));
|
|
354
|
+
const rhs = EGPTPolynomial.add(
|
|
355
|
+
EGPTPolynomial.multiply(a, b),
|
|
356
|
+
EGPTPolynomial.multiply(a, c)
|
|
357
|
+
);
|
|
358
|
+
assertEqPoly(lhs, rhs, 'distributive law broken');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test('multiply by zero polynomial collapses to [0]', () => {
|
|
362
|
+
const a = [frac(1, 3), frac(-7, 6), ONE];
|
|
363
|
+
const z = [ZERO];
|
|
364
|
+
const r = EGPTPolynomial.multiply(a, z);
|
|
365
|
+
assert(r.every(isZero), 'zero-poly multiply should be all zero');
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test('add commutes: a + b = b + a', () => {
|
|
369
|
+
const a = [frac(1, 3), frac(-7, 6), ONE];
|
|
370
|
+
const b = [frac(1, 6), frac(-5, 6), ONE];
|
|
371
|
+
assertEqPoly(EGPTPolynomial.add(a, b), EGPTPolynomial.add(b, a));
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('degree is consistent after arithmetic', () => {
|
|
375
|
+
const a = [intN(1), intN(2), intN(3)]; // degree 2
|
|
376
|
+
const b = [intN(4), intN(5)]; // degree 1
|
|
377
|
+
const prod = EGPTPolynomial.multiply(a, b);
|
|
378
|
+
assert(EGPTPolynomial.degree(prod) === 3, 'expected degree 3, got ' + EGPTPolynomial.degree(prod));
|
|
379
|
+
const diff = EGPTPolynomial.subtract(a, a);
|
|
380
|
+
assert(EGPTPolynomial.degree(diff) === 0, 'expected degree 0 (zero poly), got ' + EGPTPolynomial.degree(diff));
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
return { ec: inputs.suite.getResults() };
|
|
384
|
+
</cell>
|
|
385
|
+
|
|
386
|
+
<cell type="markdown">
|
|
387
|
+
## Stage 4 — Polynomial-chain shared-factor detection
|
|
388
|
+
|
|
389
|
+
`EGPTPolynomial.gcd` and `EGPTPolynomial.shareCommonFactor` expose the GCD walk as a
|
|
390
|
+
first-class class method rather than a standalone function. This stage verifies the API
|
|
391
|
+
directly.
|
|
392
|
+
|
|
393
|
+
Note: the Vandermonde / Toeplitz / Sylvester matrix-form demonstrations that appeared in an
|
|
394
|
+
earlier architecture have been retired. The polynomial-chain primitives (`gcd`,
|
|
395
|
+
`shareCommonFactor`, `multiply`, `evaluateAt`) now cover every operation those matrix forms
|
|
396
|
+
once demonstrated.
|
|
397
|
+
</cell>
|
|
398
|
+
|
|
399
|
+
<cell type="js" lane="math" id="stage4" in="suite" out="s4">
|
|
400
|
+
const { math } = caps;
|
|
401
|
+
const { EGPTPolynomial } = math;
|
|
402
|
+
const { test, assert, assertEqPoly, frac, intN, ONE, monicNormalize } = inputs.suite;
|
|
403
|
+
|
|
404
|
+
console.log('\n[Stage 4] polynomial-chain shared-factor detection');
|
|
405
|
+
|
|
406
|
+
test('gcd of coprime (x−1, x−2) has degree 0 — no shared factor', () => {
|
|
407
|
+
const a = [intN(-1), ONE]; // x − 1
|
|
408
|
+
const b = [intN(-2), ONE]; // x − 2
|
|
409
|
+
const g = EGPTPolynomial.gcd(a, b);
|
|
410
|
+
assert(EGPTPolynomial.degree(g) === 0, 'coprime gcd must be degree 0, got ' + EGPTPolynomial.degree(g));
|
|
411
|
+
assert(!EGPTPolynomial.shareCommonFactor(a, b),
|
|
412
|
+
'shareCommonFactor should be false for coprime polynomials');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('gcd of (x−1) and (x−1)(x−2) is (x − 1)', () => {
|
|
416
|
+
const a = [intN(-1), ONE]; // x − 1
|
|
417
|
+
const b = [intN(2), intN(-3), ONE]; // (x−1)(x−2)
|
|
418
|
+
assert(EGPTPolynomial.shareCommonFactor(a, b),
|
|
419
|
+
'shareCommonFactor should be true when there is a common factor');
|
|
420
|
+
const g = monicNormalize(EGPTPolynomial.gcd(a, b));
|
|
421
|
+
assertEqPoly(g, [intN(-1), ONE], 'common factor should be (x − 1)');
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('gcd of the stage-2 system (f1, f2) is (x − 1/2)', () => {
|
|
425
|
+
const f1 = [ frac(1, 3), frac(-7, 6), ONE ];
|
|
426
|
+
const f2 = [ frac(1, 6), frac(-5, 6), ONE ];
|
|
427
|
+
assert(EGPTPolynomial.shareCommonFactor(f1, f2),
|
|
428
|
+
'f1 and f2 share the root x = 1/2');
|
|
429
|
+
const g = monicNormalize(EGPTPolynomial.gcd(f1, f2));
|
|
430
|
+
assertEqPoly(g, [frac(-1, 2), ONE], 'gcd should be x − 1/2');
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('two quadratics with one shared root: gcd has positive degree', () => {
|
|
434
|
+
// f1 = (x−1)(x−2), f2 = (x−1)(x−3) → one shared root
|
|
435
|
+
const f1 = [intN(2), intN(-3), ONE];
|
|
436
|
+
const f2 = [intN(3), intN(-4), ONE];
|
|
437
|
+
assert(EGPTPolynomial.shareCommonFactor(f1, f2), 'shared-root predicate');
|
|
438
|
+
// Self: gcd(f, f) = f (positive degree)
|
|
439
|
+
assert(EGPTPolynomial.shareCommonFactor(f1, f1),
|
|
440
|
+
'self-gcd: f shares a factor with itself');
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
return { s4: inputs.suite.getResults() };
|
|
444
|
+
</cell>
|
|
445
|
+
|
|
446
|
+
<cell type="markdown">
|
|
447
|
+
## Summary
|
|
448
|
+
|
|
449
|
+
Final tally across all stages and edge cases.
|
|
450
|
+
</cell>
|
|
451
|
+
|
|
452
|
+
<cell type="js" lane="math" id="summary" in="s4">
|
|
453
|
+
const snap = inputs.s4;
|
|
454
|
+
const { passed, failed, failures } = snap;
|
|
455
|
+
const total = passed + failed;
|
|
456
|
+
|
|
457
|
+
console.log('');
|
|
458
|
+
console.log('='.repeat(60));
|
|
459
|
+
console.log('Results: ' + passed + ' passed, ' + failed + ' failed (total: ' + total + ')');
|
|
460
|
+
if (failures.length > 0) {
|
|
461
|
+
console.log('\nFailures:');
|
|
462
|
+
for (const { name, err } of failures) {
|
|
463
|
+
console.log(' - ' + name + ': ' + err.message);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
console.log('='.repeat(60));
|
|
467
|
+
|
|
468
|
+
if (failed > 0) throw new Error('[SolvePolynomialSystemTest] ' + failed + ' test(s) failed');
|
|
469
|
+
</cell>
|
|
470
|
+
|
|
471
|
+
</frqtl-notebook>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="Scaled Vectors — Exact Rational Arithmetic with EGPTReal">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# Scaled Vectors — Exact Rational Arithmetic with EGPTReal
|
|
5
|
+
|
|
6
|
+
**What is EGPTReal?**
|
|
7
|
+
|
|
8
|
+
In IEEE-754 floating-point, `Math.sqrt(0.5)` returns a 64-bit approximation. The exact result — `1/√2` — cannot be represented: floating-point rounds, accumulates error across operations, and ultimately loses the identity that `(√½)² = ½` to rounding.
|
|
9
|
+
|
|
10
|
+
`EGPTReal` works differently. A value lives in *compressed information space*: its prime-power factorization is stored symbolically. Multiplication is vector addition in log-space; square roots halve the exponents. The identity `(√½)² = ½` holds **exactly** — not approximately — because no floating-point boundary is ever crossed inside the computation.
|
|
11
|
+
|
|
12
|
+
This notebook demonstrates the three arithmetic operations from `sdk/egpt-math-sdk/src/examples/scaled-vectors.js`, one concept at a time, framed so a first-time reader can see what each step does and why it stays exact.
|
|
13
|
+
|
|
14
|
+
Every cell reaches its math through the single injected `math` builtin — no imports, no URLs.
|
|
15
|
+
</cell>
|
|
16
|
+
|
|
17
|
+
<cell type="js" lane="math" id="backend">
|
|
18
|
+
// Report the active math backend. The label is DERIVED — never hardcoded.
|
|
19
|
+
const { math, display } = caps;
|
|
20
|
+
const backend = math.activeMathBackend;
|
|
21
|
+
display(`Active math backend (derived from SDK registry): ${backend}`);
|
|
22
|
+
</cell>
|
|
23
|
+
|
|
24
|
+
<cell type="markdown">
|
|
25
|
+
## 1. Constructing a rational and computing its square root
|
|
26
|
+
|
|
27
|
+
`EGPTReal.fromRational(numerator, denominator)` lifts a BigInt fraction into compressed information space. The number ½ becomes a symbolic object whose prime factorization carries the exact value `2⁻¹`.
|
|
28
|
+
|
|
29
|
+
`EGPTMath.sqrt(x)` halves all exponents in that factorization. For `√(½) = √(2⁻¹) = 2⁻¹/²`, the exponent on prime 2 moves from `−1` to `−½`. The result is a new `EGPTReal` representing the exact algebraic value `1/√2` — no decimal approximation involved.
|
|
30
|
+
|
|
31
|
+
`toMathString()` renders the internal representation as a human-readable expression.
|
|
32
|
+
</cell>
|
|
33
|
+
|
|
34
|
+
<cell type="js" lane="math" id="sqrt-half" out="sqrtHalf">
|
|
35
|
+
// Lift ½ into EGPTReal and compute its exact square root.
|
|
36
|
+
const { EGPTReal, EGPTMath } = caps.math;
|
|
37
|
+
const { display } = caps; // display is on caps, not caps.math
|
|
38
|
+
|
|
39
|
+
// EGPTReal.fromRational(1n, 2n) constructs the exact rational 1/2
|
|
40
|
+
// as a symbolic prime-power vector: {prime: 2, exponent: -1}.
|
|
41
|
+
const half = EGPTReal.fromRational(1n, 2n);
|
|
42
|
+
|
|
43
|
+
// EGPTMath.sqrt halves every exponent: 2^(-1) → 2^(-1/2).
|
|
44
|
+
// Result is the exact algebraic value √(1/2) = 1/√2.
|
|
45
|
+
const sqrtHalf = EGPTMath.sqrt(half);
|
|
46
|
+
|
|
47
|
+
display(`half = ${half.toMathString()}`);
|
|
48
|
+
display(`√half = ${sqrtHalf.toMathString()}`);
|
|
49
|
+
|
|
50
|
+
return { sqrtHalf };
|
|
51
|
+
</cell>
|
|
52
|
+
|
|
53
|
+
<cell type="markdown">
|
|
54
|
+
## 2. Squaring back — verifying the identity (√½)² = ½
|
|
55
|
+
|
|
56
|
+
`EGPTMath.multiply(a, b)` adds the exponent vectors of `a` and `b`. Multiplying `√(½)` by itself adds `2^(−½)` to `2^(−½)`, yielding `2^(−1)` — which is exactly ½. No rounding, no approximation.
|
|
57
|
+
|
|
58
|
+
This is the key correctness check: an exact symbolic system must close the round-trip `x = sqrt(y)` → `x * x == y`.
|
|
59
|
+
</cell>
|
|
60
|
+
|
|
61
|
+
<cell type="js" lane="math" id="square-back" in="sqrtHalf" out="squaredBack">
|
|
62
|
+
// Multiply sqrtHalf by itself. In log-space this adds the exponents:
|
|
63
|
+
// (-1/2) + (-1/2) = -1 → the result is 2^(-1) = 1/2.
|
|
64
|
+
// The identity (√½)² = ½ holds exactly — no floating-point rounding.
|
|
65
|
+
const { EGPTMath } = caps.math;
|
|
66
|
+
const { display } = caps; // display is on caps, not caps.math
|
|
67
|
+
const sqrtHalf = inputs.sqrtHalf;
|
|
68
|
+
|
|
69
|
+
const squaredBack = EGPTMath.multiply(sqrtHalf, sqrtHalf);
|
|
70
|
+
display(`(√½)² = ${squaredBack.toMathString()}`);
|
|
71
|
+
|
|
72
|
+
return { squaredBack };
|
|
73
|
+
</cell>
|
|
74
|
+
|
|
75
|
+
<cell type="markdown">
|
|
76
|
+
## 3. Scaling by an integer — exact scalar multiplication
|
|
77
|
+
|
|
78
|
+
`EGPTReal.fromBigInt(2n)` constructs the integer 2 as a prime-power vector `{prime: 2, exponent: 1}`. Multiplying `√(½)` by `2` adds that `+1` exponent to the `−½` exponent on prime 2, yielding `2^(+½) = √2`.
|
|
79
|
+
|
|
80
|
+
The result `√½ × 2 = 2/√2 = √2` is exact. This demonstrates that scaling an irrational by an integer stays in the same symbolic space — no boundary crossing, no precision loss.
|
|
81
|
+
</cell>
|
|
82
|
+
|
|
83
|
+
<cell type="js" lane="math" id="scaled-by-two" in="sqrtHalf">
|
|
84
|
+
// Scale √½ by the integer 2.
|
|
85
|
+
// In log-space: exponent of 2 moves from -1/2 to -1/2 + 1 = +1/2.
|
|
86
|
+
// Result: 2^(1/2) = √2.
|
|
87
|
+
const { EGPTReal, EGPTMath } = caps.math;
|
|
88
|
+
const { display } = caps; // display is on caps, not caps.math
|
|
89
|
+
const sqrtHalf = inputs.sqrtHalf;
|
|
90
|
+
|
|
91
|
+
const two = EGPTReal.fromBigInt(2n);
|
|
92
|
+
const scaledByTwo = EGPTMath.multiply(sqrtHalf, two);
|
|
93
|
+
|
|
94
|
+
display(`2 × √½ = ${scaledByTwo.toMathString()}`);
|
|
95
|
+
display('(Exact algebraic value: √2)');
|
|
96
|
+
</cell>
|
|
97
|
+
|
|
98
|
+
<cell type="markdown">
|
|
99
|
+
## Summary
|
|
100
|
+
|
|
101
|
+
The three values computed here match the `scaled-vectors` example exactly:
|
|
102
|
+
|
|
103
|
+
| Expression | Exact value | What EGPTMath does |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `√(½)` | `2^(−½)` | halves the exponent on prime 2 |
|
|
106
|
+
| `(√½)²` | `2^(−1) = ½` | adds exponents: −½ + −½ = −1 |
|
|
107
|
+
| `2 × √½` | `2^(+½) = √2` | adds exponents: +1 + −½ = +½ |
|
|
108
|
+
|
|
109
|
+
Every step stays inside the prime-power symbolic representation. Floating-point arithmetic is never involved. The rounding errors that would accumulate in IEEE-754 simply do not exist here.
|
|
110
|
+
</cell>
|
|
111
|
+
|
|
112
|
+
</frqtl-notebook>
|