eyeling 1.16.3 → 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 (45) hide show
  1. package/README.md +0 -1
  2. package/package.json +2 -3
  3. package/arctifacts/README.md +0 -59
  4. package/arctifacts/ackermann.html +0 -678
  5. package/arctifacts/auroracare.html +0 -1297
  6. package/arctifacts/bike-trip.html +0 -752
  7. package/arctifacts/binomial-theorem.html +0 -631
  8. package/arctifacts/bmi.html +0 -511
  9. package/arctifacts/building-performance.html +0 -750
  10. package/arctifacts/clinical-care.html +0 -726
  11. package/arctifacts/collatz.html +0 -403
  12. package/arctifacts/complex.html +0 -321
  13. package/arctifacts/control-system.html +0 -482
  14. package/arctifacts/delfour.html +0 -849
  15. package/arctifacts/earthquake-epicenter.html +0 -982
  16. package/arctifacts/eco-route.html +0 -662
  17. package/arctifacts/euclid-infinitude.html +0 -564
  18. package/arctifacts/euler-identity.html +0 -667
  19. package/arctifacts/exoplanet-transit.html +0 -1000
  20. package/arctifacts/faltings-theorem.html +0 -1046
  21. package/arctifacts/fibonacci.html +0 -299
  22. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  23. package/arctifacts/godel-numbering.html +0 -743
  24. package/arctifacts/gps-bike.html +0 -759
  25. package/arctifacts/gps-clinical-bench.html +0 -792
  26. package/arctifacts/graph-french.html +0 -449
  27. package/arctifacts/grass-molecular.html +0 -592
  28. package/arctifacts/group-theory.html +0 -740
  29. package/arctifacts/health-info.html +0 -833
  30. package/arctifacts/kaprekar-constant.html +0 -576
  31. package/arctifacts/lee.html +0 -805
  32. package/arctifacts/linked-lists.html +0 -502
  33. package/arctifacts/lldm.html +0 -612
  34. package/arctifacts/matrix-multiplication.html +0 -502
  35. package/arctifacts/matrix.html +0 -651
  36. package/arctifacts/newton-raphson.html +0 -944
  37. package/arctifacts/peano-factorial.html +0 -456
  38. package/arctifacts/pi.html +0 -363
  39. package/arctifacts/polynomial.html +0 -646
  40. package/arctifacts/prime.html +0 -366
  41. package/arctifacts/pythagorean-theorem.html +0 -468
  42. package/arctifacts/rest-path.html +0 -469
  43. package/arctifacts/roots-of-unity.html +0 -363
  44. package/arctifacts/turing.html +0 -409
  45. package/arctifacts/wind-turbines.html +0 -726
