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.
- package/README.md +0 -1
- package/package.json +2 -3
- package/arctifacts/README.md +0 -59
- package/arctifacts/ackermann.html +0 -678
- package/arctifacts/auroracare.html +0 -1297
- package/arctifacts/bike-trip.html +0 -752
- package/arctifacts/binomial-theorem.html +0 -631
- package/arctifacts/bmi.html +0 -511
- package/arctifacts/building-performance.html +0 -750
- package/arctifacts/clinical-care.html +0 -726
- package/arctifacts/collatz.html +0 -403
- package/arctifacts/complex.html +0 -321
- package/arctifacts/control-system.html +0 -482
- package/arctifacts/delfour.html +0 -849
- package/arctifacts/earthquake-epicenter.html +0 -982
- package/arctifacts/eco-route.html +0 -662
- package/arctifacts/euclid-infinitude.html +0 -564
- package/arctifacts/euler-identity.html +0 -667
- package/arctifacts/exoplanet-transit.html +0 -1000
- package/arctifacts/faltings-theorem.html +0 -1046
- package/arctifacts/fibonacci.html +0 -299
- package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
- package/arctifacts/godel-numbering.html +0 -743
- package/arctifacts/gps-bike.html +0 -759
- package/arctifacts/gps-clinical-bench.html +0 -792
- package/arctifacts/graph-french.html +0 -449
- package/arctifacts/grass-molecular.html +0 -592
- package/arctifacts/group-theory.html +0 -740
- package/arctifacts/health-info.html +0 -833
- package/arctifacts/kaprekar-constant.html +0 -576
- package/arctifacts/lee.html +0 -805
- package/arctifacts/linked-lists.html +0 -502
- package/arctifacts/lldm.html +0 -612
- package/arctifacts/matrix-multiplication.html +0 -502
- package/arctifacts/matrix.html +0 -651
- package/arctifacts/newton-raphson.html +0 -944
- package/arctifacts/peano-factorial.html +0 -456
- package/arctifacts/pi.html +0 -363
- package/arctifacts/polynomial.html +0 -646
- package/arctifacts/prime.html +0 -366
- package/arctifacts/pythagorean-theorem.html +0 -468
- package/arctifacts/rest-path.html +0 -469
- package/arctifacts/roots-of-unity.html +0 -363
- package/arctifacts/turing.html +0 -409
- package/arctifacts/wind-turbines.html +0 -726
|
@@ -1,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>
|