eyeling 1.16.2 → 1.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/HANDBOOK.md +4 -0
  2. package/README.md +0 -1
  3. package/examples/ershov-mixed-computation.n3 +106 -0
  4. package/examples/output/ershov-mixed-computation.n3 +15 -0
  5. package/eyeling.js +510 -263
  6. package/lib/cli.js +22 -12
  7. package/lib/engine.js +488 -251
  8. package/package.json +2 -3
  9. package/arctifacts/README.md +0 -59
  10. package/arctifacts/ackermann.html +0 -678
  11. package/arctifacts/auroracare.html +0 -1297
  12. package/arctifacts/bike-trip.html +0 -752
  13. package/arctifacts/binomial-theorem.html +0 -631
  14. package/arctifacts/bmi.html +0 -511
  15. package/arctifacts/building-performance.html +0 -750
  16. package/arctifacts/clinical-care.html +0 -726
  17. package/arctifacts/collatz.html +0 -403
  18. package/arctifacts/complex.html +0 -321
  19. package/arctifacts/control-system.html +0 -482
  20. package/arctifacts/delfour.html +0 -849
  21. package/arctifacts/earthquake-epicenter.html +0 -982
  22. package/arctifacts/eco-route.html +0 -662
  23. package/arctifacts/euclid-infinitude.html +0 -564
  24. package/arctifacts/euler-identity.html +0 -667
  25. package/arctifacts/exoplanet-transit.html +0 -1000
  26. package/arctifacts/faltings-theorem.html +0 -1046
  27. package/arctifacts/fibonacci.html +0 -299
  28. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  29. package/arctifacts/godel-numbering.html +0 -743
  30. package/arctifacts/gps-bike.html +0 -759
  31. package/arctifacts/gps-clinical-bench.html +0 -792
  32. package/arctifacts/graph-french.html +0 -449
  33. package/arctifacts/grass-molecular.html +0 -592
  34. package/arctifacts/group-theory.html +0 -740
  35. package/arctifacts/health-info.html +0 -833
  36. package/arctifacts/kaprekar-constant.html +0 -576
  37. package/arctifacts/lee.html +0 -805
  38. package/arctifacts/linked-lists.html +0 -502
  39. package/arctifacts/lldm.html +0 -612
  40. package/arctifacts/matrix-multiplication.html +0 -502
  41. package/arctifacts/matrix.html +0 -651
  42. package/arctifacts/newton-raphson.html +0 -944
  43. package/arctifacts/peano-factorial.html +0 -456
  44. package/arctifacts/pi.html +0 -363
  45. package/arctifacts/polynomial.html +0 -646
  46. package/arctifacts/prime.html +0 -366
  47. package/arctifacts/pythagorean-theorem.html +0 -468
  48. package/arctifacts/rest-path.html +0 -469
  49. package/arctifacts/roots-of-unity.html +0 -363
  50. package/arctifacts/turing.html +0 -409
  51. package/arctifacts/wind-turbines.html +0 -726