@@ -1,944 +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>Newton–Raphson Method</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
- }
87
- .muted {
88
- color: var(--muted);
89
- }
90
- .mono {
91
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
92
- color: var(--code);
93
- word-break: break-word;
94
- overflow-wrap: anywhere;
95
- white-space: normal;
96
- }
97
- .kpi {
98
- display: flex;
99
- gap: 16px;
100
- flex-wrap: wrap;
101
- }
102
- .kpi .card {
103
- padding: 12px 14px;
104
- }
105
- .k {
106
- font-weight: 700;
107
- }
108
- .v {
109
- font-variant-numeric: tabular-nums;
110
- word-break: break-word;
111
- overflow-wrap: anywhere;
112
- }
113
- .list {
114
- display: flex;
115
- flex-direction: column;
116
- gap: 12px;
117
- }
118
- .term {
119
- padding: 10px;
120
- background: #f8fafc;
121
- border: 1px dashed var(--border);
122
- border-radius: 12px;
123
- }
124
- details {
125
- border: 1px solid var(--border);
126
- border-radius: 12px;
127
- padding: 10px 12px;
128
- background: #fafcff;
129
- }
130
- details summary {
131
- cursor: pointer;
132
- font-weight: 700;
133
- color: #0b3eaa;
134
- }
135
- .badge {
136
- padding: 2px 8px;
137
- border-radius: 999px;
138
- font-weight: 700;
139
- font-size: 12px;
140
- border: 1px solid #cbd5e1;
141
- background: #f1f5f9;
142
- color: #0f172a;
143
- }
144
- .ok {
145
- background: #dcfce7;
146
- color: #166534;
147
- border: 1px solid #86efac;
148
- }
149
- .fail {
150
- background: #fee2e2;
151
- color: #991b1b;
152
- border: 1px solid #fecaca;
153
- }
154
- .harness {
155
- border-left: 6px solid #c7d2fe;
156
- }
157
- .h-title {
158
- display: flex;
159
- align-items: center;
160
- justify-content: space-between;
161
- gap: 8px;
162
- }
163
- .small {
164
- font-size: 12px;
165
- }
166
- code {
167
- background: #f1f5f9;
168
- padding: 2px 6px;
169
- border-radius: 6px;
170
- }
171
- footer {
172
- color: var(--muted);
173
- font-size: 12px;
174
- text-align: center;
175
- margin: 16px 0 40px;
176
- }
177
- .divider {
178
- height: 1px;
179
- background: var(--border);
180
- margin: -4px 0 8px;
181
- }
182
-
183
- /* INPUT: compact layout */
184
- .input-grid {
185
- display: flex;
186
- flex-wrap: nowrap;
187
- gap: 10px;
188
- }
189
- .field {
190
- display: flex;
191
- align-items: center;
192
- gap: 6px;
193
- }
194
- input.short {
195
- width: 120px;
196
- padding: 6px 8px;
197
- border: 1px solid var(--border);
198
- border-radius: 10px;
199
- background: #fbfdff;
200
- }
201
- input.long {
202
- width: 100%;
203
- padding: 8px 10px;
204
- border: 1px solid var(--border);
205
- border-radius: 10px;
206
- background: #fbfdff;
207
- }
208
- .buttons-row {
209
- display: flex;
210
- gap: 8px;
211
- flex-wrap: wrap;
212
- }
213
- @media (max-width: 720px) {
214
- .input-grid {
215
- flex-wrap: wrap;
216
- }
217
- }
218
-
219
- table {
220
- width: 100%;
221
- border-collapse: collapse;
222
- }
223
- th,
224
- td {
225
- text-align: left;
226
- padding: 6px 8px;
227
- border-bottom: 1px solid var(--border);
228
- font-variant-numeric: tabular-nums;
229
- }
230
- th {
231
- color: var(--muted);
232
- font-weight: 700;
233
- }
234
- </style>
235
- </head>
236
- <body>
237
- <div class="container stack">
238
- <header class="card stack">
239
- <div class="row" style="justify-content: space-between; align-items: flex-start; width: 100%">
240
- <div>
241
- <h1>Newton–Raphson method</h1>
242
- </div>
243
- </div>
244
- <span class="badge">Problem: x_{n+1} = x_n − f(x_n)/f′(x_n) to solve f(x)=0</span>
245
- <p class="muted">
246
- Provide <em>f(x)</em> (and optionally <em>f′(x)</em>), an initial guess <em>x₀</em>, tolerance <em>ε</em>, and
247
- max iterations <em>N</em>. The page computes the iterate, explains why Newton works (and when it fails), and
248
- verifies convergence with a test harness.
249
- </p>
250
- </header>
251
-
252
- <!-- INPUT -->
253
- <section class="card stack" id="input-card">
254
- <h2 style="margin: 0">Input</h2>
255
- <div class="divider"></div>
256
- <div class="stack">
257
- <div class="field" style="width: 100%">
258
- <label class="mono" for="fInput" style="min-width: 140px">f(x)</label
259
- ><input
260
- class="long"
261
- id="fInput"
262
- type="text"
263
- value="x^2 - 2"
264
- placeholder="Example: x^2 - 2, cos(x) - x, exp(x)-3" />
265
- </div>
266
- <div class="field" style="width: 100%">
267
- <label class="mono" for="dfInput" style="min-width: 140px">f′(x) (optional)</label
268
- ><input
269
- class="long"
270
- id="dfInput"
271
- type="text"
272
- value="2*x"
273
- placeholder="Leave blank to use numerical derivative" />
274
- </div>
275
- </div>
276
- <div class="row input-grid">
277
- <div class="field">
278
- <label class="mono" for="x0Input">x₀</label
279
- ><input class="short" id="x0Input" type="number" step="any" value="1" />
280
- </div>
281
- <div class="field">
282
- <label class="mono" for="epsInput">ε</label
283
- ><input class="short" id="epsInput" type="number" step="any" value="1e-12" />
284
- </div>
285
- <div class="field">
286
- <label class="mono" for="NInput">N</label
287
- ><input class="short" id="NInput" type="number" min="1" step="1" value="40" />
288
- </div>
289
- </div>
290
- <div class="row buttons-row">
291
- <button type="button" id="solve">Run Newton</button>
292
- <button type="button" id="clear">Clear</button>
293
- </div>
294
- <p class="small muted">
295
- <strong>N</strong> is the maximum number of iterations. If <span class="mono">f′(x)</span> is omitted, a
296
- centered finite‑difference derivative is used. Convergence is declared when both the step size and the
297
- residual are ≤ <span class="mono">ε</span> (scaled by magnitude). Supported syntax: numbers,
298
- <span class="mono">x</span>, operators <span class="mono">+ - * / ^</span>, parentheses, and functions
299
- <span class="mono"
300
- >sin, cos, tan, asin, acos, atan, exp, log (ln), log10, sqrt, cbrt, abs, floor, ceil, round</span
301
- >; constants <span class="mono">pi, e</span>.
302
- </p>
303
- </section>
304
-
305
- <!-- OUTPUT -->
306
- <section class="card stack" id="output-card" aria-live="polite">
307
- <h2 style="margin: 0">Output</h2>
308
- <div class="divider"></div>
309
-
310
- <!-- Answer KPIs -->
311
- <section class="stack">
312
- <div class="kpi">
313
- <div class="card">
314
- <div class="k">Approximate root</div>
315
- <div id="root" class="v mono">—</div>
316
- </div>
317
- <div class="card">
318
- <div class="k">Residual |f(x*)|</div>
319
- <div id="residual" class="v mono">—</div>
320
- </div>
321
- <div class="card">
322
- <div class="k">Iterations</div>
323
- <div id="iters" class="v mono">—</div>
324
- </div>
325
- <div class="card">
326
- <div class="k">Observed rate</div>
327
- <div id="rate" class="v mono">—</div>
328
- </div>
329
- <div class="card">
330
- <div class="k">Derivative used</div>
331
- <div id="dftype" class="v mono">—</div>
332
- </div>
333
- <div class="card">
334
- <div class="k">Status</div>
335
- <div id="status" class="v mono">—</div>
336
- </div>
337
- </div>
338
- </section>
339
-
340
- <!-- Iterates -->
341
- <details>
342
- <summary>Show iterates</summary>
343
- <div class="term">
344
- <table id="iterTable">
345
- <thead>
346
- <tr>
347
- <th>#</th>
348
- <th>x</th>
349
- <th>f(x)</th>
350
- <th>f′(x)</th>
351
- <th>|Δx|</th>
352
- </tr>
353
- </thead>
354
- <tbody></tbody>
355
- </table>
356
- </div>
357
- </details>
358
-
359
- <!-- Reason Why (mathematical English) -->
360
- <section class="stack">
361
- <h3 style="margin: 0">Reason Why</h3>
362
- <div class="list">
363
- <div class="term">
364
- <strong>Derivation.</strong> Taylor at <span class="mono">x_n</span> yields
365
- <span class="mono">0 = f(x_n)+f′(x_n)(r−x_n)+O((r−x_n)^2)</span>; solving gives
366
- <span class="mono">r ≈ x_n − f(x_n)/f′(x_n)</span>.
367
- </div>
368
- <div class="term">
369
- <strong>Quadratic convergence.</strong> For a simple root <span class="mono">r</span> with
370
- <span class="mono">f′(r)≠0</span>, <span class="mono">e_{n+1} ≈ (f''(r)/(2f'(r)))·e_n^2</span>; digits
371
- roughly double each step.
372
- </div>
373
- <div class="term">
374
- <strong>Failure modes.</strong> Small/zero <span class="mono">f′</span>, poor starting points, or multiple
375
- roots reduce/diverge. Damping/backtracking helps.
376
- </div>
377
- </div>
378
- </section>
379
-
380
- <!-- Check (harness) for the user's input -->
381
- <section class="stack">
382
- <h3 style="margin: 0">Check (harness)</h3>
383
- <div id="checks" class="list"></div>
384
- </section>
385
- </section>
386
-
387
- <!-- PRELOADED HARNESS CASES (≥ 10) -->
388
- <section class="card stack harness" id="preloaded">
389
- <div class="h-title">
390
- <h2 style="margin: 0">Preloaded Checks (harness)</h2>
391
- <div class="row">
392
- <button id="runAll">Run all</button><button class="ghost" id="clearHarness">Clear results</button>
393
- </div>
394
- </div>
395
- <p class="small muted">
396
- Each block runs Newton and reports the approximate root, residual, iterations, status, and observed rate. Some
397
- cases are intentionally tricky.
398
- </p>
399
- <div id="harnesses" class="stack"></div>
400
- </section>
401
-
402
- <footer>
403
- <div>Built as a self‑checking artifact: program → <em>Answer</em>, <em>Reason Why</em>, <em>Check</em>.</div>
404
- <div class="small">This page performs only on‑device computation.</div>
405
- </footer>
406
- </div>
407
-
408
- <script>
409
- // ===== Tiny expression parser (safe; no eval) =====
410
- function buildFn(src) {
411
- function isDigit(c) {
412
- return c >= '0' && c <= '9';
413
- }
414
- function isAlpha(c) {
415
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '_';
416
- }
417
- function isAlnum(c) {
418
- return isAlpha(c) || isDigit(c);
419
- }
420
- function tokenize(s) {
421
- const t = [];
422
- let i = 0;
423
- s = String(s || '').trim();
424
- const L = s.length;
425
- while (i < L) {
426
- const c = s[i];
427
- if (c === ' ') {
428
- i++;
429
- continue;
430
- }
431
- if (isDigit(c) || (c === '.' && isDigit(s[i + 1]))) {
432
- let j = i + 1;
433
- while (j < L && isDigit(s[j])) j++;
434
- if (s[j] === '.' && isDigit(s[j + 1])) {
435
- j++;
436
- while (j < L && isDigit(s[j])) j++;
437
- }
438
- if (s[j] === 'e' || s[j] === 'E') {
439
- let k = j + 1;
440
- if (s[k] === '+' || s[k] === '-') k++;
441
- while (k < L && isDigit(s[k])) k++;
442
- j = k;
443
- }
444
- t.push({ type: 'num', val: parseFloat(s.slice(i, j)) });
445
- i = j;
446
- continue;
447
- }
448
- if (isAlpha(c)) {
449
- let j = i + 1;
450
- while (j < L && isAlnum(s[j])) j++;
451
- t.push({ type: 'id', name: s.slice(i, j).toLowerCase() });
452
- i = j;
453
- continue;
454
- }
455
- if ('+-*/^(),'.indexOf(c) >= 0) {
456
- t.push({ type: c });
457
- i++;
458
- continue;
459
- }
460
- throw new Error('Invalid character: ' + c);
461
- }
462
- return t;
463
- }
464
- function toRPN(tokens) {
465
- const out = [],
466
- ops = [],
467
- funcs = [];
468
- let prev = null;
469
- const prec = { '+': 2, '-': 2, '*': 3, '/': 3, '^': 4, neg: 5 };
470
- const right = { '^': true, neg: true };
471
- for (let i = 0; i < tokens.length; i++) {
472
- const t = tokens[i];
473
- if (t.type === 'num') {
474
- out.push(t);
475
- prev = t;
476
- continue;
477
- }
478
- if (t.type === 'id') {
479
- const next = tokens[i + 1];
480
- if (next && next.type === '(') {
481
- funcs.push(t.name);
482
- ops.push({ type: '(' });
483
- prev = { type: '(' };
484
- i++;
485
- continue;
486
- }
487
- out.push(t);
488
- prev = t;
489
- continue;
490
- }
491
- if (t.type === '(') {
492
- ops.push(t);
493
- prev = t;
494
- continue;
495
- }
496
- if (t.type === ')') {
497
- while (ops.length && ops[ops.length - 1].type !== '(') out.push(ops.pop());
498
- if (!ops.length) throw new Error('Mismatched parentheses');
499
- ops.pop();
500
- if (funcs.length) {
501
- out.push({ type: 'func', name: funcs.pop() });
502
- }
503
- prev = t;
504
- continue;
505
- }
506
- if (t.type === ',') {
507
- while (ops.length && ops[ops.length - 1].type !== '(') out.push(ops.pop());
508
- continue;
509
- }
510
- let op = t.type;
511
- if (op === '-' && (!prev || (prev.type !== ')' && prev.type !== 'num' && prev.type !== 'id'))) op = 'neg';
512
- while (ops.length) {
513
- const top = ops[ops.length - 1];
514
- if (top.type === '(') break;
515
- const pT = prec[top.type],
516
- pO = prec[op];
517
- if (pT > pO || (pT === pO && !right[op])) out.push(ops.pop());
518
- else break;
519
- }
520
- ops.push({ type: op });
521
- prev = t;
522
- }
523
- while (ops.length) {
524
- const o = ops.pop();
525
- if (o.type === '(' || o.type === ')') throw new Error('Mismatched parentheses');
526
- out.push(o);
527
- }
528
- return out;
529
- }
530
- function evalRPN(rpn, x) {
531
- const st = [];
532
- function fn1(n, a) {
533
- if (n === 'sin') return Math.sin(a);
534
- if (n === 'cos') return Math.cos(a);
535
- if (n === 'tan') return Math.tan(a);
536
- if (n === 'asin') return Math.asin(a);
537
- if (n === 'acos') return Math.acos(a);
538
- if (n === 'atan') return Math.atan(a);
539
- if (n === 'exp') return Math.exp(a);
540
- if (n === 'log' || n === 'ln') return Math.log(a);
541
- if (n === 'log10') return Math.log10 ? Math.log10(a) : Math.log(a) / Math.LN10;
542
- if (n === 'sqrt') return Math.sqrt(a);
543
- if (n === 'cbrt') return Math.cbrt ? Math.cbrt(a) : Math.sign(a) * Math.pow(Math.abs(a), 1 / 3);
544
- if (n === 'abs') return Math.abs(a);
545
- if (n === 'floor') return Math.floor(a);
546
- if (n === 'ceil') return Math.ceil(a);
547
- if (n === 'round') return Math.round(a);
548
- throw new Error('Unknown function: ' + n);
549
- }
550
- for (let i = 0; i < rpn.length; i++) {
551
- const t = rpn[i];
552
- if (t.type === 'num') {
553
- st.push(t.val);
554
- continue;
555
- }
556
- if (t.type === 'id') {
557
- if (t.name === 'x') {
558
- st.push(x);
559
- continue;
560
- }
561
- if (t.name === 'pi') {
562
- st.push(Math.PI);
563
- continue;
564
- }
565
- if (t.name === 'e') {
566
- st.push(Math.E);
567
- continue;
568
- }
569
- throw new Error('Unknown symbol: ' + t.name);
570
- }
571
- if (t.type === 'func') {
572
- const a = st.pop();
573
- st.push(fn1(t.name, a));
574
- continue;
575
- }
576
- if (t.type === 'neg') {
577
- const a = st.pop();
578
- st.push(-a);
579
- continue;
580
- }
581
- if (t.type === '+' || t.type === '-' || t.type === '*' || t.type === '/' || t.type === '^') {
582
- const b = st.pop(),
583
- a = st.pop();
584
- let v;
585
- if (t.type === '+') v = a + b;
586
- else if (t.type === '-') v = a - b;
587
- else if (t.type === '*') v = a * b;
588
- else if (t.type === '/') v = a / b;
589
- else v = Math.pow(a, b);
590
- st.push(v);
591
- continue;
592
- }
593
- throw new Error('Bad token');
594
- }
595
- if (st.length !== 1) throw new Error('Evaluation error');
596
- return st[0];
597
- }
598
- const rpn = toRPN(tokenize(src));
599
- return function (x) {
600
- return evalRPN(rpn, x);
601
- };
602
- }
603
-
604
- // ===== Newton solver =====
605
- function numericDf(f, x) {
606
- const h = 1e-8 * (1 + Math.abs(x));
607
- return (f(x + h) - f(x - h)) / (2 * h);
608
- }
609
- function runNewton(f, df, x0, eps = 1e-12, maxIter = 40) {
610
- const iters = [];
611
- let x = x0;
612
- let status = 'running';
613
- let used = 0;
614
- const dftype = df ? 'analytic' : 'numerical';
615
- for (let k = 0; k < maxIter; k++) {
616
- let fx = f(x);
617
- if (!isFinite(fx)) {
618
- status = 'f(x) not finite';
619
- break;
620
- }
621
- let dfx = df ? df(x) : numericDf(f, x);
622
- if (!isFinite(dfx)) {
623
- status = "f'(x) not finite";
624
- break;
625
- }
626
- if (dfx === 0) {
627
- status = 'derivative zero';
628
- iters.push({ k, x, fx, dfx, dx: NaN });
629
- break;
630
- }
631
- const dx = -fx / dfx;
632
- const x1 = x + dx;
633
- iters.push({ k, x, fx, dfx, dx: Math.abs(dx) });
634
- x = x1;
635
- used = k + 1;
636
- const stepOk = Math.abs(dx) <= eps * (1 + Math.abs(x));
637
- const resOk = Math.abs(f(x)) <= eps;
638
- if (stepOk && resOk) {
639
- status = 'converged';
640
- break;
641
- }
642
- if (!isFinite(x)) {
643
- status = 'x not finite';
644
- break;
645
- }
646
- }
647
- if (status === 'running') status = 'max iterations reached';
648
- const root = x,
649
- residual = Math.abs(f(root));
650
- let rate = '—';
651
- if (iters.length >= 3) {
652
- const e_n = iters[iters.length - 1].dx,
653
- e_nm1 = iters[iters.length - 2].dx;
654
- if (e_n > 0 && e_nm1 > 0) {
655
- const q = e_n / (e_nm1 * e_nm1);
656
- if (isFinite(q)) rate = q.toExponential(3);
657
- }
658
- }
659
- return { root, residual, used, status, iters, dftype, rate };
660
- }
661
-
662
- // ===== UI helpers =====
663
- const el = (tag, attrs = {}, children = []) => {
664
- const node = document.createElement(tag);
665
- Object.entries(attrs).forEach(([k, v]) => {
666
- if (k === 'class') node.className = v;
667
- else if (k === 'html') node.innerHTML = v;
668
- else if (k.startsWith('on')) node.addEventListener(k.slice(2).toLowerCase(), v);
669
- else node.setAttribute(k, v);
670
- });
671
- children.forEach((c) => node.appendChild(typeof c === 'string' ? document.createTextNode(c) : c));
672
- return node;
673
- };
674
- const row = (ok, label, msg) =>
675
- el('div', { class: 'row' }, [
676
- el('span', { class: 'badge ' + (ok ? 'ok' : 'fail') }, [ok ? 'PASS' : 'FAIL']),
677
- el('span', { class: 'mono' }, [label]),
678
- document.createTextNode(' — '),
679
- el('span', { class: 'mono' }, [msg]),
680
- ]);
681
-
682
- function showError(msg) {
683
- document.getElementById('root').textContent = '—';
684
- document.getElementById('residual').textContent = '—';
685
- document.getElementById('iters').textContent = '—';
686
- document.getElementById('rate').textContent = '—';
687
- document.getElementById('dftype').textContent = '—';
688
- document.getElementById('status').textContent = msg;
689
- const tbody = document.querySelector('#iterTable tbody');
690
- if (tbody) tbody.replaceChildren();
691
- const checks = document.getElementById('checks');
692
- if (checks)
693
- checks.replaceChildren(
694
- el('div', { class: 'term' }, [
695
- el('span', { class: 'badge fail' }, ['FAIL']),
696
- document.createTextNode(' ' + msg),
697
- ]),
698
- );
699
- }
700
-
701
- function renderResult(R) {
702
- const fmt = (v, prec = 12) => (typeof v === 'number' && isFinite(v) ? v.toPrecision(prec) : String(v));
703
- document.getElementById('root').textContent = fmt(R.root);
704
- document.getElementById('residual').textContent = isFinite(R.residual)
705
- ? R.residual.toExponential(6)
706
- : String(R.residual);
707
- document.getElementById('iters').textContent = String(R.used);
708
- document.getElementById('rate').textContent = R.rate;
709
- document.getElementById('dftype').textContent = R.dftype;
710
- document.getElementById('status').textContent = R.status;
711
- const tbody = document.querySelector('#iterTable tbody');
712
- const rows = R.iters.map((it) =>
713
- el('tr', {}, [
714
- el('td', {}, [String(it.k)]),
715
- el('td', { class: 'mono' }, [fmt(it.x)]),
716
- el('td', { class: 'mono' }, [
717
- typeof it.fx === 'number' && isFinite(it.fx) ? it.fx.toExponential(6) : String(it.fx),
718
- ]),
719
- el('td', { class: 'mono' }, [
720
- typeof it.dfx === 'number' && isFinite(it.dfx) ? it.dfx.toExponential(6) : String(it.dfx),
721
- ]),
722
- el('td', { class: 'mono' }, [
723
- typeof it.dx === 'number' && isFinite(it.dx) ? it.dx.toExponential(6) : String(it.dx),
724
- ]),
725
- ]),
726
- );
727
- tbody.replaceChildren(...rows);
728
- renderChecks(R);
729
- }
730
-
731
- function renderChecks(R) {
732
- const checks = document.getElementById('checks');
733
- const list = [];
734
- const conv = R.status === 'converged';
735
- list.push(
736
- row(
737
- conv,
738
- 'Convergence',
739
- `status=${R.status}, |f(x*)|≈${isFinite(R.residual) ? R.residual.toExponential(3) : R.residual}`,
740
- ),
741
- );
742
- const stepDecr = R.iters.length >= 3 ? R.iters[R.iters.length - 1].dx <= R.iters[R.iters.length - 2].dx : true;
743
- let lastDx = '—';
744
- if (R.iters.length) {
745
- const d = R.iters[R.iters.length - 1].dx;
746
- lastDx = typeof d === 'number' && isFinite(d) ? d.toExponential(3) : String(d);
747
- }
748
- list.push(row(stepDecr, 'Step sizes non‑increasing (tail)', `last Δx≈${lastDx}`));
749
- list.push(row(R.rate !== '—', 'Rate computable (≈quadratic if simple root)', `q≈${R.rate}`));
750
- checks.replaceChildren(...list);
751
- }
752
-
753
- function evaluate() {
754
- try {
755
- const fSrc = document.getElementById('fInput').value.trim();
756
- const dfSrc = document.getElementById('dfInput').value.trim();
757
- const x0 = Number(document.getElementById('x0Input').value);
758
- const eps = Number(document.getElementById('epsInput').value);
759
- const N = Math.max(1, Number(document.getElementById('NInput').value));
760
- let f,
761
- df = null;
762
- try {
763
- f = buildFn(fSrc);
764
- } catch (e) {
765
- showError('Invalid f(x): ' + e.message);
766
- return;
767
- }
768
- if (dfSrc) {
769
- try {
770
- df = buildFn(dfSrc);
771
- } catch (e) {
772
- df = null;
773
- }
774
- }
775
- const R = runNewton(f, df, x0, eps, N);
776
- renderResult(R);
777
- } catch (err) {
778
- showError('Runtime error: ' + (err && err.message ? err.message : String(err)));
779
- }
780
- }
781
-
782
- // ===== Harness =====
783
- const harnessList = [
784
- { title: '√2 via f=x^2−2', f: 'x^2-2', df: '2*x', x0: 1, eps: 1e-12, N: 30, expect: Math.SQRT2 },
785
- { title: 'cos x − x', f: 'cos(x)-x', df: '-sin(x)-1', x0: 0.5, eps: 1e-12, N: 40, expect: 0.7390851332151607 },
786
- { title: 'x^3 − 1', f: 'x^3-1', df: '3*x^2', x0: 0.3, eps: 1e-12, N: 30, expect: 1 },
787
- { title: 'x^3 − 2', f: 'x^3-2', df: '3*x^2', x0: 1, eps: 1e-12, N: 30, expect: 1.2599210498948732 },
788
- { title: 'exp(x) − 3', f: 'exp(x)-3', df: 'exp(x)', x0: 1, eps: 1e-12, N: 30, expect: Math.log(3) },
789
- {
790
- title: 'Multiple root (linear conv): (x−1)^2',
791
- f: '(x-1)^2',
792
- df: '2*(x-1)',
793
- x0: 2,
794
- eps: 1e-12,
795
- N: 40,
796
- expect: 1,
797
- },
798
- {
799
- title: 'Numeric derivative only: sin(x)→0',
800
- f: 'sin(x)',
801
- df: '',
802
- x0: 3.1,
803
- eps: 1e-12,
804
- N: 40,
805
- expect: Math.PI,
806
- },
807
- {
808
- title: 'Derivative zero at start (should fail): cos x',
809
- f: 'cos(x)',
810
- df: '-sin(x)',
811
- x0: 0,
812
- eps: 1e-12,
813
- N: 5,
814
- negative: true,
815
- },
816
- {
817
- title: 'Likely divergence: tan x near π/2',
818
- f: 'tan(x)',
819
- df: '1+tan(x)^2',
820
- x0: 1.5,
821
- eps: 1e-12,
822
- N: 10,
823
- negative: true,
824
- },
825
- { title: 'x^2 − 1 from left', f: 'x^2-1', df: '2*x', x0: -2, eps: 1e-12, N: 30, expect: -1 },
826
- ];
827
-
828
- function makeHarnessCard(h) {
829
- const card = el('section', { class: 'card stack' });
830
- const head = el('div', { class: 'h-title' }, [
831
- el('div', {}, [el('strong', {}, [h.title])]),
832
- el('div', { class: 'small muted' }, ['Check (harness)']),
833
- ]);
834
- const kpis = el('div', { class: 'kpi' });
835
- const root = el('div', { class: 'card' });
836
- root.append(el('div', { class: 'k' }, ['Root']), el('div', { class: 'v mono' }, ['—']));
837
- const resid = el('div', { class: 'card' });
838
- resid.append(el('div', { class: 'k' }, ['Residual']), el('div', { class: 'v mono' }, ['—']));
839
- const iters = el('div', { class: 'card' });
840
- iters.append(el('div', { class: 'k' }, ['Iterations']), el('div', { class: 'v mono' }, ['—']));
841
- const status = el('div', { class: 'card' });
842
- status.append(el('div', { class: 'k' }, ['Status']), el('div', { class: 'v mono' }, ['—']));
843
- kpis.append(root, resid, iters, status);
844
- const reason = el('p', { class: 'muted small' }, [
845
- 'Newton update x→x−f/f′; simple roots ~quadratic; multiple roots ~linear; zero derivative or poor start may fail.',
846
- ]);
847
- const checks = el('div', { class: 'list' });
848
- const run = el('button', { class: 'secondary' }, ['Run']);
849
- run.onclick = () => {
850
- let f,
851
- df = null;
852
- try {
853
- f = buildFn(h.f);
854
- } catch {
855
- checks.replaceChildren(
856
- el('div', { class: 'term' }, [
857
- el('span', { class: 'badge fail' }, ['FAIL']),
858
- document.createTextNode(' invalid f(x)'),
859
- ]),
860
- );
861
- return;
862
- }
863
- if (h.df) {
864
- try {
865
- df = buildFn(h.df);
866
- } catch {
867
- df = null;
868
- }
869
- }
870
- const R = runNewton(f, df, h.x0, h.eps, h.N);
871
- root.querySelector('.v').textContent =
872
- typeof R.root === 'number' && isFinite(R.root) ? R.root.toPrecision(12) : String(R.root);
873
- resid.querySelector('.v').textContent =
874
- typeof R.residual === 'number' && isFinite(R.residual) ? R.residual.toExponential(6) : String(R.residual);
875
- iters.querySelector('.v').textContent = String(R.used);
876
- status.querySelector('.v').textContent = R.status;
877
- const list = [];
878
- const conv = R.status === 'converged';
879
- list.push(row(conv, 'Convergence', `status=${R.status}`));
880
- if (typeof h.expect === 'number') {
881
- const close =
882
- typeof R.root === 'number' &&
883
- isFinite(R.root) &&
884
- Math.abs(R.root - h.expect) <= Math.max(1e-10, h.eps * 10);
885
- list.push(
886
- row(
887
- close,
888
- 'Matches expected root',
889
- `x*≈${typeof R.root === 'number' && isFinite(R.root) ? R.root.toPrecision(12) : R.root} vs ${h.expect.toPrecision ? h.expect.toPrecision(12) : String(h.expect)}`,
890
- ),
891
- );
892
- }
893
- if (h.negative) {
894
- list.push(row(!conv, 'Intended to not converge', `status=${R.status}`));
895
- }
896
- checks.replaceChildren(...list);
897
- };
898
- if (h.negative) {
899
- const warn = el('div', { class: 'small muted' }, [
900
- 'This case is intentionally difficult and may not converge.',
901
- ]);
902
- card.append(head, kpis, reason, warn, checks, run);
903
- } else {
904
- card.append(head, kpis, reason, checks, run);
905
- }
906
- return card;
907
- }
908
-
909
- function buildHarness() {
910
- const harnessDiv = document.getElementById('harnesses');
911
- harnessDiv.replaceChildren();
912
- harnessList.forEach((h) => harnessDiv.appendChild(makeHarnessCard(h)));
913
- document.getElementById('runAll').onclick = () => {
914
- [...harnessDiv.querySelectorAll('button.secondary')].forEach((b) => b.click());
915
- };
916
- document.getElementById('clearHarness').onclick = () => {
917
- harnessDiv.querySelectorAll('.list').forEach((div) => div.replaceChildren());
918
- harnessDiv.querySelectorAll('.v').forEach((div) => (div.textContent = '—'));
919
- };
920
- }
921
-
922
- // ===== Wire up =====
923
- document.addEventListener('DOMContentLoaded', () => {
924
- document.getElementById('solve').addEventListener('click', evaluate);
925
- document.getElementById('clear').addEventListener('click', () => {
926
- document.getElementById('fInput').value = 'x^2 - 2';
927
- document.getElementById('dfInput').value = '2*x';
928
- document.getElementById('x0Input').value = '1';
929
- document.getElementById('epsInput').value = '1e-12';
930
- document.getElementById('NInput').value = '40';
931
- evaluate();
932
- });
933
- buildHarness();
934
- evaluate();
935
- });
936
-
937
- window.addEventListener('error', (e) => {
938
- try {
939
- document.getElementById('status').textContent = 'JS error: ' + e.message;
940
- } catch (_) {}
941
- });
942
- </script>
943
- </body>
944
- </html>