@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,241 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="EGPTMatrix SDK Parity Tests">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# EGPTMatrix SDK Parity Tests
|
|
5
|
+
|
|
6
|
+
`EGPTMatrix` is the linear-algebra surface of the EGPT math library. Its central insight is that **every matrix row is a polynomial in coefficient form**, so every linear-algebra operation — dot products, GEMM, value-representation — routes through `EGPTPolynomial` rather than through classical Vandermonde / Toeplitz / Sylvester machinery.
|
|
7
|
+
|
|
8
|
+
The bijective chain (Lean-formalized in `Translation4.lean` / `Translation5.lean`):
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
matrix (rows = polynomials)
|
|
12
|
+
↕ EGPTPolynomial.multiply + EGPTPolynomial.evaluateAt
|
|
13
|
+
coefficient form ⇆ value representation
|
|
14
|
+
↕ bijective encoding (choice-free)
|
|
15
|
+
EntropyNat ≃ ℕ
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This notebook ports **all six parity tests** from `sdk/egpt-math-sdk/src/editor/tests/EGPTMatrixTest.js` into individually runnable cells, grouped by concept:
|
|
19
|
+
|
|
20
|
+
1. **Setup** — shared test harness (`test` / `assert` / `assertMatrixEquals`)
|
|
21
|
+
2. **Construction** — `EGPTMatrix.from` converts integers, rationals, and `EGPTReal` instances
|
|
22
|
+
3. **Dot product** — `dotViaPolynomial` extracts the dot product as coefficient `n−1` of a convolution
|
|
23
|
+
4. **GEMM** — `matMul` (plain product) and `gemm` (with α / β / accumulator)
|
|
24
|
+
5. **Value representation** — coefficient form ⇄ spectral samples round-trips exactly
|
|
25
|
+
6. **Polynomial delegation** — `evaluateRowsAt` delegates per-row to `EGPTPolynomial.evaluateAt`
|
|
26
|
+
7. **Summary** — total pass/fail count
|
|
27
|
+
|
|
28
|
+
All tests use the `math` builtin exclusively — no URL imports.
|
|
29
|
+
</cell>
|
|
30
|
+
|
|
31
|
+
<cell type="markdown">
|
|
32
|
+
## Setup — shared test harness
|
|
33
|
+
|
|
34
|
+
The original file opens with a small `test` / `assert` / `assertMatrixEquals` harness shared across all six tests. We extract it here into a setup cell and expose it as the `suite` binding that every phase cell consumes.
|
|
35
|
+
</cell>
|
|
36
|
+
|
|
37
|
+
<cell type="js" lane="math" id="matrix-setup" out="suite">
|
|
38
|
+
const { math } = caps;
|
|
39
|
+
const { EGPTReal, EGPTMatrix } = math;
|
|
40
|
+
|
|
41
|
+
let passed = 0;
|
|
42
|
+
let failed = 0;
|
|
43
|
+
const failures = [];
|
|
44
|
+
|
|
45
|
+
function test(name, fn) {
|
|
46
|
+
try {
|
|
47
|
+
fn();
|
|
48
|
+
passed += 1;
|
|
49
|
+
console.log(`PASS: ${name}`);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
failed += 1;
|
|
52
|
+
failures.push({ name, message: error.message });
|
|
53
|
+
console.log(`FAIL: ${name} (${error.message})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function assert(condition, message) {
|
|
58
|
+
if (!condition) throw new Error(message || 'assertion failed');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function assertMatrixEquals(actual, expected, message) {
|
|
62
|
+
assert(EGPTMatrix.equals(actual, expected), message || 'matrices are not equal');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { suite: { test, assert, assertMatrixEquals, getPassed: () => passed, getFailed: () => failed, getFailures: () => failures } };
|
|
66
|
+
</cell>
|
|
67
|
+
|
|
68
|
+
<cell type="markdown">
|
|
69
|
+
## Phase 1 — Construction: `EGPTMatrix.from`
|
|
70
|
+
|
|
71
|
+
`EGPTMatrix.from` accepts a rectangular array whose entries can be:
|
|
72
|
+
|
|
73
|
+
- **`number`** (JS integer) → `EGPTReal.fromBigInt(BigInt(n))`
|
|
74
|
+
- **`bigint`** → `EGPTReal.fromBigInt(v)`
|
|
75
|
+
- **`[num, den]`** tuple → `EGPTReal.fromRational(num, den)`
|
|
76
|
+
- **`EGPTReal`** instance → passes through unchanged
|
|
77
|
+
|
|
78
|
+
This test builds a 2×2 matrix mixing all four input forms and verifies each entry resolves to the expected `EGPTReal`.
|
|
79
|
+
</cell>
|
|
80
|
+
|
|
81
|
+
<cell type="js" lane="math" id="matrix-construction" in="suite">
|
|
82
|
+
const { math } = caps;
|
|
83
|
+
const { EGPTReal, EGPTMatrix } = math;
|
|
84
|
+
const { test, assert } = inputs.suite;
|
|
85
|
+
|
|
86
|
+
test('EGPTMatrix.from converts integers and rationals', () => {
|
|
87
|
+
const M = EGPTMatrix.from([
|
|
88
|
+
[1, [1n, 2n]],
|
|
89
|
+
[3n, EGPTReal.fromRational(5n, 7n)]
|
|
90
|
+
]);
|
|
91
|
+
assert(M[0][0].equals(EGPTReal.fromBigInt(1n)), 'M[0][0] should equal 1');
|
|
92
|
+
assert(M[0][1].equals(EGPTReal.fromRational(1n, 2n)), 'M[0][1] should equal 1/2');
|
|
93
|
+
assert(M[1][0].equals(EGPTReal.fromBigInt(3n)), 'M[1][0] should equal 3');
|
|
94
|
+
assert(M[1][1].equals(EGPTReal.fromRational(5n, 7n)), 'M[1][1] should equal 5/7');
|
|
95
|
+
});
|
|
96
|
+
</cell>
|
|
97
|
+
|
|
98
|
+
<cell type="markdown">
|
|
99
|
+
## Phase 2 — Dot product via polynomial multiplication
|
|
100
|
+
|
|
101
|
+
The key identity: for vectors `a = [a₀, …, aₙ₋₁]` and `b = [b₀, …, bₙ₋₁]`, the dot product `Σᵢ aᵢ·bᵢ` equals the **(n−1)-th coefficient** of the convolution `P_a(x) · P_b_reversed(x)`.
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
P_a(x) = a₀ + a₁x + … + aₙ₋₁xⁿ⁻¹
|
|
105
|
+
P_b_rev(x) = bₙ₋₁ + bₙ₋₂x + … + b₀xⁿ⁻¹
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
At index `n−1`: `Σ_{i+j=n−1} aᵢ · b_rev_j = Σᵢ aᵢ · b_{n−1−(n−1−i)} = Σᵢ aᵢ · bᵢ` ✓
|
|
109
|
+
|
|
110
|
+
For `a = [1,2,3]` and `b = [4,5,6]`: `1·4 + 2·5 + 3·6 = 32`.
|
|
111
|
+
</cell>
|
|
112
|
+
|
|
113
|
+
<cell type="js" lane="math" id="matrix-dot-product" in="suite">
|
|
114
|
+
const { math } = caps;
|
|
115
|
+
const { EGPTReal, EGPTMatrix } = math;
|
|
116
|
+
const { test, assert } = inputs.suite;
|
|
117
|
+
|
|
118
|
+
test('EGPTMatrix.dotViaPolynomial equals direct dot product', () => {
|
|
119
|
+
const a = [1n, 2n, 3n].map(EGPTReal.fromBigInt);
|
|
120
|
+
const b = [4n, 5n, 6n].map(EGPTReal.fromBigInt);
|
|
121
|
+
// 1·4 + 2·5 + 3·6 = 4 + 10 + 18 = 32
|
|
122
|
+
assert(EGPTMatrix.dotViaPolynomial(a, b).equals(EGPTReal.fromBigInt(32n)),
|
|
123
|
+
'dot([1,2,3],[4,5,6]) should equal 32');
|
|
124
|
+
});
|
|
125
|
+
</cell>
|
|
126
|
+
|
|
127
|
+
<cell type="markdown">
|
|
128
|
+
## Phase 3 — Matrix multiplication: `matMul` and `gemm`
|
|
129
|
+
|
|
130
|
+
### `matMul` — plain product A · B
|
|
131
|
+
|
|
132
|
+
`matMul(A, B)` is a convenience alias for `gemm(A, B)` with default α=1 and β=0. Each output entry is one `dotViaPolynomial` call.
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
A = [[1,2],[3,4]] B = [[5,6],[7,8]]
|
|
136
|
+
A·B = [[1·5+2·7, 1·6+2·8],[3·5+4·7, 3·6+4·8]]
|
|
137
|
+
= [[19, 22],[43, 50]]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `gemm` — general GEMM with α / β / accumulator C
|
|
141
|
+
|
|
142
|
+
`gemm(A, B, α, β, C)` computes `α·(A·B) + β·C`. With α=2 and β=1 and `C = ones(2×2)`:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
2·[[19,22],[43,50]] + 1·[[1,1],[1,1]] = [[39,45],[87,101]]
|
|
146
|
+
```
|
|
147
|
+
</cell>
|
|
148
|
+
|
|
149
|
+
<cell type="js" lane="math" id="matrix-gemm" in="suite">
|
|
150
|
+
const { math } = caps;
|
|
151
|
+
const { EGPTReal, EGPTMatrix } = math;
|
|
152
|
+
const { test, assertMatrixEquals } = inputs.suite;
|
|
153
|
+
|
|
154
|
+
test('EGPTMatrix.matMul computes 2x2 product', () => {
|
|
155
|
+
const A = EGPTMatrix.from([[1, 2], [3, 4]]);
|
|
156
|
+
const B = EGPTMatrix.from([[5, 6], [7, 8]]);
|
|
157
|
+
const expected = EGPTMatrix.from([[19, 22], [43, 50]]);
|
|
158
|
+
assertMatrixEquals(EGPTMatrix.matMul(A, B), expected, 'matMul([[1,2],[3,4]], [[5,6],[7,8]]) should be [[19,22],[43,50]]');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('EGPTMatrix.gemm supports alpha beta accumulator', () => {
|
|
162
|
+
const A = EGPTMatrix.from([[1, 2], [3, 4]]);
|
|
163
|
+
const B = EGPTMatrix.from([[5, 6], [7, 8]]);
|
|
164
|
+
const C = EGPTMatrix.from([[1, 1], [1, 1]]);
|
|
165
|
+
const expected = EGPTMatrix.from([[39, 45], [87, 101]]);
|
|
166
|
+
const result = EGPTMatrix.gemm(A, B, EGPTReal.fromBigInt(2n), EGPTReal.fromBigInt(1n), C);
|
|
167
|
+
assertMatrixEquals(result, expected, 'gemm(A,B,2,1,C) should be 2·(A·B) + C');
|
|
168
|
+
});
|
|
169
|
+
</cell>
|
|
170
|
+
|
|
171
|
+
<cell type="markdown">
|
|
172
|
+
## Phase 4 — Value representation round-trip
|
|
173
|
+
|
|
174
|
+
Every matrix row is a polynomial `P(x) = a₀ + a₁x + … + aₙ₋₁xⁿ⁻¹`. The **value representation** evaluates it at integer powers of 2: `[P(2⁰), P(2¹), …, P(2ⁿ⁻¹)]`.
|
|
175
|
+
|
|
176
|
+
Because the evaluation points are distinct, the Vandermonde system is invertible — `fromValueReps(toValueReps(M)) = M` exactly (no floating-point error, because `EGPTReal` is exact rational arithmetic throughout).
|
|
177
|
+
|
|
178
|
+
This round-trip is the operational form of `Translation4` / `Translation5` (coefficient form ⇄ spectral form as a bijection over `EntropyNat`).
|
|
179
|
+
</cell>
|
|
180
|
+
|
|
181
|
+
<cell type="js" lane="math" id="matrix-value-rep" in="suite">
|
|
182
|
+
const { math } = caps;
|
|
183
|
+
const { EGPTMatrix } = math;
|
|
184
|
+
const { test, assertMatrixEquals } = inputs.suite;
|
|
185
|
+
|
|
186
|
+
test('EGPTMatrix value representation round trips rows', () => {
|
|
187
|
+
const M = EGPTMatrix.from([[1, 2, 3], [4, 0, 5]]);
|
|
188
|
+
assertMatrixEquals(
|
|
189
|
+
EGPTMatrix.fromValueReps(EGPTMatrix.toValueReps(M)),
|
|
190
|
+
M,
|
|
191
|
+
'fromValueReps(toValueReps(M)) should equal M (exact round-trip)'
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
</cell>
|
|
195
|
+
|
|
196
|
+
<cell type="markdown">
|
|
197
|
+
## Phase 5 — Row evaluation delegates to `EGPTPolynomial.evaluateAt`
|
|
198
|
+
|
|
199
|
+
`EGPTMatrix.evaluateRowsAt(M, x)` evaluates each row as a polynomial at `x`, producing a column vector. It is explicitly specified to delegate to `EGPTPolynomial.evaluateAt` per row — this test verifies that the results agree element-wise, confirming that the matrix layer does not duplicate polynomial logic.
|
|
200
|
+
|
|
201
|
+
For `M = [[1,2],[3,4]]` and `x = 3`:
|
|
202
|
+
- row 0: `1 + 2·3 = 7`
|
|
203
|
+
- row 1: `3 + 4·3 = 15`
|
|
204
|
+
</cell>
|
|
205
|
+
|
|
206
|
+
<cell type="js" lane="math" id="matrix-evaluate-rows" in="suite">
|
|
207
|
+
const { math } = caps;
|
|
208
|
+
const { EGPTReal, EGPTMatrix, EGPTPolynomial } = math;
|
|
209
|
+
const { test, assert } = inputs.suite;
|
|
210
|
+
|
|
211
|
+
test('EGPTMatrix.evaluateRowsAt delegates to EGPTPolynomial.evaluateAt', () => {
|
|
212
|
+
const M = EGPTMatrix.from([[1, 2], [3, 4]]);
|
|
213
|
+
const x = EGPTReal.fromBigInt(3n);
|
|
214
|
+
const values = EGPTMatrix.evaluateRowsAt(M, x);
|
|
215
|
+
assert(values[0].equals(EGPTPolynomial.evaluateAt(M[0], x)),
|
|
216
|
+
'row 0 evaluated at 3 should equal EGPTPolynomial.evaluateAt(M[0], 3)');
|
|
217
|
+
assert(values[1].equals(EGPTPolynomial.evaluateAt(M[1], x)),
|
|
218
|
+
'row 1 evaluated at 3 should equal EGPTPolynomial.evaluateAt(M[1], 3)');
|
|
219
|
+
});
|
|
220
|
+
</cell>
|
|
221
|
+
|
|
222
|
+
<cell type="markdown">
|
|
223
|
+
## Summary
|
|
224
|
+
|
|
225
|
+
Run this cell after all phase cells to see the total pass/fail count. If any test failed, the cell throws with the failure list so the error surfaces in the console pane.
|
|
226
|
+
</cell>
|
|
227
|
+
|
|
228
|
+
<cell type="js" lane="math" id="matrix-summary" in="suite">
|
|
229
|
+
const { getPassed, getFailed, getFailures } = inputs.suite;
|
|
230
|
+
const p = getPassed();
|
|
231
|
+
const f = getFailed();
|
|
232
|
+
const total = p + f;
|
|
233
|
+
console.log(`EGPTMatrixTest TOTAL: ${p}/${total} passed`);
|
|
234
|
+
if (f > 0) {
|
|
235
|
+
const list = getFailures().map(({ name, message }) => ` - ${name}: ${message}`).join('\n');
|
|
236
|
+
throw new Error(`EGPTMatrixTest failed: ${f}\n${list}`);
|
|
237
|
+
}
|
|
238
|
+
console.log('All EGPTMatrix SDK parity tests passed.');
|
|
239
|
+
</cell>
|
|
240
|
+
|
|
241
|
+
</frqtl-notebook>
|