@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.
Files changed (114) hide show
  1. package/README.md +224 -0
  2. package/dist/egpt-math-sdk.browser.js +5 -0
  3. package/dist/index.d.ts +100 -0
  4. package/dist/index.js +985 -0
  5. package/dist/notebooks/arithmetic-basics.html +166 -0
  6. package/dist/notebooks/complex-twiddle.html +148 -0
  7. package/dist/notebooks/double-slit-rosetta-qft.html +154 -0
  8. package/dist/notebooks/egpt-fft-test.html +202 -0
  9. package/dist/notebooks/egpt-matrix-test.html +241 -0
  10. package/dist/notebooks/egpt-polynomial-test.html +703 -0
  11. package/dist/notebooks/egpt-prime-composite-test.html +561 -0
  12. package/dist/notebooks/egpt-test-suite.html +1387 -0
  13. package/dist/notebooks/egpt-topology-test.html +410 -0
  14. package/dist/notebooks/factorization-basics.html +131 -0
  15. package/dist/notebooks/fft.html +155 -0
  16. package/dist/notebooks/frqtl-qft-benchmark.html +207 -0
  17. package/dist/notebooks/gallery-atomic-model.html +51 -0
  18. package/dist/notebooks/gallery-big-bang.html +62 -0
  19. package/dist/notebooks/gallery-blackbody.html +52 -0
  20. package/dist/notebooks/gallery-circuit-sat.html +104 -0
  21. package/dist/notebooks/gallery-collision-matrix-3d.html +51 -0
  22. package/dist/notebooks/gallery-double-slit.html +53 -0
  23. package/dist/notebooks/gallery-gun-chamber.html +62 -0
  24. package/dist/notebooks/gallery-particle-walk-3d.html +53 -0
  25. package/dist/notebooks/gallery-wave-interference.html +52 -0
  26. package/dist/notebooks/getting-started-hello-frqtl.html +53 -0
  27. package/dist/notebooks/iso-canonical-chain.html +203 -0
  28. package/dist/notebooks/iso-entropy-log.html +127 -0
  29. package/dist/notebooks/iso-fft-polynomial.html +136 -0
  30. package/dist/notebooks/iso-matrix-four-views.html +131 -0
  31. package/dist/notebooks/iso-number-theory-four-views.html +99 -0
  32. package/dist/notebooks/iso-pi-log-fraction-loop.html +140 -0
  33. package/dist/notebooks/matrix.html +202 -0
  34. package/dist/notebooks/notebooks.json +240 -0
  35. package/dist/notebooks/polynomials.html +201 -0
  36. package/dist/notebooks/prime-composite.html +268 -0
  37. package/dist/notebooks/rational-core.html +179 -0
  38. package/dist/notebooks/reference-egpt-fft-test.html +187 -0
  39. package/dist/notebooks/reference-order-finder-test.html +196 -0
  40. package/dist/notebooks/reference-prime-atom-polynomial-test.html +542 -0
  41. package/dist/notebooks/reference-solve-polynomial-system-test.html +471 -0
  42. package/dist/notebooks/scaled-vectors.html +112 -0
  43. package/dist/notebooks/stats.html +206 -0
  44. package/dist/notebooks/transcendentals.html +195 -0
  45. package/dist/notebooks/view-vendored-d3.html +78 -0
  46. package/dist/vendor/d3-array/src/array.js +4 -0
  47. package/dist/vendor/d3-array/src/ascending.js +3 -0
  48. package/dist/vendor/d3-array/src/bin.js +125 -0
  49. package/dist/vendor/d3-array/src/bisect.js +9 -0
  50. package/dist/vendor/d3-array/src/bisector.js +56 -0
  51. package/dist/vendor/d3-array/src/blur.js +115 -0
  52. package/dist/vendor/d3-array/src/constant.js +3 -0
  53. package/dist/vendor/d3-array/src/count.js +18 -0
  54. package/dist/vendor/d3-array/src/cross.js +33 -0
  55. package/dist/vendor/d3-array/src/cumsum.js +6 -0
  56. package/dist/vendor/d3-array/src/descending.js +7 -0
  57. package/dist/vendor/d3-array/src/deviation.js +6 -0
  58. package/dist/vendor/d3-array/src/difference.js +11 -0
  59. package/dist/vendor/d3-array/src/disjoint.js +15 -0
  60. package/dist/vendor/d3-array/src/every.js +10 -0
  61. package/dist/vendor/d3-array/src/extent.js +29 -0
  62. package/dist/vendor/d3-array/src/filter.js +11 -0
  63. package/dist/vendor/d3-array/src/fsum.js +69 -0
  64. package/dist/vendor/d3-array/src/greatest.js +29 -0
  65. package/dist/vendor/d3-array/src/greatestIndex.js +19 -0
  66. package/dist/vendor/d3-array/src/group.js +65 -0
  67. package/dist/vendor/d3-array/src/groupSort.js +10 -0
  68. package/dist/vendor/d3-array/src/identity.js +3 -0
  69. package/dist/vendor/d3-array/src/index.js +57 -0
  70. package/dist/vendor/d3-array/src/intersection.js +19 -0
  71. package/dist/vendor/d3-array/src/least.js +29 -0
  72. package/dist/vendor/d3-array/src/leastIndex.js +19 -0
  73. package/dist/vendor/d3-array/src/map.js +5 -0
  74. package/dist/vendor/d3-array/src/max.js +20 -0
  75. package/dist/vendor/d3-array/src/maxIndex.js +22 -0
  76. package/dist/vendor/d3-array/src/mean.js +19 -0
  77. package/dist/vendor/d3-array/src/median.js +9 -0
  78. package/dist/vendor/d3-array/src/merge.js +9 -0
  79. package/dist/vendor/d3-array/src/min.js +20 -0
  80. package/dist/vendor/d3-array/src/minIndex.js +22 -0
  81. package/dist/vendor/d3-array/src/mode.js +28 -0
  82. package/dist/vendor/d3-array/src/nice.js +18 -0
  83. package/dist/vendor/d3-array/src/number.js +20 -0
  84. package/dist/vendor/d3-array/src/pairs.js +15 -0
  85. package/dist/vendor/d3-array/src/permute.js +3 -0
  86. package/dist/vendor/d3-array/src/quantile.js +47 -0
  87. package/dist/vendor/d3-array/src/quickselect.js +53 -0
  88. package/dist/vendor/d3-array/src/range.js +13 -0
  89. package/dist/vendor/d3-array/src/rank.js +24 -0
  90. package/dist/vendor/d3-array/src/reduce.js +14 -0
  91. package/dist/vendor/d3-array/src/reverse.js +4 -0
  92. package/dist/vendor/d3-array/src/scan.js +6 -0
  93. package/dist/vendor/d3-array/src/shuffle.js +13 -0
  94. package/dist/vendor/d3-array/src/some.js +10 -0
  95. package/dist/vendor/d3-array/src/sort.js +39 -0
  96. package/dist/vendor/d3-array/src/subset.js +5 -0
  97. package/dist/vendor/d3-array/src/sum.js +18 -0
  98. package/dist/vendor/d3-array/src/superset.js +19 -0
  99. package/dist/vendor/d3-array/src/threshold/freedmanDiaconis.js +7 -0
  100. package/dist/vendor/d3-array/src/threshold/scott.js +7 -0
  101. package/dist/vendor/d3-array/src/threshold/sturges.js +5 -0
  102. package/dist/vendor/d3-array/src/ticks.js +55 -0
  103. package/dist/vendor/d3-array/src/transpose.js +15 -0
  104. package/dist/vendor/d3-array/src/union.js +11 -0
  105. package/dist/vendor/d3-array/src/variance.js +25 -0
  106. package/dist/vendor/d3-array/src/zip.js +5 -0
  107. package/dist/vendor/internmap/src/index.js +61 -0
  108. package/dist/vendor/manifest.json +11 -0
  109. package/dist/wasm/egpt_math_sdk_wasm.d.ts +214 -0
  110. package/dist/wasm/egpt_math_sdk_wasm.js +1120 -0
  111. package/dist/wasm/egpt_math_sdk_wasm_bg.wasm +0 -0
  112. package/dist/wasm/egpt_math_sdk_wasm_bg.wasm.d.ts +66 -0
  113. package/dist/wasm/manifest.json +5 -0
  114. package/package.json +53 -0
