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.
Files changed (51) hide show
  1. package/HANDBOOK.md +4 -0
  2. package/README.md +0 -1
  3. package/examples/ershov-mixed-computation.n3 +106 -0
  4. package/examples/output/ershov-mixed-computation.n3 +15 -0
  5. package/eyeling.js +510 -263
  6. package/lib/cli.js +22 -12
  7. package/lib/engine.js +488 -251
  8. package/package.json +2 -3
  9. package/arctifacts/README.md +0 -59
  10. package/arctifacts/ackermann.html +0 -678
  11. package/arctifacts/auroracare.html +0 -1297
  12. package/arctifacts/bike-trip.html +0 -752
  13. package/arctifacts/binomial-theorem.html +0 -631
  14. package/arctifacts/bmi.html +0 -511
  15. package/arctifacts/building-performance.html +0 -750
  16. package/arctifacts/clinical-care.html +0 -726
  17. package/arctifacts/collatz.html +0 -403
  18. package/arctifacts/complex.html +0 -321
  19. package/arctifacts/control-system.html +0 -482
  20. package/arctifacts/delfour.html +0 -849
  21. package/arctifacts/earthquake-epicenter.html +0 -982
  22. package/arctifacts/eco-route.html +0 -662
  23. package/arctifacts/euclid-infinitude.html +0 -564
  24. package/arctifacts/euler-identity.html +0 -667
  25. package/arctifacts/exoplanet-transit.html +0 -1000
  26. package/arctifacts/faltings-theorem.html +0 -1046
  27. package/arctifacts/fibonacci.html +0 -299
  28. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  29. package/arctifacts/godel-numbering.html +0 -743
  30. package/arctifacts/gps-bike.html +0 -759
  31. package/arctifacts/gps-clinical-bench.html +0 -792
  32. package/arctifacts/graph-french.html +0 -449
  33. package/arctifacts/grass-molecular.html +0 -592
  34. package/arctifacts/group-theory.html +0 -740
  35. package/arctifacts/health-info.html +0 -833
  36. package/arctifacts/kaprekar-constant.html +0 -576
  37. package/arctifacts/lee.html +0 -805
  38. package/arctifacts/linked-lists.html +0 -502
  39. package/arctifacts/lldm.html +0 -612
  40. package/arctifacts/matrix-multiplication.html +0 -502
  41. package/arctifacts/matrix.html +0 -651
  42. package/arctifacts/newton-raphson.html +0 -944
  43. package/arctifacts/peano-factorial.html +0 -456
  44. package/arctifacts/pi.html +0 -363
  45. package/arctifacts/polynomial.html +0 -646
  46. package/arctifacts/prime.html +0 -366
  47. package/arctifacts/pythagorean-theorem.html +0 -468
  48. package/arctifacts/rest-path.html +0 -469
  49. package/arctifacts/roots-of-unity.html +0 -363
  50. package/arctifacts/turing.html +0 -409
  51. package/arctifacts/wind-turbines.html +0 -726