@@ -1,651 +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>Matrix basics</title>
7
- <style>
8
- :root {
9
- --bg: #ffffff;
10
- --ink: #111827;
11
- --muted: #6b7280;
12
- --panel: #f8fafc;
13
- --border: #e5e7eb;
14
- --accent: #0ea5e9;
15
- --good: #16a34a;
16
- --amber: #f59e0b;
17
- --blue: #3b82f6;
18
- --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
19
- --ui:
20
- system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', 'Apple Color Emoji',
21
- 'Segoe UI Emoji';
22
- }
23
- body {
24
- margin: 0;
25
- background: var(--bg);
26
- color: var(--ink);
27
- font: 15px/1.6 var(--ui);
28
- }
29
- .wrap {
30
- max-width: 980px;
31
- margin: 36px auto;
32
- padding: 0 16px;
33
- }
34
- header {
35
- display: flex;
36
- align-items: center;
37
- justify-content: space-between;
38
- margin-bottom: 16px;
39
- }
40
- h1 {
41
- font-size: 22px;
42
- margin: 0;
43
- letter-spacing: 0.2px;
44
- }
45
- .pill {
46
- display: inline-flex;
47
- align-items: center;
48
- gap: 8px;
49
- padding: 6px 10px;
50
- border: 1px solid var(--border);
51
- border-radius: 999px;
52
- color: var(--muted);
53
- background: #fff;
54
- }
55
- .row {
56
- display: flex;
57
- flex-direction: column;
58
- gap: 16px;
59
- }
60
- .card {
61
- background: var(--panel);
62
- border: 1px solid var(--border);
63
- border-radius: 14px;
64
- overflow: hidden;
65
- }
66
- .head {
67
- display: flex;
68
- align-items: center;
69
- justify-content: space-between;
70
- padding: 10px 12px;
71
- border-bottom: 1px solid var(--border);
72
- }
73
- .head h2 {
74
- font-size: 13px;
75
- text-transform: uppercase;
76
- letter-spacing: 0.08em;
77
- color: var(--muted);
78
- margin: 0;
79
- }
80
- .body {
81
- padding: 12px;
82
- }
83
- pre {
84
- white-space: pre-wrap;
85
- background: #fff;
86
- border: 1px solid var(--border);
87
- border-radius: 10px;
88
- padding: 12px;
89
- overflow: auto;
90
- font-family: var(--mono);
91
- font-size: 13.25px;
92
- }
93
- code {
94
- background: #fff;
95
- border: 1px solid var(--border);
96
- border-radius: 6px;
97
- padding: 0 6px;
98
- }
99
- .arc-grid {
100
- display: flex;
101
- flex-direction: column;
102
- gap: 12px;
103
- }
104
- .arc-card {
105
- background: #fff;
106
- border: 1px solid var(--border);
107
- border-radius: 14px;
108
- overflow: hidden;
109
- }
110
- .arc-card .ac-head {
111
- display: flex;
112
- align-items: center;
113
- gap: 8px;
114
- padding: 10px 12px;
115
- border-bottom: 1px solid var(--border);
116
- font-size: 12px;
117
- letter-spacing: 0.08em;
118
- text-transform: uppercase;
119
- color: var(--muted);
120
- }
121
- .arc-card .ac-body {
122
- padding: 12px;
123
- }
124
- .arc-card.answer {
125
- border-left: 4px solid var(--good);
126
- }
127
- .arc-card.reason {
128
- border-left: 4px solid var(--blue);
129
- }
130
- .arc-card.check {
131
- border-left: 4px solid var(--amber);
132
- }
133
- .btn {
134
- all: unset;
135
- background: #fff;
136
- border: 1px solid var(--border);
137
- padding: 8px 12px;
138
- border-radius: 10px;
139
- cursor: pointer;
140
- }
141
- .btn:hover {
142
- border-color: #cbd5e1;
143
- }
144
- </style>
145
- </head>
146
- <body>
147
- <div class="wrap">
148
- <header>
149
- <h1>Matrix basics</h1>
150
- <div class="pill" id="status">Ready</div>
151
- </header>
152
-
153
- <div class="card" style="margin-bottom: 16px">
154
- <div class="body">
155
- <p><strong>What this is?</strong> A self‑contained, browser‑only reproduction of five matrix calls:</p>
156
- <ul>
157
- <li><code>Matrix([[1, 3], [-2, 3]]) * Matrix([[0, 3], [0, 7]])</code></li>
158
- <li><code>Matrix([[1, 3], [-2, 3]]) ** 2</code></li>
159
- <li><code>Matrix([[1, 3], [-2, 3]]) ** -1</code> (2×2 inverse via adjugate/determinant)</li>
160
- <li><code>Matrix([[1, 0, 1], [2, -1, 3], [4, 3, 2]]).det()</code> (Sarrus/Laplace)</li>
161
- <li>
162
- <code>Matrix([[3, -2, 4, -2], [5, 3, -3, -2], [5, -2, 2, -2], [5, -2, -3, 3]]).eigenvals()</code>
163
- (Faddeev–LeVerrier → characteristic polynomial → integer roots)
164
- </li>
165
- </ul>
166
- <p>
167
- The harness then checks the answers with algebraic identities (e.g., Cayley–Hamilton, inverse correctness).
168
- </p>
169
- </div>
170
- </div>
171
-
172
- <div class="row">
173
- <div class="card">
174
- <div class="head">
175
- <h2>Controls</h2>
176
- <div style="display: flex; gap: 8px; align-items: center">
177
- <button class="btn" onclick="run()">▶ Run</button>
178
- <button class="btn" onclick="downloadARC()">⬇ Export .txt</button>
179
- </div>
180
- </div>
181
- <div class="body">Deterministic run (no inputs). Auto‑runs on load.</div>
182
- </div>
183
-
184
- <div class="card">
185
- <div class="head"><h2>ARC Output</h2></div>
186
- <div class="body">
187
- <div class="arc-grid">
188
- <div class="arc-card answer">
189
- <div class="ac-head">Answer</div>
190
- <div class="ac-body"><pre id="ans">(no run yet)</pre></div>
191
- </div>
192
- <div class="arc-card reason">
193
- <div class="ac-head">Reason why</div>
194
- <div class="ac-body"><pre id="why">(no run yet)</pre></div>
195
- </div>
196
- <div class="arc-card check">
197
- <div class="ac-head">Check (harness)</div>
198
- <div class="ac-body"><pre id="chk">(no run yet)</pre></div>
199
- </div>
200
- </div>
201
- </div>
202
- </div>
203
- </div>
204
- </div>
205
-
206
- <script>
207
- // -------- Fractions (exact) --------
208
- function gcd(a, b) {
209
- a = Math.abs(a);
210
- b = Math.abs(b);
211
- while (b) {
212
- const t = b;
213
- b = a % b;
214
- a = t;
215
- }
216
- return a || 1;
217
- }
218
- function makeF(n, d) {
219
- if (d === 0) throw new Error('den 0');
220
- if (d < 0) {
221
- n = -n;
222
- d = -d;
223
- }
224
- const g = gcd(n, d);
225
- return { n: Math.trunc(n / g), d: Math.trunc(d / g) };
226
- }
227
- function toF(x) {
228
- return x && typeof x === 'object' && 'n' in x && 'd' in x ? x : { n: Math.trunc(x), d: 1 };
229
- }
230
- function addF(a, b) {
231
- a = toF(a);
232
- b = toF(b);
233
- return makeF(a.n * b.d + b.n * a.d, a.d * b.d);
234
- }
235
- function subF(a, b) {
236
- a = toF(a);
237
- b = toF(b);
238
- return makeF(a.n * b.d - b.n * a.d, a.d * b.d);
239
- }
240
- function mulF(a, b) {
241
- a = toF(a);
242
- b = toF(b);
243
- return makeF(a.n * b.n, a.d * b.d);
244
- }
245
- function divF(a, b) {
246
- a = toF(a);
247
- b = toF(b);
248
- return makeF(a.n * b.d, a.d * b.n);
249
- }
250
- function eqF(a, b) {
251
- a = toF(a);
252
- b = toF(b);
253
- return a.n === b.n && a.d === b.d;
254
- }
255
- function f2s(a) {
256
- a = toF(a);
257
- return a.d === 1 ? String(a.n) : a.n + '/' + a.d;
258
- }
259
-
260
- // -------- Matrix ops (fractions) --------
261
- function matStr(M) {
262
- return 'Matrix([' + M.map((r) => '[' + r.map(f2s).join(', ') + ']').join(', ') + '])';
263
- }
264
- function shape(A) {
265
- return [A.length, A[0].length];
266
- }
267
- function identityFrac(n) {
268
- return Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => makeF(i === j ? 1 : 0, 1)));
269
- }
270
- function zeros(n, m) {
271
- return Array.from({ length: n }, () => Array.from({ length: m }, () => makeF(0, 1)));
272
- }
273
- function matMul(A, B) {
274
- const n = A.length,
275
- m = A[0].length,
276
- m2 = B.length,
277
- p = B[0].length;
278
- if (m !== m2) throw new Error('shape mismatch');
279
- const C = zeros(n, p);
280
- for (let i = 0; i < n; i++)
281
- for (let j = 0; j < p; j++) {
282
- let s = makeF(0, 1);
283
- for (let k = 0; k < m; k++) s = addF(s, mulF(A[i][k], B[k][j]));
284
- C[i][j] = s;
285
- }
286
- return C;
287
- }
288
- function matAdd(A, B) {
289
- const n = A.length,
290
- m = A[0].length;
291
- const C = zeros(n, m);
292
- for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) C[i][j] = addF(A[i][j], B[i][j]);
293
- return C;
294
- }
295
- function matScalar(A, s) {
296
- s = toF(s);
297
- const n = A.length,
298
- m = A[0].length;
299
- const C = zeros(n, m);
300
- for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) C[i][j] = mulF(A[i][j], s);
301
- return C;
302
- }
303
- function matEq(A, B) {
304
- const n = A.length,
305
- m = A[0].length;
306
- for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) if (!eqF(A[i][j], B[i][j])) return false;
307
- return true;
308
- }
309
- function det2(A) {
310
- return subF(mulF(A[0][0], A[1][1]), mulF(A[0][1], A[1][0]));
311
- }
312
- function inv2(A) {
313
- const a = A[0][0],
314
- b = A[0][1],
315
- c = A[1][0],
316
- d = A[1][1];
317
- const det = det2(A);
318
- if (eqF(det, makeF(0, 1))) throw new Error('singular');
319
- const adj = [
320
- [d, makeF(-toF(b).n, 1)],
321
- [makeF(-toF(c).n, 1), a],
322
- ];
323
- return { inv: matScalar(adj, divF(makeF(1, 1), det)), det, adj };
324
- }
325
- function det3(M) {
326
- const [a, b, c] = M[0],
327
- [d, e, f] = M[1],
328
- [g, h, i] = M[2];
329
- const term1 = mulF(a, subF(mulF(e, i), mulF(f, h)));
330
- const term2 = mulF(b, subF(mulF(d, i), mulF(f, g)));
331
- const term3 = mulF(c, subF(mulF(d, h), mulF(e, g)));
332
- return addF(subF(term1, term2), term3);
333
- }
334
- function det3SarrusTerms(M) {
335
- const [a, b, c] = M[0],
336
- [d, e, f] = M[1],
337
- [g, h, i] = M[2];
338
- const pos = [mulF(a, mulF(e, i)), mulF(b, mulF(f, g)), mulF(c, mulF(d, h))];
339
- const neg = [mulF(c, mulF(e, g)), mulF(a, mulF(f, h)), mulF(b, mulF(d, i))];
340
- return { pos, neg };
341
- }
342
-
343
- // -------- Integer-matrix helpers for eigenvals --------
344
- function toIntMat(A) {
345
- const n = A.length,
346
- m = A[0].length,
347
- B = Array.from({ length: n }, () => Array(m).fill(0));
348
- for (let i = 0; i < n; i++)
349
- for (let j = 0; j < m; j++) {
350
- const x = toF(A[i][j]);
351
- if (x.d !== 1) throw new Error('need int matrix');
352
- B[i][j] = x.n;
353
- }
354
- return B;
355
- }
356
- function identityInt(n) {
357
- return Array.from({ length: n }, (_, i) => Array.from({ length: n }, (_, j) => (i === j ? 1 : 0)));
358
- }
359
- function addI(A, B) {
360
- const n = A.length,
361
- m = A[0].length,
362
- C = Array.from({ length: n }, () => Array(m).fill(0));
363
- for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) C[i][j] = A[i][j] + B[i][j];
364
- return C;
365
- }
366
- function scalI(A, s) {
367
- const n = A.length,
368
- m = A[0].length,
369
- C = Array.from({ length: n }, () => Array(m).fill(0));
370
- for (let i = 0; i < n; i++) for (let j = 0; j < m; j++) C[i][j] = A[i][j] * s;
371
- return C;
372
- }
373
- function mulI(A, B) {
374
- const n = A.length,
375
- m = A[0].length,
376
- p = B[0].length,
377
- C = Array.from({ length: n }, () => Array(p).fill(0));
378
- for (let i = 0; i < n; i++)
379
- for (let j = 0; j < p; j++) {
380
- let s = 0;
381
- for (let k = 0; k < m; k++) s += A[i][k] * B[k][j];
382
- C[i][j] = s;
383
- }
384
- return C;
385
- }
386
- function traceI(A) {
387
- let t = 0;
388
- for (let i = 0; i < A.length; i++) t += A[i][i];
389
- return t;
390
- }
391
- function leverrier(A) {
392
- const n = A.length;
393
- let B = A.map((r) => r.slice());
394
- const I = identityInt(n);
395
- const coeff = new Array(n + 1).fill(0);
396
- coeff[0] = 1;
397
- for (let k = 1; k <= n; k++) {
398
- const tr = traceI(B);
399
- if (tr % k !== 0) throw new Error('non-integer step');
400
- const ck = -Math.trunc(tr / k);
401
- coeff[k] = ck;
402
- B = mulI(A, addI(B, scalI(I, ck)));
403
- }
404
- return coeff; // descending: [1, c1, ..., cn]
405
- }
406
- function evalPolyDesc(coeff, x) {
407
- let v = 0;
408
- for (const c of coeff) {
409
- v = v * x + c;
410
- }
411
- return v;
412
- }
413
- function divisors(n) {
414
- n = Math.abs(n);
415
- const ds = [];
416
- for (let d = 1; d <= n; d++) if (n % d === 0) ds.push(d);
417
- return ds.concat(ds.map((d) => -d));
418
- }
419
- function syntheticDivDesc(coeff, r) {
420
- const out = [coeff[0]];
421
- let carry = coeff[0];
422
- for (let i = 1; i < coeff.length; i++) {
423
- carry = coeff[i] + carry * r;
424
- out.push(carry);
425
- }
426
- const rem = out.pop();
427
- if (rem !== 0) throw new Error('not a root');
428
- return out;
429
- }
430
-
431
- // -------- ARC generators --------
432
- function runAll() {
433
- // Matrices from the cases
434
- const A = [
435
- [makeF(1, 1), makeF(3, 1)],
436
- [makeF(-2, 1), makeF(3, 1)],
437
- ];
438
- const B = [
439
- [makeF(0, 1), makeF(3, 1)],
440
- [makeF(0, 1), makeF(7, 1)],
441
- ];
442
- const M3 = [
443
- [makeF(1, 1), makeF(0, 1), makeF(1, 1)],
444
- [makeF(2, 1), makeF(-1, 1), makeF(3, 1)],
445
- [makeF(4, 1), makeF(3, 1), makeF(2, 1)],
446
- ];
447
- const M4 = [
448
- [makeF(3, 1), makeF(-2, 1), makeF(4, 1), makeF(-2, 1)],
449
- [makeF(5, 1), makeF(3, 1), makeF(-3, 1), makeF(-2, 1)],
450
- [makeF(5, 1), makeF(-2, 1), makeF(2, 1), makeF(-2, 1)],
451
- [makeF(5, 1), makeF(-2, 1), makeF(-3, 1), makeF(3, 1)],
452
- ];
453
-
454
- // 1) A * B
455
- const AB = matMul(A, B);
456
-
457
- // 2) A ** 2
458
- const A2 = matMul(A, A);
459
-
460
- // 3) A ** -1 (2x2)
461
- const invInfo = inv2(A);
462
- const Ainv = invInfo.inv;
463
-
464
- // 4) det(M3)
465
- const detM3 = det3(M3);
466
- const sTerms = det3SarrusTerms(M3);
467
-
468
- // 5) eigenvals(M4) via LeVerrier + integer roots
469
- const M4i = toIntMat(M4);
470
- const coeff = leverrier(M4i); // [1, c1, c2, c3, c4]
471
- let rem = coeff.slice();
472
- const roots = [];
473
- const cand = divisors(coeff[coeff.length - 1]);
474
- for (const r of cand) {
475
- while (evalPolyDesc(rem, r) === 0) {
476
- roots.push(r);
477
- rem = syntheticDivDesc(rem, r);
478
- }
479
- }
480
- // multiplicities map
481
- const mult = {};
482
- for (const r of roots) {
483
- mult[r] = (mult[r] || 0) + 1;
484
- }
485
- const eigOrder = Object.keys(mult)
486
- .map(Number)
487
- .sort((a, b) => a - b);
488
- const eigDump = '{ ' + eigOrder.map((v) => `${v}: ${mult[v]}`).join(', ') + ' }';
489
-
490
- // Pretty Answer
491
- const ans = [];
492
- ans.push('Case 1:');
493
- ans.push(`Matrix([[1, 3], [-2, 3]])*Matrix([[0, 3], [0, 7]]) = ${matStr(AB)}`);
494
- ans.push('');
495
- ans.push('Case 2:');
496
- ans.push(`Matrix([[1, 3], [-2, 3]])**2 = ${matStr(A2)}`);
497
- ans.push('');
498
- ans.push('Case 3:');
499
- ans.push(`Matrix([[1, 3], [-2, 3]])**-1 = ${matStr(Ainv)}`);
500
- ans.push('');
501
- ans.push('Case 4:');
502
- ans.push(`Matrix([[1, 0, 1], [2, -1, 3], [4, 3, 2]]).det() = ${f2s(detM3)}`);
503
- ans.push('');
504
- ans.push('Case 5:');
505
- ans.push(`Matrix([[3, -2, 4, -2], [5, 3, -3, -2], [5, -2, 2, -2], [5, -2, -3, 3]]).eigenvals() = ${eigDump}`);
506
-
507
- // Reason why
508
- const why = [];
509
- // Case 1 explanation
510
- why.push('[1] Matrix product entry-wise:');
511
- why.push(' C[1,1] = 1·0 + 3·0 = 0');
512
- why.push(' C[1,2] = 1·3 + 3·7 = 3 + 21 = 24');
513
- why.push(' C[2,1] = (−2)·0 + 3·0 = 0');
514
- why.push(' C[2,2] = (−2)·3 + 3·7 = −6 + 21 = 15');
515
- // Case 2
516
- why.push('[2] Power: A**2 = A·A (standard multiplication).');
517
- // Case 3 inverse
518
- const detA = invInfo.det,
519
- adjA = invInfo.adj;
520
- why.push('[3] Inverse via adjugate/determinant:');
521
- why.push(` det(A) = 1·3 − 3·(−2) = 3 + 6 = ${f2s(detA)}`);
522
- why.push(` adj(A) = [[3, −3],[2, 1]]; A^{-1} = (1/det)·adj(A).`);
523
- // Case 4 determinant
524
- why.push('[4] det(3×3) by Sarrus:');
525
- why.push(` Pos = ${sTerms.pos.map(f2s).join(', ')}`);
526
- why.push(` Neg = ${sTerms.neg.map(f2s).join(', ')}`);
527
- why.push(
528
- ` det = (Pos sum) − (Neg sum) = ${f2s(addF(addF(sTerms.pos[0], addF(sTerms.pos[1], sTerms.pos[2])), makeF(0, 1)))} − ${f2s(addF(addF(sTerms.neg[0], addF(sTerms.neg[1], sTerms.neg[2])), makeF(0, 1)))} = ${f2s(detM3)}`,
529
- );
530
- // Case 5 eigen
531
- const polyStr = polyToStringDesc(coeff);
532
- why.push('[5] Faddeev–LeVerrier gives characteristic polynomial:');
533
- why.push(` χ_A(λ) = ${polyStr}`);
534
- why.push(' Integer-root factorization yields eigenvalues with multiplicities as printed.');
535
-
536
- // Check
537
- const chk = [];
538
- // Case 1 recompute expected
539
- const AB_expected = [
540
- [makeF(0, 1), makeF(24, 1)],
541
- [makeF(0, 1), makeF(15, 1)],
542
- ];
543
- chk.push(matEq(AB, AB_expected) ? '• Case 1 OK (product matches expected). ✓' : '• Case 1 mismatch. ✗');
544
- // Case 2: multiply A by Ainv*A*Ainv? simpler: compute by mul and compare
545
- const A2_expected = matMul(A, A);
546
- chk.push(matEq(A2, A2_expected) ? '• Case 2 OK (square computed). ✓' : '• Case 2 mismatch. ✗');
547
- // Case 3: A*Ainv==I and Ainv*A==I
548
- const I2 = identityFrac(2);
549
- chk.push(
550
- matEq(matMul(A, Ainv), I2) && matEq(matMul(Ainv, A), I2)
551
- ? '• Case 3 OK (inverse check both sides). ✓'
552
- : '• Case 3 inverse failed. ✗',
553
- );
554
- // Case 4: cross-check det3 by expansion swapping a row sign trick (simple recompute)
555
- chk.push(f2s(detM3) === '-1' ? '• Case 4 OK (determinant = -1). ✓' : '• Case 4 determinant unexpected. ✗');
556
- // Case 5: verify invariants: trace = sum eigenvalues; det = product eigenvalues; Cayley–Hamilton
557
- const trace = M4i[0][0] + M4i[1][1] + M4i[2][2] + M4i[3][3];
558
- const sumEig = eigOrder.reduce((s, v) => s + v * mult[v], 0);
559
- const prodEig = eigOrder.reduce((p, v) => p * v ** mult[v], 1);
560
- chk.push(trace === sumEig ? '• Case 5: trace equals sum of eigenvalues. ✓' : '• Case 5: trace/sum mismatch. ✗');
561
- // determinant from coefficients is (-1)^n*c_n = c4 with sign? For χ = λ^4 + c1 λ^3 + ... + c4, det = c4 (up to sign pattern already baked)
562
- const detFromCoeff = coeff[coeff.length - 1];
563
- chk.push(
564
- detFromCoeff === prodEig
565
- ? '• Case 5: det equals product of eigenvalues. ✓'
566
- : '• Case 5: det/product mismatch. ✗',
567
- );
568
- // Cayley–Hamilton: evaluate χ_A(A) == 0
569
- const zeroCH = isZeroMatrix(polyEvalMat(M4, coeff));
570
- chk.push(zeroCH ? '• Case 5: Cayley–Hamilton (χ_A(A)=0). ✓' : '• Case 5: Cayley–Hamilton failed. ✗');
571
-
572
- return { ans: ans.join('\n'), why: why.join('\n'), chk: chk.join('\n') };
573
- }
574
-
575
- // Pretty polynomial string (descending)
576
- function polyToStringDesc(coeff) {
577
- const n = coeff.length - 1; // degree
578
- const parts = [];
579
- for (let i = 0; i < coeff.length; i++) {
580
- const c = coeff[i];
581
- const p = n - i;
582
- if (c === 0) continue;
583
- const sign = c > 0 ? '+' : '-';
584
- const a = Math.abs(c);
585
- let term = '';
586
- if (p === 0) {
587
- term = '' + a;
588
- } else if (p === 1) {
589
- term = (a === 1 ? '' : '%d '.replace('%d', a)) + 'λ';
590
- } else {
591
- term = (a === 1 ? '' : '%d '.replace('%d', a)) + 'λ^' + p;
592
- }
593
- parts.push([sign, term]);
594
- }
595
- if (!parts.length) return '0';
596
- let out = (parts[0][0] === '+' ? '' : '-') + parts[0][1];
597
- for (let i = 1; i < parts.length; i++) out += ' ' + parts[i][0] + ' ' + parts[i][1];
598
- return out;
599
- }
600
-
601
- // Evaluate polynomial matrix χ(A) with fraction arithmetic (descending coeff)
602
- function polyEvalMat(A_int, coeff) {
603
- // Lift integer matrix to fractions
604
- const A = A_int.map((row) => row.map((x) => makeF(x, 1)));
605
- const n = A.length,
606
- deg = coeff.length - 1;
607
- // Horner-like accumulation: start with zero matrix
608
- let M = zeros(n, n);
609
- for (const c of coeff) {
610
- M = matMul(M, A); // M = M*A
611
- M = matAdd(M, matScalar(identityFrac(n), makeF(c, 1))); // + c*I
612
- }
613
- return M;
614
- }
615
- function isZeroMatrix(M) {
616
- for (const r of M) for (const x of r) if (!eqF(x, makeF(0, 1))) return false;
617
- return true;
618
- }
619
-
620
- // UI
621
- function run() {
622
- const r = runAll();
623
- document.getElementById('ans').textContent = r.ans;
624
- document.getElementById('why').textContent = r.why;
625
- document.getElementById('chk').textContent = r.chk;
626
- document.getElementById('status').textContent = 'Computed';
627
- }
628
- function downloadARC() {
629
- const blob = new Blob(
630
- [
631
- 'Answer\n------\n',
632
- document.getElementById('ans').textContent,
633
- '\n\n',
634
- 'Reason why\n----------\n',
635
- document.getElementById('why').textContent,
636
- '\n\n',
637
- 'Check (harness)\n---------------\n',
638
- document.getElementById('chk').textContent,
639
- ],
640
- { type: 'text/plain' },
641
- );
642
- const a = document.createElement('a');
643
- a.href = URL.createObjectURL(blob);
644
- a.download = 'matrix_output.txt';
645
- a.click();
646
- }
647
-
648
- window.addEventListener('DOMContentLoaded', run);
649
- </script>
650
- </body>
651
- </html>