@@ -0,0 +1,268 @@
1
+ <frqtl-notebook version="1" title="Prime Structure as Arithmetic Identity — PrimeAtomPolynomial &amp; EGPTPrimeComposite">
2
+
3
+ <cell type="markdown">
4
+ # Prime Structure as Arithmetic Identity
5
+
6
+ Every natural number `N` is uniquely determined by a vector of non-negative integer exponents — one per prime — with almost all of them zero. The Fundamental Theorem of Arithmetic is normally stated as a factoring result, but in EGPT it is a *bijection*: `N ↔ its prime-exponent vector`. Two numbers are the same if and only if their vectors are identical.
7
+
8
+ This notebook makes that bijection tangible through two SDK classes:
9
+
10
+ | Class | Role |
11
+ |---|---|
12
+ | `PrimeAtomPolynomial` | Exposes the factorization vector (the LFTA bijection) as an ordered list of `{ prime, exponent }` atoms. |
13
+ | `EGPTPrimeComposite` | Extends `EGPTReal` to carry the full prime provenance of a *rational* through multiplication and division — without ever reducing. |
14
+
15
+ All computation in this notebook flows through a single injected capability, `math`, with no URL imports.
16
+ </cell>
17
+
18
+ <cell type="js" lane="math" id="backend">
19
+ // Report the active backend — derived from the SDK, never hardcoded.
20
+ const { math, display } = caps;
21
+ const backend = math.activeMathBackend;
22
+ display(`Active math backend (derived): ${backend}`);
23
+ </cell>
24
+
25
+ <cell type="markdown">
26
+ ## 1. Factorizing an integer into its prime-atom polynomial
27
+
28
+ `PrimeAtomPolynomial.factorize(N)` implements trial division and returns the prime-atom polynomial as a sorted list of `{ prime: bigint, exponent: bigint }` pairs. The result is the LFTA (Logarithmic Fundamental Theorem of Arithmetic) representation: `log₂ N = Σ v_p(N) · log₂ p`.
29
+
30
+ We start with `N = 12 = 2² × 3¹`. The atom list should have exactly two entries, in ascending prime order.
31
+ </cell>
32
+
33
+ <cell type="js" lane="math" id="factorize-12" out="factors12">
34
+ // Factorize 12 via the prime-atom polynomial bijection.
35
+ // PrimeAtomPolynomial.factorize(N) → [{ prime: bigint, exponent: bigint }, …]
36
+ const { math, display } = caps;
37
+ const { PrimeAtomPolynomial } = math;
38
+
39
+ const N = 12n;
40
+ const factors = PrimeAtomPolynomial.factorize(N);
41
+
42
+ // Verify the bijection for 12 = 2^2 * 3^1
43
+ if (factors.length !== 2) throw new Error(`Expected 2 prime atoms, got ${factors.length}`);
44
+ if (factors[0].prime !== 2n || factors[0].exponent !== 2n)
45
+ throw new Error(`Expected first atom 2^2, got ${factors[0].prime}^${factors[0].exponent}`);
46
+ if (factors[1].prime !== 3n || factors[1].exponent !== 1n)
47
+ throw new Error(`Expected second atom 3^1, got ${factors[1].prime}^${factors[1].exponent}`);
48
+
49
+ const factorizeStr = factors.map(f => `${f.prime}^${f.exponent}`).join(' × ');
50
+ display(`PrimeAtomPolynomial.factorize(${N}) = [ ${factorizeStr} ]`);
51
+ display(` → 12 = ${factors.map(f => `${f.prime}^${f.exponent}`).join(' × ')} ✓`);
52
+
53
+ return { factors12: factors.map(f => ({ prime: f.prime, exponent: f.exponent })) };
54
+ </cell>
55
+
56
+ <cell type="markdown">
57
+ ## 2. The prime-atom polynomial for a larger number
58
+
59
+ The same call generalizes. `N = 360 = 2³ × 3² × 5¹`. The atom polynomial has three terms with exponents `[3, 2, 1]`. Notice how all three prime atoms are listed in order — the polynomial is *sparse*: only the atoms with nonzero exponent appear.
60
+ </cell>
61
+
62
+ <cell type="js" lane="math" id="factorize-360" out="factors360">
63
+ const { math, display } = caps;
64
+ const { PrimeAtomPolynomial } = math;
65
+
66
+ const N = 360n;
67
+ const factors = PrimeAtomPolynomial.factorize(N);
68
+
69
+ const lines = [
70
+ `PrimeAtomPolynomial.factorize(${N}):`,
71
+ ...factors.map(f => ` prime ${f.prime}, exponent ${f.exponent}`)
72
+ ];
73
+ display(lines.join('\n'));
74
+
75
+ // Also compute and display the LFTA log-space sum: log₂(360) = 3·log₂(2) + 2·log₂(3) + 1·log₂(5)
76
+ // We verify the sum approximates the true log₂(360) = 8.49...
77
+ const log2N_approx = factors.reduce((acc, f) => {
78
+ return acc + Number(f.exponent) * Math.log2(Number(f.prime));
79
+ }, 0);
80
+ const log2N_exact = Math.log2(Number(N));
81
+ display(`LFTA check: Σ v_p·log₂(p) = ${log2N_approx.toFixed(6)} vs log₂(${N}) = ${log2N_exact.toFixed(6)}`);
82
+
83
+ return { factors360: factors.map(f => ({ prime: f.prime, exponent: f.exponent })) };
84
+ </cell>
85
+
86
+ <cell type="markdown">
87
+ ## 3. Auxiliary combinatorics from the prime-atom view
88
+
89
+ Once you hold the prime-atom polynomial `{ p → v_p(N) }`, several classical number-theory quantities become simple:
90
+
91
+ - **`omega(N)`** — the number of *distinct* prime atoms (nonzero exponents).
92
+ - **`bigOmega(N)`** — the *total* exponent count, `Σ v_p(N)`, i.e. counting with multiplicity.
93
+ - **`totient(N)`** — Euler's φ(N) = N · ∏_{p | N} (1 − 1/p), computed directly from the factorization vector.
94
+ - **`divisorsOf(N)`** — all positive divisors, enumerated from the exponent vector without brute-force trial.
95
+
96
+ These are all O(log N) or O(d(N)) operations once the factorization is in hand — no looping up to N.
97
+ </cell>
98
+
99
+ <cell type="js" lane="math" id="combinatorics" in="factors360">
100
+ const { math, display } = caps;
101
+ const { PrimeAtomPolynomial } = math;
102
+
103
+ const N = 360n;
104
+ const omega = PrimeAtomPolynomial.omega(N);
105
+ const bigOmega = PrimeAtomPolynomial.bigOmega(N);
106
+ const totient = PrimeAtomPolynomial.totient(N);
107
+ const divisors = PrimeAtomPolynomial.divisorsOf(N);
108
+
109
+ const lines = [
110
+ `N = ${N}`,
111
+ ` omega(N) = ${omega} (distinct prime atoms: 2, 3, 5)`,
112
+ ` bigOmega(N) = ${bigOmega} (total atoms counting multiplicity: 3+2+1)`,
113
+ ` totient(N) = ${totient} (numbers 1..360 coprime to 360)`,
114
+ ` divisors = [${divisors.slice(0, 10).join(', ')}, … (${divisors.length} total)]`
115
+ ];
116
+ display(lines.join('\n'));
117
+
118
+ // Spot-check: totient(360) = 96
119
+ if (totient !== 96n) throw new Error(`Expected totient 96, got ${totient}`);
120
+ // Spot-check: 360 has 24 divisors (from (3+1)(2+1)(1+1))
121
+ if (divisors.length !== 24) throw new Error(`Expected 24 divisors, got ${divisors.length}`);
122
+ display(`All checks passed ✓`);
123
+ </cell>
124
+
125
+ <cell type="markdown">
126
+ ## 4. From integer factorization to rational prime provenance
127
+
128
+ Standard `EGPTReal` reduces every rational to lowest terms in its constructor. That is correct for arithmetic, but it *discards provenance*: multiplying `3 × (1/3)` produces `1`, losing the information that a `3` appeared in both the numerator and denominator.
129
+
130
+ `EGPTPrimeComposite` preserves that provenance. It extends `EGPTReal` so it is valid wherever an `EGPTReal` is expected, but it stores every prime factor of every multiplication step as an explicit `{ prime, location }` record. Reduction never happens inside the type — the prime structure is the point.
131
+
132
+ The construction route for a rational is:
133
+ 1. Build an `EGPTReal` with `EGPTReal.fromRational(numerator, denominator)`.
134
+ 2. Wrap it: `EGPTPrimeComposite.fromEGPTReal(real)` — trial-divides the un-reduced numerator and denominator and populates the record list.
135
+
136
+ We demonstrate with `12/5`. Its prime atoms are `2², 3¹` in the numerator and `5¹` in the denominator.
137
+ </cell>
138
+
139
+ <cell type="js" lane="math" id="composite-from-real" out="comp12over5">
140
+ // Build EGPTReal(12/5) then wrap into EGPTPrimeComposite.
141
+ const { math, display } = caps;
142
+ const { EGPTReal, EGPTPrimeComposite } = math;
143
+
144
+ const num = EGPTReal.fromRational(12n, 5n);
145
+ const comp = EGPTPrimeComposite.fromEGPTReal(num);
146
+
147
+ const primesNum = comp.getPrimesInNumerator();
148
+ const primesDen = comp.getPrimesInDenominator();
149
+
150
+ // Verify prime provenance — same assertions as the original run(sdk)
151
+ if (primesNum.get(2n) !== 2n) throw new Error(`Expected 2^2 in numerator, got ${primesNum.get(2n)}`);
152
+ if (primesNum.get(3n) !== 1n) throw new Error(`Expected 3^1 in numerator, got ${primesNum.get(3n)}`);
153
+ if (primesDen.get(5n) !== 1n) throw new Error(`Expected 5^1 in denominator, got ${primesDen.get(5n)}`);
154
+
155
+ const numStr = Array.from(primesNum.entries()).map(([p, e]) => `${p}^${e}`).join(' × ');
156
+ const denStr = Array.from(primesDen.entries()).map(([p, e]) => `${p}^${e}`).join(' × ');
157
+
158
+ display(`EGPTReal(12/5) → EGPTPrimeComposite`);
159
+ display(` Numerator primes: ${numStr || '(none)'}`);
160
+ display(` Denominator primes: ${denStr || '(none)'}`);
161
+ display(` Sign: ${comp.getSign()}`);
162
+ display(` Record count: ${comp.records.length}`);
163
+ display(`All provenance checks passed ✓`);
164
+
165
+ return { comp12over5: { numStr, denStr } };
166
+ </cell>
167
+
168
+ <cell type="markdown">
169
+ ## 5. Multiplication preserves, not discards, prime structure
170
+
171
+ The core discipline of `EGPTPrimeComposite` is that multiplication **concatenates** the prime-record lists — no cancellation. Multiplying `(12/5) × (7/12)` normally reduces to `7/5`, discarding the shared factor of `12`. The composite type keeps all four record lists (2², 3¹ from the first numerator; 5¹ from the first denominator; 7¹ from the second numerator; 2², 3¹ from the second denominator).
172
+
173
+ The *value* of the result is still `7/5` (the composite inherits from `EGPTReal` and reports the correct rational). But `getPrimesInNumerator()` exposes the net exponents: the `2^2` and `3^1` that appeared in both numerator and denominator show up with *zero net exponent*, visible in the underlying `primes` map.
174
+
175
+ This provenance record is what the CNF→polynomial SAT decoding step uses: each literal's polarity is reconstructed from whether its prime landed in the numerator or denominator of the product composite.
176
+ </cell>
177
+
178
+ <cell type="js" lane="math" id="multiply-preserves">
179
+ const { math, display } = caps;
180
+ const { EGPTReal, EGPTPrimeComposite } = math;
181
+
182
+ // a = 12/5, b = 7/12
183
+ const a = EGPTPrimeComposite.fromEGPTReal(EGPTReal.fromRational(12n, 5n));
184
+ const b = EGPTPrimeComposite.fromEGPTReal(EGPTReal.fromRational(7n, 12n));
185
+
186
+ const product = a.multiply(b); // records from a and b are concatenated
187
+
188
+ // Net exponents after multiplication:
189
+ // Numerator: 2^2 * 3^1 from a-num, 7^1 from b-num
190
+ // Denominator: 5^1 from a-den, 2^2 * 3^1 from b-den
191
+ // Net: 2^(2-2)=0, 3^(1-1)=0, 7^1 in num, 5^1 in den → value = 7/5 ✓
192
+ //
193
+ // NOTE: _getPPFRationalParts() returns the un-reduced internal form (84/60)
194
+ // because EGPTPrimeComposite uses skip_reduce: true to preserve prime structure.
195
+ // Use .equals() to check value equivalence — that is the canonical comparison method.
196
+
197
+ const netNum = product.getPrimesInNumerator();
198
+ const netDen = product.getPrimesInDenominator();
199
+
200
+ const expected = EGPTReal.fromRational(7n, 5n);
201
+ const valueOk = product.equals(expected);
202
+
203
+ const lines = [
204
+ `(12/5) × (7/12):`,
205
+ ` Total prime records: ${product.records.length} (4 from a + 4 from b, never discarded)`,
206
+ ` Net numerator primes: ${Array.from(netNum.entries()).map(([p,e]) => `${p}^${e}`).join(', ') || '(all cancelled)'}`,
207
+ ` Net denominator primes: ${Array.from(netDen.entries()).map(([p,e]) => `${p}^${e}`).join(', ')}`,
208
+ ` Value equals 7/5: ${valueOk}`
209
+ ];
210
+ display(lines.join('\n'));
211
+
212
+ if (!valueOk) throw new Error(`Expected value equal to 7/5 but .equals() returned false`);
213
+ if (!netNum.has(7n)) throw new Error('Expected 7^1 in net numerator');
214
+ if (!netDen.has(5n)) throw new Error('Expected 5^1 in net denominator');
215
+ display(`Multiplication preserves record count and rational value ✓`);
216
+ </cell>
217
+
218
+ <cell type="markdown">
219
+ ## 6. Division flips provenance — the SAT literal recovery
220
+
221
+ Division in `EGPTPrimeComposite` is multiplication by the reciprocal: the divisor's records have their `location` field *flipped* (`numerator ↔ denominator`) before concatenation. This is exactly the operation that converts a positive literal (prime in numerator) to a negative literal (prime in denominator) in the BIPP CNF→polynomial decoding.
222
+
223
+ Dividing `12/5` by itself must yield value `1`. The prime records for `12` cancel exactly — but only the *net* exponent goes to zero; the raw records list still holds both the numerator copies and the flipped denominator copies.
224
+ </cell>
225
+
226
+ <cell type="js" lane="math" id="divide-flips">
227
+ const { math, display } = caps;
228
+ const { EGPTReal, EGPTPrimeComposite } = math;
229
+
230
+ const a = EGPTPrimeComposite.fromEGPTReal(EGPTReal.fromRational(12n, 5n));
231
+ const quotient = a.divide(a); // (12/5) / (12/5) = 1
232
+
233
+ // NOTE: _getPPFRationalParts() returns un-reduced internal form (e.g. 60/60)
234
+ // because EGPTPrimeComposite uses skip_reduce: true. Use .equals() for value checks.
235
+ const netNum = quotient.getPrimesInNumerator();
236
+ const netDen = quotient.getPrimesInDenominator();
237
+
238
+ const expectedOne = EGPTReal.fromRational(1n, 1n);
239
+ const valueOk = quotient.equals(expectedOne);
240
+
241
+ display(`(12/5) ÷ (12/5):`);
242
+ display(` Raw record count: ${quotient.records.length} (records from a and flipped-a, all kept)`);
243
+ display(` Net numerator primes: ${Array.from(netNum.entries()).map(([p,e]) => `${p}^${e}`).join(', ') || '(none — all cancelled to zero)'}`);
244
+ display(` Net denominator primes: ${Array.from(netDen.entries()).map(([p,e]) => `${p}^${e}`).join(', ') || '(none)'}`);
245
+ display(` Value equals 1: ${valueOk}`);
246
+
247
+ if (!valueOk) throw new Error(`Expected value equal to 1 but .equals() returned false`);
248
+ display(`Division preserves records, net exponents correctly zero ✓`);
249
+ </cell>
250
+
251
+ <cell type="markdown">
252
+ ## Summary
253
+
254
+ This notebook demonstrated two interoperable views of the same EGPT bijection:
255
+
256
+ | Tool | What it does |
257
+ |---|---|
258
+ | `PrimeAtomPolynomial.factorize(N)` | Returns the ordered prime-atom list — the LFTA bijection made explicit. |
259
+ | `PrimeAtomPolynomial.omega / bigOmega / totient / divisorsOf` | Classical number theory, O(log N) once the factorization vector is in hand. |
260
+ | `EGPTPrimeComposite.fromEGPTReal(r)` | Wraps any `EGPTReal` rational, trial-dividing its un-reduced numerator and denominator into provenance records. |
261
+ | `comp.multiply(other)` | Concatenates record lists — no cancellation, no reduction. Value is correct; provenance is intact. |
262
+ | `comp.divide(other)` | Flips the divisor's record locations before concatenation — the SAT literal-polarity flip made explicit. |
263
+ | `comp.getPrimesInNumerator()` / `.getPrimesInDenominator()` | Net-exponent views for downstream decoding (CNF→polynomial SAT witness extraction). |
264
+
265
+ The computations in cells 1–6 are the exact assertions the original `prime-composite.js` demo verifies, expanded with narrative and auxiliary combinatorics for a newcomer.
266
+ </cell>
267
+
268
+ </frqtl-notebook>
@@ -0,0 +1,179 @@
1
+ <frqtl-notebook version="1" title="Rational-Core: Exact Arithmetic with EGPTReal and EGPTMath">
2
+
3
+ <cell type="markdown">
4
+ # Rational-Core: Exact Arithmetic with EGPTReal and EGPTMath
5
+
6
+ EGPTMath stores numbers as **exact rational values** — numerator and denominator as arbitrary-precision
7
+ BigInts — rather than as IEEE-754 floats. This eliminates the rounding errors that accumulate in
8
+ floating-point pipelines and is the foundation of FRAQTL's bit-exact codec chain.
9
+
10
+ Two classes carry all the weight:
11
+
12
+ - **`EGPTReal`** — an immutable, symbolic rational value. You construct one via
13
+ `EGPTReal.fromRational(numerator, denominator)` (BigInt arguments). It lives in _compressed
14
+ information space_ and blocks implicit coercion to JS primitives so that accidental
15
+ float-leaks cause a loud error rather than a silent precision loss.
16
+
17
+ - **`EGPTMath`** — a static algebra engine. All arithmetic between `EGPTReal` values goes through
18
+ its methods (`add`, `multiply`, `normalDivide`, `compare`, …). It returns new `EGPTReal` instances
19
+ and never mutates its inputs.
20
+
21
+ This notebook walks through the four fundamental operations on two concrete rational numbers:
22
+ `left = 11/7` and `right = 2/3`.
23
+ </cell>
24
+
25
+ <cell type="js" lane="math" id="construct" out="left, right">
26
+ // Step 1: construct two exact rational values.
27
+ //
28
+ // EGPTReal.fromRational(numerator, denominator) accepts BigInt arguments and
29
+ // reduces the fraction to lowest terms automatically.
30
+ //
31
+ // 11n and 7n are already coprime, so left stores H(11/7) exactly.
32
+ // 2n and 3n are already coprime, so right stores H(2/3) exactly.
33
+ const { EGPTReal, display } = { ...caps.math, display: caps.display }; // math + display are on caps, never bare globals
34
+
35
+ const left = EGPTReal.fromRational(11n, 7n);
36
+ const right = EGPTReal.fromRational(2n, 3n);
37
+
38
+ display(`left = ${left.toMathString()}`);
39
+ display(`right = ${right.toMathString()}`);
40
+
41
+ return { left, right };
42
+ </cell>
43
+
44
+ <cell type="markdown">
45
+ ## Addition — `EGPTMath.add`
46
+
47
+ `EGPTMath.add(a, b)` performs exact rational addition using cross-multiplication:
48
+
49
+ ```
50
+ a/b + c/d = (a·d + b·c) / (b·d)
51
+ ```
52
+
53
+ The result is immediately reduced to lowest terms. For our two values:
54
+
55
+ ```
56
+ 11/7 + 2/3 = (11·3 + 2·7) / (7·3) = (33 + 14) / 21 = 47/21
57
+ ```
58
+ </cell>
59
+
60
+ <cell type="js" lane="math" id="addition" in="left, right" out="sum">
61
+ // EGPTMath.add — exact rational addition.
62
+ const { EGPTMath, display } = { ...caps.math, display: caps.display }; // math + display are on caps, never bare globals
63
+ const { left, right } = inputs;
64
+
65
+ const sum = EGPTMath.add(left, right);
66
+
67
+ display(`11/7 + 2/3 = ${sum.toMathString()}`);
68
+
69
+ return { sum };
70
+ </cell>
71
+
72
+ <cell type="markdown">
73
+ ## Multiplication — `EGPTMath.multiply`
74
+
75
+ `EGPTMath.multiply(a, b)` multiplies two rational values. In Shannon information space,
76
+ multiplication in normal space maps to **vector addition** (`H(p×q) = H(p) + H(q)`),
77
+ but the result surfaces back as a reduced rational via `toMathString()`:
78
+
79
+ ```
80
+ 11/7 × 2/3 = 22/21
81
+ ```
82
+ </cell>
83
+
84
+ <cell type="js" lane="math" id="multiplication" in="left, right" out="product">
85
+ // EGPTMath.multiply — exact rational multiplication.
86
+ const { EGPTMath, display } = { ...caps.math, display: caps.display }; // math + display are on caps, never bare globals
87
+ const { left, right } = inputs;
88
+
89
+ const product = EGPTMath.multiply(left, right);
90
+
91
+ display(`11/7 × 2/3 = ${product.toMathString()}`);
92
+
93
+ return { product };
94
+ </cell>
95
+
96
+ <cell type="markdown">
97
+ ## Division — `EGPTMath.normalDivide`
98
+
99
+ `EGPTMath.normalDivide(a, b)` computes the ratio `a / b` in normal space (not Shannon space).
100
+ Internally it cross-multiplies the two rational pairs:
101
+
102
+ ```
103
+ (a/b) ÷ (c/d) = (a·d) / (b·c)
104
+ ```
105
+
106
+ For our values:
107
+
108
+ ```
109
+ (11/7) ÷ (2/3) = (11·3) / (7·2) = 33/14
110
+ ```
111
+ </cell>
112
+
113
+ <cell type="js" lane="math" id="division" in="left, right" out="quotient">
114
+ // EGPTMath.normalDivide — exact rational division.
115
+ const { EGPTMath, display } = { ...caps.math, display: caps.display }; // math + display are on caps, never bare globals
116
+ const { left, right } = inputs;
117
+
118
+ const quotient = EGPTMath.normalDivide(left, right);
119
+
120
+ display(`(11/7) ÷ (2/3) = ${quotient.toMathString()}`);
121
+
122
+ return { quotient };
123
+ </cell>
124
+
125
+ <cell type="markdown">
126
+ ## Comparison — `EGPTMath.compare`
127
+
128
+ `EGPTMath.compare(a, b)` returns `-1`, `0`, or `1` using exact BigInt cross-multiplication
129
+ (no floating-point involved):
130
+
131
+ | Return | Meaning |
132
+ |--------|---------|
133
+ | `-1` | a &lt; b |
134
+ | `0` | a = b |
135
+ | `1` | a &gt; b |
136
+
137
+ Since `11/7 ≈ 1.571` and `2/3 ≈ 0.667`, we expect `compare(left, right)` to return `1`.
138
+ </cell>
139
+
140
+ <cell type="js" lane="math" id="comparison" in="left, right" out="cmp">
141
+ // EGPTMath.compare — exact rational ordering.
142
+ const { EGPTMath, display } = { ...caps.math, display: caps.display }; // math + display are on caps, never bare globals
143
+ const { left, right } = inputs;
144
+
145
+ const cmp = EGPTMath.compare(left, right);
146
+
147
+ const label = cmp === -1 ? 'left < right'
148
+ : cmp === 0 ? 'left = right'
149
+ : 'left > right';
150
+
151
+ display(`compare(11/7, 2/3) = ${cmp} → ${label}`);
152
+
153
+ return { cmp };
154
+ </cell>
155
+
156
+ <cell type="markdown">
157
+ ## Summary
158
+
159
+ All four operations produce exact rational results with no floating-point rounding.
160
+ The final cell mirrors the shape returned by the original `rational-core.js` demo.
161
+ </cell>
162
+
163
+ <cell type="js" lane="math" id="summary" in="sum, product, quotient, cmp">
164
+ // Reproduce the exact return shape of the original rational-core.js demo.
165
+ const { display } = caps;
166
+ const { sum, product, quotient, cmp } = inputs;
167
+
168
+ const result = {
169
+ category: "rational-core",
170
+ sum: sum.toMathString(),
171
+ product: product.toMathString(),
172
+ quotient: quotient.toMathString(),
173
+ cmp
174
+ };
175
+
176
+ display(result);
177
+ </cell>
178
+
179
+ </frqtl-notebook>
@@ -0,0 +1,187 @@
1
+ <frqtl-notebook version="1" title="EGPTFFT — forward / inverse round-trip tests">
2
+
3
+ <cell type="markdown">
4
+ # EGPTFFT — forward / inverse round-trip tests
5
+
6
+ `EGPTFFT` is a pedagogical wrapper that makes the polynomial-transform interpretation explicit.
7
+
8
+ Two canonical methods are exposed:
9
+
10
+ | Method | What it computes |
11
+ |--------|-----------------|
12
+ | `EGPTFFT.forward(coeffs, N)` | Evaluates the polynomial `P(x) = c₀ + c₁x + …` at the N uniform rational nodes `k/N` (k = 0 … N−1) via Horner's method. Each sample is an exact `EGPTReal` rational. |
13
+ | `EGPTFFT.inverse(values, N)` | Recovers the original coefficient vector from those N samples via Newton divided-differences — bit-exact inverse. |
14
+
15
+ The bijection is:
16
+
17
+ ```
18
+ coeffs ─forward─► values at k/N
19
+ values ─inverse─► coeffs (exactly)
20
+ ```
21
+
22
+ No floating-point, no approximate inversion — the round trip holds to the last bit because both operations stay in Shannon/PPF (rational) space throughout.
23
+
24
+ This notebook runs the three canonical tests from `EGPTFFTTest.js`:
25
+ 1. Integer-coefficient round trip (N = 4)
26
+ 2. Fractional-coefficient round trip (N = 5)
27
+ 3. `forward` output agrees with per-point Horner evaluation via `EGPTPolynomial.evaluateAt`
28
+ </cell>
29
+
30
+ <cell type="markdown">
31
+ ## Setup — helpers and test harness
32
+
33
+ Inline helpers for constructing `EGPTReal` values from integers and fractions, plus a
34
+ minimal test harness (`test` / `assert` / `assertEqPoly`) that mirrors the original file.
35
+ The harness accumulates pass/fail counts; phase cells consume it via `in="suite"`.
36
+ </cell>
37
+
38
+ <cell type="js" lane="math" id="fft-setup" out="suite">
39
+ const { math } = caps;
40
+ const { EGPTReal, EGPTPolynomial } = math;
41
+
42
+ const ONE = EGPTReal.fromBigInt(1n);
43
+ const intN = (n) => EGPTReal.fromBigInt(BigInt(n));
44
+ const frac = (n, d) => EGPTReal.fromRational(BigInt(n), BigInt(d));
45
+
46
+ let passed = 0, failed = 0;
47
+ const failures = [];
48
+
49
+ function test(name, fn) {
50
+ try {
51
+ fn();
52
+ console.log(' ok — ' + name);
53
+ passed++;
54
+ } catch (err) {
55
+ console.log(' FAIL — ' + name + '\n ' + err.message);
56
+ failed++;
57
+ failures.push(name);
58
+ }
59
+ }
60
+
61
+ function assert(cond, msg) {
62
+ if (!cond) throw new Error(msg || 'assertion failed');
63
+ }
64
+
65
+ function assertEqPoly(a, b, label) {
66
+ if (a.length !== b.length)
67
+ throw new Error(label + ': length ' + a.length + ' vs ' + b.length);
68
+ for (let i = 0; i < a.length; i++) {
69
+ if (!a[i].equals(b[i])) {
70
+ throw new Error(
71
+ label + ': index ' + i + ': ' +
72
+ a[i].breakSymbolicToString() + ' vs ' + b[i].breakSymbolicToString()
73
+ );
74
+ }
75
+ }
76
+ }
77
+
78
+ return { suite: { test, assert, assertEqPoly, intN, frac, ONE, getResults: () => ({ passed, failed, failures }) } };
79
+ </cell>
80
+
81
+ <cell type="markdown">
82
+ ## Round-trip tests
83
+
84
+ ### Test 1 — integer coefficients, N = 4
85
+
86
+ Coefficients `[3, 1, 4, 1]` (the first four digits of π). After `forward` the four samples are
87
+ rational values at `0/4, 1/4, 2/4, 3/4`; `inverse` must return exactly `[3, 1, 4, 1]`.
88
+
89
+ ### Test 2 — fractional coefficients, N = 5
90
+
91
+ Coefficients `[1/3, −7/6, 1, 2/5, −1/2]`. Fractions stress the rational arithmetic path;
92
+ the round trip must still be bit-exact.
93
+ </cell>
94
+
95
+ <cell type="js" lane="math" id="fft-round-trip" in="suite" out="rt">
96
+ const { math } = caps;
97
+ const { EGPTFFT } = math;
98
+ const { test, assertEqPoly, intN, frac, ONE } = inputs.suite;
99
+
100
+ console.log('\n[EGPTFFT] forward / inverse round trip');
101
+
102
+ test('integer coeffs round trip exactly at N=4', () => {
103
+ const c = [intN(3), intN(1), intN(4), intN(1)];
104
+ const values = EGPTFFT.forward(c);
105
+ const recovered = EGPTFFT.inverse(values);
106
+ assertEqPoly(recovered, c, 'integer N=4');
107
+ });
108
+
109
+ test('fractional coeffs round trip exactly at N=5', () => {
110
+ const c = [frac(1, 3), frac(-7, 6), ONE, frac(2, 5), frac(-1, 2)];
111
+ const values = EGPTFFT.forward(c);
112
+ const recovered = EGPTFFT.inverse(values);
113
+ assertEqPoly(recovered, c, 'fractional N=5');
114
+ });
115
+
116
+ const snap = inputs.suite.getResults();
117
+ return { rt: snap };
118
+ </cell>
119
+
120
+ <cell type="markdown">
121
+ ## Horner agreement test
122
+
123
+ ### Test 3 — `forward(c)` agrees with per-point `EGPTPolynomial.evaluateAt`
124
+
125
+ `EGPTFFT.forward(c, N)` must return the same values as evaluating `c` at each node `k/N`
126
+ individually via `EGPTPolynomial.evaluateAt`.
127
+
128
+ This is T1 from the canonical chain: the transform IS Horner evaluation at uniform nodes.
129
+
130
+ **Note on error-message construction:** the message strings are built lazily (inside the
131
+ catch branch only) to avoid calling `.breakSymbolicToString()` on values that are equal —
132
+ calling it eagerly would throw `DiscontinuumViolationError` for any value that actually
133
+ passes the `.equals()` check.
134
+ </cell>
135
+
136
+ <cell type="js" lane="math" id="fft-horner-agree" in="suite" out="ha">
137
+ const { math } = caps;
138
+ const { EGPTFFT, EGPTPolynomial, EGPTReal } = math;
139
+ const { test, assert, intN, frac } = inputs.suite;
140
+
141
+ console.log('\n[EGPTFFT] forward agrees with per-point Horner evaluation');
142
+
143
+ test('forward(c) agrees with per-point Horner evaluation', () => {
144
+ const c = [intN(5), intN(-2), intN(7), intN(-1)];
145
+ const N = c.length;
146
+ const values = EGPTFFT.forward(c, N);
147
+ for (let k = 0; k < N; k++) {
148
+ const x = EGPTReal.fromRational(BigInt(k), BigInt(N));
149
+ const expected = EGPTPolynomial.evaluateAt(c, x);
150
+ // Build the diagnostic string ONLY when the check fails (lazy construction)
151
+ // to avoid DiscontinuumViolationError on equal values.
152
+ if (!values[k].equals(expected)) {
153
+ throw new Error(
154
+ 'bin ' + k + ': forward ' + values[k].breakSymbolicToString() +
155
+ ' vs Horner ' + expected.breakSymbolicToString()
156
+ );
157
+ }
158
+ }
159
+ });
160
+
161
+ const snap = inputs.suite.getResults();
162
+ return { ha: snap };
163
+ </cell>
164
+
165
+ <cell type="markdown">
166
+ ## Summary
167
+
168
+ Final tally across all three tests.
169
+ </cell>
170
+
171
+ <cell type="js" lane="math" id="fft-summary" in="ha">
172
+ const { passed, failed, failures } = inputs.ha;
173
+ const total = passed + failed;
174
+
175
+ console.log('');
176
+ console.log('='.repeat(60));
177
+ console.log('Results: ' + passed + ' passed, ' + failed + ' failed (total: ' + total + ')');
178
+ if (failures.length > 0) {
179
+ console.log('Failed tests:');
180
+ failures.forEach((f) => console.log(' - ' + f));
181
+ }
182
+ console.log('='.repeat(60));
183
+
184
+ if (failed > 0) throw new Error('[EGPTFFTTest] ' + failed + ' test(s) failed');
185
+ </cell>
186
+
187
+ </frqtl-notebook>