@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,206 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="Exact-Arithmetic Statistics with EGPTStat — Mean, Variance, and Deviation Analysis">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# Exact-Arithmetic Statistics with EGPTStat
|
|
5
|
+
|
|
6
|
+
Standard statistical libraries operate on floating-point numbers. Accumulated rounding error is invisible — it hides inside the mantissa and silently distorts results when values are very large, very small, or precisely rational.
|
|
7
|
+
|
|
8
|
+
`EGPTStat` computes **mean, variance, and deviation in exact rational arithmetic** end-to-end, using the PPF (Prime-Probability Field) representation carried by every `EGPTReal`. No rounding occurs inside any statistical operation. The result you read back from `.toMathString()` is the *exact* rational answer.
|
|
9
|
+
|
|
10
|
+
This notebook works through a small integer sample — `[3, 6, 9, 12]` — so you can verify the results by hand and see what exact-arithmetic statistics look like at the API boundary. Every cell reaches its compute through the injected `math` builtin; no URL import is used.
|
|
11
|
+
</cell>
|
|
12
|
+
|
|
13
|
+
<cell type="markdown">
|
|
14
|
+
## 1. The active math backend
|
|
15
|
+
|
|
16
|
+
`math.activeMathBackend` is a **derived** field — it reads whatever `MathBackendRegistry.active()` reports at runtime. Never hardcoded.
|
|
17
|
+
</cell>
|
|
18
|
+
|
|
19
|
+
<cell type="js" lane="math" id="backend">
|
|
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
|
+
## 2. Constructing the sample
|
|
27
|
+
|
|
28
|
+
The source example uses a four-element integer sample: `[3, 6, 9, 12]`.
|
|
29
|
+
|
|
30
|
+
Each value is lifted into exact rational form via `EGPTReal.fromBigInt(n)`. From this point, every operation stays in the PPF basis — no floating-point intermediate values exist.
|
|
31
|
+
</cell>
|
|
32
|
+
|
|
33
|
+
<cell type="js" lane="math" id="sample" out="sample">
|
|
34
|
+
const { math, display } = caps;
|
|
35
|
+
const { EGPTReal } = math;
|
|
36
|
+
|
|
37
|
+
const sample = [
|
|
38
|
+
EGPTReal.fromBigInt(3n),
|
|
39
|
+
EGPTReal.fromBigInt(6n),
|
|
40
|
+
EGPTReal.fromBigInt(9n),
|
|
41
|
+
EGPTReal.fromBigInt(12n)
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
display(`Sample (${sample.length} values): [${sample.map(v => v.toMathString()).join(', ')}]`);
|
|
45
|
+
return { sample };
|
|
46
|
+
</cell>
|
|
47
|
+
|
|
48
|
+
<cell type="markdown">
|
|
49
|
+
## 3. Computing the mean
|
|
50
|
+
|
|
51
|
+
`EGPTStat.mean(sample)` computes the arithmetic mean in **normal space**: it sums all values via `EGPTMath.add`, then divides by the count via `EGPTMath.normalDivide`.
|
|
52
|
+
|
|
53
|
+
For `[3, 6, 9, 12]`, the exact mean is `30/4 = 15/2 = 7.5`. The `.toMathString()` representation surfaces the exact rational.
|
|
54
|
+
</cell>
|
|
55
|
+
|
|
56
|
+
<cell type="js" lane="math" id="mean" in="sample" out="mean">
|
|
57
|
+
const { math, display } = caps;
|
|
58
|
+
const { EGPTStat } = math;
|
|
59
|
+
const { sample } = inputs;
|
|
60
|
+
|
|
61
|
+
const mean = EGPTStat.mean(sample);
|
|
62
|
+
const meanStr = mean.toMathString();
|
|
63
|
+
|
|
64
|
+
display(`Mean of [3, 6, 9, 12] = ${meanStr}`);
|
|
65
|
+
display(`(Expected exact rational: 15/2)`);
|
|
66
|
+
return { mean };
|
|
67
|
+
</cell>
|
|
68
|
+
|
|
69
|
+
<cell type="markdown">
|
|
70
|
+
## 4. Computing the variance
|
|
71
|
+
|
|
72
|
+
`EGPTStat.variance(sample)` computes the population variance `Σ(xᵢ − μ)² / N` in normal space.
|
|
73
|
+
|
|
74
|
+
Two design choices are worth understanding:
|
|
75
|
+
|
|
76
|
+
- **Absolute difference:** Because PPF encoding can struggle with negative intermediate values, `EGPTStat.absoluteDifference(x, μ)` is used for each deviation — it returns `|x − μ|` by checking the sign first and subtracting the smaller from the larger.
|
|
77
|
+
- **Normal-space squaring:** Each squared deviation uses `EGPTMath.normalMultiply` (rational multiply), not the Shannon-space product. Variance is a normal-space quantity.
|
|
78
|
+
|
|
79
|
+
For `[3, 6, 9, 12]` with mean `15/2`:
|
|
80
|
+
- Deviations: `|3 − 7.5| = 4.5`, `|6 − 7.5| = 1.5`, `|9 − 7.5| = 1.5`, `|12 − 7.5| = 4.5`
|
|
81
|
+
- Squared deviations: `81/4`, `9/4`, `9/4`, `81/4`
|
|
82
|
+
- Sum: `180/4 = 45`
|
|
83
|
+
- Variance: `45/4`
|
|
84
|
+
|
|
85
|
+
The `variance()` call also returns **metadata** describing how many values fell below the mean (negative deviations). This is diagnostic information — it confirms the algorithm correctly identified which values were below the mean even though it computed unsigned absolute differences.
|
|
86
|
+
</cell>
|
|
87
|
+
|
|
88
|
+
<cell type="js" lane="math" id="variance" in="sample, mean" out="varianceResult">
|
|
89
|
+
const { math, display } = caps;
|
|
90
|
+
const { EGPTStat } = math;
|
|
91
|
+
const { sample, mean } = inputs;
|
|
92
|
+
|
|
93
|
+
const varianceResult = EGPTStat.variance(sample);
|
|
94
|
+
const varianceStr = varianceResult.variance.toMathString();
|
|
95
|
+
const meta = varianceResult.metadata;
|
|
96
|
+
|
|
97
|
+
display(`Variance of [3, 6, 9, 12] = ${varianceStr}`);
|
|
98
|
+
display(`(Expected exact rational: 45/4)`);
|
|
99
|
+
display(`Metadata:`);
|
|
100
|
+
display(` total_vectors: ${meta.total_vectors}`);
|
|
101
|
+
display(` negative_deviations: ${meta.negative_deviations} (values that fell below the mean)`);
|
|
102
|
+
display(` has_negative_deviations: ${meta.has_negative_deviations}`);
|
|
103
|
+
return { varianceResult };
|
|
104
|
+
</cell>
|
|
105
|
+
|
|
106
|
+
<cell type="markdown">
|
|
107
|
+
## 5. Absolute difference — the PPF-safe subtraction primitive
|
|
108
|
+
|
|
109
|
+
`EGPTStat.absoluteDifference(a, b)` is the building block that makes variance possible in PPF space. It uses `EGPTMath.compare(a, b)` to determine order, then calls `EGPTMath.subtract(larger, smaller)` — never producing a negative intermediate.
|
|
110
|
+
|
|
111
|
+
Here we compute it directly for a pair of values from our sample to illustrate the pattern.
|
|
112
|
+
</cell>
|
|
113
|
+
|
|
114
|
+
<cell type="js" lane="math" id="absdiff" in="sample">
|
|
115
|
+
const { math, display } = caps;
|
|
116
|
+
const { EGPTStat } = math;
|
|
117
|
+
const { sample } = inputs;
|
|
118
|
+
|
|
119
|
+
// |12 - 3| = 9
|
|
120
|
+
const diff = EGPTStat.absoluteDifference(sample[3], sample[0]);
|
|
121
|
+
display(`|12 − 3| = ${diff.toMathString()}`);
|
|
122
|
+
display(`(Expected: 9/1)`);
|
|
123
|
+
</cell>
|
|
124
|
+
|
|
125
|
+
<cell type="markdown">
|
|
126
|
+
## 6. Replicating the original demo output
|
|
127
|
+
|
|
128
|
+
The source `stats.js` example returns exactly:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"category": "stats",
|
|
133
|
+
"mean": "<mean as math string>",
|
|
134
|
+
"variance": "<variance as math string>",
|
|
135
|
+
"metadata": { ... }
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
This cell reproduces that return value verbatim so any downstream consumer sees the same result as the original `run(sdk)`.
|
|
140
|
+
</cell>
|
|
141
|
+
|
|
142
|
+
<cell type="js" lane="math" id="demo-output" in="sample" out="demoOutput">
|
|
143
|
+
const { math, display } = caps;
|
|
144
|
+
const { EGPTReal, EGPTStat } = math;
|
|
145
|
+
|
|
146
|
+
// Replicate the original stats.js run(sdk) exactly
|
|
147
|
+
const sample = [
|
|
148
|
+
EGPTReal.fromBigInt(3n),
|
|
149
|
+
EGPTReal.fromBigInt(6n),
|
|
150
|
+
EGPTReal.fromBigInt(9n),
|
|
151
|
+
EGPTReal.fromBigInt(12n)
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
const mean = EGPTStat.mean(sample);
|
|
155
|
+
const varianceResult = EGPTStat.variance(sample);
|
|
156
|
+
|
|
157
|
+
const demoOutput = {
|
|
158
|
+
category: "stats",
|
|
159
|
+
mean: mean.toMathString(),
|
|
160
|
+
variance: varianceResult.variance.toMathString(),
|
|
161
|
+
metadata: varianceResult.metadata
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
display(demoOutput);
|
|
165
|
+
return { demoOutput };
|
|
166
|
+
</cell>
|
|
167
|
+
|
|
168
|
+
<cell type="markdown">
|
|
169
|
+
## 7. Verify correctness
|
|
170
|
+
|
|
171
|
+
The cell below asserts the two expected exact rational results. A PASS confirms that `EGPTStat.mean` and `EGPTStat.variance` both produce bit-exact rational answers matching hand calculation. A FAIL would indicate a regression in the statistical layer.
|
|
172
|
+
</cell>
|
|
173
|
+
|
|
174
|
+
<cell type="js" lane="math" id="verify" in="demoOutput">
|
|
175
|
+
const { display } = caps;
|
|
176
|
+
const { demoOutput } = inputs;
|
|
177
|
+
|
|
178
|
+
const EXPECTED_MEAN = '15/2';
|
|
179
|
+
const EXPECTED_VARIANCE = '45/4';
|
|
180
|
+
|
|
181
|
+
const meanOk = demoOutput.mean === EXPECTED_MEAN;
|
|
182
|
+
const varianceOk = demoOutput.variance === EXPECTED_VARIANCE;
|
|
183
|
+
const allOk = meanOk && varianceOk;
|
|
184
|
+
|
|
185
|
+
const el = document.createElement('div');
|
|
186
|
+
el.style.cssText =
|
|
187
|
+
'font:600 0.95rem/1.5 system-ui,sans-serif;padding:10px 14px;border-radius:6px;margin:4px 0;' +
|
|
188
|
+
(allOk
|
|
189
|
+
? 'background:#0f2417;border:1px solid #1f5a36;color:#7ee2a8;'
|
|
190
|
+
: 'background:#2a0c0c;border:1px solid #5a1f1f;color:#ff8a8a;');
|
|
191
|
+
|
|
192
|
+
if (allOk) {
|
|
193
|
+
el.textContent =
|
|
194
|
+
`PASS — mean=${demoOutput.mean} (expected ${EXPECTED_MEAN}); ` +
|
|
195
|
+
`variance=${demoOutput.variance} (expected ${EXPECTED_VARIANCE}). ` +
|
|
196
|
+
`Exact-arithmetic statistics verified.`;
|
|
197
|
+
} else {
|
|
198
|
+
const failures = [];
|
|
199
|
+
if (!meanOk) failures.push(`mean: got "${demoOutput.mean}", expected "${EXPECTED_MEAN}"`);
|
|
200
|
+
if (!varianceOk) failures.push(`variance: got "${demoOutput.variance}", expected "${EXPECTED_VARIANCE}"`);
|
|
201
|
+
el.textContent = `FAIL — ${failures.join('; ')}`;
|
|
202
|
+
}
|
|
203
|
+
display(el);
|
|
204
|
+
</cell>
|
|
205
|
+
|
|
206
|
+
</frqtl-notebook>
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="Transcendental Functions in EGPT — exp₂, log₂, and the Riemann Zeta Function">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# Transcendental Functions in EGPT
|
|
5
|
+
|
|
6
|
+
Classical mathematics has a tension at its core: transcendental numbers like π, e, and √2 are *irrational* — they cannot be expressed as a ratio of integers — yet every practical computation must eventually approximate them.
|
|
7
|
+
|
|
8
|
+
EGPT resolves this tension by working in **Shannon information space**. An `EGPTReal` is not a decimal approximation; it is a canonical rational PPF (Prime-Probability Form) representation. Transcendental operations — `exp2`, `log2`, complex exponentiation — are carried out as **exact rational operations on this canonical form**. The approximate decimal you see at the end is a projection out of information space at the output boundary, deliberately late and deliberately labelled.
|
|
9
|
+
|
|
10
|
+
This notebook demonstrates three transcendental capabilities:
|
|
11
|
+
|
|
12
|
+
1. **`EGPTranscendental.exp2(x)`** — computes 2^x in exact rational arithmetic (2^3 = 8 exactly).
|
|
13
|
+
2. **`EGPTranscendental.log2(x)`** — computes log₂(x) by recursive halving (log₂(32) = 5 exactly).
|
|
14
|
+
3. **`EGPTComplex.riemannZeta(s, terms)`** — evaluates the Riemann Zeta function ζ(s) as a partial sum of n^(-s) over complex argument s, verifying ζ(2) = π²/6.
|
|
15
|
+
|
|
16
|
+
Every cell reaches compute through the injected `math` builtin only — no URL imports.
|
|
17
|
+
</cell>
|
|
18
|
+
|
|
19
|
+
<cell type="js" lane="math" id="backend">
|
|
20
|
+
// Report the live math backend — DERIVED from the SDK, never hardcoded.
|
|
21
|
+
const { math, display } = caps;
|
|
22
|
+
const backend = math.activeMathBackend;
|
|
23
|
+
display(`Live math backend (derived from the SDK): ${backend}`);
|
|
24
|
+
</cell>
|
|
25
|
+
|
|
26
|
+
<cell type="markdown">
|
|
27
|
+
## 1. Base-2 Exponentiation — exp₂(x) = 2^x
|
|
28
|
+
|
|
29
|
+
`EGPTranscendental.exp2` computes 2^x where x is an `EGPTReal`. Internally it extracts the rational parts of x via `_getPPFRationalParts()` and delegates to `EGPTMath.pow(2, numerator, denominator)` — a single closed-form operation on the canonical rational representation, not an iterative floating-point approximation.
|
|
30
|
+
|
|
31
|
+
For integer inputs like x = 3, the result is exact: 2^3 = **8**.
|
|
32
|
+
|
|
33
|
+
`toMathString()` renders the canonical form; for a pure integer the output is the integer itself.
|
|
34
|
+
</cell>
|
|
35
|
+
|
|
36
|
+
<cell type="js" lane="math" id="exp2" out="exp2Three">
|
|
37
|
+
const { math, display } = caps;
|
|
38
|
+
const { EGPTReal, EGPTranscendental } = math;
|
|
39
|
+
|
|
40
|
+
// Construct x = 3 as an EGPTReal from a BigInt literal.
|
|
41
|
+
const three = EGPTReal.fromBigInt(3n);
|
|
42
|
+
|
|
43
|
+
// Compute 2^3 in EGPT information space — exact rational result.
|
|
44
|
+
const exp2Three = EGPTranscendental.exp2(three);
|
|
45
|
+
|
|
46
|
+
display(`exp₂(3) = 2^3 = ${exp2Three.toMathString()}`);
|
|
47
|
+
return { exp2Three };
|
|
48
|
+
</cell>
|
|
49
|
+
|
|
50
|
+
<cell type="markdown">
|
|
51
|
+
## 2. Base-2 Logarithm — log₂(x)
|
|
52
|
+
|
|
53
|
+
`EGPTranscendental.log2` computes log₂(x) by **recursive halving**: log₂(x) = log₂(x/2) + 1, bottoming out at log₂(1) = 0. Each recursive division is exact rational arithmetic inside EGPT, so for any power of 2 the answer is a perfect integer.
|
|
54
|
+
|
|
55
|
+
For x = 32 = 2^5, the result is **5** — no floating-point rounding, no approximation.
|
|
56
|
+
|
|
57
|
+
Notice that `log2` and `exp2` are mutual inverses: log₂(exp₂(3)) = 3 and exp₂(log₂(32)) = 32. Both are confirmed in the cells below.
|
|
58
|
+
</cell>
|
|
59
|
+
|
|
60
|
+
<cell type="js" lane="math" id="log2" out="log2ThirtyTwo">
|
|
61
|
+
const { math, display } = caps;
|
|
62
|
+
const { EGPTReal, EGPTranscendental } = math;
|
|
63
|
+
|
|
64
|
+
// Construct x = 32 = 2^5 as an EGPTReal.
|
|
65
|
+
const thirtyTwo = EGPTReal.fromBigInt(32n);
|
|
66
|
+
|
|
67
|
+
// Compute log₂(32) — expect exact integer 5.
|
|
68
|
+
const log2ThirtyTwo = EGPTranscendental.log2(thirtyTwo);
|
|
69
|
+
|
|
70
|
+
display(`log₂(32) = ${log2ThirtyTwo.toMathString()}`);
|
|
71
|
+
return { log2ThirtyTwo };
|
|
72
|
+
</cell>
|
|
73
|
+
|
|
74
|
+
<cell type="markdown">
|
|
75
|
+
## 3. Inverse Consistency — exp₂ ∘ log₂ and log₂ ∘ exp₂
|
|
76
|
+
|
|
77
|
+
Because both operations are exact in rational EGPT space, their compositions are exact too. This cell verifies the round-trip on integer inputs.
|
|
78
|
+
</cell>
|
|
79
|
+
|
|
80
|
+
<cell type="js" lane="math" id="inverse-check" in="exp2Three, log2ThirtyTwo">
|
|
81
|
+
const { math, display } = caps;
|
|
82
|
+
const { EGPTReal, EGPTranscendental } = math;
|
|
83
|
+
const { exp2Three, log2ThirtyTwo } = inputs;
|
|
84
|
+
|
|
85
|
+
// log₂(exp₂(3)) should be exactly 3.
|
|
86
|
+
const roundTrip1 = EGPTranscendental.log2(exp2Three);
|
|
87
|
+
|
|
88
|
+
// exp₂(log₂(32)) should be exactly 32.
|
|
89
|
+
const roundTrip2 = EGPTranscendental.exp2(log2ThirtyTwo);
|
|
90
|
+
|
|
91
|
+
display(`log₂(exp₂(3)) = ${roundTrip1.toMathString()} (expected 3)`);
|
|
92
|
+
display(`exp₂(log₂(32)) = ${roundTrip2.toMathString()} (expected 32)`);
|
|
93
|
+
</cell>
|
|
94
|
+
|
|
95
|
+
<cell type="markdown">
|
|
96
|
+
## 4. Complex Numbers and the Riemann Zeta Function
|
|
97
|
+
|
|
98
|
+
`ComplexEGPTReal` represents a complex number a + bi where both a and b are `EGPTReal` values — meaning the complex number lives entirely in rational EGPT space.
|
|
99
|
+
|
|
100
|
+
`EGPTComplex.riemannZeta(s, maxTerms)` evaluates:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
ζ(s) = Σ_{n=1}^{maxTerms} n^{-s}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
using `EGPTComplex.complexPower(n, -s)` for each term. Each `n^(-s)` is computed via the canonical complex-power routine — rational magnitude from `EGPTMath.pow`, phase from the topology-native wave vector, no `Math.sin`/`Math.cos` in the compressed domain.
|
|
107
|
+
|
|
108
|
+
**The landmark identity:** ζ(2) = π²/6 ≈ 1.6449. With 100 terms the partial sum approximates this to about 1% accuracy (the series converges slowly; full convergence requires the analytic continuation). The test below uses a tolerance of 0.01.
|
|
109
|
+
</cell>
|
|
110
|
+
|
|
111
|
+
<cell type="js" lane="math" id="riemann-zeta" out="riemannZetaResult">
|
|
112
|
+
const { math, display } = caps;
|
|
113
|
+
const { EGPTReal, ComplexEGPTReal, EGPTComplex } = math;
|
|
114
|
+
|
|
115
|
+
// s = 2 + 0i (a purely real complex argument at s=2).
|
|
116
|
+
// EGPTComplex.riemannZeta expects a ComplexEGPTReal; this is NOT ComplexEGPTReal —
|
|
117
|
+
// EGPTComplex is the static utility class hosting riemannZeta and complexPower.
|
|
118
|
+
const s = new ComplexEGPTReal(
|
|
119
|
+
EGPTReal.fromBigInt(2n),
|
|
120
|
+
EGPTReal.fromBigInt(0n)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Evaluate ζ(2) with 100 terms of the Dirichlet series.
|
|
124
|
+
const rzf = EGPTComplex.riemannZeta(s, 100);
|
|
125
|
+
|
|
126
|
+
// Project the real part out of EGPT information space at the output boundary.
|
|
127
|
+
// breakSymbolicToApproximateJSNumber() is intentionally labelled "break" —
|
|
128
|
+
// it exits the rational canonical form into a JS floating-point number (lossy).
|
|
129
|
+
const rzf_val = rzf.real.breakSymbolicToApproximateJSNumber();
|
|
130
|
+
|
|
131
|
+
// The known closed-form value π²/6 — computed here using the JS approximation
|
|
132
|
+
// of π only at the BOUNDARY for the human-facing comparison.
|
|
133
|
+
const pi_squared_over_6 = (Math.PI ** 2) / 6;
|
|
134
|
+
|
|
135
|
+
const riemannZetaResult = {
|
|
136
|
+
rzf_val,
|
|
137
|
+
pi_squared_over_6,
|
|
138
|
+
difference: Math.abs(rzf_val - pi_squared_over_6)
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
display(`ζ(2) ≈ ${rzf_val.toFixed(6)} (partial sum, 100 terms)`);
|
|
142
|
+
display(`π²/6 ≈ ${pi_squared_over_6.toFixed(6)} (reference value)`);
|
|
143
|
+
display(`|difference| = ${riemannZetaResult.difference.toFixed(6)}`);
|
|
144
|
+
|
|
145
|
+
return { riemannZetaResult };
|
|
146
|
+
</cell>
|
|
147
|
+
|
|
148
|
+
<cell type="markdown">
|
|
149
|
+
## 5. Precision Assertion
|
|
150
|
+
|
|
151
|
+
The original demo asserts that the 100-term partial sum agrees with π²/6 to within **0.01**. This cell reproduces that assertion as an explicit pass/fail check, using the same tolerance.
|
|
152
|
+
|
|
153
|
+
Note that `.toFixed(4)` on the projected JS number matches the original demo's output format.
|
|
154
|
+
</cell>
|
|
155
|
+
|
|
156
|
+
<cell type="js" lane="math" id="precision-check" in="riemannZetaResult">
|
|
157
|
+
const { math, display } = caps;
|
|
158
|
+
const { riemannZetaResult } = inputs;
|
|
159
|
+
const { rzf_val, pi_squared_over_6, difference } = riemannZetaResult;
|
|
160
|
+
|
|
161
|
+
const TOLERANCE = 0.01;
|
|
162
|
+
const pass = difference < TOLERANCE;
|
|
163
|
+
|
|
164
|
+
if (!pass) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Riemann Zeta precision error: ${rzf_val} vs ${pi_squared_over_6} ` +
|
|
167
|
+
`(difference ${difference} exceeds tolerance ${TOLERANCE})`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const el = document.createElement('div');
|
|
172
|
+
el.style.cssText =
|
|
173
|
+
'font:600 0.95rem/1.5 system-ui,sans-serif;padding:10px 14px;border-radius:6px;margin:4px 0;' +
|
|
174
|
+
'background:#0f2417;border:1px solid #1f5a36;color:#7ee2a8;';
|
|
175
|
+
el.textContent =
|
|
176
|
+
`PASS ζ(2) = ${rzf_val.toFixed(4)} ≈ π²/6 = ${pi_squared_over_6.toFixed(4)}` +
|
|
177
|
+
` |diff| = ${difference.toFixed(6)} < ${TOLERANCE}`;
|
|
178
|
+
display(el);
|
|
179
|
+
</cell>
|
|
180
|
+
|
|
181
|
+
<cell type="markdown">
|
|
182
|
+
## Summary
|
|
183
|
+
|
|
184
|
+
| Operation | Input | Result |
|
|
185
|
+
|---|---|---|
|
|
186
|
+
| `EGPTranscendental.exp2` | 3 (EGPTReal) | 2^3 = **8** (exact) |
|
|
187
|
+
| `EGPTranscendental.log2` | 32 (EGPTReal) | log₂(32) = **5** (exact) |
|
|
188
|
+
| `EGPTComplex.riemannZeta` | s=2, 100 terms | ≈ **1.6350** (within 0.01 of π²/6) |
|
|
189
|
+
|
|
190
|
+
All three results agree with `sdk/egpt-math-sdk/src/examples/transcendentals.js` — same classes, same logic, the same projected output at the boundary. The notebook simply makes each step visible and independently runnable for a developer encountering EGPT transcendentals for the first time.
|
|
191
|
+
|
|
192
|
+
**Key architectural point:** `EGPTranscendental` and `EGPTComplex` are *static utility classes* — they cannot be instantiated. `ComplexEGPTReal` is the *value class* that holds a + bi. These are distinct: `EGPTComplex.riemannZeta(s)` works; `s.riemannZeta()` does not.
|
|
193
|
+
</cell>
|
|
194
|
+
|
|
195
|
+
</frqtl-notebook>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<frqtl-notebook version="1" title="View Cell — Vendored d3-array Import">
|
|
2
|
+
|
|
3
|
+
<cell type="markdown">
|
|
4
|
+
# The `view` cell — arbitrary user render code in the realm
|
|
5
|
+
|
|
6
|
+
A **`view` cell** runs your own render code **in the realm** (the browser context), imports
|
|
7
|
+
**vendored libraries** through the import-map, gets a handle, and draws into a DOM `mount`.
|
|
8
|
+
|
|
9
|
+
This notebook is the worked example for WS-NOTEBOOK-VENDOR-VIEW: a view cell whose module
|
|
10
|
+
`import`s the vendored **`d3-array`** library (placed under `/vendor` and listed in the
|
|
11
|
+
committed manifest), computes with it, and renders a tiny SVG bar chart into its mount —
|
|
12
|
+
proving the import resolves and the view runs end-to-end.
|
|
13
|
+
|
|
14
|
+
The pieces:
|
|
15
|
+
|
|
16
|
+
1. a `type="module"` cell exporting `render(ctx)` — it `import { max, sum } from 'd3-array'`,
|
|
17
|
+
2. a `type="view"` cell that names that module (`module="…#render"`) and selects the realm.
|
|
18
|
+
|
|
19
|
+
The bare specifier `'d3-array'` resolves via the 2a import-map (it maps `d3-array` →
|
|
20
|
+
`/vendor/d3-array/src/index.js`). An UNLISTED specifier would throw in the realm loader and
|
|
21
|
+
the cell badge would show **ERROR** (FAIL-LOUD) — there is no arbitrary CDN/network import.
|
|
22
|
+
</cell>
|
|
23
|
+
|
|
24
|
+
<cell type="module" name="./views/d3-bars.js">
|
|
25
|
+
// A view render module. Its export is invoked IN THE REALM with the view `ctx`:
|
|
26
|
+
// ctx.mount — a DOM element to render into
|
|
27
|
+
// ctx.handle — the frozen ViewHandle for this realm (on/call/start/stop/step/dispose)
|
|
28
|
+
// ctx.getBinding — read an `in` binding value (data IN)
|
|
29
|
+
// ctx.commit — commit a `produces` value (data OUT, serializable-asserted)
|
|
30
|
+
// ctx.imports — pre-resolved blessed helpers (makeChartEnv / makeLiveChartController)
|
|
31
|
+
//
|
|
32
|
+
// The bare import below is resolved by the realm's vendor import-map (Phase 2a). d3-array
|
|
33
|
+
// is genuinely-ESM; the import-map maps the bare specifier to the placed /vendor artifact.
|
|
34
|
+
import { max, sum } from 'd3-array';
|
|
35
|
+
|
|
36
|
+
export function render(ctx) {
|
|
37
|
+
// A small dataset; compute with d3-array (the WHOLE point — a vendored lib runs in-realm).
|
|
38
|
+
const data = [4, 9, 2, 7, 5, 8, 3];
|
|
39
|
+
const peak = max(data); // 9
|
|
40
|
+
const total = sum(data); // 38
|
|
41
|
+
|
|
42
|
+
// Render a tiny inline-SVG bar chart into the mount. No physics — pure user render code.
|
|
43
|
+
const W = 320, H = 120, pad = 8, n = data.length;
|
|
44
|
+
const bw = (W - pad * 2) / n - 4;
|
|
45
|
+
const bars = data.map((d, i) => {
|
|
46
|
+
const h = Math.round((d / peak) * (H - pad * 2));
|
|
47
|
+
const x = pad + i * ((W - pad * 2) / n);
|
|
48
|
+
const y = H - pad - h;
|
|
49
|
+
return `<rect x="${x.toFixed(1)}" y="${y}" width="${bw.toFixed(1)}" height="${h}" fill="#5ad1ff" rx="2"></rect>`;
|
|
50
|
+
}).join('');
|
|
51
|
+
|
|
52
|
+
ctx.mount.innerHTML =
|
|
53
|
+
`<div style="font:0.75rem/1.4 ui-monospace,monospace;color:#9fe6ff;margin-bottom:6px;" data-role="d3-summary">` +
|
|
54
|
+
`d3-array in-realm — max=${peak}, sum=${total}` +
|
|
55
|
+
`</div>` +
|
|
56
|
+
`<svg width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" role="img" aria-label="d3 bar chart">${bars}</svg>`;
|
|
57
|
+
}
|
|
58
|
+
</cell>
|
|
59
|
+
|
|
60
|
+
<cell type="view" id="c-d3-view" realm="frqtl" module="./views/d3-bars.js#render"></cell>
|
|
61
|
+
|
|
62
|
+
<cell type="markdown">
|
|
63
|
+
## What just happened
|
|
64
|
+
|
|
65
|
+
- The view module ran **in the realm** (same-cell — its render is local; nothing crossed the
|
|
66
|
+
renderer↔realm seam).
|
|
67
|
+
- `import { max, sum } from 'd3-array'` resolved through the **import-map** to the placed
|
|
68
|
+
`/vendor/d3-array/src/index.js` artifact (and its transitive `internmap` bare dep — the
|
|
69
|
+
import-map allowlist covers the whole bare-specifier graph).
|
|
70
|
+
- The mount shows a d3-computed SVG bar chart with the summary `max=9, sum=38`.
|
|
71
|
+
|
|
72
|
+
`liveChart=` on a canvas is a **blessed preset** over this same `view` substrate: it is a
|
|
73
|
+
same-cell view that imports the blessed chart module (`makeChartEnv`) and drives
|
|
74
|
+
`makeLiveChartController` — both available to any view via `ctx.imports`. The Step-1
|
|
75
|
+
`liveChart=` path is unchanged; the `view` cell is the general form beneath it.
|
|
76
|
+
</cell>
|
|
77
|
+
|
|
78
|
+
</frqtl-notebook>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {slice} from "./array.js";
|
|
2
|
+
import bisect from "./bisect.js";
|
|
3
|
+
import constant from "./constant.js";
|
|
4
|
+
import extent from "./extent.js";
|
|
5
|
+
import identity from "./identity.js";
|
|
6
|
+
import nice from "./nice.js";
|
|
7
|
+
import ticks, {tickIncrement} from "./ticks.js";
|
|
8
|
+
import sturges from "./threshold/sturges.js";
|
|
9
|
+
|
|
10
|
+
export default function bin() {
|
|
11
|
+
var value = identity,
|
|
12
|
+
domain = extent,
|
|
13
|
+
threshold = sturges;
|
|
14
|
+
|
|
15
|
+
function histogram(data) {
|
|
16
|
+
if (!Array.isArray(data)) data = Array.from(data);
|
|
17
|
+
|
|
18
|
+
var i,
|
|
19
|
+
n = data.length,
|
|
20
|
+
x,
|
|
21
|
+
step,
|
|
22
|
+
values = new Array(n);
|
|
23
|
+
|
|
24
|
+
for (i = 0; i < n; ++i) {
|
|
25
|
+
values[i] = value(data[i], i, data);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var xz = domain(values),
|
|
29
|
+
x0 = xz[0],
|
|
30
|
+
x1 = xz[1],
|
|
31
|
+
tz = threshold(values, x0, x1);
|
|
32
|
+
|
|
33
|
+
// Convert number of thresholds into uniform thresholds, and nice the
|
|
34
|
+
// default domain accordingly.
|
|
35
|
+
if (!Array.isArray(tz)) {
|
|
36
|
+
const max = x1, tn = +tz;
|
|
37
|
+
if (domain === extent) [x0, x1] = nice(x0, x1, tn);
|
|
38
|
+
tz = ticks(x0, x1, tn);
|
|
39
|
+
|
|
40
|
+
// If the domain is aligned with the first tick (which it will by
|
|
41
|
+
// default), then we can use quantization rather than bisection to bin
|
|
42
|
+
// values, which is substantially faster.
|
|
43
|
+
if (tz[0] <= x0) step = tickIncrement(x0, x1, tn);
|
|
44
|
+
|
|
45
|
+
// If the last threshold is coincident with the domain’s upper bound, the
|
|
46
|
+
// last bin will be zero-width. If the default domain is used, and this
|
|
47
|
+
// last threshold is coincident with the maximum input value, we can
|
|
48
|
+
// extend the niced upper bound by one tick to ensure uniform bin widths;
|
|
49
|
+
// otherwise, we simply remove the last threshold. Note that we don’t
|
|
50
|
+
// coerce values or the domain to numbers, and thus must be careful to
|
|
51
|
+
// compare order (>=) rather than strict equality (===)!
|
|
52
|
+
if (tz[tz.length - 1] >= x1) {
|
|
53
|
+
if (max >= x1 && domain === extent) {
|
|
54
|
+
const step = tickIncrement(x0, x1, tn);
|
|
55
|
+
if (isFinite(step)) {
|
|
56
|
+
if (step > 0) {
|
|
57
|
+
x1 = (Math.floor(x1 / step) + 1) * step;
|
|
58
|
+
} else if (step < 0) {
|
|
59
|
+
x1 = (Math.ceil(x1 * -step) + 1) / -step;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
tz.pop();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Remove any thresholds outside the domain.
|
|
69
|
+
// Be careful not to mutate an array owned by the user!
|
|
70
|
+
var m = tz.length, a = 0, b = m;
|
|
71
|
+
while (tz[a] <= x0) ++a;
|
|
72
|
+
while (tz[b - 1] > x1) --b;
|
|
73
|
+
if (a || b < m) tz = tz.slice(a, b), m = b - a;
|
|
74
|
+
|
|
75
|
+
var bins = new Array(m + 1),
|
|
76
|
+
bin;
|
|
77
|
+
|
|
78
|
+
// Initialize bins.
|
|
79
|
+
for (i = 0; i <= m; ++i) {
|
|
80
|
+
bin = bins[i] = [];
|
|
81
|
+
bin.x0 = i > 0 ? tz[i - 1] : x0;
|
|
82
|
+
bin.x1 = i < m ? tz[i] : x1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Assign data to bins by value, ignoring any outside the domain.
|
|
86
|
+
if (isFinite(step)) {
|
|
87
|
+
if (step > 0) {
|
|
88
|
+
for (i = 0; i < n; ++i) {
|
|
89
|
+
if ((x = values[i]) != null && x0 <= x && x <= x1) {
|
|
90
|
+
bins[Math.min(m, Math.floor((x - x0) / step))].push(data[i]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else if (step < 0) {
|
|
94
|
+
for (i = 0; i < n; ++i) {
|
|
95
|
+
if ((x = values[i]) != null && x0 <= x && x <= x1) {
|
|
96
|
+
const j = Math.floor((x0 - x) * step);
|
|
97
|
+
bins[Math.min(m, j + (tz[j] <= x))].push(data[i]); // handle off-by-one due to rounding
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
for (i = 0; i < n; ++i) {
|
|
103
|
+
if ((x = values[i]) != null && x0 <= x && x <= x1) {
|
|
104
|
+
bins[bisect(tz, x, 0, m)].push(data[i]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return bins;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
histogram.value = function(_) {
|
|
113
|
+
return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
histogram.domain = function(_) {
|
|
117
|
+
return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
histogram.thresholds = function(_) {
|
|
121
|
+
return arguments.length ? (threshold = typeof _ === "function" ? _ : constant(Array.isArray(_) ? slice.call(_) : _), histogram) : threshold;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return histogram;
|
|
125
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ascending from "./ascending.js";
|
|
2
|
+
import bisector from "./bisector.js";
|
|
3
|
+
import number from "./number.js";
|
|
4
|
+
|
|
5
|
+
const ascendingBisect = bisector(ascending);
|
|
6
|
+
export const bisectRight = ascendingBisect.right;
|
|
7
|
+
export const bisectLeft = ascendingBisect.left;
|
|
8
|
+
export const bisectCenter = bisector(number).center;
|
|
9
|
+
export default bisectRight;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ascending from "./ascending.js";
|
|
2
|
+
import descending from "./descending.js";
|
|
3
|
+
|
|
4
|
+
export default function bisector(f) {
|
|
5
|
+
let compare1, compare2, delta;
|
|
6
|
+
|
|
7
|
+
// If an accessor is specified, promote it to a comparator. In this case we
|
|
8
|
+
// can test whether the search value is (self-) comparable. We can’t do this
|
|
9
|
+
// for a comparator (except for specific, known comparators) because we can’t
|
|
10
|
+
// tell if the comparator is symmetric, and an asymmetric comparator can’t be
|
|
11
|
+
// used to test whether a single value is comparable.
|
|
12
|
+
if (f.length !== 2) {
|
|
13
|
+
compare1 = ascending;
|
|
14
|
+
compare2 = (d, x) => ascending(f(d), x);
|
|
15
|
+
delta = (d, x) => f(d) - x;
|
|
16
|
+
} else {
|
|
17
|
+
compare1 = f === ascending || f === descending ? f : zero;
|
|
18
|
+
compare2 = f;
|
|
19
|
+
delta = f;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function left(a, x, lo = 0, hi = a.length) {
|
|
23
|
+
if (lo < hi) {
|
|
24
|
+
if (compare1(x, x) !== 0) return hi;
|
|
25
|
+
do {
|
|
26
|
+
const mid = (lo + hi) >>> 1;
|
|
27
|
+
if (compare2(a[mid], x) < 0) lo = mid + 1;
|
|
28
|
+
else hi = mid;
|
|
29
|
+
} while (lo < hi);
|
|
30
|
+
}
|
|
31
|
+
return lo;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function right(a, x, lo = 0, hi = a.length) {
|
|
35
|
+
if (lo < hi) {
|
|
36
|
+
if (compare1(x, x) !== 0) return hi;
|
|
37
|
+
do {
|
|
38
|
+
const mid = (lo + hi) >>> 1;
|
|
39
|
+
if (compare2(a[mid], x) <= 0) lo = mid + 1;
|
|
40
|
+
else hi = mid;
|
|
41
|
+
} while (lo < hi);
|
|
42
|
+
}
|
|
43
|
+
return lo;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function center(a, x, lo = 0, hi = a.length) {
|
|
47
|
+
const i = left(a, x, lo, hi - 1);
|
|
48
|
+
return i > lo && delta(a[i - 1], x) > -delta(a[i], x) ? i - 1 : i;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {left, center, right};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function zero() {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|