eyeling 1.15.12 → 1.15.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/follows-from/artifacts/ackermann.html +678 -0
  3. package/follows-from/artifacts/auroracare.html +1297 -0
  4. package/follows-from/artifacts/bike-trip.html +752 -0
  5. package/follows-from/artifacts/binomial-theorem.html +631 -0
  6. package/follows-from/artifacts/bmi.html +511 -0
  7. package/follows-from/artifacts/building-performance.html +750 -0
  8. package/follows-from/artifacts/clinical-care.html +726 -0
  9. package/follows-from/artifacts/collatz.html +403 -0
  10. package/follows-from/artifacts/complex.html +321 -0
  11. package/follows-from/artifacts/control-system.html +482 -0
  12. package/follows-from/artifacts/delfour.html +849 -0
  13. package/follows-from/artifacts/earthquake-epicenter.html +982 -0
  14. package/follows-from/artifacts/eco-route.html +662 -0
  15. package/follows-from/artifacts/euclid-infinitude.html +564 -0
  16. package/follows-from/artifacts/euler-identity.html +667 -0
  17. package/follows-from/artifacts/exoplanet-transit.html +1000 -0
  18. package/follows-from/artifacts/faltings-theorem.html +1046 -0
  19. package/follows-from/artifacts/fibonacci.html +299 -0
  20. package/follows-from/artifacts/fundamental-theorem-arithmetic.html +398 -0
  21. package/follows-from/artifacts/godel-numbering.html +743 -0
  22. package/follows-from/artifacts/gps-bike.html +759 -0
  23. package/follows-from/artifacts/gps-clinical-bench.html +792 -0
  24. package/follows-from/artifacts/graph-french.html +449 -0
  25. package/follows-from/artifacts/grass-molecular.html +592 -0
  26. package/follows-from/artifacts/group-theory.html +740 -0
  27. package/follows-from/artifacts/health-info.html +833 -0
  28. package/follows-from/artifacts/kaprekar-constant.html +576 -0
  29. package/follows-from/artifacts/lee.html +805 -0
  30. package/follows-from/artifacts/linked-lists.html +502 -0
  31. package/follows-from/artifacts/lldm.html +612 -0
  32. package/follows-from/artifacts/matrix-multiplication.html +502 -0
  33. package/follows-from/artifacts/matrix.html +651 -0
  34. package/follows-from/artifacts/newton-raphson.html +944 -0
  35. package/follows-from/artifacts/peano-factorial.html +456 -0
  36. package/follows-from/artifacts/pi.html +363 -0
  37. package/follows-from/artifacts/polynomial.html +646 -0
  38. package/follows-from/artifacts/prime.html +366 -0
  39. package/follows-from/artifacts/pythagorean-theorem.html +468 -0
  40. package/follows-from/artifacts/rest-path.html +469 -0
  41. package/follows-from/artifacts/roots-of-unity.html +363 -0
  42. package/follows-from/artifacts/turing.html +409 -0
  43. package/follows-from/artifacts/wind-turbines.html +726 -0
  44. package/follows-from/index.html +549 -0
  45. package/follows-from/library/index.md +22 -0
  46. package/follows-from/logo.svg +12 -0
  47. package/follows-from/manifesto.md +48 -0
  48. package/follows-from/method/index.md +30 -0
  49. package/follows-from/path/index.md +20 -0
  50. package/package.json +4 -3
