eyeling 1.16.3 → 1.16.5

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 (61) hide show
  1. package/HANDBOOK.md +153 -0
  2. package/README.md +0 -1
  3. package/examples/auroracare.n3 +528 -0
  4. package/examples/control-system.n3 +223 -47
  5. package/examples/delfour.n3 +409 -0
  6. package/examples/gps.n3 +144 -53
  7. package/examples/ill-formed-literals.n3 +195 -0
  8. package/examples/output/auroracare.n3 +118 -0
  9. package/examples/output/control-system.n3 +24 -1
  10. package/examples/output/delfour.n3 +40 -0
  11. package/examples/output/gps.n3 +14 -2
  12. package/examples/output/ill-formed-literals.n3 +27 -0
  13. package/examples/output/parcellocker.n3 +15 -0
  14. package/examples/parcellocker.n3 +164 -0
  15. package/eyeling.js +16 -1
  16. package/lib/engine.js +14 -1
  17. package/lib/prelude.js +2 -0
  18. package/package.json +2 -3
  19. package/arctifacts/README.md +0 -59
  20. package/arctifacts/ackermann.html +0 -678
  21. package/arctifacts/auroracare.html +0 -1297
  22. package/arctifacts/bike-trip.html +0 -752
  23. package/arctifacts/binomial-theorem.html +0 -631
  24. package/arctifacts/bmi.html +0 -511
  25. package/arctifacts/building-performance.html +0 -750
  26. package/arctifacts/clinical-care.html +0 -726
  27. package/arctifacts/collatz.html +0 -403
  28. package/arctifacts/complex.html +0 -321
  29. package/arctifacts/control-system.html +0 -482
  30. package/arctifacts/delfour.html +0 -849
  31. package/arctifacts/earthquake-epicenter.html +0 -982
  32. package/arctifacts/eco-route.html +0 -662
  33. package/arctifacts/euclid-infinitude.html +0 -564
  34. package/arctifacts/euler-identity.html +0 -667
  35. package/arctifacts/exoplanet-transit.html +0 -1000
  36. package/arctifacts/faltings-theorem.html +0 -1046
  37. package/arctifacts/fibonacci.html +0 -299
  38. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  39. package/arctifacts/godel-numbering.html +0 -743
  40. package/arctifacts/gps-bike.html +0 -759
  41. package/arctifacts/gps-clinical-bench.html +0 -792
  42. package/arctifacts/graph-french.html +0 -449
  43. package/arctifacts/grass-molecular.html +0 -592
  44. package/arctifacts/group-theory.html +0 -740
  45. package/arctifacts/health-info.html +0 -833
  46. package/arctifacts/kaprekar-constant.html +0 -576
  47. package/arctifacts/lee.html +0 -805
  48. package/arctifacts/linked-lists.html +0 -502
  49. package/arctifacts/lldm.html +0 -612
  50. package/arctifacts/matrix-multiplication.html +0 -502
  51. package/arctifacts/matrix.html +0 -651
  52. package/arctifacts/newton-raphson.html +0 -944
  53. package/arctifacts/peano-factorial.html +0 -456
  54. package/arctifacts/pi.html +0 -363
  55. package/arctifacts/polynomial.html +0 -646
  56. package/arctifacts/prime.html +0 -366
  57. package/arctifacts/pythagorean-theorem.html +0 -468
  58. package/arctifacts/rest-path.html +0 -469
  59. package/arctifacts/roots-of-unity.html +0 -363
  60. package/arctifacts/turing.html +0 -409
  61. 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>