@@ -1,667 +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>Euler's Identity</title>
7
- <style>
8
- :root {
9
- --bg: #f7f9fc;
10
- --card: #ffffff;
11
- --text: #0f172a;
12
- --muted: #475569;
13
- --accent: #2563eb;
14
- --good: #16a34a;
15
- --bad: #dc2626;
16
- --border: #e2e8f0;
17
- --chip: #eef2ff;
18
- --code: #111827;
19
- }
20
- html,
21
- body {
22
- background: var(--bg);
23
- color: var(--text);
24
- font-family:
25
- ui-sans-serif,
26
- system-ui,
27
- -apple-system,
28
- Segoe UI,
29
- Roboto,
30
- Helvetica,
31
- Arial,
32
- 'Apple Color Emoji',
33
- 'Segoe UI Emoji';
34
- }
35
- * {
36
- box-sizing: border-box;
37
- }
38
- .container {
39
- max-width: 960px;
40
- margin: 24px auto;
41
- padding: 0 16px;
42
- }
43
- header {
44
- display: flex;
45
- flex-direction: column;
46
- gap: 8px;
47
- margin-bottom: 16px;
48
- }
49
- h1 {
50
- font-size: 1.8rem;
51
- margin: 0;
52
- letter-spacing: -0.02em;
53
- }
54
- .subtitle {
55
- color: var(--muted);
56
- }
57
- .card {
58
- background: var(--card);
59
- border: 1px solid var(--border);
60
- border-radius: 16px;
61
- padding: 16px;
62
- box-shadow: 0 6px 20px rgba(2, 6, 23, 0.05);
63
- }
64
- .stack {
65
- display: flex;
66
- flex-direction: column;
67
- gap: 16px;
68
- }
69
- .pill {
70
- display: inline-flex;
71
- align-items: center;
72
- gap: 8px;
73
- padding: 4px 10px;
74
- background: var(--chip);
75
- color: #1e3a8a;
76
- border-radius: 999px;
77
- font-size: 12px;
78
- font-weight: 600;
79
- border: 1px solid #c7d2fe;
80
- }
81
- .row {
82
- display: flex;
83
- gap: 12px;
84
- align-items: center;
85
- min-width: 0;
86
- flex-wrap: wrap;
87
- }
88
- input[type='number'] {
89
- width: 160px;
90
- padding: 10px 12px;
91
- border-radius: 10px;
92
- border: 1px solid var(--border);
93
- background: #fbfdff;
94
- }
95
- button {
96
- appearance: none;
97
- border: none;
98
- background: var(--accent);
99
- color: white;
100
- padding: 10px 14px;
101
- border-radius: 12px;
102
- font-weight: 700;
103
- cursor: pointer;
104
- box-shadow: 0 4px 14px rgba(37, 99, 235, 0.25);
105
- }
106
- button.secondary {
107
- background: #0ea5e9;
108
- }
109
- button.ghost {
110
- background: transparent;
111
- color: var(--accent);
112
- border: 1px solid var(--accent);
113
- }
114
- .muted {
115
- color: var(--muted);
116
- }
117
- .mono {
118
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
119
- color: var(--code);
120
- word-break: break-word;
121
- overflow-wrap: anywhere;
122
- white-space: normal;
123
- }
124
- .kpi {
125
- display: flex;
126
- gap: 16px;
127
- flex-wrap: wrap;
128
- }
129
- .kpi .card {
130
- padding: 12px 14px;
131
- }
132
- .k {
133
- font-weight: 700;
134
- }
135
- .v {
136
- font-variant-numeric: tabular-nums;
137
- word-break: break-word;
138
- overflow-wrap: anywhere;
139
- }
140
- .list {
141
- display: flex;
142
- flex-direction: column;
143
- gap: 12px;
144
- }
145
- .term {
146
- padding: 10px;
147
- background: #f8fafc;
148
- border: 1px dashed var(--border);
149
- border-radius: 12px;
150
- }
151
- details {
152
- border: 1px solid var(--border);
153
- border-radius: 12px;
154
- padding: 10px 12px;
155
- background: #fafcff;
156
- }
157
- details summary {
158
- cursor: pointer;
159
- font-weight: 700;
160
- color: #0b3eaa;
161
- }
162
- .badge {
163
- padding: 2px 8px;
164
- border-radius: 999px;
165
- font-weight: 700;
166
- font-size: 12px;
167
- border: 1px solid #cbd5e1;
168
- background: #f1f5f9;
169
- color: #0f172a;
170
- }
171
- .ok {
172
- background: #dcfce7;
173
- color: #166534;
174
- border: 1px solid #86efac;
175
- }
176
- .fail {
177
- background: #fee2e2;
178
- color: #991b1b;
179
- border: 1px solid #fecaca;
180
- }
181
- .harness {
182
- border-left: 6px solid #c7d2fe;
183
- }
184
- .h-title {
185
- display: flex;
186
- align-items: center;
187
- justify-content: space-between;
188
- gap: 8px;
189
- }
190
- .small {
191
- font-size: 12px;
192
- }
193
- code {
194
- background: #f1f5f9;
195
- padding: 2px 6px;
196
- border-radius: 6px;
197
- }
198
- footer {
199
- color: var(--muted);
200
- font-size: 12px;
201
- text-align: center;
202
- margin: 16px 0 40px;
203
- }
204
- .divider {
205
- height: 1px;
206
- background: var(--border);
207
- margin: -4px 0 8px;
208
- }
209
- svg {
210
- width: 100%;
211
- height: auto;
212
- background: #ffffff;
213
- border: 1px solid var(--border);
214
- border-radius: 12px;
215
- }
216
- .input-grid {
217
- flex-wrap: nowrap;
218
- gap: 10px;
219
- }
220
- .field {
221
- display: flex;
222
- align-items: center;
223
- gap: 6px;
224
- }
225
- input.short {
226
- width: 110px;
227
- padding: 6px 8px;
228
- }
229
- .buttons-row {
230
- gap: 8px;
231
- }
232
- @media (max-width: 520px) {
233
- .input-grid {
234
- flex-wrap: wrap;
235
- }
236
- }
237
- </style>
238
- </head>
239
- <body>
240
- <div class="container stack">
241
- <header class="card stack">
242
- <div class="row" style="justify-content: space-between; align-items: flex-start; width: 100%">
243
- <div>
244
- <h1>Euler's identity</h1>
245
- </div>
246
- </div>
247
- <span class="badge">Problem: e^{iπ} + 1 = 0</span>
248
- <p class="muted">
249
- This page states Euler's identity, explains why <span class="mono">e^{iπ} = -1</span> in mathematical English,
250
- illustrates it on the unit circle, and verifies it numerically with a small test harness.
251
- </p>
252
- </header>
253
-
254
- <!-- SVG ILLUSTRATION -->
255
- <section class="card stack">
256
- <h2 style="margin: 0">Illustration</h2>
257
- <div class="divider"></div>
258
- <svg viewBox="0 0 600 300" role="img" aria-label="Unit circle with angle π showing e^{iπ} at (-1,0)">
259
- <defs>
260
- <marker
261
- id="arrow"
262
- markerWidth="8"
263
- markerHeight="8"
264
- refX="6"
265
- refY="3"
266
- orient="auto"
267
- markerUnits="strokeWidth">
268
- <path d="M0,0 L0,6 L6,3 z" fill="#0b3eaa" />
269
- </marker>
270
- </defs>
271
- <!-- background grid -->
272
- <rect x="0" y="0" width="600" height="300" fill="#fff" rx="12" />
273
- <!-- axes -->
274
- <g stroke="#cbd5e1" stroke-width="1">
275
- <line x1="50" y1="150" x2="550" y2="150" />
276
- <line x1="300" y1="20" x2="300" y2="280" />
277
- </g>
278
- <!-- unit circle -->
279
- <circle cx="300" cy="150" r="100" fill="none" stroke="#94a3b8" stroke-width="2" />
280
- <!-- angle arc π from +x to -x -->
281
- <path d="M 400 150 A 100 100 0 0 1 200 150" fill="none" stroke="#0b3eaa" stroke-width="3" />
282
- <text x="300" y="125" fill="#0b3eaa" text-anchor="middle" font-size="12">θ = π</text>
283
- <!-- vector e^{iπ} -->
284
- <line x1="300" y1="150" x2="200" y2="150" stroke="#0b3eaa" stroke-width="3" marker-end="url(#arrow)" />
285
- <!-- points 1 and -1 -->
286
- <circle cx="400" cy="150" r="4" fill="#22c55e" />
287
- <text x="400" y="140" fill="#166534" text-anchor="middle" font-size="12">1</text>
288
- <circle cx="200" cy="150" r="4" fill="#ef4444" />
289
- <text x="200" y="140" fill="#991b1b" text-anchor="middle" font-size="12">−1 = e^{iπ}</text>
290
- <!-- tip annotation e^{iπ}+1=0 showing cancellation -->
291
- <g>
292
- <line x1="200" y1="150" x2="400" y2="150" stroke="#10b981" stroke-dasharray="4 4" />
293
- <text x="300" y="170" fill="#0f172a" text-anchor="middle" font-size="12">e^{iπ} + 1 → 0</text>
294
- </g>
295
- <!-- axis labels -->
296
- <text x="555" y="160" fill="#475569" font-size="12">Re</text>
297
- <text x="310" y="25" fill="#475569" font-size="12">Im</text>
298
- </svg>
299
- <p class="small muted">
300
- The complex exponential <span class="mono">e^{iθ}</span> traces the unit circle. At
301
- <span class="mono">θ=π</span> the point is <span class="mono">−1</span>, so
302
- <span class="mono">e^{iπ}+1=0</span>.
303
- </p>
304
- </section>
305
-
306
- <!-- INPUT -->
307
- <section class="card stack" id="input-card">
308
- <h2 style="margin: 0">Input</h2>
309
- <div class="divider"></div>
310
- <div class="row input-grid">
311
- <div class="field">
312
- <label class="mono" for="kInput">k (integer)</label
313
- ><input class="short" id="kInput" type="number" step="1" value="0" />
314
- </div>
315
- <div class="field">
316
- <label class="mono" for="epsInput">ε (tolerance)</label
317
- ><input class="short" id="epsInput" type="number" step="any" value="1e-12" />
318
- </div>
319
- <div class="field">
320
- <label class="mono" for="NInput">Series terms N</label
321
- ><input class="short" id="NInput" type="number" min="1" step="1" value="40" />
322
- </div>
323
- </div>
324
- <div class="row buttons-row">
325
- <button type="button" id="solve">Evaluate</button>
326
- <button type="button" id="clear">Clear</button>
327
- </div>
328
- <p class="small muted">
329
- We evaluate at <span class="mono">θ = π + 2πk</span>. <strong>N</strong> is the number of terms used in the
330
- power‑series expansions for <span class="mono">cos</span> and <span class="mono">sin</span>; higher
331
- <strong>N</strong> gives a tighter approximation (30–50 is typically plenty after angle reduction to
332
- <span class="mono">[−π,π]</span>). The tolerance <span class="mono">ε</span> is used by the numerical checks.
333
- </p>
334
- </section>
335
-
336
- <!-- OUTPUT -->
337
- <section class="card stack" id="output-card" aria-live="polite">
338
- <h2 style="margin: 0">Output</h2>
339
- <div class="divider"></div>
340
-
341
- <!-- Answer KPIs -->
342
- <section class="stack">
343
- <div class="kpi">
344
- <div class="card">
345
- <div class="k">Answer</div>
346
- <div id="answer" class="v mono">—</div>
347
- </div>
348
- <div class="card">
349
- <div class="k">Angle θ</div>
350
- <div id="theta" class="v mono">—</div>
351
- </div>
352
- <div class="card">
353
- <div class="k">e^{iθ} (cos/sin)</div>
354
- <div id="cis" class="v mono">—</div>
355
- </div>
356
- <div class="card">
357
- <div class="k">Series approx</div>
358
- <div id="seriesVal" class="v mono">—</div>
359
- </div>
360
- </div>
361
- </section>
362
-
363
- <!-- Reason Why (mathematical English) -->
364
- <section class="stack">
365
- <h3 style="margin: 0">Reason Why</h3>
366
- <div class="list">
367
- <div class="term">
368
- <strong>Power‑series derivation.</strong>
369
- The exponential, sine, and cosine have the absolutely convergent series for real
370
- <span class="mono">x</span>:
371
- <div class="mono">
372
- e^z = Σ_{n≥0} z^n/n!,  cos x = Σ_{n≥0} (−1)^n x^{2n}/(2n)!,  sin x = Σ_{n≥0} (−1)^n x^{2n+1}/(2n+1)!.
373
- </div>
374
- Substituting <span class="mono">z = ix</span> and regrouping even/odd powers gives
375
- <span class="mono">e^{ix} = cos x + i sin x</span> (Euler's formula). Taking
376
- <span class="mono">x = π</span> yields <span class="mono">e^{iπ} = cos π + i sin π = −1 + i·0</span>,
377
- hence <span class="mono">e^{iπ} + 1 = 0</span>.
378
- </div>
379
- <div class="term">
380
- <strong>Differential‑equation proof.</strong>
381
- Define <span class="mono">f(x) = e^{-ix}(cos x + i sin x)</span>. Then
382
- <span class="mono">f'(x) = 0</span>, so <span class="mono">f</span> is constant. As
383
- <span class="mono">f(0)=1</span>, we have <span class="mono">f(x)=1</span> for all
384
- <span class="mono">x</span>, and again <span class="mono">e^{ix} = cos x + i sin x</span>.
385
- </div>
386
- <div class="term">
387
- <strong>Geometric picture.</strong>
388
- The map <span class="mono">x ↦ e^{ix}</span> parametrizes the unit circle counterclockwise. The angle
389
- <span class="mono">π</span> lands at <span class="mono">−1</span> on the real axis, so adding
390
- <span class="mono">1</span> returns the origin.
391
- </div>
392
- </div>
393
- </section>
394
-
395
- <!-- Check (harness) for the user's inputs -->
396
- <section class="stack">
397
- <h3 style="margin: 0">Check (harness)</h3>
398
- <div id="checks" class="list"></div>
399
- </section>
400
- </section>
401
-
402
- <!-- PRELOADED HARNESS CASES (≥ 8) -->
403
- <section class="card stack harness" id="preloaded">
404
- <div class="h-title">
405
- <h2 style="margin: 0">Preloaded Checks (harness)</h2>
406
- <div class="row">
407
- <button id="runAll">Run all</button>
408
- <button class="ghost" id="clearHarness">Clear results</button>
409
- </div>
410
- </div>
411
- <p class="small muted">
412
- Each block checks that <span class="mono">|e^{iθ}+1| ≤ ε</span> for <span class="mono">θ = π + 2πk</span> and
413
- fails for a noncongruent angle.
414
- </p>
415
- <div id="harnesses" class="stack"></div>
416
- </section>
417
-
418
- <footer>
419
- <div>Built as a self‑checking artifact: program → <em>Answer</em>, <em>Reason Why</em>, <em>Check</em>.</div>
420
- <div class="small">This page performs only on‑device computation.</div>
421
- </footer>
422
- </div>
423
-
424
- <script>
425
- // ---------- Math helpers ----------
426
- const TAU = Math.PI * 2;
427
- const normAngle = (t) => {
428
- // wrap to [-π, π]
429
- let x = ((((t + Math.PI) % TAU) + TAU) % TAU) - Math.PI; // robust modulo
430
- // handle −0
431
- return x === 0 ? 0 : x;
432
- };
433
- const cis = (t) => ({ re: Math.cos(t), im: Math.sin(t) });
434
- const add = (a, b) => ({ re: a.re + b.re, im: a.im + b.im });
435
- const abs = (z) => Math.hypot(z.re, z.im);
436
- const mul = (a, b) => ({ re: a.re * b.re - a.im * b.im, im: a.re * b.im + a.im * b.re });
437
- const powComplex = (z, n) => {
438
- let r = { re: 1, im: 0 };
439
- for (let i = 0; i < n; i++) r = mul(r, z);
440
- return r;
441
- };
442
-
443
- // power series for cos/sin with N terms and range reduction
444
- function cosSeries(x, N) {
445
- x = normAngle(x);
446
- let term = 1,
447
- sum = 1; // k=0
448
- for (let k = 1; k < N; k++) {
449
- term *= (-x * x) / ((2 * k - 1) * (2 * k));
450
- sum += term;
451
- }
452
- return sum;
453
- }
454
- function sinSeries(x, N) {
455
- x = normAngle(x);
456
- let term = x,
457
- sum = x; // k=0
458
- for (let k = 1; k < N; k++) {
459
- term *= (-x * x) / (2 * k * (2 * k + 1));
460
- sum += term;
461
- }
462
- return sum;
463
- }
464
-
465
- function evaluate() {
466
- const k = Number(document.getElementById('kInput').value || 0);
467
- const eps = Number(document.getElementById('epsInput').value || 1e-12);
468
- const N = Math.max(1, Number(document.getElementById('NInput').value || 40));
469
-
470
- const theta = Math.PI + TAU * k;
471
- const z = cis(theta);
472
- const sRe = cosSeries(theta, N),
473
- sIm = sinSeries(theta, N);
474
- const zSeries = { re: sRe, im: sIm };
475
-
476
- const ans = { re: z.re + 1, im: z.im };
477
- const ansSeries = { re: zSeries.re + 1, im: zSeries.im };
478
-
479
- document.getElementById('answer').textContent = `|e^{iπ+2πi·${k}} + 1| ≈ ${abs(ans).toExponential(3)}`;
480
- document.getElementById('theta').textContent = `θ = π + 2π·${k} ≈ ${theta.toPrecision(6)}`;
481
- document.getElementById('cis').textContent =
482
- `cos θ + i sin θ ≈ ${z.re.toPrecision(12)} + ${z.im.toPrecision(12)} i`;
483
- document.getElementById('seriesVal').textContent =
484
- `series_N=${N} ⇒ ${zSeries.re.toPrecision(12)} + ${zSeries.im.toPrecision(12)} i`;
485
-
486
- renderChecks(k, eps, N, theta, z, zSeries, ans, ansSeries);
487
- }
488
-
489
- function renderChecks(k, eps, N, theta, z, zSeries, ans, ansSeries) {
490
- const checks = document.getElementById('checks');
491
- const list = [];
492
-
493
- const eq = abs(ans) <= eps;
494
- list.push(row(eq, `Equality |e^{iθ}+1| ≤ ε`, `|⋅| ≈ ${abs(ans).toExponential(6)} ≤ ${eps}`));
495
-
496
- const reOk = Math.abs(z.re + 1) <= eps;
497
- list.push(
498
- row(reOk, `Real part ≈ −1`, `Re ≈ ${z.re.toPrecision(12)}, |Re+1| ≈ ${Math.abs(z.re + 1).toExponential(6)}`),
499
- );
500
-
501
- const imOk = Math.abs(z.im) <= eps;
502
- list.push(row(imOk, `Imag part ≈ 0`, `Im ≈ ${z.im.toPrecision(12)}`));
503
-
504
- const seriesOk = abs(ansSeries) <= Math.max(eps, 1e-15);
505
- list.push(row(seriesOk, `Series (N=${N})`, `|series+1| ≈ ${abs(ansSeries).toExponential(6)}`));
506
-
507
- const squareOk = abs(add(mul(z, z), { re: -1, im: 0 })) <= eps * 10; // |(e^{iπ})^2 - 1| small
508
- list.push(
509
- row(
510
- squareOk,
511
- `Square check`,
512
- `|(e^{iθ})^2 − 1| ≈ ${abs(add(mul(z, z), { re: -1, im: 0 })).toExponential(6)}`,
513
- ),
514
- );
515
-
516
- const periodicOk = abs(cis(theta + TAU)).toExponential
517
- ? abs(add(cis(theta + TAU), { re: 1, im: 0 })) <= eps
518
- : true;
519
- list.push(
520
- row(
521
- periodicOk,
522
- `Periodicity`,
523
- `θ → θ+2π ⇒ |e^{i(θ+2π)}+1| ≈ ${abs(add(cis(theta + TAU), { re: 1, im: 0 })).toExponential(6)}`,
524
- ),
525
- );
526
-
527
- list.push(
528
- el('div', { class: 'row' }, [
529
- el('span', { class: 'badge ok' }, ['FACT']),
530
- el('span', { class: 'small muted' }, ['Euler’s formula: e^{ix}=cos x+i sin x; at x=π we get −1.']),
531
- ]),
532
- );
533
-
534
- checks.replaceChildren(...list);
535
- }
536
-
537
- const row = (ok, label, msg) =>
538
- el('div', { class: 'row' }, [
539
- el('span', { class: 'badge ' + (ok ? 'ok' : 'fail') }, [ok ? 'PASS' : 'FAIL']),
540
- el('span', { class: 'mono' }, [label]),
541
- document.createTextNode(' — '),
542
- el('span', { class: 'mono' }, [msg]),
543
- ]);
544
-
545
- // ---------- UI helper ----------
546
- const el = (tag, attrs = {}, children = []) => {
547
- const node = document.createElement(tag);
548
- Object.entries(attrs).forEach(([k, v]) => {
549
- if (k === 'class') node.className = v;
550
- else if (k === 'html') node.innerHTML = v;
551
- else if (k.startsWith('on')) node.addEventListener(k.slice(2).toLowerCase(), v);
552
- else node.setAttribute(k, v);
553
- });
554
- children.forEach((c) => node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c));
555
- return node;
556
- };
557
-
558
- document.getElementById('solve').onclick = evaluate;
559
- document.getElementById('clear').onclick = () => {
560
- document.getElementById('kInput').value = '0';
561
- document.getElementById('epsInput').value = '1e-12';
562
- document.getElementById('NInput').value = '40';
563
- document.getElementById('answer').textContent = '—';
564
- document.getElementById('theta').textContent = '—';
565
- document.getElementById('cis').textContent = '—';
566
- document.getElementById('seriesVal').textContent = '—';
567
- document.getElementById('checks').replaceChildren();
568
- };
569
-
570
- // ---------- Preloaded harnesses (≥ 9 including one invalid) ----------
571
- const harnessList = [
572
- { k: 0, title: 'θ = π (k=0) ⇒ pass' },
573
- { k: 1, title: 'θ = π + 2π (k=1) ⇒ pass' },
574
- { k: -1, title: 'θ = π − 2π (k=−1) ⇒ pass' },
575
- { k: 6, title: 'θ = π + 12π (k=6) ⇒ pass' },
576
- { k: 123, title: 'θ = π + 2π·123 ⇒ pass' },
577
- { k: 10 ** 6, title: 'θ = π + 2π·10^6 ⇒ pass (reduced)' },
578
- { k: 0, off: Math.PI / 2, title: 'Invalid (should fail): θ = π/2', negative: true },
579
- { k: 0, off: Math.PI / 3, title: 'Invalid (should fail): θ = π/3', negative: true },
580
- { k: 0, off: 355 / 113 - Math.PI, title: 'θ ≈ 355/113 (π approx) ⇒ nearly pass' },
581
- ];
582
-
583
- const harnessDiv = document.getElementById('harnesses');
584
-
585
- function makeHarnessCard(h) {
586
- const card = el('section', { class: 'card stack' });
587
- const head = el('div', { class: 'h-title' }, [
588
- el('div', {}, [el('strong', {}, [h.title])]),
589
- el('div', { class: 'small muted' }, ['Check (harness)']),
590
- ]);
591
- const kpis = el('div', { class: 'kpi' });
592
- const ans = el('div', { class: 'card' });
593
- ans.append(el('div', { class: 'k' }, ['Answer']), el('div', { class: 'v mono' }, ['—']));
594
- const thetaCard = el('div', { class: 'card' });
595
- thetaCard.append(el('div', { class: 'k' }, ['Angle θ']), el('div', { class: 'v mono' }, ['—']));
596
- const method = el('div', { class: 'card' });
597
- method.append(el('div', { class: 'k' }, ['Method']), el('div', { class: 'v mono' }, ['cos/sin + series']));
598
- kpis.append(ans, thetaCard, method);
599
-
600
- const reason = el('p', { class: 'muted small' }, [
601
- 'We check |e^{iθ}+1| ≤ ε with ε=1e−12, also confirm real/imag parts and series agreement.',
602
- ]);
603
- const checks = el('div', { class: 'list' });
604
- const run = el('button', { class: 'secondary' }, ['Run']);
605
-
606
- run.onclick = () => {
607
- const eps = 1e-12;
608
- const N = 40;
609
- const theta = h.off !== undefined ? h.off : Math.PI + TAU * h.k;
610
- const z = cis(theta);
611
- const sRe = cosSeries(theta, N),
612
- sIm = sinSeries(theta, N);
613
- const zSeries = { re: sRe, im: sIm };
614
- const ansZ = { re: z.re + 1, im: z.im };
615
- const ansSeriesZ = { re: zSeries.re + 1, im: zSeries.im };
616
-
617
- ans.querySelector('.v').textContent = `|e^{iθ}+1| ≈ ${abs(ansZ).toExponential(6)}`;
618
- thetaCard.querySelector('.v').textContent = `θ ≈ ${theta.toPrecision(8)}`;
619
-
620
- const list = [];
621
- const eq = abs(ansZ) <= eps;
622
- list.push(row(eq, 'Equality |e^{iθ}+1| ≤ ε', `|⋅| ≈ ${abs(ansZ).toExponential(6)} ≤ ${eps}`));
623
- const reOk = Math.abs(z.re + 1) <= eps;
624
- list.push(row(reOk, 'Real ≈ −1', `|Re+1| ≈ ${Math.abs(z.re + 1).toExponential(6)}`));
625
- const imOk = Math.abs(z.im) <= eps;
626
- list.push(row(imOk, 'Imag ≈ 0', `|Im| ≈ ${Math.abs(z.im).toExponential(6)}`));
627
- const seriesOk = abs(ansSeriesZ) <= Math.max(eps, 1e-15);
628
- list.push(row(seriesOk, `Series (N=${N})`, `|series+1| ≈ ${abs(ansSeriesZ).toExponential(6)}`));
629
- const squareOk = abs(add(mul(z, z), { re: -1, im: 0 })) <= eps * 10;
630
- list.push(
631
- row(
632
- squareOk,
633
- 'Square check',
634
- `|(e^{iθ})^2 − 1| ≈ ${abs(add(mul(z, z), { re: -1, im: 0 })).toExponential(6)}`,
635
- ),
636
- );
637
- const periodicOk = abs(add(cis(theta + TAU), { re: 1, im: 0 })) <= eps;
638
- list.push(row(periodicOk, 'Periodicity', `θ→θ+2π check`));
639
-
640
- checks.replaceChildren(...list);
641
- };
642
-
643
- if (h.negative) {
644
- const warn = el('div', { class: 'small muted' }, [
645
- 'This case is intentionally invalid and should report a failure.',
646
- ]);
647
- card.append(head, kpis, reason, warn, checks, run);
648
- } else {
649
- card.append(head, kpis, reason, checks, run);
650
- }
651
- return card;
652
- }
653
-
654
- harnessList.forEach((h) => harnessDiv.appendChild(makeHarnessCard(h)));
655
- document.getElementById('runAll').onclick = () => {
656
- [...harnessDiv.querySelectorAll('button.secondary')].forEach((b) => b.click());
657
- };
658
- document.getElementById('clearHarness').onclick = () => {
659
- harnessDiv.querySelectorAll('.list').forEach((div) => div.replaceChildren());
660
- harnessDiv.querySelectorAll('.v').forEach((div) => (div.textContent = '—'));
661
- };
662
-
663
- // auto-evaluate initial sample
664
- evaluate();
665
- </script>
666
- </body>
667
- </html>