eyeling 1.16.2 → 1.16.4
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/HANDBOOK.md +4 -0
- package/README.md +0 -1
- package/examples/ershov-mixed-computation.n3 +106 -0
- package/examples/output/ershov-mixed-computation.n3 +15 -0
- package/eyeling.js +510 -263
- package/lib/cli.js +22 -12
- package/lib/engine.js +488 -251
- package/package.json +2 -3
- package/arctifacts/README.md +0 -59
- package/arctifacts/ackermann.html +0 -678
- package/arctifacts/auroracare.html +0 -1297
- package/arctifacts/bike-trip.html +0 -752
- package/arctifacts/binomial-theorem.html +0 -631
- package/arctifacts/bmi.html +0 -511
- package/arctifacts/building-performance.html +0 -750
- package/arctifacts/clinical-care.html +0 -726
- package/arctifacts/collatz.html +0 -403
- package/arctifacts/complex.html +0 -321
- package/arctifacts/control-system.html +0 -482
- package/arctifacts/delfour.html +0 -849
- package/arctifacts/earthquake-epicenter.html +0 -982
- package/arctifacts/eco-route.html +0 -662
- package/arctifacts/euclid-infinitude.html +0 -564
- package/arctifacts/euler-identity.html +0 -667
- package/arctifacts/exoplanet-transit.html +0 -1000
- package/arctifacts/faltings-theorem.html +0 -1046
- package/arctifacts/fibonacci.html +0 -299
- package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
- package/arctifacts/godel-numbering.html +0 -743
- package/arctifacts/gps-bike.html +0 -759
- package/arctifacts/gps-clinical-bench.html +0 -792
- package/arctifacts/graph-french.html +0 -449
- package/arctifacts/grass-molecular.html +0 -592
- package/arctifacts/group-theory.html +0 -740
- package/arctifacts/health-info.html +0 -833
- package/arctifacts/kaprekar-constant.html +0 -576
- package/arctifacts/lee.html +0 -805
- package/arctifacts/linked-lists.html +0 -502
- package/arctifacts/lldm.html +0 -612
- package/arctifacts/matrix-multiplication.html +0 -502
- package/arctifacts/matrix.html +0 -651
- package/arctifacts/newton-raphson.html +0 -944
- package/arctifacts/peano-factorial.html +0 -456
- package/arctifacts/pi.html +0 -363
- package/arctifacts/polynomial.html +0 -646
- package/arctifacts/prime.html +0 -366
- package/arctifacts/pythagorean-theorem.html +0 -468
- package/arctifacts/rest-path.html +0 -469
- package/arctifacts/roots-of-unity.html +0 -363
- package/arctifacts/turing.html +0 -409
- package/arctifacts/wind-turbines.html +0 -726
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Fibonacci via Fast Doubling</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--fg: #101014;
|
|
10
|
-
--bg: #ffffff;
|
|
11
|
-
--muted: #666;
|
|
12
|
-
--accent: #2563eb;
|
|
13
|
-
--chip: #eef2ff;
|
|
14
|
-
}
|
|
15
|
-
@media (prefers-color-scheme: dark) {
|
|
16
|
-
:root {
|
|
17
|
-
--fg: #eaeaf0;
|
|
18
|
-
--bg: #0b0b10;
|
|
19
|
-
--muted: #a0a0b0;
|
|
20
|
-
--accent: #60a5fa;
|
|
21
|
-
--chip: #0e1a32;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
html,
|
|
25
|
-
body {
|
|
26
|
-
margin: 0;
|
|
27
|
-
padding: 0;
|
|
28
|
-
background: var(--bg);
|
|
29
|
-
color: var(--fg);
|
|
30
|
-
font:
|
|
31
|
-
15px/1.6 ui-sans-serif,
|
|
32
|
-
system-ui,
|
|
33
|
-
-apple-system,
|
|
34
|
-
Segoe UI,
|
|
35
|
-
Roboto,
|
|
36
|
-
Helvetica,
|
|
37
|
-
Arial;
|
|
38
|
-
}
|
|
39
|
-
main {
|
|
40
|
-
max-width: 1100px;
|
|
41
|
-
margin: 0 auto;
|
|
42
|
-
padding: 28px 16px 80px;
|
|
43
|
-
}
|
|
44
|
-
h1 {
|
|
45
|
-
font-size: clamp(1.6rem, 2.6vw + 1rem, 2.2rem);
|
|
46
|
-
margin: 0 0 6px;
|
|
47
|
-
}
|
|
48
|
-
header p {
|
|
49
|
-
margin: 0;
|
|
50
|
-
color: var(--muted);
|
|
51
|
-
}
|
|
52
|
-
section {
|
|
53
|
-
margin: 18px 0 22px;
|
|
54
|
-
padding: 14px 14px 16px;
|
|
55
|
-
border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
|
|
56
|
-
border-radius: 14px;
|
|
57
|
-
background: color-mix(in srgb, var(--accent) 4%, transparent);
|
|
58
|
-
}
|
|
59
|
-
section h2 {
|
|
60
|
-
margin: 0 0 8px;
|
|
61
|
-
font-size: 1.15rem;
|
|
62
|
-
}
|
|
63
|
-
.grid {
|
|
64
|
-
display: grid;
|
|
65
|
-
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
66
|
-
gap: 10px;
|
|
67
|
-
}
|
|
68
|
-
.chip {
|
|
69
|
-
display: inline-block;
|
|
70
|
-
padding: 2px 8px;
|
|
71
|
-
border-radius: 999px;
|
|
72
|
-
background: var(--chip);
|
|
73
|
-
font-weight: 600;
|
|
74
|
-
}
|
|
75
|
-
.muted {
|
|
76
|
-
color: var(--muted);
|
|
77
|
-
}
|
|
78
|
-
.mono {
|
|
79
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
80
|
-
}
|
|
81
|
-
.tbl {
|
|
82
|
-
width: 100%;
|
|
83
|
-
border-collapse: collapse;
|
|
84
|
-
}
|
|
85
|
-
.tbl th,
|
|
86
|
-
.tbl td {
|
|
87
|
-
padding: 6px 8px;
|
|
88
|
-
border-bottom: 1px dashed color-mix(in srgb, var(--fg) 18%, transparent);
|
|
89
|
-
vertical-align: top;
|
|
90
|
-
}
|
|
91
|
-
.tbl th {
|
|
92
|
-
text-align: left;
|
|
93
|
-
}
|
|
94
|
-
.ok {
|
|
95
|
-
color: #16a34a;
|
|
96
|
-
}
|
|
97
|
-
.bad {
|
|
98
|
-
color: #dc2626;
|
|
99
|
-
}
|
|
100
|
-
.small {
|
|
101
|
-
font-size: 0.92em;
|
|
102
|
-
}
|
|
103
|
-
code {
|
|
104
|
-
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
105
|
-
padding: 0.1rem 0.35rem;
|
|
106
|
-
border-radius: 0.35rem;
|
|
107
|
-
}
|
|
108
|
-
details {
|
|
109
|
-
border-radius: 10px;
|
|
110
|
-
padding: 10px 12px;
|
|
111
|
-
background: color-mix(in srgb, var(--accent) 6%, transparent);
|
|
112
|
-
}
|
|
113
|
-
summary {
|
|
114
|
-
cursor: pointer;
|
|
115
|
-
font-weight: 700;
|
|
116
|
-
}
|
|
117
|
-
</style>
|
|
118
|
-
</head>
|
|
119
|
-
<body>
|
|
120
|
-
<main>
|
|
121
|
-
<header>
|
|
122
|
-
<h1>Fibonacci via Fast Doubling</h1>
|
|
123
|
-
</header>
|
|
124
|
-
|
|
125
|
-
<section>
|
|
126
|
-
<h2>What this is?</h2>
|
|
127
|
-
<p>
|
|
128
|
-
A self‑contained explainer that computes selected Fibonacci numbers exactly using the
|
|
129
|
-
<em>fast‑doubling</em> identities. It shows the <strong>Answer</strong> (exact values and digit counts), the
|
|
130
|
-
<strong>Reason why</strong> (the identities used), and a <strong>Check</strong> harness that verifies base
|
|
131
|
-
cases, the recurrence, an addition identity, and cross‑checks against a simple linear iterator.
|
|
132
|
-
</p>
|
|
133
|
-
<p class="small muted">Time per F<sub>n</sub> is O(log n); everything is integer‑exact with BigInt.</p>
|
|
134
|
-
</section>
|
|
135
|
-
|
|
136
|
-
<section id="answer">
|
|
137
|
-
<h2>Answer</h2>
|
|
138
|
-
<div id="answer-body"></div>
|
|
139
|
-
</section>
|
|
140
|
-
|
|
141
|
-
<section id="reason">
|
|
142
|
-
<h2>Reason why</h2>
|
|
143
|
-
<div>
|
|
144
|
-
<p>
|
|
145
|
-
Define Fibonacci by F<sub>0</sub>=0, F<sub>1</sub>=1, and F<sub>n+2</sub>=F<sub>n+1</sub>+F<sub>n</sub>.
|
|
146
|
-
</p>
|
|
147
|
-
<p>A standard addition identity (provable by induction) is:</p>
|
|
148
|
-
<p class="mono">
|
|
149
|
-
(A) F<sub>m+n</sub> = F<sub>m</sub>·F<sub>n+1</sub> + F<sub>m−1</sub>·F<sub>n</sub> (for m,n ≥
|
|
150
|
-
1)
|
|
151
|
-
</p>
|
|
152
|
-
<p>Specializing (A) yields fast‑doubling from (F<sub>k</sub>, F<sub>k+1</sub>):</p>
|
|
153
|
-
<ul class="mono">
|
|
154
|
-
<li>F<sub>2k</sub> = F<sub>k</sub> ( 2F<sub>k+1</sub> − F<sub>k</sub> )</li>
|
|
155
|
-
<li>F<sub>2k+1</sub> = F<sub>k+1</sub><sup>2</sup> + F<sub>k</sub><sup>2</sup></li>
|
|
156
|
-
</ul>
|
|
157
|
-
<p>
|
|
158
|
-
One recursive call on k=⌊n/2⌋ computes both F<sub>2k</sub> and F<sub>2k+1</sub>, then picks (F<sub>n</sub>,
|
|
159
|
-
F<sub>n+1</sub>) by parity.
|
|
160
|
-
</p>
|
|
161
|
-
</div>
|
|
162
|
-
</section>
|
|
163
|
-
|
|
164
|
-
<section id="check">
|
|
165
|
-
<h2>Check (harness)</h2>
|
|
166
|
-
<div id="check-body"></div>
|
|
167
|
-
</section>
|
|
168
|
-
</main>
|
|
169
|
-
|
|
170
|
-
<script>
|
|
171
|
-
(function () {
|
|
172
|
-
'use strict';
|
|
173
|
-
|
|
174
|
-
// ------------------ BigInt fast-doubling core ------------------
|
|
175
|
-
const Z0 = 0n,
|
|
176
|
-
Z1 = 1n,
|
|
177
|
-
Z2 = 2n;
|
|
178
|
-
|
|
179
|
-
function fibFastDoubling(n) {
|
|
180
|
-
// returns [F_n, F_{n+1}] as BigInts
|
|
181
|
-
if (n === 0) return [Z0, Z1];
|
|
182
|
-
const [fk, fk1] = fibFastDoubling(n >> 1);
|
|
183
|
-
const c = fk * (Z2 * fk1 - fk); // F_{2k}
|
|
184
|
-
const d = fk * fk + fk1 * fk1; // F_{2k+1}
|
|
185
|
-
if (n & 1) {
|
|
186
|
-
// odd
|
|
187
|
-
return [d, c + d];
|
|
188
|
-
} else {
|
|
189
|
-
// even
|
|
190
|
-
return [c, d];
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function fibLinear(n) {
|
|
195
|
-
let a = Z0,
|
|
196
|
-
b = Z1;
|
|
197
|
-
for (let i = 0; i < n; i++) {
|
|
198
|
-
const t = a + b;
|
|
199
|
-
a = b;
|
|
200
|
-
b = t;
|
|
201
|
-
}
|
|
202
|
-
return a;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function digitsOf(x) {
|
|
206
|
-
// x is BigInt, nonnegative
|
|
207
|
-
return x === Z0 ? 1 : x.toString().length;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ------------------ Answer ------------------
|
|
211
|
-
const indices = [0, 1, 6, 91, 283, 3674];
|
|
212
|
-
const answerBody = document.getElementById('answer-body');
|
|
213
|
-
|
|
214
|
-
(function renderAnswer() {
|
|
215
|
-
let html =
|
|
216
|
-
'<table class="tbl mono small"><thead><tr><th>n</th><th>F_n (exact)</th><th>digits</th></tr></thead><tbody>';
|
|
217
|
-
for (const n of indices) {
|
|
218
|
-
const [fn] = fibFastDoubling(n);
|
|
219
|
-
html += `<tr><td>${n}</td><td style="word-break:break-all">${fn.toString()}</td><td>${digitsOf(fn)}</td></tr>`;
|
|
220
|
-
}
|
|
221
|
-
html += '</tbody></table>';
|
|
222
|
-
answerBody.innerHTML = html;
|
|
223
|
-
})();
|
|
224
|
-
|
|
225
|
-
// ------------------ Harness (checks) ------------------
|
|
226
|
-
function runChecks() {
|
|
227
|
-
let okAll = true;
|
|
228
|
-
const lines = [];
|
|
229
|
-
|
|
230
|
-
// 1) Base pair
|
|
231
|
-
const [f0, f1] = fibFastDoubling(0);
|
|
232
|
-
const okBase = f0 === Z0 && f1 === Z1;
|
|
233
|
-
lines.push(`Base pair correct? ${okBase}`);
|
|
234
|
-
okAll = okAll && okBase;
|
|
235
|
-
|
|
236
|
-
// 2) Recurrence on n in [0,1000)
|
|
237
|
-
let okRecur = true;
|
|
238
|
-
for (let n = 0; n < 1000; n++) {
|
|
239
|
-
const [fn, fn1] = fibFastDoubling(n);
|
|
240
|
-
const [fn2] = fibFastDoubling(n + 2);
|
|
241
|
-
if (fn + fn1 !== fn2) {
|
|
242
|
-
okRecur = false;
|
|
243
|
-
lines.push(` Recurrence failed at n=${n}`);
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
lines.push(`Recurrence holds on n ∈ [0,1000)? ${okRecur}`);
|
|
248
|
-
okAll = okAll && okRecur;
|
|
249
|
-
|
|
250
|
-
// 3) Addition identity grid
|
|
251
|
-
let okAdd = true;
|
|
252
|
-
for (let m = 1; m < 60 && okAdd; m++) {
|
|
253
|
-
const [fm, fm1] = fibFastDoubling(m);
|
|
254
|
-
const [fm_1] = fibFastDoubling(m - 1);
|
|
255
|
-
for (let n = 0; n < 60; n++) {
|
|
256
|
-
const [lhs] = fibFastDoubling(m + n);
|
|
257
|
-
const [fn, fn1] = fibFastDoubling(n);
|
|
258
|
-
const rhs = fm * fn1 + fm_1 * fn;
|
|
259
|
-
if (lhs !== rhs) {
|
|
260
|
-
okAdd = false;
|
|
261
|
-
lines.push(` (A) failed at m=${m}, n=${n}`);
|
|
262
|
-
break;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
lines.push(`Addition identity (A) holds on m∈[1,60), n∈[0,60)? ${okAdd}`);
|
|
267
|
-
okAll = okAll && okAdd;
|
|
268
|
-
|
|
269
|
-
// 4) Cross-check vs linear on n in [0,2000)
|
|
270
|
-
let okCross = true;
|
|
271
|
-
for (let n = 0; n < 2000; n++) {
|
|
272
|
-
if (fibFastDoubling(n)[0] !== fibLinear(n)) {
|
|
273
|
-
okCross = false;
|
|
274
|
-
lines.push(` Cross-check failed at n=${n}`);
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
lines.push(`Cross-check vs linear on n ∈ [0,2000)? ${okCross}`);
|
|
279
|
-
okAll = okAll && okCross;
|
|
280
|
-
|
|
281
|
-
lines.push('');
|
|
282
|
-
lines.push(`All checks passed? ${okAll}`);
|
|
283
|
-
|
|
284
|
-
return lines;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
(function renderChecks() {
|
|
288
|
-
const out = runChecks();
|
|
289
|
-
const wrap = document.getElementById('check-body');
|
|
290
|
-
const pre = document.createElement('pre');
|
|
291
|
-
pre.className = 'mono';
|
|
292
|
-
pre.style.whiteSpace = 'pre-wrap';
|
|
293
|
-
pre.textContent = out.join('\n'); // use real newlines
|
|
294
|
-
wrap.replaceChildren(pre);
|
|
295
|
-
})();
|
|
296
|
-
})();
|
|
297
|
-
</script>
|
|
298
|
-
</body>
|
|
299
|
-
</html>
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Fundamental Theorem of Arithmetic</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--bg: #f7f9fc;
|
|
10
|
-
--card: #ffffff;
|
|
11
|
-
--text: #0f172a;
|
|
12
|
-
--muted: #475569;
|
|
13
|
-
--border: #e2e8f0;
|
|
14
|
-
}
|
|
15
|
-
html,
|
|
16
|
-
body {
|
|
17
|
-
background: var(--bg);
|
|
18
|
-
color: var(--text);
|
|
19
|
-
font-family:
|
|
20
|
-
ui-sans-serif,
|
|
21
|
-
system-ui,
|
|
22
|
-
-apple-system,
|
|
23
|
-
Segoe UI,
|
|
24
|
-
Roboto,
|
|
25
|
-
Helvetica,
|
|
26
|
-
Arial;
|
|
27
|
-
}
|
|
28
|
-
.container {
|
|
29
|
-
max-width: 960px;
|
|
30
|
-
margin: 24px auto;
|
|
31
|
-
padding: 0 16px;
|
|
32
|
-
}
|
|
33
|
-
.card {
|
|
34
|
-
background: var(--card);
|
|
35
|
-
border: 1px solid var(--border);
|
|
36
|
-
border-radius: 16px;
|
|
37
|
-
padding: 16px;
|
|
38
|
-
box-shadow: 0 6px 20px rgba(2, 6, 23, 0.05);
|
|
39
|
-
}
|
|
40
|
-
.stack {
|
|
41
|
-
display: flex;
|
|
42
|
-
flex-direction: column;
|
|
43
|
-
gap: 16px;
|
|
44
|
-
}
|
|
45
|
-
.row {
|
|
46
|
-
display: flex;
|
|
47
|
-
align-items: center;
|
|
48
|
-
gap: 12px;
|
|
49
|
-
}
|
|
50
|
-
.mono {
|
|
51
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
|
52
|
-
word-break: break-word;
|
|
53
|
-
overflow-wrap: anywhere;
|
|
54
|
-
}
|
|
55
|
-
.badge {
|
|
56
|
-
padding: 2px 8px;
|
|
57
|
-
border: 1px solid #cbd5e1;
|
|
58
|
-
border-radius: 999px;
|
|
59
|
-
font-weight: 700;
|
|
60
|
-
font-size: 12px;
|
|
61
|
-
background: #f1f5f9;
|
|
62
|
-
color: #0f172a;
|
|
63
|
-
}
|
|
64
|
-
.ok {
|
|
65
|
-
background: #dcfce7;
|
|
66
|
-
border: 1px solid #86efac;
|
|
67
|
-
}
|
|
68
|
-
.fail {
|
|
69
|
-
background: #fee2e2;
|
|
70
|
-
border: 1px solid #fecaca;
|
|
71
|
-
}
|
|
72
|
-
.kpi {
|
|
73
|
-
display: flex;
|
|
74
|
-
gap: 16px;
|
|
75
|
-
flex-wrap: wrap;
|
|
76
|
-
}
|
|
77
|
-
.kpi .card {
|
|
78
|
-
padding: 12px 14px;
|
|
79
|
-
}
|
|
80
|
-
input.short {
|
|
81
|
-
width: 180px;
|
|
82
|
-
padding: 6px 8px;
|
|
83
|
-
border: 1px solid var(--border);
|
|
84
|
-
border-radius: 10px;
|
|
85
|
-
background: #fbfdff;
|
|
86
|
-
}
|
|
87
|
-
</style>
|
|
88
|
-
</head>
|
|
89
|
-
<body>
|
|
90
|
-
<div class="container stack">
|
|
91
|
-
<header class="card stack">
|
|
92
|
-
<h1 style="margin: 0">Fundamental Theorem of Arithmetic</h1>
|
|
93
|
-
<div class="badge">
|
|
94
|
-
Every integer <span class="mono">n≥2</span> factors as a product of primes, uniquely up to order.
|
|
95
|
-
</div>
|
|
96
|
-
<div class="row">
|
|
97
|
-
<label class="mono" for="nInput">n (integer ≥ 2)</label
|
|
98
|
-
><input class="short" id="nInput" type="number" min="2" step="1" value="67564329" /><button id="factorBtn">
|
|
99
|
-
Factor</button
|
|
100
|
-
><button id="clearBtn">Clear</button>
|
|
101
|
-
</div>
|
|
102
|
-
<div class="mono" id="status" style="color: #475569"></div>
|
|
103
|
-
</header>
|
|
104
|
-
|
|
105
|
-
<!-- 1) ANSWER -->
|
|
106
|
-
<section class="card stack" id="answer">
|
|
107
|
-
<h2 style="margin: 0">Answer</h2>
|
|
108
|
-
<div class="kpi">
|
|
109
|
-
<div class="card">
|
|
110
|
-
<div class="k">n</div>
|
|
111
|
-
<div id="a_n" class="mono">—</div>
|
|
112
|
-
</div>
|
|
113
|
-
<div class="card">
|
|
114
|
-
<div class="k">Prime factors</div>
|
|
115
|
-
<div id="a_pf" class="mono">—</div>
|
|
116
|
-
</div>
|
|
117
|
-
<div class="card">
|
|
118
|
-
<div class="k">Prime‑power form</div>
|
|
119
|
-
<div id="a_pp" class="mono">—</div>
|
|
120
|
-
</div>
|
|
121
|
-
<div class="card">
|
|
122
|
-
<div class="k">Check product</div>
|
|
123
|
-
<div id="a_prod" class="mono">—</div>
|
|
124
|
-
</div>
|
|
125
|
-
<div class="card">
|
|
126
|
-
<div class="k"># distinct primes</div>
|
|
127
|
-
<div id="a_cnt" class="mono">—</div>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</section>
|
|
131
|
-
|
|
132
|
-
<!-- 2) REASON WHY -->
|
|
133
|
-
<section class="card stack" id="reason">
|
|
134
|
-
<h2 style="margin: 0">Reason Why</h2>
|
|
135
|
-
<div class="mono">
|
|
136
|
-
(Existence) If <span class="mono">n≥2</span> is composite, write <span class="mono">n=ab</span> with
|
|
137
|
-
<span class="mono">a,b≥2</span>; repeat until primes — the process terminates. (Uniqueness) If
|
|
138
|
-
<span class="mono">n=p_1⋯p_r=q_1⋯q_s</span> with primes, then by Euclid’s lemma a prime dividing a product
|
|
139
|
-
divides one factor; match equal primes and cancel, showing the multisets are equal. Hence factorization is
|
|
140
|
-
unique up to order.
|
|
141
|
-
</div>
|
|
142
|
-
</section>
|
|
143
|
-
|
|
144
|
-
<!-- 3) CHECK (HARNESS) -->
|
|
145
|
-
<section class="card stack" id="check">
|
|
146
|
-
<h2 style="margin: 0">Check (harness)</h2>
|
|
147
|
-
<div id="checks" class="stack"></div>
|
|
148
|
-
<div class="row"><button id="runAll">Run all</button><button id="clearChecks">Clear results</button></div>
|
|
149
|
-
</section>
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
<script>
|
|
153
|
-
(function () {
|
|
154
|
-
function $(id) {
|
|
155
|
-
return document.getElementById(id);
|
|
156
|
-
}
|
|
157
|
-
function setStatus(msg) {
|
|
158
|
-
var s = $('status');
|
|
159
|
-
if (s) s.textContent = msg;
|
|
160
|
-
}
|
|
161
|
-
function isInteger(x) {
|
|
162
|
-
return typeof x === 'number' && isFinite(x) && Math.floor(x) === x;
|
|
163
|
-
}
|
|
164
|
-
function isPrime(n) {
|
|
165
|
-
if (n < 2) return false;
|
|
166
|
-
if (n % 2 === 0) return n === 2;
|
|
167
|
-
if (n % 3 === 0) return n === 3;
|
|
168
|
-
var i = 5,
|
|
169
|
-
step = 2;
|
|
170
|
-
while (i * i <= n) {
|
|
171
|
-
if (n % i === 0) return false;
|
|
172
|
-
i += step;
|
|
173
|
-
step = 6 - step;
|
|
174
|
-
}
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
function factor(n) {
|
|
178
|
-
var m = n;
|
|
179
|
-
var fac = [];
|
|
180
|
-
while (m % 2 === 0) {
|
|
181
|
-
fac.push(2);
|
|
182
|
-
m /= 2;
|
|
183
|
-
}
|
|
184
|
-
while (m % 3 === 0) {
|
|
185
|
-
fac.push(3);
|
|
186
|
-
m /= 3;
|
|
187
|
-
}
|
|
188
|
-
var f = 5,
|
|
189
|
-
step = 2; // 6k±1 wheel: 5,7,11,13,17,19,...
|
|
190
|
-
while (f * f <= m) {
|
|
191
|
-
while (m % f === 0) {
|
|
192
|
-
fac.push(f);
|
|
193
|
-
m /= f;
|
|
194
|
-
}
|
|
195
|
-
f += step;
|
|
196
|
-
step = 6 - step;
|
|
197
|
-
}
|
|
198
|
-
if (m > 1) fac.push(m);
|
|
199
|
-
return fac;
|
|
200
|
-
}
|
|
201
|
-
function product(list) {
|
|
202
|
-
var r = 1,
|
|
203
|
-
i;
|
|
204
|
-
for (i = 0; i < list.length; i++) r *= list[i];
|
|
205
|
-
return r;
|
|
206
|
-
}
|
|
207
|
-
function groupPowers(list) {
|
|
208
|
-
if (!list.length) return { pairs: [], text: '1' };
|
|
209
|
-
list = list.slice().sort(function (a, b) {
|
|
210
|
-
return a - b;
|
|
211
|
-
});
|
|
212
|
-
var pairs = [],
|
|
213
|
-
i = 0;
|
|
214
|
-
while (i < list.length) {
|
|
215
|
-
var p = list[i],
|
|
216
|
-
e = 1;
|
|
217
|
-
i++;
|
|
218
|
-
while (i < list.length && list[i] === p) {
|
|
219
|
-
e++;
|
|
220
|
-
i++;
|
|
221
|
-
}
|
|
222
|
-
pairs.push([p, e]);
|
|
223
|
-
}
|
|
224
|
-
var text = pairs
|
|
225
|
-
.map(function (pe) {
|
|
226
|
-
return pe[1] === 1 ? String(pe[0]) : pe[0] + '^' + pe[1];
|
|
227
|
-
})
|
|
228
|
-
.join(' · ');
|
|
229
|
-
return { pairs: pairs, text: text, list: list };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function showAnswer(n, fac) {
|
|
233
|
-
var grp = groupPowers(fac);
|
|
234
|
-
$('a_n').textContent = String(n);
|
|
235
|
-
$('a_pf').textContent = grp.list.length ? grp.list.join(' · ') : '—';
|
|
236
|
-
$('a_pp').textContent = grp.text;
|
|
237
|
-
$('a_cnt').textContent = String(grp.pairs.length);
|
|
238
|
-
$('a_prod').textContent = grp.list.length ? String(product(grp.list)) : '—';
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function row(ok, label, msg) {
|
|
242
|
-
var div = document.createElement('div');
|
|
243
|
-
div.className = 'row';
|
|
244
|
-
var b = document.createElement('span');
|
|
245
|
-
b.className = 'badge ' + (ok ? 'ok' : 'fail');
|
|
246
|
-
b.appendChild(document.createTextNode(ok ? 'PASS' : 'FAIL'));
|
|
247
|
-
var l = document.createElement('span');
|
|
248
|
-
l.className = 'mono';
|
|
249
|
-
l.appendChild(document.createTextNode(label));
|
|
250
|
-
var t = document.createElement('span');
|
|
251
|
-
t.className = 'mono';
|
|
252
|
-
t.appendChild(document.createTextNode(' — ' + msg));
|
|
253
|
-
div.appendChild(b);
|
|
254
|
-
div.appendChild(l);
|
|
255
|
-
div.appendChild(t);
|
|
256
|
-
return div;
|
|
257
|
-
}
|
|
258
|
-
function renderChecks(list) {
|
|
259
|
-
var c = $('checks');
|
|
260
|
-
while (c.firstChild) c.removeChild(c.firstChild);
|
|
261
|
-
for (var i = 0; i < list.length; i++) c.appendChild(list[i]);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function evaluate() {
|
|
265
|
-
try {
|
|
266
|
-
setStatus('');
|
|
267
|
-
var n = Number($('nInput').value);
|
|
268
|
-
if (!isInteger(n) || n < 2) {
|
|
269
|
-
showAnswer('—', []);
|
|
270
|
-
renderChecks([row(false, 'Input valid', 'n must be an integer ≥ 2')]);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
var fac = factor(n);
|
|
274
|
-
var grp = groupPowers(fac);
|
|
275
|
-
showAnswer(n, grp.list);
|
|
276
|
-
var checks = [];
|
|
277
|
-
checks.push(row(product(grp.list) === n, 'Recompose equals n', '∏ primes = n'));
|
|
278
|
-
checks.push(row(grp.list.every(isPrime), 'All factors are prime', 'each p_i is prime'));
|
|
279
|
-
var asc = grp.list.slice(1).every(function (v, i) {
|
|
280
|
-
return v >= grp.list[i];
|
|
281
|
-
});
|
|
282
|
-
checks.push(row(asc, 'Canonical order', 'nondecreasing list'));
|
|
283
|
-
var rebuilt = [],
|
|
284
|
-
i,
|
|
285
|
-
j;
|
|
286
|
-
for (i = 0; i < grp.pairs.length; i++) {
|
|
287
|
-
for (j = 0; j < grp.pairs[i][1]; j++) {
|
|
288
|
-
rebuilt.push(grp.pairs[i][0]);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
checks.push(
|
|
292
|
-
row(
|
|
293
|
-
JSON.stringify(rebuilt) === JSON.stringify(grp.list),
|
|
294
|
-
'Prime-power ↔ list agree',
|
|
295
|
-
'expand powers equals list',
|
|
296
|
-
),
|
|
297
|
-
);
|
|
298
|
-
var uniq =
|
|
299
|
-
JSON.stringify(grp.list) ===
|
|
300
|
-
JSON.stringify(
|
|
301
|
-
grp.list.slice().sort(function (a, b) {
|
|
302
|
-
return a - b;
|
|
303
|
-
}),
|
|
304
|
-
);
|
|
305
|
-
checks.push(row(uniq, 'Uniqueness (order-free)', 'multiset identity'));
|
|
306
|
-
renderChecks(checks);
|
|
307
|
-
} catch (e) {
|
|
308
|
-
setStatus('Error: ' + e.message);
|
|
309
|
-
showAnswer('—', []);
|
|
310
|
-
renderChecks([row(false, 'Runtime error', e.message)]);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function buildHarness() {
|
|
315
|
-
var H = [
|
|
316
|
-
{ n: 2, title: 'Prime: 2' },
|
|
317
|
-
{ n: 97, title: 'Prime: 97' },
|
|
318
|
-
{ n: 360, title: 'Composite: 360 = 2^3·3^2·5' },
|
|
319
|
-
{ n: 1024, title: 'Power of a prime: 2^10' },
|
|
320
|
-
{ n: 67564329, title: 'n = 67564329' },
|
|
321
|
-
];
|
|
322
|
-
var host = document.getElementById('check');
|
|
323
|
-
var list = document.createElement('div');
|
|
324
|
-
list.id = 'hlist';
|
|
325
|
-
list.className = 'stack';
|
|
326
|
-
host.appendChild(list);
|
|
327
|
-
function makeCard(h) {
|
|
328
|
-
var card = document.createElement('section');
|
|
329
|
-
card.className = 'card stack';
|
|
330
|
-
var head = document.createElement('div');
|
|
331
|
-
head.className = 'row';
|
|
332
|
-
head.appendChild(document.createElement('strong')).appendChild(document.createTextNode(h.title));
|
|
333
|
-
var kpi = document.createElement('div');
|
|
334
|
-
kpi.className = 'kpi';
|
|
335
|
-
var pf = document.createElement('div');
|
|
336
|
-
pf.className = 'card';
|
|
337
|
-
pf.appendChild(document.createTextNode('Prime factors: '));
|
|
338
|
-
var pfv = document.createElement('span');
|
|
339
|
-
pfv.className = 'mono';
|
|
340
|
-
pfv.appendChild(document.createTextNode('—'));
|
|
341
|
-
pf.appendChild(pfv);
|
|
342
|
-
kpi.appendChild(pf);
|
|
343
|
-
var btn = document.createElement('button');
|
|
344
|
-
btn.appendChild(document.createTextNode('Run'));
|
|
345
|
-
var checks = document.createElement('div');
|
|
346
|
-
checks.className = 'stack';
|
|
347
|
-
btn.onclick = function () {
|
|
348
|
-
var fac = factor(h.n);
|
|
349
|
-
var grp = groupPowers(fac);
|
|
350
|
-
pfv.textContent = grp.list.join(' · ');
|
|
351
|
-
var L = [];
|
|
352
|
-
L.push(row(product(grp.list) === h.n, 'Recompose equals n', '∏ primes = n'));
|
|
353
|
-
L.push(row(grp.list.every(isPrime), 'All factors are prime', 'each p_i is prime'));
|
|
354
|
-
L.push(
|
|
355
|
-
row(
|
|
356
|
-
grp.list.slice(1).every(function (v, i) {
|
|
357
|
-
return v >= grp.list[i];
|
|
358
|
-
}),
|
|
359
|
-
'Canonical order',
|
|
360
|
-
'nondecreasing list',
|
|
361
|
-
),
|
|
362
|
-
);
|
|
363
|
-
checks.innerHTML = '';
|
|
364
|
-
for (var i = 0; i < L.length; i++) checks.appendChild(L[i]);
|
|
365
|
-
};
|
|
366
|
-
card.appendChild(head);
|
|
367
|
-
card.appendChild(kpi);
|
|
368
|
-
card.appendChild(checks);
|
|
369
|
-
card.appendChild(btn);
|
|
370
|
-
return card;
|
|
371
|
-
}
|
|
372
|
-
for (var i = 0; i < H.length; i++) list.appendChild(makeCard(H[i]));
|
|
373
|
-
$('runAll').onclick = function () {
|
|
374
|
-
var btns = list.querySelectorAll('button');
|
|
375
|
-
for (var i = 0; i < btns.length; i++) btns[i].click();
|
|
376
|
-
};
|
|
377
|
-
$('clearChecks').onclick = function () {
|
|
378
|
-
var c = $('checks');
|
|
379
|
-
while (c.firstChild) c.removeChild(c.firstChild);
|
|
380
|
-
var vs = list.querySelectorAll('.mono');
|
|
381
|
-
for (var i = 0; i < vs.length; i++)
|
|
382
|
-
if (vs[i].parentNode && vs[i].parentNode.firstChild && vs[i].parentNode.firstChild.nodeType === 3)
|
|
383
|
-
vs[i].textContent = '—';
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
$('factorBtn').onclick = evaluate;
|
|
388
|
-
$('clearBtn').onclick = function () {
|
|
389
|
-
$('nInput').value = '67564329';
|
|
390
|
-
evaluate();
|
|
391
|
-
};
|
|
392
|
-
// First render
|
|
393
|
-
evaluate();
|
|
394
|
-
buildHarness();
|
|
395
|
-
})();
|
|
396
|
-
</script>
|
|
397
|
-
</body>
|
|
398
|
-
</html>
|