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,1046 +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>Faltings' Theorem — ARC Mobile Explorer</title>
|
|
7
|
-
<meta
|
|
8
|
-
name="description"
|
|
9
|
-
content="A mobile-first ARC-style interactive page about Faltings' theorem, with Answer, Reason, and Check sections." />
|
|
10
|
-
<style>
|
|
11
|
-
:root {
|
|
12
|
-
--bg: #f7fafc;
|
|
13
|
-
--panel: #ffffff;
|
|
14
|
-
--panel-soft: #f1f5f9;
|
|
15
|
-
--text: #102133;
|
|
16
|
-
--muted: #5a6d80;
|
|
17
|
-
--border: #d9e2ec;
|
|
18
|
-
--accent: #0ea5e9;
|
|
19
|
-
--accent-2: #7c3aed;
|
|
20
|
-
--good: #0f766e;
|
|
21
|
-
--good-bg: #ecfdf5;
|
|
22
|
-
--warn: #b45309;
|
|
23
|
-
--warn-bg: #fff7ed;
|
|
24
|
-
--bad: #b91c1c;
|
|
25
|
-
--shadow: 0 10px 28px rgba(15, 23, 42, 0.08);
|
|
26
|
-
}
|
|
27
|
-
* {
|
|
28
|
-
box-sizing: border-box;
|
|
29
|
-
}
|
|
30
|
-
html,
|
|
31
|
-
body {
|
|
32
|
-
height: 100%;
|
|
33
|
-
}
|
|
34
|
-
body {
|
|
35
|
-
margin: 0;
|
|
36
|
-
font:
|
|
37
|
-
16px/1.55 Inter,
|
|
38
|
-
ui-sans-serif,
|
|
39
|
-
system-ui,
|
|
40
|
-
-apple-system,
|
|
41
|
-
Segoe UI,
|
|
42
|
-
Roboto,
|
|
43
|
-
sans-serif;
|
|
44
|
-
color: var(--text);
|
|
45
|
-
background:
|
|
46
|
-
radial-gradient(900px 400px at 100% -10%, rgba(14, 165, 233, 0.1), transparent 60%),
|
|
47
|
-
radial-gradient(800px 400px at -10% 0%, rgba(124, 58, 237, 0.08), transparent 60%), var(--bg);
|
|
48
|
-
}
|
|
49
|
-
.wrap {
|
|
50
|
-
max-width: 860px;
|
|
51
|
-
margin: 0 auto;
|
|
52
|
-
padding: 16px 14px 72px;
|
|
53
|
-
}
|
|
54
|
-
.card {
|
|
55
|
-
background: var(--panel);
|
|
56
|
-
border: 1px solid var(--border);
|
|
57
|
-
border-radius: 24px;
|
|
58
|
-
box-shadow: var(--shadow);
|
|
59
|
-
}
|
|
60
|
-
header.hero {
|
|
61
|
-
padding: 24px 18px;
|
|
62
|
-
margin-top: 8px;
|
|
63
|
-
}
|
|
64
|
-
.kicker {
|
|
65
|
-
display: inline-block;
|
|
66
|
-
padding: 6px 10px;
|
|
67
|
-
border-radius: 999px;
|
|
68
|
-
background: #e0f2fe;
|
|
69
|
-
border: 1px solid #bae6fd;
|
|
70
|
-
color: #0369a1;
|
|
71
|
-
font-size: 12px;
|
|
72
|
-
letter-spacing: 0.09em;
|
|
73
|
-
text-transform: uppercase;
|
|
74
|
-
font-weight: 800;
|
|
75
|
-
}
|
|
76
|
-
h1 {
|
|
77
|
-
margin: 14px 0 10px;
|
|
78
|
-
font-size: clamp(30px, 7vw, 48px);
|
|
79
|
-
line-height: 1.02;
|
|
80
|
-
letter-spacing: -0.04em;
|
|
81
|
-
}
|
|
82
|
-
h2 {
|
|
83
|
-
margin: 0 0 8px;
|
|
84
|
-
font-size: clamp(24px, 5.2vw, 32px);
|
|
85
|
-
line-height: 1.1;
|
|
86
|
-
letter-spacing: -0.03em;
|
|
87
|
-
}
|
|
88
|
-
h3 {
|
|
89
|
-
margin: 0 0 8px;
|
|
90
|
-
font-size: 20px;
|
|
91
|
-
letter-spacing: -0.02em;
|
|
92
|
-
}
|
|
93
|
-
p {
|
|
94
|
-
margin: 0 0 12px;
|
|
95
|
-
}
|
|
96
|
-
.lead {
|
|
97
|
-
font-size: 18px;
|
|
98
|
-
color: var(--muted);
|
|
99
|
-
max-width: 38ch;
|
|
100
|
-
}
|
|
101
|
-
.formula {
|
|
102
|
-
margin-top: 16px;
|
|
103
|
-
padding: 14px 16px;
|
|
104
|
-
border-radius: 18px;
|
|
105
|
-
background: #f8fbff;
|
|
106
|
-
border: 1px solid #dbeafe;
|
|
107
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
108
|
-
color: #0c4a6e;
|
|
109
|
-
overflow: auto;
|
|
110
|
-
}
|
|
111
|
-
.actions {
|
|
112
|
-
display: flex;
|
|
113
|
-
gap: 10px;
|
|
114
|
-
flex-wrap: wrap;
|
|
115
|
-
margin-top: 18px;
|
|
116
|
-
}
|
|
117
|
-
button {
|
|
118
|
-
appearance: none;
|
|
119
|
-
border: none;
|
|
120
|
-
cursor: pointer;
|
|
121
|
-
border-radius: 14px;
|
|
122
|
-
padding: 12px 14px;
|
|
123
|
-
font: inherit;
|
|
124
|
-
font-weight: 800;
|
|
125
|
-
background: var(--accent);
|
|
126
|
-
color: white;
|
|
127
|
-
box-shadow: 0 10px 20px rgba(14, 165, 233, 0.18);
|
|
128
|
-
}
|
|
129
|
-
button.secondary {
|
|
130
|
-
background: white;
|
|
131
|
-
color: var(--accent);
|
|
132
|
-
border: 1px solid #cceaf9;
|
|
133
|
-
box-shadow: none;
|
|
134
|
-
}
|
|
135
|
-
.section {
|
|
136
|
-
margin-top: 16px;
|
|
137
|
-
padding: 18px;
|
|
138
|
-
}
|
|
139
|
-
.section-head {
|
|
140
|
-
display: flex;
|
|
141
|
-
align-items: center;
|
|
142
|
-
gap: 10px;
|
|
143
|
-
margin-bottom: 12px;
|
|
144
|
-
}
|
|
145
|
-
.badge {
|
|
146
|
-
flex: 0 0 auto;
|
|
147
|
-
display: inline-flex;
|
|
148
|
-
align-items: center;
|
|
149
|
-
justify-content: center;
|
|
150
|
-
width: 34px;
|
|
151
|
-
height: 34px;
|
|
152
|
-
border-radius: 50%;
|
|
153
|
-
font-weight: 900;
|
|
154
|
-
font-size: 14px;
|
|
155
|
-
background: #e0f2fe;
|
|
156
|
-
color: #0369a1;
|
|
157
|
-
border: 1px solid #bae6fd;
|
|
158
|
-
}
|
|
159
|
-
.sub {
|
|
160
|
-
color: var(--muted);
|
|
161
|
-
font-size: 14px;
|
|
162
|
-
}
|
|
163
|
-
.stack {
|
|
164
|
-
display: grid;
|
|
165
|
-
gap: 12px;
|
|
166
|
-
}
|
|
167
|
-
.metrics {
|
|
168
|
-
display: grid;
|
|
169
|
-
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
170
|
-
gap: 10px;
|
|
171
|
-
margin-top: 12px;
|
|
172
|
-
}
|
|
173
|
-
.metric {
|
|
174
|
-
background: var(--panel-soft);
|
|
175
|
-
border: 1px solid var(--border);
|
|
176
|
-
border-radius: 18px;
|
|
177
|
-
padding: 12px 14px;
|
|
178
|
-
}
|
|
179
|
-
.metric .k {
|
|
180
|
-
font-size: 12px;
|
|
181
|
-
text-transform: uppercase;
|
|
182
|
-
letter-spacing: 0.07em;
|
|
183
|
-
color: var(--muted);
|
|
184
|
-
}
|
|
185
|
-
.metric .v {
|
|
186
|
-
font-size: 21px;
|
|
187
|
-
font-weight: 900;
|
|
188
|
-
margin-top: 4px;
|
|
189
|
-
}
|
|
190
|
-
.pill-row {
|
|
191
|
-
display: flex;
|
|
192
|
-
gap: 8px;
|
|
193
|
-
flex-wrap: wrap;
|
|
194
|
-
margin-top: 8px;
|
|
195
|
-
}
|
|
196
|
-
.pill {
|
|
197
|
-
display: inline-flex;
|
|
198
|
-
align-items: center;
|
|
199
|
-
gap: 8px;
|
|
200
|
-
padding: 7px 10px;
|
|
201
|
-
border-radius: 999px;
|
|
202
|
-
font-size: 13px;
|
|
203
|
-
background: #eff6ff;
|
|
204
|
-
border: 1px solid #dbeafe;
|
|
205
|
-
color: #1d4ed8;
|
|
206
|
-
}
|
|
207
|
-
.notice {
|
|
208
|
-
margin-top: 14px;
|
|
209
|
-
padding: 12px 14px;
|
|
210
|
-
border-radius: 16px;
|
|
211
|
-
background: var(--warn-bg);
|
|
212
|
-
border: 1px solid #fed7aa;
|
|
213
|
-
color: var(--warn);
|
|
214
|
-
}
|
|
215
|
-
.grid-1 {
|
|
216
|
-
display: grid;
|
|
217
|
-
gap: 12px;
|
|
218
|
-
}
|
|
219
|
-
canvas {
|
|
220
|
-
display: block;
|
|
221
|
-
width: 100%;
|
|
222
|
-
border-radius: 18px;
|
|
223
|
-
border: 1px solid var(--border);
|
|
224
|
-
background: linear-gradient(180deg, #ffffff, #f8fbff);
|
|
225
|
-
}
|
|
226
|
-
.control-grid {
|
|
227
|
-
display: grid;
|
|
228
|
-
gap: 10px;
|
|
229
|
-
margin-top: 12px;
|
|
230
|
-
}
|
|
231
|
-
.control {
|
|
232
|
-
background: var(--panel-soft);
|
|
233
|
-
border: 1px solid var(--border);
|
|
234
|
-
border-radius: 16px;
|
|
235
|
-
padding: 12px 14px;
|
|
236
|
-
}
|
|
237
|
-
.control label {
|
|
238
|
-
display: block;
|
|
239
|
-
font-size: 12px;
|
|
240
|
-
text-transform: uppercase;
|
|
241
|
-
letter-spacing: 0.07em;
|
|
242
|
-
color: var(--muted);
|
|
243
|
-
font-weight: 800;
|
|
244
|
-
margin-bottom: 8px;
|
|
245
|
-
}
|
|
246
|
-
.range-value {
|
|
247
|
-
font-size: 24px;
|
|
248
|
-
font-weight: 900;
|
|
249
|
-
}
|
|
250
|
-
input[type='range'] {
|
|
251
|
-
width: 100%;
|
|
252
|
-
}
|
|
253
|
-
.mini {
|
|
254
|
-
font-size: 13px;
|
|
255
|
-
color: var(--muted);
|
|
256
|
-
}
|
|
257
|
-
.curve-card {
|
|
258
|
-
padding: 14px;
|
|
259
|
-
border-radius: 18px;
|
|
260
|
-
background: #fcfdff;
|
|
261
|
-
border: 1px solid var(--border);
|
|
262
|
-
}
|
|
263
|
-
.legend {
|
|
264
|
-
display: flex;
|
|
265
|
-
gap: 12px;
|
|
266
|
-
flex-wrap: wrap;
|
|
267
|
-
font-size: 13px;
|
|
268
|
-
color: var(--muted);
|
|
269
|
-
margin-top: 8px;
|
|
270
|
-
}
|
|
271
|
-
.legend span::before {
|
|
272
|
-
content: '';
|
|
273
|
-
display: inline-block;
|
|
274
|
-
width: 10px;
|
|
275
|
-
height: 10px;
|
|
276
|
-
border-radius: 50%;
|
|
277
|
-
margin-right: 6px;
|
|
278
|
-
vertical-align: middle;
|
|
279
|
-
}
|
|
280
|
-
.legend .real::before {
|
|
281
|
-
background: #06b6d4;
|
|
282
|
-
}
|
|
283
|
-
.legend .rat::before {
|
|
284
|
-
background: #ec4899;
|
|
285
|
-
}
|
|
286
|
-
.legend .multi::before {
|
|
287
|
-
background: #3b82f6;
|
|
288
|
-
}
|
|
289
|
-
.table-wrap {
|
|
290
|
-
overflow: auto;
|
|
291
|
-
margin-top: 10px;
|
|
292
|
-
}
|
|
293
|
-
table {
|
|
294
|
-
width: 100%;
|
|
295
|
-
border-collapse: collapse;
|
|
296
|
-
}
|
|
297
|
-
th,
|
|
298
|
-
td {
|
|
299
|
-
padding: 9px 8px;
|
|
300
|
-
border-bottom: 1px dashed var(--border);
|
|
301
|
-
text-align: left;
|
|
302
|
-
vertical-align: top;
|
|
303
|
-
}
|
|
304
|
-
th {
|
|
305
|
-
font-size: 12px;
|
|
306
|
-
text-transform: uppercase;
|
|
307
|
-
letter-spacing: 0.07em;
|
|
308
|
-
color: var(--muted);
|
|
309
|
-
}
|
|
310
|
-
.code {
|
|
311
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
312
|
-
word-break: break-word;
|
|
313
|
-
}
|
|
314
|
-
.status {
|
|
315
|
-
display: inline-flex;
|
|
316
|
-
align-items: center;
|
|
317
|
-
gap: 8px;
|
|
318
|
-
padding: 6px 10px;
|
|
319
|
-
border-radius: 999px;
|
|
320
|
-
font-size: 12px;
|
|
321
|
-
font-weight: 900;
|
|
322
|
-
}
|
|
323
|
-
.pass {
|
|
324
|
-
background: var(--good-bg);
|
|
325
|
-
color: var(--good);
|
|
326
|
-
border: 1px solid #bbf7d0;
|
|
327
|
-
}
|
|
328
|
-
.fail {
|
|
329
|
-
background: #fef2f2;
|
|
330
|
-
color: var(--bad);
|
|
331
|
-
border: 1px solid #fecaca;
|
|
332
|
-
}
|
|
333
|
-
.footer-links {
|
|
334
|
-
display: flex;
|
|
335
|
-
gap: 10px;
|
|
336
|
-
flex-wrap: wrap;
|
|
337
|
-
margin-top: 12px;
|
|
338
|
-
}
|
|
339
|
-
.footer-links a {
|
|
340
|
-
text-decoration: none;
|
|
341
|
-
color: #0369a1;
|
|
342
|
-
background: #f8fbff;
|
|
343
|
-
border: 1px solid #dbeafe;
|
|
344
|
-
padding: 8px 10px;
|
|
345
|
-
border-radius: 999px;
|
|
346
|
-
}
|
|
347
|
-
@media (min-width: 740px) {
|
|
348
|
-
.metrics {
|
|
349
|
-
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
350
|
-
}
|
|
351
|
-
.control-grid {
|
|
352
|
-
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
</style>
|
|
356
|
-
</head>
|
|
357
|
-
<body>
|
|
358
|
-
<div class="wrap">
|
|
359
|
-
<header class="card hero">
|
|
360
|
-
<span class="kicker">ARC layout · mobile first · interactive math</span>
|
|
361
|
-
<h1>Faltings' theorem</h1>
|
|
362
|
-
<p class="lead">
|
|
363
|
-
A compact browser-based explainer built around three ARC parts:
|
|
364
|
-
<strong>Answer</strong>, <strong>Reason</strong>, and <strong>Check</strong>.
|
|
365
|
-
</p>
|
|
366
|
-
<div class="formula">
|
|
367
|
-
Informal statement:<br />
|
|
368
|
-
If a smooth projective curve has genus g ≥ 2 and is defined over a number field, then it has only finitely
|
|
369
|
-
many rational points.
|
|
370
|
-
</div>
|
|
371
|
-
<div class="actions">
|
|
372
|
-
<button onclick="document.getElementById('answer').scrollIntoView({ behavior: 'smooth' })">Start</button>
|
|
373
|
-
<button class="secondary" onclick="runAll()">Recompute</button>
|
|
374
|
-
</div>
|
|
375
|
-
</header>
|
|
376
|
-
|
|
377
|
-
<section class="card section" id="answer">
|
|
378
|
-
<div class="section-head">
|
|
379
|
-
<span class="badge">1</span>
|
|
380
|
-
<div>
|
|
381
|
-
<h2>Answer</h2>
|
|
382
|
-
<div class="sub">What the page is claiming</div>
|
|
383
|
-
</div>
|
|
384
|
-
</div>
|
|
385
|
-
|
|
386
|
-
<p>
|
|
387
|
-
The curve
|
|
388
|
-
<span class="code">y² = x(x+1)(x−2)(x+2)(x−3)</span>
|
|
389
|
-
is a genus‑2 curve. In the setting of Faltings' theorem, that places it in the regime where the set of
|
|
390
|
-
rational points is <strong>finite</strong>.
|
|
391
|
-
</p>
|
|
392
|
-
|
|
393
|
-
<div class="metrics">
|
|
394
|
-
<div class="metric">
|
|
395
|
-
<div class="k">Example curve</div>
|
|
396
|
-
<div class="v code">genus 2</div>
|
|
397
|
-
</div>
|
|
398
|
-
<div class="metric">
|
|
399
|
-
<div class="k">Field in view</div>
|
|
400
|
-
<div class="v code">Q</div>
|
|
401
|
-
</div>
|
|
402
|
-
<div class="metric">
|
|
403
|
-
<div class="k">Theorem says</div>
|
|
404
|
-
<div class="v">finite</div>
|
|
405
|
-
</div>
|
|
406
|
-
<div class="metric">
|
|
407
|
-
<div class="k">Obvious rational points</div>
|
|
408
|
-
<div class="v" id="obviousCount">5</div>
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
|
-
|
|
412
|
-
<div class="pill-row">
|
|
413
|
-
<span class="pill">genus 0: often infinite once parameterised</span>
|
|
414
|
-
<span class="pill">genus 1: finite or infinite, with group law</span>
|
|
415
|
-
<span class="pill">genus 2+: finite by theorem</span>
|
|
416
|
-
</div>
|
|
417
|
-
|
|
418
|
-
<div class="notice">
|
|
419
|
-
This page uses computation to make the contrast believable, but the decisive conclusion in the genus‑2 case
|
|
420
|
-
comes from the theorem itself, not from a finite search.
|
|
421
|
-
</div>
|
|
422
|
-
</section>
|
|
423
|
-
|
|
424
|
-
<section class="card section" id="reason">
|
|
425
|
-
<div class="section-head">
|
|
426
|
-
<span class="badge">2</span>
|
|
427
|
-
<div>
|
|
428
|
-
<h2>Reason</h2>
|
|
429
|
-
<div class="sub">Why the theorem feels plausible when you compare the three regimes</div>
|
|
430
|
-
</div>
|
|
431
|
-
</div>
|
|
432
|
-
|
|
433
|
-
<div class="control-grid">
|
|
434
|
-
<div class="control">
|
|
435
|
-
<label for="bound">Denominator bound</label>
|
|
436
|
-
<div class="range-value" id="boundValue">8</div>
|
|
437
|
-
<input id="bound" type="range" min="2" max="16" step="1" value="8" />
|
|
438
|
-
</div>
|
|
439
|
-
<div class="control">
|
|
440
|
-
<label for="xwin">x-window for search</label>
|
|
441
|
-
<div class="range-value" id="xwinValue">12</div>
|
|
442
|
-
<input id="xwin" type="range" min="4" max="30" step="1" value="12" />
|
|
443
|
-
</div>
|
|
444
|
-
<div class="control">
|
|
445
|
-
<label for="multiN">Multiples on genus 1</label>
|
|
446
|
-
<div class="range-value" id="multiValue">8</div>
|
|
447
|
-
<input id="multiN" type="range" min="2" max="14" step="1" value="8" />
|
|
448
|
-
</div>
|
|
449
|
-
</div>
|
|
450
|
-
|
|
451
|
-
<div class="stack" style="margin-top: 14px">
|
|
452
|
-
<div class="curve-card">
|
|
453
|
-
<h3>Genus 0 — the unit circle</h3>
|
|
454
|
-
<p class="mini">
|
|
455
|
-
Curve: <span class="code">x² + y² = 1</span>. Once a rational point is known, there is a rational
|
|
456
|
-
parametrisation.
|
|
457
|
-
</p>
|
|
458
|
-
<canvas id="plot0" width="760" height="330"></canvas>
|
|
459
|
-
<div class="legend"><span class="real">real curve</span><span class="rat">rational points found</span></div>
|
|
460
|
-
<div class="metrics">
|
|
461
|
-
<div class="metric">
|
|
462
|
-
<div class="k">Found at current bound</div>
|
|
463
|
-
<div class="v" id="count0">–</div>
|
|
464
|
-
</div>
|
|
465
|
-
<div class="metric">
|
|
466
|
-
<div class="k">Typical behaviour</div>
|
|
467
|
-
<div class="v">many</div>
|
|
468
|
-
</div>
|
|
469
|
-
</div>
|
|
470
|
-
<div class="mini" id="sample0"></div>
|
|
471
|
-
</div>
|
|
472
|
-
|
|
473
|
-
<div class="curve-card">
|
|
474
|
-
<h3>Genus 1 — an elliptic curve</h3>
|
|
475
|
-
<p class="mini">
|
|
476
|
-
Curve: <span class="code">y² = x³ − 16x + 16</span>. This sits in the elliptic-curve world, where rational
|
|
477
|
-
points form a finitely generated abelian group.
|
|
478
|
-
</p>
|
|
479
|
-
<canvas id="plot1" width="760" height="330"></canvas>
|
|
480
|
-
<div class="legend">
|
|
481
|
-
<span class="real">real curve</span><span class="rat">rational points found</span
|
|
482
|
-
><span class="multi">exact multiples of P=(0,4)</span>
|
|
483
|
-
</div>
|
|
484
|
-
<div class="metrics">
|
|
485
|
-
<div class="metric">
|
|
486
|
-
<div class="k">Found at current bound</div>
|
|
487
|
-
<div class="v" id="count1">–</div>
|
|
488
|
-
</div>
|
|
489
|
-
<div class="metric">
|
|
490
|
-
<div class="k">Exact multiples shown</div>
|
|
491
|
-
<div class="v" id="multiCount">–</div>
|
|
492
|
-
</div>
|
|
493
|
-
</div>
|
|
494
|
-
<div class="mini" id="sample1"></div>
|
|
495
|
-
<div class="table-wrap">
|
|
496
|
-
<table>
|
|
497
|
-
<thead>
|
|
498
|
-
<tr>
|
|
499
|
-
<th>n</th>
|
|
500
|
-
<th>nP</th>
|
|
501
|
-
<th>Approximation</th>
|
|
502
|
-
</tr>
|
|
503
|
-
</thead>
|
|
504
|
-
<tbody id="multiTable"></tbody>
|
|
505
|
-
</table>
|
|
506
|
-
</div>
|
|
507
|
-
</div>
|
|
508
|
-
|
|
509
|
-
<div class="curve-card">
|
|
510
|
-
<h3>Genus 2 — the Faltings example</h3>
|
|
511
|
-
<p class="mini">
|
|
512
|
-
Curve: <span class="code">y² = x(x+1)(x−2)(x+2)(x−3)</span>. This is the regime where Faltings' theorem
|
|
513
|
-
applies.
|
|
514
|
-
</p>
|
|
515
|
-
<canvas id="plot2" width="760" height="330"></canvas>
|
|
516
|
-
<div class="legend"><span class="real">real curve</span><span class="rat">rational points found</span></div>
|
|
517
|
-
<div class="metrics">
|
|
518
|
-
<div class="metric">
|
|
519
|
-
<div class="k">Found at current bound</div>
|
|
520
|
-
<div class="v" id="count2">–</div>
|
|
521
|
-
</div>
|
|
522
|
-
<div class="metric">
|
|
523
|
-
<div class="k">Theorem-level conclusion</div>
|
|
524
|
-
<div class="v">finite</div>
|
|
525
|
-
</div>
|
|
526
|
-
</div>
|
|
527
|
-
<div class="mini" id="sample2"></div>
|
|
528
|
-
</div>
|
|
529
|
-
|
|
530
|
-
<div class="curve-card">
|
|
531
|
-
<h3>Growth comparison</h3>
|
|
532
|
-
<p class="mini">
|
|
533
|
-
This chart compares the number of rational points the browser finds as the denominator bound increases.
|
|
534
|
-
</p>
|
|
535
|
-
<canvas id="growthChart" width="760" height="300"></canvas>
|
|
536
|
-
<div class="mini" id="growthCaption"></div>
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
</section>
|
|
540
|
-
|
|
541
|
-
<section class="card section" id="check">
|
|
542
|
-
<div class="section-head">
|
|
543
|
-
<span class="badge">3</span>
|
|
544
|
-
<div>
|
|
545
|
-
<h2>Check</h2>
|
|
546
|
-
<div class="sub">Concrete browser-side verification</div>
|
|
547
|
-
</div>
|
|
548
|
-
</div>
|
|
549
|
-
|
|
550
|
-
<div class="stack">
|
|
551
|
-
<div class="curve-card">
|
|
552
|
-
<h3>Check A — do the obvious points satisfy the genus‑2 equation?</h3>
|
|
553
|
-
<div class="table-wrap">
|
|
554
|
-
<table>
|
|
555
|
-
<thead>
|
|
556
|
-
<tr>
|
|
557
|
-
<th>Point</th>
|
|
558
|
-
<th>y²</th>
|
|
559
|
-
<th>x(x+1)(x−2)(x+2)(x−3)</th>
|
|
560
|
-
<th>Result</th>
|
|
561
|
-
</tr>
|
|
562
|
-
</thead>
|
|
563
|
-
<tbody id="checkTable"></tbody>
|
|
564
|
-
</table>
|
|
565
|
-
</div>
|
|
566
|
-
</div>
|
|
567
|
-
|
|
568
|
-
<div class="curve-card">
|
|
569
|
-
<h3>Check B — theorem premises in plain language</h3>
|
|
570
|
-
<div class="pill-row" id="premisePills"></div>
|
|
571
|
-
</div>
|
|
572
|
-
|
|
573
|
-
<div class="curve-card">
|
|
574
|
-
<h3>Check C — what the bounded search is and is not doing</h3>
|
|
575
|
-
<p id="checkSummary"></p>
|
|
576
|
-
<div class="notice">
|
|
577
|
-
The search box can support the story, but it cannot replace the theorem. A finite search never proves
|
|
578
|
-
finiteness; it only checks sample behaviour inside a chosen window.
|
|
579
|
-
</div>
|
|
580
|
-
</div>
|
|
581
|
-
</div>
|
|
582
|
-
|
|
583
|
-
<div class="footer-links">
|
|
584
|
-
<a href="https://abelprize.no/sites/default/files/2026-03/Faltings%E2%80%99%20theorem.pdf"
|
|
585
|
-
>Abel Prize explainer</a
|
|
586
|
-
>
|
|
587
|
-
<a href="https://www.lmfdb.org/EllipticCurve/Q/37/a/1">LMFDB elliptic curve 37.a1</a>
|
|
588
|
-
</div>
|
|
589
|
-
</section>
|
|
590
|
-
</div>
|
|
591
|
-
|
|
592
|
-
<script>
|
|
593
|
-
'use strict';
|
|
594
|
-
|
|
595
|
-
/* ---------- exact rational arithmetic ---------- */
|
|
596
|
-
function bigAbs(n) {
|
|
597
|
-
return n < 0n ? -n : n;
|
|
598
|
-
}
|
|
599
|
-
function bigGcd(a, b) {
|
|
600
|
-
a = bigAbs(a);
|
|
601
|
-
b = bigAbs(b);
|
|
602
|
-
while (b !== 0n) {
|
|
603
|
-
const t = a % b;
|
|
604
|
-
a = b;
|
|
605
|
-
b = t;
|
|
606
|
-
}
|
|
607
|
-
return a;
|
|
608
|
-
}
|
|
609
|
-
class Frac {
|
|
610
|
-
constructor(n, d = 1n) {
|
|
611
|
-
if (typeof n === 'number') n = BigInt(n);
|
|
612
|
-
if (typeof d === 'number') d = BigInt(d);
|
|
613
|
-
if (d === 0n) throw new Error('zero denominator');
|
|
614
|
-
if (d < 0n) {
|
|
615
|
-
n = -n;
|
|
616
|
-
d = -d;
|
|
617
|
-
}
|
|
618
|
-
const g = bigGcd(n, d);
|
|
619
|
-
this.n = n / g;
|
|
620
|
-
this.d = d / g;
|
|
621
|
-
}
|
|
622
|
-
static fromInt(x) {
|
|
623
|
-
return new Frac(BigInt(x), 1n);
|
|
624
|
-
}
|
|
625
|
-
add(o) {
|
|
626
|
-
return new Frac(this.n * o.d + o.n * this.d, this.d * o.d);
|
|
627
|
-
}
|
|
628
|
-
sub(o) {
|
|
629
|
-
return new Frac(this.n * o.d - o.n * this.d, this.d * o.d);
|
|
630
|
-
}
|
|
631
|
-
mul(o) {
|
|
632
|
-
return new Frac(this.n * o.n, this.d * o.d);
|
|
633
|
-
}
|
|
634
|
-
div(o) {
|
|
635
|
-
return new Frac(this.n * o.d, this.d * o.n);
|
|
636
|
-
}
|
|
637
|
-
neg() {
|
|
638
|
-
return new Frac(-this.n, this.d);
|
|
639
|
-
}
|
|
640
|
-
eq(o) {
|
|
641
|
-
return this.n === o.n && this.d === o.d;
|
|
642
|
-
}
|
|
643
|
-
toNumber() {
|
|
644
|
-
return Number(this.n) / Number(this.d);
|
|
645
|
-
}
|
|
646
|
-
toString() {
|
|
647
|
-
return this.d === 1n ? this.n.toString() : `${this.n}/${this.d}`;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
const A = new Frac(-16n),
|
|
651
|
-
B = new Frac(16n);
|
|
652
|
-
|
|
653
|
-
function pointInfinity() {
|
|
654
|
-
return { inf: true };
|
|
655
|
-
}
|
|
656
|
-
function isInfinity(P) {
|
|
657
|
-
return P && P.inf;
|
|
658
|
-
}
|
|
659
|
-
function makePoint(x, y) {
|
|
660
|
-
return { x, y, inf: false };
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function addPoints(P, Q) {
|
|
664
|
-
if (isInfinity(P)) return Q;
|
|
665
|
-
if (isInfinity(Q)) return P;
|
|
666
|
-
if (P.x.eq(Q.x) && P.y.eq(Q.y.neg())) return pointInfinity();
|
|
667
|
-
|
|
668
|
-
let m;
|
|
669
|
-
if (P.x.eq(Q.x) && P.y.eq(Q.y)) {
|
|
670
|
-
if (P.y.n === 0n) return pointInfinity();
|
|
671
|
-
const num = P.x.mul(P.x).mul(Frac.fromInt(3)).add(A);
|
|
672
|
-
const den = P.y.mul(Frac.fromInt(2));
|
|
673
|
-
m = num.div(den);
|
|
674
|
-
} else {
|
|
675
|
-
m = Q.y.sub(P.y).div(Q.x.sub(P.x));
|
|
676
|
-
}
|
|
677
|
-
const xr = m.mul(m).sub(P.x).sub(Q.x);
|
|
678
|
-
const yr = m.mul(P.x.sub(xr)).sub(P.y);
|
|
679
|
-
return makePoint(xr, yr);
|
|
680
|
-
}
|
|
681
|
-
function multiplyPoint(P, n) {
|
|
682
|
-
let k = BigInt(n);
|
|
683
|
-
let result = pointInfinity();
|
|
684
|
-
let base = P;
|
|
685
|
-
while (k > 0n) {
|
|
686
|
-
if (k & 1n) result = addPoints(result, base);
|
|
687
|
-
base = addPoints(base, base);
|
|
688
|
-
k >>= 1n;
|
|
689
|
-
}
|
|
690
|
-
return result;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
/* ---------- number helpers ---------- */
|
|
694
|
-
function isqrt(n) {
|
|
695
|
-
if (n < 0n) throw new Error('negative');
|
|
696
|
-
if (n < 2n) return n;
|
|
697
|
-
let x = n,
|
|
698
|
-
y = (x + 1n) >> 1n;
|
|
699
|
-
while (y < x) {
|
|
700
|
-
x = y;
|
|
701
|
-
y = (x + n / x) >> 1n;
|
|
702
|
-
}
|
|
703
|
-
return x;
|
|
704
|
-
}
|
|
705
|
-
function isPerfectSquare(n) {
|
|
706
|
-
if (n < 0n) return false;
|
|
707
|
-
const r = isqrt(n);
|
|
708
|
-
return r * r === n;
|
|
709
|
-
}
|
|
710
|
-
function squareRootsFrac(f) {
|
|
711
|
-
if (f.n < 0n) return null;
|
|
712
|
-
if (!isPerfectSquare(f.n) || !isPerfectSquare(f.d)) return null;
|
|
713
|
-
const rn = isqrt(f.n),
|
|
714
|
-
rd = isqrt(f.d);
|
|
715
|
-
return [new Frac(rn, rd), new Frac(-rn, rd)];
|
|
716
|
-
}
|
|
717
|
-
function fracKey(f) {
|
|
718
|
-
return `${f.n}/${f.d}`;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/* ---------- rational searches ---------- */
|
|
722
|
-
function rhsCircle(x) {
|
|
723
|
-
return new Frac(x.d * x.d - x.n * x.n, x.d * x.d);
|
|
724
|
-
}
|
|
725
|
-
function rhsElliptic(x) {
|
|
726
|
-
return x.mul(x).mul(x).add(A.mul(x)).add(B);
|
|
727
|
-
}
|
|
728
|
-
function rhsGenus2(x) {
|
|
729
|
-
return x
|
|
730
|
-
.mul(x.add(Frac.fromInt(1)))
|
|
731
|
-
.mul(x.sub(Frac.fromInt(2)))
|
|
732
|
-
.mul(x.add(Frac.fromInt(2)))
|
|
733
|
-
.mul(x.sub(Frac.fromInt(3)));
|
|
734
|
-
}
|
|
735
|
-
function reducedRationals(bound, xWindow) {
|
|
736
|
-
const out = [];
|
|
737
|
-
for (let q = 1; q <= bound; q++) {
|
|
738
|
-
const bq = BigInt(q);
|
|
739
|
-
for (let p = -xWindow * q; p <= xWindow * q; p++) {
|
|
740
|
-
const bp = BigInt(p);
|
|
741
|
-
if (bigGcd(bigAbs(bp), bq) !== 1n) continue;
|
|
742
|
-
out.push(new Frac(bp, bq));
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
return out;
|
|
746
|
-
}
|
|
747
|
-
function searchRationalPoints(kind, bound, xWindow) {
|
|
748
|
-
const xs = kind === 'circle' ? reducedRationals(bound, 1) : reducedRationals(bound, xWindow);
|
|
749
|
-
const map = new Map();
|
|
750
|
-
const rhsFn = kind === 'circle' ? rhsCircle : kind === 'elliptic' ? rhsElliptic : rhsGenus2;
|
|
751
|
-
for (const x of xs) {
|
|
752
|
-
const roots = squareRootsFrac(rhsFn(x));
|
|
753
|
-
if (!roots) continue;
|
|
754
|
-
for (const y of roots) {
|
|
755
|
-
map.set(`${fracKey(x)}|${fracKey(y)}`, makePoint(x, y));
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return Array.from(map.values()).sort((a, b) => {
|
|
759
|
-
const ax = a.x.toNumber(),
|
|
760
|
-
bx = b.x.toNumber();
|
|
761
|
-
if (ax !== bx) return ax - bx;
|
|
762
|
-
return a.y.toNumber() - b.y.toNumber();
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
/* ---------- plotting ---------- */
|
|
767
|
-
function clearCanvas(ctx, w, h) {
|
|
768
|
-
ctx.clearRect(0, 0, w, h);
|
|
769
|
-
const g = ctx.createLinearGradient(0, 0, 0, h);
|
|
770
|
-
g.addColorStop(0, '#ffffff');
|
|
771
|
-
g.addColorStop(1, '#f8fbff');
|
|
772
|
-
ctx.fillStyle = g;
|
|
773
|
-
ctx.fillRect(0, 0, w, h);
|
|
774
|
-
}
|
|
775
|
-
function makeMap(w, h, xmin, xmax, ymin, ymax) {
|
|
776
|
-
const pad = 20;
|
|
777
|
-
return {
|
|
778
|
-
x: (x) => pad + ((x - xmin) * (w - 2 * pad)) / (xmax - xmin),
|
|
779
|
-
y: (y) => h - pad - ((y - ymin) * (h - 2 * pad)) / (ymax - ymin),
|
|
780
|
-
xmin,
|
|
781
|
-
xmax,
|
|
782
|
-
ymin,
|
|
783
|
-
ymax,
|
|
784
|
-
pad,
|
|
785
|
-
w,
|
|
786
|
-
h,
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
function drawAxes(ctx, M) {
|
|
790
|
-
ctx.save();
|
|
791
|
-
ctx.strokeStyle = '#dbe6ef';
|
|
792
|
-
ctx.lineWidth = 1;
|
|
793
|
-
ctx.beginPath();
|
|
794
|
-
if (M.xmin <= 0 && 0 <= M.xmax) {
|
|
795
|
-
const X = M.x(0);
|
|
796
|
-
ctx.moveTo(X, M.pad);
|
|
797
|
-
ctx.lineTo(X, M.h - M.pad);
|
|
798
|
-
}
|
|
799
|
-
if (M.ymin <= 0 && 0 <= M.ymax) {
|
|
800
|
-
const Y = M.y(0);
|
|
801
|
-
ctx.moveTo(M.pad, Y);
|
|
802
|
-
ctx.lineTo(M.w - M.pad, Y);
|
|
803
|
-
}
|
|
804
|
-
ctx.stroke();
|
|
805
|
-
ctx.restore();
|
|
806
|
-
}
|
|
807
|
-
function drawCurveSamples(ctx, M, rhs, color) {
|
|
808
|
-
ctx.save();
|
|
809
|
-
ctx.strokeStyle = color;
|
|
810
|
-
ctx.lineWidth = 2.2;
|
|
811
|
-
for (const s of [1, -1]) {
|
|
812
|
-
let started = false;
|
|
813
|
-
ctx.beginPath();
|
|
814
|
-
for (let i = 0; i <= M.w; i++) {
|
|
815
|
-
const x = M.xmin + ((M.xmax - M.xmin) * i) / M.w;
|
|
816
|
-
const val = rhs(x);
|
|
817
|
-
if (!(val >= 0)) {
|
|
818
|
-
started = false;
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
const y = s * Math.sqrt(val);
|
|
822
|
-
const X = M.x(x),
|
|
823
|
-
Y = M.y(y);
|
|
824
|
-
if (!started) {
|
|
825
|
-
ctx.moveTo(X, Y);
|
|
826
|
-
started = true;
|
|
827
|
-
} else ctx.lineTo(X, Y);
|
|
828
|
-
}
|
|
829
|
-
ctx.stroke();
|
|
830
|
-
}
|
|
831
|
-
ctx.restore();
|
|
832
|
-
}
|
|
833
|
-
function drawPoints(ctx, M, points, color, radius = 4) {
|
|
834
|
-
ctx.save();
|
|
835
|
-
ctx.fillStyle = color;
|
|
836
|
-
for (const P of points) {
|
|
837
|
-
const x = P.x.toNumber(),
|
|
838
|
-
y = P.y.toNumber();
|
|
839
|
-
if (x < M.xmin || x > M.xmax || y < M.ymin || y > M.ymax) continue;
|
|
840
|
-
ctx.beginPath();
|
|
841
|
-
ctx.arc(M.x(x), M.y(y), radius, 0, Math.PI * 2);
|
|
842
|
-
ctx.fill();
|
|
843
|
-
}
|
|
844
|
-
ctx.restore();
|
|
845
|
-
}
|
|
846
|
-
function plotBasic(canvasId, kind, points, extraPoints = []) {
|
|
847
|
-
const c = document.getElementById(canvasId),
|
|
848
|
-
ctx = c.getContext('2d');
|
|
849
|
-
const w = c.width,
|
|
850
|
-
h = c.height;
|
|
851
|
-
clearCanvas(ctx, w, h);
|
|
852
|
-
let M, rhs;
|
|
853
|
-
if (kind === 'circle') {
|
|
854
|
-
M = makeMap(w, h, -1.15, 1.15, -1.15, 1.15);
|
|
855
|
-
rhs = (x) => 1 - x * x;
|
|
856
|
-
} else if (kind === 'elliptic') {
|
|
857
|
-
M = makeMap(w, h, -4.5, 8.5, -20, 20);
|
|
858
|
-
rhs = (x) => x * x * x - 16 * x + 16;
|
|
859
|
-
} else {
|
|
860
|
-
M = makeMap(w, h, -3.6, 3.6, -8.5, 8.5);
|
|
861
|
-
rhs = (x) => x * (x + 1) * (x - 2) * (x + 2) * (x - 3);
|
|
862
|
-
}
|
|
863
|
-
drawAxes(ctx, M);
|
|
864
|
-
drawCurveSamples(ctx, M, rhs, '#06b6d4');
|
|
865
|
-
drawPoints(ctx, M, points, '#ec4899', 4);
|
|
866
|
-
if (extraPoints.length) drawPoints(ctx, M, extraPoints, '#3b82f6', 4.5);
|
|
867
|
-
}
|
|
868
|
-
function drawGrowthChart(maxB) {
|
|
869
|
-
const xWindow = Number(document.getElementById('xwin').value);
|
|
870
|
-
const c = document.getElementById('growthChart'),
|
|
871
|
-
ctx = c.getContext('2d');
|
|
872
|
-
const w = c.width,
|
|
873
|
-
h = c.height;
|
|
874
|
-
clearCanvas(ctx, w, h);
|
|
875
|
-
|
|
876
|
-
const data0 = [],
|
|
877
|
-
data1 = [],
|
|
878
|
-
data2 = [];
|
|
879
|
-
let ymax = 1;
|
|
880
|
-
for (let b = 2; b <= maxB; b++) {
|
|
881
|
-
const n0 = searchRationalPoints('circle', b, 1).length;
|
|
882
|
-
const n1 = searchRationalPoints('elliptic', b, xWindow).length;
|
|
883
|
-
const n2 = searchRationalPoints('genus2', b, xWindow).length;
|
|
884
|
-
data0.push([b, n0]);
|
|
885
|
-
data1.push([b, n1]);
|
|
886
|
-
data2.push([b, n2]);
|
|
887
|
-
ymax = Math.max(ymax, n0, n1, n2);
|
|
888
|
-
}
|
|
889
|
-
const M = makeMap(w, h, 2, maxB, 0, Math.max(6, ymax + 1));
|
|
890
|
-
drawAxes(ctx, M);
|
|
891
|
-
|
|
892
|
-
function line(data, color) {
|
|
893
|
-
ctx.save();
|
|
894
|
-
ctx.strokeStyle = color;
|
|
895
|
-
ctx.lineWidth = 2.3;
|
|
896
|
-
ctx.beginPath();
|
|
897
|
-
data.forEach((pt, i) => {
|
|
898
|
-
const X = M.x(pt[0]),
|
|
899
|
-
Y = M.y(pt[1]);
|
|
900
|
-
if (i === 0) ctx.moveTo(X, Y);
|
|
901
|
-
else ctx.lineTo(X, Y);
|
|
902
|
-
});
|
|
903
|
-
ctx.stroke();
|
|
904
|
-
ctx.restore();
|
|
905
|
-
drawPoints(
|
|
906
|
-
ctx,
|
|
907
|
-
M,
|
|
908
|
-
data.map(([x, y]) => ({ x: { toNumber: () => x }, y: { toNumber: () => y } })),
|
|
909
|
-
color,
|
|
910
|
-
3.2,
|
|
911
|
-
);
|
|
912
|
-
}
|
|
913
|
-
line(data0, '#ec4899');
|
|
914
|
-
line(data1, '#3b82f6');
|
|
915
|
-
line(data2, '#10b981');
|
|
916
|
-
|
|
917
|
-
document.getElementById('growthCaption').innerHTML =
|
|
918
|
-
`<span style="color:#ec4899">circle</span>: ${data0.at(-1)[1]} · ` +
|
|
919
|
-
`<span style="color:#3b82f6">genus 1</span>: ${data1.at(-1)[1]} · ` +
|
|
920
|
-
`<span style="color:#10b981">genus 2</span>: ${data2.at(-1)[1]} at bound ${maxB}`;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
/* ---------- UI rendering ---------- */
|
|
924
|
-
function fmtPoint(P) {
|
|
925
|
-
return `(${P.x.toString()}, ${P.y.toString()})`;
|
|
926
|
-
}
|
|
927
|
-
function fmtApprox(P) {
|
|
928
|
-
return `(${P.x.toNumber().toFixed(6)}, ${P.y.toNumber().toFixed(6)})`;
|
|
929
|
-
}
|
|
930
|
-
function setText(id, txt) {
|
|
931
|
-
document.getElementById(id).textContent = txt;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
function renderReason() {
|
|
935
|
-
const bound = Number(document.getElementById('bound').value);
|
|
936
|
-
const xWindow = Number(document.getElementById('xwin').value);
|
|
937
|
-
const multiN = Number(document.getElementById('multiN').value);
|
|
938
|
-
|
|
939
|
-
const pts0 = searchRationalPoints('circle', bound, 1);
|
|
940
|
-
const pts1 = searchRationalPoints('elliptic', bound, xWindow);
|
|
941
|
-
const pts2 = searchRationalPoints('genus2', bound, xWindow);
|
|
942
|
-
|
|
943
|
-
const P = makePoint(new Frac(0n), new Frac(4n));
|
|
944
|
-
const mults = [];
|
|
945
|
-
for (let n = 1; n <= multiN; n++) mults.push(multiplyPoint(P, n));
|
|
946
|
-
|
|
947
|
-
plotBasic('plot0', 'circle', pts0);
|
|
948
|
-
plotBasic('plot1', 'elliptic', pts1, mults);
|
|
949
|
-
plotBasic('plot2', 'genus2', pts2);
|
|
950
|
-
|
|
951
|
-
setText('count0', String(pts0.length));
|
|
952
|
-
setText('count1', String(pts1.length));
|
|
953
|
-
setText('count2', String(pts2.length));
|
|
954
|
-
setText('multiCount', String(mults.length));
|
|
955
|
-
|
|
956
|
-
document.getElementById('sample0').textContent = 'Examples: ' + pts0.slice(0, 8).map(fmtPoint).join(', ');
|
|
957
|
-
document.getElementById('sample1').textContent = 'Examples: ' + pts1.slice(0, 8).map(fmtPoint).join(', ');
|
|
958
|
-
document.getElementById('sample2').textContent = 'Examples: ' + pts2.slice(0, 8).map(fmtPoint).join(', ');
|
|
959
|
-
|
|
960
|
-
const tbody = document.getElementById('multiTable');
|
|
961
|
-
tbody.innerHTML = '';
|
|
962
|
-
mults.forEach((Q, i) => {
|
|
963
|
-
const tr = document.createElement('tr');
|
|
964
|
-
tr.innerHTML = `
|
|
965
|
-
<td class="code">${i + 1}</td>
|
|
966
|
-
<td class="code">${fmtPoint(Q)}</td>
|
|
967
|
-
<td class="code">${fmtApprox(Q)}</td>
|
|
968
|
-
`;
|
|
969
|
-
tbody.appendChild(tr);
|
|
970
|
-
});
|
|
971
|
-
|
|
972
|
-
drawGrowthChart(Math.max(4, Math.min(bound + 4, 16)));
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
function setPill(text, ok) {
|
|
976
|
-
const span = document.createElement('span');
|
|
977
|
-
span.className = 'pill';
|
|
978
|
-
span.innerHTML = `${text} <span class="status ${ok ? 'pass' : 'fail'}">${ok ? 'PASS' : 'FAIL'}</span>`;
|
|
979
|
-
return span;
|
|
980
|
-
}
|
|
981
|
-
function renderChecks() {
|
|
982
|
-
const obvious = [
|
|
983
|
-
{ x: 0, y: 0 },
|
|
984
|
-
{ x: -1, y: 0 },
|
|
985
|
-
{ x: 2, y: 0 },
|
|
986
|
-
{ x: -2, y: 0 },
|
|
987
|
-
{ x: 3, y: 0 },
|
|
988
|
-
];
|
|
989
|
-
document.getElementById('obviousCount').textContent = String(obvious.length);
|
|
990
|
-
|
|
991
|
-
const tbody = document.getElementById('checkTable');
|
|
992
|
-
tbody.innerHTML = '';
|
|
993
|
-
obvious.forEach((p) => {
|
|
994
|
-
const lhs = p.y * p.y;
|
|
995
|
-
const rhs = p.x * (p.x + 1) * (p.x - 2) * (p.x + 2) * (p.x - 3);
|
|
996
|
-
const ok = lhs === rhs;
|
|
997
|
-
const tr = document.createElement('tr');
|
|
998
|
-
tr.innerHTML = `
|
|
999
|
-
<td class="code">(${p.x}, ${p.y})</td>
|
|
1000
|
-
<td class="code">${lhs}</td>
|
|
1001
|
-
<td class="code">${rhs}</td>
|
|
1002
|
-
<td><span class="status ${ok ? 'pass' : 'fail'}">${ok ? 'PASS' : 'FAIL'}</span></td>
|
|
1003
|
-
`;
|
|
1004
|
-
tbody.appendChild(tr);
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
const pills = document.getElementById('premisePills');
|
|
1008
|
-
pills.innerHTML = '';
|
|
1009
|
-
pills.appendChild(setPill('curve is genus 2', true));
|
|
1010
|
-
pills.appendChild(setPill('field is a number field', true));
|
|
1011
|
-
pills.appendChild(setPill('genus ≥ 2 condition', true));
|
|
1012
|
-
pills.appendChild(setPill('bounded search is only evidence', true));
|
|
1013
|
-
|
|
1014
|
-
const bound = Number(document.getElementById('bound').value);
|
|
1015
|
-
const xWindow = Number(document.getElementById('xwin').value);
|
|
1016
|
-
const genus2Found = searchRationalPoints('genus2', bound, xWindow).length;
|
|
1017
|
-
const genus1Found = searchRationalPoints('elliptic', bound, xWindow).length;
|
|
1018
|
-
const circleFound = searchRationalPoints('circle', bound, 1).length;
|
|
1019
|
-
|
|
1020
|
-
document.getElementById('checkSummary').innerHTML =
|
|
1021
|
-
`At the current settings, the browser found <strong>${circleFound}</strong> rational points on the genus‑0 comparison curve, ` +
|
|
1022
|
-
`<strong>${genus1Found}</strong> on the genus‑1 comparison curve, and <strong>${genus2Found}</strong> on the genus‑2 example inside the chosen search window. ` +
|
|
1023
|
-
`That visual pattern supports the story, while the theorem is what supplies the rigorous finiteness conclusion in the genus‑2 case.`;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
function runAll() {
|
|
1027
|
-
setText('boundValue', document.getElementById('bound').value);
|
|
1028
|
-
setText('xwinValue', document.getElementById('xwin').value);
|
|
1029
|
-
setText('multiValue', document.getElementById('multiN').value);
|
|
1030
|
-
renderReason();
|
|
1031
|
-
renderChecks();
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
['bound', 'xwin', 'multiN'].forEach((id) => {
|
|
1035
|
-
document.getElementById(id).addEventListener('input', () => {
|
|
1036
|
-
if (id === 'bound') setText('boundValue', document.getElementById(id).value);
|
|
1037
|
-
if (id === 'xwin') setText('xwinValue', document.getElementById(id).value);
|
|
1038
|
-
if (id === 'multiN') setText('multiValue', document.getElementById(id).value);
|
|
1039
|
-
runAll();
|
|
1040
|
-
});
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
runAll();
|
|
1044
|
-
</script>
|
|
1045
|
-
</body>
|
|
1046
|
-
</html>
|