package/README.md CHANGED
@@ -14,8 +14,8 @@ A compact [Notation3 (N3)](https://notation3.org/) reasoner in **JavaScript**.
14
14
  - **Handbook:** [https://eyereasoner.github.io/eyeling/HANDBOOK](https://eyereasoner.github.io/eyeling/HANDBOOK)
15
15
  - **Semantics:** [https://eyereasoner.github.io/eyeling/SEMANTICS](https://eyereasoner.github.io/eyeling/SEMANTICS)
16
16
  - **Playground:** [https://eyereasoner.github.io/eyeling/demo](https://eyereasoner.github.io/eyeling/demo)
17
- - **Notation3 test suite:** [https://codeberg.org/phochste/notation3tests](https://codeberg.org/phochste/notation3tests)
18
- - **Eyeling conformance report:** [https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
17
+ - **Conformance report:** [https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md](https://codeberg.org/phochste/notation3tests/src/branch/main/reports/report.md)
18
+ - **Follows From:** [https://eyereasoner.github.io/eyeling/follows-from/](https://eyereasoner.github.io/eyeling/follows-from/)
19
19
 
20
20
  Eyeling is regularly checked against the community Notation3 test suite. If you want implementation details (parser, unifier, proof search, skolemization, scoped closure, builtins), start with the handbook.
21
21
 
@@ -0,0 +1,678 @@
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>A₂ (Ackermann via hyper-operations)</title>
7
+ <style>
8
+ :root {
9
+ --fg: #101014;
10
+ --bg: #ffffff;
11
+ --muted: #666;
12
+ --accent: #2563eb;
13
+ --chip: #eef2ff;
14
+ --ok: #16a34a;
15
+ --bad: #dc2626;
16
+ --warn: #ca8a04;
17
+ }
18
+ @media (prefers-color-scheme: dark) {
19
+ :root {
20
+ --fg: #eaeaf0;
21
+ --bg: #0b0b10;
22
+ --muted: #a0a0b0;
23
+ --accent: #60a5fa;
24
+ --chip: #0e1a32;
25
+ }
26
+ }
27
+ html,
28
+ body {
29
+ margin: 0;
30
+ padding: 0;
31
+ background: var(--bg);
32
+ color: var(--fg);
33
+ font:
34
+ 15px/1.6 ui-sans-serif,
35
+ system-ui,
36
+ -apple-system,
37
+ Segoe UI,
38
+ Roboto,
39
+ Helvetica,
40
+ Arial;
41
+ }
42
+ main {
43
+ max-width: 1100px;
44
+ margin: 0 auto;
45
+ padding: 28px 16px 80px;
46
+ }
47
+ h1 {
48
+ font-size: clamp(1.6rem, 2.6vw + 1rem, 2.2rem);
49
+ margin: 0 0 6px;
50
+ }
51
+ header p {
52
+ margin: 0;
53
+ color: var(--muted);
54
+ }
55
+ section {
56
+ margin: 18px 0 22px;
57
+ padding: 14px 14px 16px;
58
+ border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
59
+ border-radius: 14px;
60
+ background: color-mix(in srgb, var(--accent) 4%, transparent);
61
+ }
62
+ section h2 {
63
+ margin: 0 0 8px;
64
+ font-size: 1.15rem;
65
+ }
66
+ .row {
67
+ display: flex;
68
+ gap: 12px;
69
+ align-items: center;
70
+ flex-wrap: wrap;
71
+ }
72
+ .btn {
73
+ appearance: none;
74
+ border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
75
+ background: color-mix(in srgb, var(--accent) 8%, transparent);
76
+ color: var(--fg);
77
+ border-radius: 10px;
78
+ padding: 8px 12px;
79
+ font-weight: 700;
80
+ cursor: pointer;
81
+ }
82
+ .mono {
83
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
84
+ }
85
+ .muted {
86
+ color: var(--muted);
87
+ }
88
+ .small {
89
+ font-size: 0.92em;
90
+ }
91
+ .chip {
92
+ display: inline-block;
93
+ padding: 2px 8px;
94
+ border-radius: 999px;
95
+ background: var(--chip);
96
+ font-weight: 600;
97
+ }
98
+ input[type='number'] {
99
+ width: 100px;
100
+ border-radius: 10px;
101
+ border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
102
+ padding: 8px 10px;
103
+ }
104
+ label {
105
+ user-select: none;
106
+ }
107
+ code {
108
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
109
+ padding: 0.1rem 0.35rem;
110
+ border-radius: 0.35rem;
111
+ }
112
+ details > summary {
113
+ cursor: pointer;
114
+ }
115
+ .ok {
116
+ color: var(--ok);
117
+ }
118
+ .bad {
119
+ color: var(--bad);
120
+ }
121
+ .spinner {
122
+ display: inline-block;
123
+ width: 1em;
124
+ height: 1em;
125
+ border: 2px solid color-mix(in srgb, var(--accent) 30%, transparent);
126
+ border-top-color: var(--accent);
127
+ border-radius: 50%;
128
+ animation: spin 1s linear infinite;
129
+ vertical-align: middle;
130
+ }
131
+ @keyframes spin {
132
+ to {
133
+ transform: rotate(360deg);
134
+ }
135
+ }
136
+
137
+ /* Ensure the Answer card can scroll horizontally for long lines */
138
+ #answer {
139
+ overflow-x: auto;
140
+ }
141
+ #answer pre {
142
+ white-space: pre !important;
143
+ overflow-x: auto;
144
+ overflow-y: auto;
145
+ max-width: 100%;
146
+ }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <main>
151
+ <header class="row">
152
+ <div>
153
+ <h1>A₂ (Ackermann via hyper-operations)</h1>
154
+ </div>
155
+ <div class="row" style="margin-left: auto">
156
+ <label class="muted small">x: <input id="x" type="number" min="0" max="6" value="3" /></label>
157
+ <label class="muted small">y: <input id="y" type="number" min="0" max="100000" value="4" /></label>
158
+ <button id="compute" class="btn">Compute</button>
159
+ <button id="demo" class="btn">Run demo set</button>
160
+ </div>
161
+ </header>
162
+
163
+ <section>
164
+ <h2>What this is?</h2>
165
+ <p>This page mirrors the case file definition:</p>
166
+ <p class="mono">A₂(x, y) = H(x, y+3, 2) − 3</p>
167
+ <p class="small">
168
+ with hyper‑operations <code>H</code> defined by <code>H(0,y,z)=y+1</code>, <code>H(1,y,z)=y+z</code>,
169
+ <code>H(2,y,z)=y·z</code>, <code>H(x≥3,0,z)=1</code>, <code>H(x≥3,y>0,z)</code>: repeat <code>y</code> times
170
+ <code>result = H(x−1, result, z)</code> starting from <code>1</code>. For <code>z=2</code>, this yields
171
+ exponentiation for <code>x=3</code> and tetration for <code>x=4</code>.
172
+ </p>
173
+ </section>
174
+
175
+ <section id="answer">
176
+ <h2>Answer</h2>
177
+ <div id="chips" class="row small" style="gap: 8px"></div>
178
+ <div id="one"></div>
179
+ <details id="demos">
180
+ <summary>Demo queries (12)</summary>
181
+ <div id="demosBody" class="mono small"></div>
182
+ </details>
183
+ </section>
184
+
185
+ <section id="reason">
186
+ <h2>Reason why</h2>
187
+ <div class="small">
188
+ <p>
189
+ Closed forms: <code>A₂(0,y)=y+1</code>, <code>A₂(1,y)=y+2</code>, <code>A₂(2,y)=2y+3</code>,
190
+ <code>A₂(3,y)=2^(y+3)−3</code>.
191
+ </p>
192
+ <p>
193
+ For <code>x=4</code> we hit <code>2^^(y+3)</code>. When that’s still realistically printable (like
194
+ <code>2^65536−3</code>), we expand it exactly using a background worker.
195
+ </p>
196
+ </div>
197
+ </section>
198
+
199
+ <section id="check">
200
+ <h2>Check (harness)</h2>
201
+ <div id="check-body"></div>
202
+ </section>
203
+ </main>
204
+
205
+ <script>
206
+ (function () {
207
+ 'use strict';
208
+ const $ = (id) => document.getElementById(id);
209
+ const setHTML = (id, html) => {
210
+ const el = $(id);
211
+ if (el) el.innerHTML = html;
212
+ };
213
+ const now = () => performance.now();
214
+
215
+ // ---------- Limits ----------
216
+ const DECIMAL_LIMIT = 70000; // auto-expand if decimal digits <= this
217
+
218
+ // ---------- Worker for heavy expansion ----------
219
+ function createPow2Minus3Worker() {
220
+ const code = `
221
+ self.onmessage = function(e){
222
+ const expStr = e.data && e.data.exp;
223
+ try{
224
+ const exp = BigInt(expStr);
225
+ function pow2ExactExp(e){
226
+ try { return (1n << e); }
227
+ catch (_){
228
+ // fallback to exponentiation by squaring
229
+ let base = 2n, res = 1n, x = e;
230
+ while (x > 0n){
231
+ if (x & 1n) res *= base;
232
+ x >>= 1n;
233
+ if (x > 0n) base *= base;
234
+ }
235
+ return res;
236
+ }
237
+ }
238
+ const t0 = Date.now();
239
+ const val = pow2ExactExp(exp) - 3n;
240
+ const s = val.toString();
241
+ const t1 = Date.now();
242
+ self.postMessage({ ok:true, digits: s.length, ms: t1 - t0, text: s });
243
+ } catch(err){
244
+ self.postMessage({ ok:false, error: (err && err.message) || String(err) });
245
+ }
246
+ };
247
+ `;
248
+ const blob = new Blob([code], { type: 'application/javascript' });
249
+ return new Worker(URL.createObjectURL(blob));
250
+ }
251
+
252
+ // ---------- Representations ----------
253
+ // {kind:'bigint', value: BigInt}
254
+ // {kind:'pow2', exp: BigInt} // represents 2^exp
255
+ // After A2: {kind:'pow2minus3', exp: BigInt}
256
+
257
+ // Hyper-operations (z fixed to 2)
258
+ function hyper2(x, y) {
259
+ x = Number(x);
260
+ y = Number(y);
261
+ if (x === 0) return { kind: 'bigint', value: BigInt(y) + 1n };
262
+ if (x === 1) return { kind: 'bigint', value: BigInt(y) + 2n };
263
+ if (x === 2) return { kind: 'bigint', value: BigInt(y) * 2n };
264
+ if (y === 0) return { kind: 'bigint', value: 1n };
265
+ let result = { kind: 'bigint', value: 1n };
266
+ for (let i = 0; i < y; i++) {
267
+ if (x === 3) {
268
+ result = { kind: 'bigint', value: result.kind === 'bigint' ? result.value * 2n : 1n << result.exp }; // safe for small
269
+ } else if (x === 4) {
270
+ if (result.kind === 'bigint') {
271
+ const k = result.value;
272
+ // If k is modest, compute 2^k exactly; otherwise keep symbolic
273
+ if (k <= 4096n) {
274
+ result = { kind: 'bigint', value: 1n << k };
275
+ } else {
276
+ result = { kind: 'pow2', exp: k };
277
+ }
278
+ } else {
279
+ // result is already 2^k; next step would be astronomical; keep symbolic
280
+ result = { kind: 'pow2', exp: result.exp > 4096n ? result.exp : 1n << result.exp };
281
+ }
282
+ } else {
283
+ if (result.kind !== 'bigint') return result;
284
+ result = hyper2(x - 1, Number(result.value));
285
+ }
286
+ }
287
+ return result;
288
+ }
289
+
290
+ function A2_repr(x, y) {
291
+ if (x < 0 || y < 0) throw new Error('x and y must be non-negative integers.');
292
+ const h = hyper2(x, y + 3);
293
+ if (h.kind === 'bigint') {
294
+ return { kind: 'bigint', value: h.value - 3n };
295
+ } else if (h.kind === 'pow2') {
296
+ return { kind: 'pow2minus3', exp: h.exp };
297
+ } else {
298
+ return h;
299
+ }
300
+ }
301
+
302
+ // ---------- Meta ----------
303
+ function bitLengthBig(x) {
304
+ return x.toString(2).length;
305
+ }
306
+ function digitsOfPow2(exp) {
307
+ return Math.floor(Number(exp) * Math.LOG10E * Math.log(2)) + 1;
308
+ }
309
+ function chips(pairs) {
310
+ return pairs
311
+ .map(([k, v, cls]) => '<span class="chip ' + (cls || '') + '">' + k + ': ' + v + '</span>')
312
+ .join(' ');
313
+ }
314
+
315
+ // ---------- Render one (uses worker if needed) ----------
316
+ function renderOne(x, y) {
317
+ const t0 = now();
318
+ let rep,
319
+ err = null;
320
+ try {
321
+ rep = A2_repr(x, y);
322
+ } catch (e) {
323
+ err = e.message || String(e);
324
+ }
325
+ const t1 = now();
326
+ if (err) {
327
+ setHTML('one', '<p class="bad">Error: ' + err + '</p>');
328
+ return;
329
+ }
330
+
331
+ if (rep.kind === 'bigint') {
332
+ const v = rep.value;
333
+ const s = v.toString();
334
+ const digits = s.length;
335
+ const bits = bitLengthBig(v + 3n);
336
+ setHTML(
337
+ 'chips',
338
+ chips([
339
+ ['x', x],
340
+ ['y', y],
341
+ ['time', (t1 - t0).toFixed(2) + ' ms'],
342
+ ['digits', digits],
343
+ ['bit_length(A₂+3)', bits],
344
+ ]),
345
+ );
346
+ setHTML(
347
+ 'one',
348
+ '<p><strong>A₂(' +
349
+ x +
350
+ ',' +
351
+ y +
352
+ ')</strong></p><pre class="mono" style="white-space:pre-wrap;overflow:auto">' +
353
+ s +
354
+ '</pre>',
355
+ );
356
+ } else if (rep.kind === 'pow2minus3') {
357
+ const digits = digitsOfPow2(rep.exp);
358
+ const bits = Number(rep.exp) + 1;
359
+ if (digits <= DECIMAL_LIMIT) {
360
+ // Expand in a worker so the UI stays responsive
361
+ setHTML(
362
+ 'chips',
363
+ chips([
364
+ ['x', x],
365
+ ['y', y],
366
+ ['time', (t1 - t0).toFixed(2) + ' ms'],
367
+ ['digits', digits],
368
+ ['bit_length(A₂+3)', bits],
369
+ ]),
370
+ );
371
+ setHTML(
372
+ 'one',
373
+ '<p><strong>A₂(' +
374
+ x +
375
+ ',' +
376
+ y +
377
+ ')</strong> <span class="small muted"><span class="spinner"></span> expanding…</span></p>',
378
+ );
379
+ const w = createPow2Minus3Worker();
380
+ w.onmessage = (ev) => {
381
+ const d = ev.data || {};
382
+ if (d.ok) {
383
+ setHTML(
384
+ 'chips',
385
+ chips([
386
+ ['x', x],
387
+ ['y', y],
388
+ ['time', (t1 - t0).toFixed(2) + ' ms'],
389
+ ['expand', d.ms.toFixed ? d.ms.toFixed(1) + ' ms' : d.ms + ' ms'],
390
+ ['digits', d.digits],
391
+ ['bit_length(A₂+3)', bits],
392
+ ]),
393
+ );
394
+ setHTML(
395
+ 'one',
396
+ '<p><strong>A₂(' +
397
+ x +
398
+ ',' +
399
+ y +
400
+ ')</strong></p><pre class="mono" style="white-space:pre-wrap;overflow:auto">' +
401
+ d.text +
402
+ '</pre>',
403
+ );
404
+ } else {
405
+ setHTML(
406
+ 'one',
407
+ '<p class="bad">Expansion failed: ' +
408
+ (d.error || 'unknown') +
409
+ '</p><div class="small mono muted">A₂(' +
410
+ x +
411
+ ',' +
412
+ y +
413
+ ') = 2^(' +
414
+ String(rep.exp) +
415
+ ') − 3</div>',
416
+ );
417
+ }
418
+ w.terminate();
419
+ };
420
+ w.postMessage({ exp: String(rep.exp) });
421
+ } else {
422
+ setHTML(
423
+ 'chips',
424
+ chips([
425
+ ['x', x],
426
+ ['y', y],
427
+ ['time', (t1 - t0).toFixed(2) + ' ms'],
428
+ ['digits', digits],
429
+ ['bit_length(A₂+3)', bits],
430
+ ]),
431
+ );
432
+ setHTML(
433
+ 'one',
434
+ '<p><strong>A₂(' +
435
+ x +
436
+ ',' +
437
+ y +
438
+ ')</strong> <em>(not expanded)</em></p><div class="small mono muted">A₂(' +
439
+ x +
440
+ ',' +
441
+ y +
442
+ ') = 2^(' +
443
+ String(rep.exp) +
444
+ ') − 3</div>',
445
+ );
446
+ }
447
+ } else {
448
+ setHTML('one', '<p class="bad">Unexpected representation.</p>');
449
+ }
450
+ }
451
+
452
+ // ---------- Demo set (expand safe heavy case via worker) ----------
453
+ const DEMO = [
454
+ [0, 0],
455
+ [0, 6],
456
+ [1, 2],
457
+ [1, 7],
458
+ [2, 2],
459
+ [2, 9],
460
+ [3, 4],
461
+ [3, 14],
462
+ [4, 0],
463
+ [4, 1],
464
+ [4, 2],
465
+ [5, 0],
466
+ ];
467
+
468
+ function runDemos() {
469
+ const lines = new Array(DEMO.length);
470
+ for (let i = 0; i < DEMO.length; i++) {
471
+ const x = DEMO[i][0],
472
+ y = DEMO[i][1];
473
+ try {
474
+ const rep = A2_repr(x, y);
475
+ if (rep.kind === 'bigint') {
476
+ const s = rep.value.toString();
477
+ lines[i] =
478
+ 'A' +
479
+ String(i).padStart(2, '0') +
480
+ ' = A₂(' +
481
+ x +
482
+ ',' +
483
+ y +
484
+ ') = ' +
485
+ s +
486
+ ' (digits=' +
487
+ s.length +
488
+ ')';
489
+ } else if (rep.kind === 'pow2minus3') {
490
+ const digits = digitsOfPow2(rep.exp);
491
+ if (digits <= DECIMAL_LIMIT) {
492
+ // placeholder; expand via worker
493
+ lines[i] =
494
+ 'A' +
495
+ String(i).padStart(2, '0') +
496
+ ' = A₂(' +
497
+ x +
498
+ ',' +
499
+ y +
500
+ ') = (expanding …) (digits≈' +
501
+ digits +
502
+ ')';
503
+ // kick off worker for this line
504
+ const w = createPow2Minus3Worker();
505
+ ((idx, X, Y) => {
506
+ w.onmessage = (ev) => {
507
+ const d = ev.data || {};
508
+ if (d.ok) {
509
+ const s = d.text;
510
+ lines[idx] =
511
+ 'A' +
512
+ String(idx).padStart(2, '0') +
513
+ ' = A₂(' +
514
+ X +
515
+ ',' +
516
+ Y +
517
+ ') = ' +
518
+ s +
519
+ ' (digits=' +
520
+ s.length +
521
+ ')';
522
+ } else {
523
+ lines[idx] =
524
+ 'A' +
525
+ String(idx).padStart(2, '0') +
526
+ ' = A₂(' +
527
+ X +
528
+ ',' +
529
+ Y +
530
+ ') = (expand failed) (digits≈' +
531
+ digits +
532
+ ')';
533
+ }
534
+ setHTML(
535
+ 'demosBody',
536
+ '<pre class="mono" style="white-space:pre-wrap">' + lines.join('\n') + '</pre>',
537
+ );
538
+ w.terminate();
539
+ };
540
+ })(i, x, y);
541
+ w.postMessage({ exp: String(rep.exp) });
542
+ } else {
543
+ lines[i] =
544
+ 'A' +
545
+ String(i).padStart(2, '0') +
546
+ ' = A₂(' +
547
+ x +
548
+ ',' +
549
+ y +
550
+ ') = (2^' +
551
+ String(rep.exp) +
552
+ ' − 3) (digits≈' +
553
+ digits +
554
+ ')';
555
+ }
556
+ } else {
557
+ lines[i] = 'A' + String(i).padStart(2, '0') + ' = A₂(' + x + ',' + y + ') = [unexpected rep]';
558
+ }
559
+ } catch (e) {
560
+ lines[i] =
561
+ 'A' + String(i).padStart(2, '0') + ' = A₂(' + x + ',' + y + ') = [error: ' + (e.message || e) + ']';
562
+ }
563
+ }
564
+ setHTML('demosBody', '<pre class="mono" style="white-space:pre-wrap">' + lines.join('\n') + '</pre>');
565
+ $('demos').open = true;
566
+ }
567
+
568
+ // ---------- Check (unchanged from previous version) ----------
569
+ function runChecks() {
570
+ const lines = [];
571
+ const ok = (b) => (b ? '✓' : '✗');
572
+ const t0 = now();
573
+ let okAll = true;
574
+
575
+ // Closed forms
576
+ let o = true;
577
+ for (let y = 0; y <= 50; y++) {
578
+ const r = A2_repr(0, y);
579
+ if (r.kind !== 'bigint' || r.value !== BigInt(y) + 1n) {
580
+ o = false;
581
+ break;
582
+ }
583
+ }
584
+ lines.push('A₂(0,y) = y+1 on y∈[0,50]? ' + ok(o));
585
+ okAll = okAll && o;
586
+
587
+ o = true;
588
+ for (let y = 0; y <= 50; y++) {
589
+ const r = A2_repr(1, y);
590
+ if (r.kind !== 'bigint' || r.value !== BigInt(y) + 2n) {
591
+ o = false;
592
+ break;
593
+ }
594
+ }
595
+ lines.push('A₂(1,y) = y+2 on y∈[0,50]? ' + ok(o));
596
+ okAll = okAll && o;
597
+
598
+ o = true;
599
+ for (let y = 0; y <= 200; y++) {
600
+ const r = A2_repr(2, y);
601
+ if (r.kind !== 'bigint' || r.value !== 2n * BigInt(y) + 3n) {
602
+ o = false;
603
+ break;
604
+ }
605
+ }
606
+ lines.push('A₂(2,y) = 2y+3 on y∈[0,200]? ' + ok(o));
607
+ okAll = okAll && o;
608
+
609
+ o = true;
610
+ for (let y = 0; y <= 20; y++) {
611
+ const r = A2_repr(3, y);
612
+ const want = (1n << BigInt(y + 3)) - 3n;
613
+ if (r.kind !== 'bigint' || r.value !== want) {
614
+ o = false;
615
+ break;
616
+ }
617
+ }
618
+ lines.push('A₂(3,y) = 2^(y+3)−3 on y∈[0,20]? ' + ok(o));
619
+ okAll = okAll && o;
620
+
621
+ // Spot checks (11/12 exact)
622
+ const expected = new Map([
623
+ ['0,0', 1n],
624
+ ['0,6', 7n],
625
+ ['1,2', 4n],
626
+ ['1,7', 9n],
627
+ ['2,2', 7n],
628
+ ['2,9', 21n],
629
+ ['3,4', 125n],
630
+ ['3,14', (1n << 17n) - 3n],
631
+ ['4,0', 13n],
632
+ ['4,1', 65533n],
633
+ ['5,0', 65533n],
634
+ ]);
635
+ o = true;
636
+ for (const [key, want] of expected) {
637
+ const parts = key.split(',');
638
+ const x = Number(parts[0]),
639
+ y = Number(parts[1]);
640
+ const r = A2_repr(x, y);
641
+ if (r.kind !== 'bigint' || r.value !== want) {
642
+ o = false;
643
+ }
644
+ }
645
+ lines.push('Exact checks for 11/12 queries OK? ' + ok(o));
646
+ okAll = okAll && o;
647
+
648
+ // Big one handled symbolically in the rep (we expand later via worker if needed)
649
+ const r = A2_repr(4, 2);
650
+ const pass = r.kind === 'pow2minus3' && String(r.exp) === '65536';
651
+ lines.push('A₂(4,2) handled as 2^65536−3 symbolically? ' + ok(pass));
652
+ okAll = okAll && pass;
653
+
654
+ const t1 = now();
655
+ lines.push('');
656
+ lines.push('All checks passed? ' + ok(okAll) + ' (' + (t1 - t0).toFixed(1) + ' ms)');
657
+ const pre = document.createElement('pre');
658
+ pre.className = 'mono';
659
+ pre.style.whiteSpace = 'pre-wrap';
660
+ pre.textContent = lines.join('\n');
661
+ $('check-body').replaceChildren(pre);
662
+ }
663
+
664
+ // ---------- Wire up ----------
665
+ $('compute').addEventListener('click', function () {
666
+ const x = Math.max(0, Math.floor(Number($('x').value)));
667
+ const y = Math.max(0, Math.floor(Number($('y').value)));
668
+ renderOne(x, y);
669
+ });
670
+ $('demo').addEventListener('click', runDemos);
671
+
672
+ // Initial
673
+ renderOne(3, 4);
674
+ runChecks();
675
+ })();
676
+ </script>
677
+ </body>
678
+ </html>