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.
- package/HANDBOOK.md +4 -0
- package/README.md +0 -1
- package/examples/ershov-mixed-computation.n3 +106 -0
- package/examples/output/ershov-mixed-computation.n3 +15 -0
- package/eyeling.js +510 -263
- package/lib/cli.js +22 -12
- package/lib/engine.js +488 -251
- 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,502 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<title>Matrix Multiplication • Not Commutative (AB ≠ BA)</title>
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--bg: #f7f8fb;
|
|
10
|
-
--card: #ffffff;
|
|
11
|
-
--ink: #0f172a;
|
|
12
|
-
--muted: #4b5563;
|
|
13
|
-
--accent: #2563eb;
|
|
14
|
-
--ok: #059669;
|
|
15
|
-
--bad: #b91c1c;
|
|
16
|
-
--radius: 16px;
|
|
17
|
-
--shadow: 0 10px 22px rgba(2, 6, 23, 0.06), 0 2px 6px rgba(2, 6, 23, 0.06);
|
|
18
|
-
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
|
19
|
-
--sans: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, 'Helvetica Neue', Arial;
|
|
20
|
-
}
|
|
21
|
-
* {
|
|
22
|
-
box-sizing: border-box;
|
|
23
|
-
}
|
|
24
|
-
body {
|
|
25
|
-
margin: 0;
|
|
26
|
-
background: var(--bg);
|
|
27
|
-
color: var(--ink);
|
|
28
|
-
font: 16px/1.6 var(--sans);
|
|
29
|
-
-webkit-font-smoothing: antialiased;
|
|
30
|
-
}
|
|
31
|
-
.wrap {
|
|
32
|
-
max-width: 980px;
|
|
33
|
-
margin: auto;
|
|
34
|
-
padding: 28px 16px 64px;
|
|
35
|
-
}
|
|
36
|
-
header {
|
|
37
|
-
display: flex;
|
|
38
|
-
flex-direction: column;
|
|
39
|
-
gap: 10px;
|
|
40
|
-
margin-bottom: 22px;
|
|
41
|
-
}
|
|
42
|
-
h1 {
|
|
43
|
-
font-size: clamp(24px, 3vw, 34px);
|
|
44
|
-
line-height: 1.2;
|
|
45
|
-
margin: 0;
|
|
46
|
-
}
|
|
47
|
-
.sub {
|
|
48
|
-
color: var(--muted);
|
|
49
|
-
}
|
|
50
|
-
.pill {
|
|
51
|
-
display: inline-flex;
|
|
52
|
-
align-items: center;
|
|
53
|
-
gap: 8px;
|
|
54
|
-
background: #eef2ff;
|
|
55
|
-
color: #3730a3;
|
|
56
|
-
border-radius: 999px;
|
|
57
|
-
padding: 8px 12px;
|
|
58
|
-
font-weight: 600;
|
|
59
|
-
font-size: 14px;
|
|
60
|
-
width: max-content;
|
|
61
|
-
}
|
|
62
|
-
.grid {
|
|
63
|
-
display: flex;
|
|
64
|
-
flex-direction: column;
|
|
65
|
-
gap: 16px;
|
|
66
|
-
}
|
|
67
|
-
.card {
|
|
68
|
-
background: var(--card);
|
|
69
|
-
border-radius: var(--radius);
|
|
70
|
-
box-shadow: var(--shadow);
|
|
71
|
-
padding: 18px;
|
|
72
|
-
border: 1px solid #eef2f7;
|
|
73
|
-
}
|
|
74
|
-
.kicker {
|
|
75
|
-
font-size: 12px;
|
|
76
|
-
letter-spacing: 0.12em;
|
|
77
|
-
text-transform: uppercase;
|
|
78
|
-
color: #6b7280;
|
|
79
|
-
}
|
|
80
|
-
.muted {
|
|
81
|
-
color: var(--muted);
|
|
82
|
-
}
|
|
83
|
-
.answer {
|
|
84
|
-
font-size: 18px;
|
|
85
|
-
font-weight: 800;
|
|
86
|
-
}
|
|
87
|
-
.ok {
|
|
88
|
-
color: var(--ok);
|
|
89
|
-
}
|
|
90
|
-
.fail {
|
|
91
|
-
color: var(--bad);
|
|
92
|
-
font-weight: 800;
|
|
93
|
-
}
|
|
94
|
-
.result {
|
|
95
|
-
margin-top: 10px;
|
|
96
|
-
padding: 10px 12px;
|
|
97
|
-
border-radius: 10px;
|
|
98
|
-
background: #fafbff;
|
|
99
|
-
border: 1px dashed #d7dbe5;
|
|
100
|
-
}
|
|
101
|
-
code,
|
|
102
|
-
pre {
|
|
103
|
-
font-family: var(--mono);
|
|
104
|
-
}
|
|
105
|
-
pre {
|
|
106
|
-
background: #fafbff;
|
|
107
|
-
color: #0f172a;
|
|
108
|
-
padding: 14px;
|
|
109
|
-
border-radius: 12px;
|
|
110
|
-
white-space: pre-wrap;
|
|
111
|
-
overflow-wrap: anywhere;
|
|
112
|
-
word-break: break-word;
|
|
113
|
-
overflow-x: hidden;
|
|
114
|
-
overflow-y: auto;
|
|
115
|
-
max-width: 100%;
|
|
116
|
-
}
|
|
117
|
-
.small {
|
|
118
|
-
font-size: 13px;
|
|
119
|
-
}
|
|
120
|
-
textarea.input {
|
|
121
|
-
width: 100%;
|
|
122
|
-
min-height: 92px;
|
|
123
|
-
padding: 10px 12px;
|
|
124
|
-
border-radius: 10px;
|
|
125
|
-
border: 1px solid #d9e1f1;
|
|
126
|
-
background: #fdfefe;
|
|
127
|
-
font-family: var(--mono);
|
|
128
|
-
}
|
|
129
|
-
.badge {
|
|
130
|
-
display: inline-block;
|
|
131
|
-
font-size: 12px;
|
|
132
|
-
padding: 2px 8px;
|
|
133
|
-
border-radius: 999px;
|
|
134
|
-
background: #ecfeff;
|
|
135
|
-
color: #155e75;
|
|
136
|
-
border: 1px solid #a5f3fc;
|
|
137
|
-
}
|
|
138
|
-
.mat {
|
|
139
|
-
display: inline-block;
|
|
140
|
-
padding: 6px 10px;
|
|
141
|
-
border: 1px solid #e5eaf3;
|
|
142
|
-
border-radius: 10px;
|
|
143
|
-
background: #fff;
|
|
144
|
-
box-shadow: var(--shadow);
|
|
145
|
-
font-family: var(--mono);
|
|
146
|
-
}
|
|
147
|
-
</style>
|
|
148
|
-
</head>
|
|
149
|
-
<body>
|
|
150
|
-
<div class="wrap">
|
|
151
|
-
<header>
|
|
152
|
-
<h1>Matrix Multiplication • Not Commutative (AB ≠ BA)</h1>
|
|
153
|
-
</header>
|
|
154
|
-
|
|
155
|
-
<section class="grid">
|
|
156
|
-
<!-- Problem statement -->
|
|
157
|
-
<article class="card">
|
|
158
|
-
<div class="kicker">Problem</div>
|
|
159
|
-
<h2>Show there exist square matrices A,B with AB ≠ BA</h2>
|
|
160
|
-
<p>We will use explicit 2×2 matrices and several independent checks.</p>
|
|
161
|
-
<p class="small muted">
|
|
162
|
-
Notation: matrices over ℤ (integers), standard matrix product. “Commutative” would require AB = BA for all
|
|
163
|
-
A,B — which is false.
|
|
164
|
-
</p>
|
|
165
|
-
</article>
|
|
166
|
-
|
|
167
|
-
<!-- Answer -->
|
|
168
|
-
<article class="card">
|
|
169
|
-
<div class="kicker">Answer</div>
|
|
170
|
-
<p class="answer ok">
|
|
171
|
-
Not commutative. A concrete counterexample is <span class="mat">A = [[1,2],[0,1]]</span> and
|
|
172
|
-
<span class="mat">B = [[1,0],[3,1]]</span>, for which <span class="mat">AB = [[7,2],[3,1]]</span> but
|
|
173
|
-
<span class="mat">BA = [[1,2],[3,7]]</span>, so AB ≠ BA.
|
|
174
|
-
</p>
|
|
175
|
-
</article>
|
|
176
|
-
|
|
177
|
-
<!-- Reason Why -->
|
|
178
|
-
<article class="card">
|
|
179
|
-
<div class="kicker">Reason Why</div>
|
|
180
|
-
<h2>Mathematical English justification</h2>
|
|
181
|
-
<ol>
|
|
182
|
-
<li>Take A = [[1, 2], [0, 1]] and B = [[1, 0], [3, 1]].</li>
|
|
183
|
-
<li>Compute AB = [[1·1 + 2·3, 1·0 + 2·1], [0·1 + 1·3, 0·0 + 1·1]] = [[7, 2], [3, 1]].</li>
|
|
184
|
-
<li>Compute BA = [[1·1 + 0·0, 1·2 + 0·1], [3·1 + 1·0, 3·2 + 1·1]] = [[1, 2], [3, 7]].</li>
|
|
185
|
-
<li>
|
|
186
|
-
Since AB ≠ BA, matrix multiplication is not commutative in general (a single counterexample suffices).
|
|
187
|
-
</li>
|
|
188
|
-
</ol>
|
|
189
|
-
</article>
|
|
190
|
-
|
|
191
|
-
<!-- Checks -->
|
|
192
|
-
<article class="card" id="h1">
|
|
193
|
-
<div class="kicker">Check (harness) #1</div>
|
|
194
|
-
<h3>Counterexample computation</h3>
|
|
195
|
-
<p class="small muted">Compute AB and BA for the fixed A,B above.</p>
|
|
196
|
-
<div class="result" id="r1"></div>
|
|
197
|
-
</article>
|
|
198
|
-
|
|
199
|
-
<article class="card" id="h2">
|
|
200
|
-
<div class="kicker">Check (harness) #2</div>
|
|
201
|
-
<h3>Random 2×2 integer matrices</h3>
|
|
202
|
-
<p class="small muted">
|
|
203
|
-
Sample 300 random pairs with entries in −3..3 (not all zero). Report how many satisfy AB = BA.
|
|
204
|
-
</p>
|
|
205
|
-
<div class="result" id="r2"></div>
|
|
206
|
-
</article>
|
|
207
|
-
|
|
208
|
-
<article class="card" id="h3">
|
|
209
|
-
<div class="kicker">Check (harness) #3</div>
|
|
210
|
-
<h3>Exhaustive tiny space</h3>
|
|
211
|
-
<p class="small muted">
|
|
212
|
-
Enumerate all 2×2 matrices with entries in {0,1} (except both zero simultaneously for all entries) and count
|
|
213
|
-
commuting pairs.
|
|
214
|
-
</p>
|
|
215
|
-
<div class="result" id="r3"></div>
|
|
216
|
-
</article>
|
|
217
|
-
|
|
218
|
-
<article class="card" id="h4">
|
|
219
|
-
<div class="kicker">Check (harness) #4</div>
|
|
220
|
-
<h3>Special cases that do commute</h3>
|
|
221
|
-
<p class="small muted">
|
|
222
|
-
Identity I, zero 0, and scalars λI commute with everything; diagonal matrices commute with each other.
|
|
223
|
-
</p>
|
|
224
|
-
<div class="result" id="r4"></div>
|
|
225
|
-
</article>
|
|
226
|
-
|
|
227
|
-
<article class="card" id="h5">
|
|
228
|
-
<div class="kicker">Check (harness) #5</div>
|
|
229
|
-
<h3>Commutator</h3>
|
|
230
|
-
<p class="small muted">
|
|
231
|
-
Compute [A,B]=AB-BA for the counterexample; nonzero commutator certifies non-commutativity.
|
|
232
|
-
</p>
|
|
233
|
-
<div class="result" id="r5"></div>
|
|
234
|
-
</article>
|
|
235
|
-
|
|
236
|
-
<article class="card" id="h6">
|
|
237
|
-
<div class="kicker">Check (harness) #6</div>
|
|
238
|
-
<h3>Your matrices (CSV)</h3>
|
|
239
|
-
<p class="small muted">
|
|
240
|
-
Paste A and B as four comma-separated rows each. Example:<br />
|
|
241
|
-
<code>A: 1,2; 0,1</code><br /><code>B: 1,0; 3,1</code>
|
|
242
|
-
</p>
|
|
243
|
-
<textarea
|
|
244
|
-
class="input"
|
|
245
|
-
id="csv"
|
|
246
|
-
placeholder="A: 1,2; 0,1
|
|
247
|
-
B: 1,0; 3,1"></textarea>
|
|
248
|
-
<div class="result" id="r6"></div>
|
|
249
|
-
</article>
|
|
250
|
-
|
|
251
|
-
<article class="card" id="h7">
|
|
252
|
-
<div class="kicker">Check (harness) #7</div>
|
|
253
|
-
<h3>Geometric intuition via shears</h3>
|
|
254
|
-
<p class="small muted">
|
|
255
|
-
Apply the two shears to a sample of points in both orders. The sets differ, showing the compositions differ.
|
|
256
|
-
</p>
|
|
257
|
-
<div class="result" id="r7"></div>
|
|
258
|
-
</article>
|
|
259
|
-
</section>
|
|
260
|
-
</div>
|
|
261
|
-
|
|
262
|
-
<script>
|
|
263
|
-
// ---------- Matrix helpers ----------
|
|
264
|
-
const mul = (A, B) => {
|
|
265
|
-
const r = A.length,
|
|
266
|
-
c = B[0].length,
|
|
267
|
-
k = A[0].length;
|
|
268
|
-
const C = Array.from({ length: r }, () => Array(c).fill(0));
|
|
269
|
-
for (let i = 0; i < r; i++) {
|
|
270
|
-
for (let j = 0; j < c; j++) {
|
|
271
|
-
let s = 0;
|
|
272
|
-
for (let t = 0; t < k; t++) s += A[i][t] * B[t][j];
|
|
273
|
-
C[i][j] = s;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return C;
|
|
277
|
-
};
|
|
278
|
-
const addM = (A, B) => A.map((row, i) => row.map((v, j) => v + B[i][j]));
|
|
279
|
-
const subM = (A, B) => A.map((row, i) => row.map((v, j) => v - B[i][j]));
|
|
280
|
-
const eqM = (A, B) =>
|
|
281
|
-
A.length === B.length && A[0].length === B[0].length && A.every((row, i) => row.every((v, j) => v === B[i][j]));
|
|
282
|
-
const fmt = (M) => `[${M.map((r) => '[' + r.join(', ') + ']').join(', ')}]`;
|
|
283
|
-
const I2 = [
|
|
284
|
-
[1, 0],
|
|
285
|
-
[0, 1],
|
|
286
|
-
],
|
|
287
|
-
Z2 = [
|
|
288
|
-
[0, 0],
|
|
289
|
-
[0, 0],
|
|
290
|
-
];
|
|
291
|
-
const isDiag = (M) => M[0][1] === 0 && M[1][0] === 0;
|
|
292
|
-
|
|
293
|
-
// Fixed counterexample (two shears)
|
|
294
|
-
const A = [
|
|
295
|
-
[1, 2],
|
|
296
|
-
[0, 1],
|
|
297
|
-
];
|
|
298
|
-
const B = [
|
|
299
|
-
[1, 0],
|
|
300
|
-
[3, 1],
|
|
301
|
-
];
|
|
302
|
-
|
|
303
|
-
// ---------- Harness #1 ----------
|
|
304
|
-
function check1() {
|
|
305
|
-
const AB = mul(A, B),
|
|
306
|
-
BA = mul(B, A);
|
|
307
|
-
const pass = !eqM(AB, BA);
|
|
308
|
-
const msg = `A = ${fmt(A)}\nB = ${fmt(B)}\nAB = ${fmt(AB)}\nBA = ${fmt(BA)}\nAB == BA ? ${eqM(AB, BA)}`;
|
|
309
|
-
document.getElementById('r1').innerHTML =
|
|
310
|
-
`<div class="${pass ? 'ok' : 'fail'}">${pass ? 'PASS' : 'FAIL'}</div><pre>${msg}</pre>`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// ---------- Harness #2 ----------
|
|
314
|
-
function randInt(a, b) {
|
|
315
|
-
return a + Math.floor(Math.random() * (b - a + 1));
|
|
316
|
-
}
|
|
317
|
-
function randomMat() {
|
|
318
|
-
return [
|
|
319
|
-
[randInt(-3, 3), randInt(-3, 3)],
|
|
320
|
-
[randInt(-3, 3), randInt(-3, 3)],
|
|
321
|
-
];
|
|
322
|
-
}
|
|
323
|
-
function isAllZero(M) {
|
|
324
|
-
return M.every((r) => r.every((v) => v === 0));
|
|
325
|
-
}
|
|
326
|
-
function check2() {
|
|
327
|
-
const trials = 300;
|
|
328
|
-
let commute = 0,
|
|
329
|
-
total = 0;
|
|
330
|
-
for (let t = 0; t < trials; t++) {
|
|
331
|
-
let A = randomMat(),
|
|
332
|
-
B = randomMat();
|
|
333
|
-
if (isAllZero(A) && isAllZero(B)) {
|
|
334
|
-
t--;
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
|
-
total++;
|
|
338
|
-
if (eqM(mul(A, B), mul(B, A))) commute++;
|
|
339
|
-
}
|
|
340
|
-
const msg = `Random pairs tested: ${total}\nPairs with AB = BA: ${commute}\nPairs with AB ≠ BA: ${total - commute}`;
|
|
341
|
-
const pass = total - commute > 0;
|
|
342
|
-
document.getElementById('r2').innerHTML =
|
|
343
|
-
`<div class="${pass ? 'ok' : 'fail'}">${pass ? 'PASS' : 'FAIL'}</div><pre>${msg}</pre>`;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// ---------- Harness #3 ----------
|
|
347
|
-
function all01Mats() {
|
|
348
|
-
// all 2x2 with entries in {0,1}
|
|
349
|
-
const mats = [];
|
|
350
|
-
for (let a = 0; a <= 1; a++)
|
|
351
|
-
for (let b = 0; b <= 1; b++)
|
|
352
|
-
for (let c = 0; c <= 1; c++)
|
|
353
|
-
for (let d = 0; d <= 1; d++) {
|
|
354
|
-
mats.push([
|
|
355
|
-
[a, b],
|
|
356
|
-
[c, d],
|
|
357
|
-
]);
|
|
358
|
-
}
|
|
359
|
-
return mats;
|
|
360
|
-
}
|
|
361
|
-
function check3() {
|
|
362
|
-
const X = all01Mats();
|
|
363
|
-
let commute = 0,
|
|
364
|
-
total = 0;
|
|
365
|
-
for (const M1 of X) {
|
|
366
|
-
for (const M2 of X) {
|
|
367
|
-
total++;
|
|
368
|
-
if (eqM(mul(M1, M2), mul(M2, M1))) commute++;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
const msg = `Total ordered pairs (including identity & zero): ${total}\nCommuting pairs: ${commute}\nNon-commuting pairs: ${total - commute}`;
|
|
372
|
-
const pass = total - commute > 0;
|
|
373
|
-
document.getElementById('r3').innerHTML =
|
|
374
|
-
`<div class="${pass ? 'ok' : 'fail'}">${pass ? 'PASS' : 'FAIL'}</div><pre>${msg}</pre>`;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// ---------- Harness #4 ----------
|
|
378
|
-
function check4() {
|
|
379
|
-
const lambda = 5;
|
|
380
|
-
const S = [
|
|
381
|
-
[2, 0],
|
|
382
|
-
[0, 3],
|
|
383
|
-
]; // diagonal
|
|
384
|
-
const T = [
|
|
385
|
-
[7, 0],
|
|
386
|
-
[0, -4],
|
|
387
|
-
]; // diagonal
|
|
388
|
-
const cases = [
|
|
389
|
-
{ name: 'I commutes with A', ok: eqM(mul(I2, A), mul(A, I2)) },
|
|
390
|
-
{ name: '0 commutes with B', ok: eqM(mul(Z2, B), mul(B, Z2)) },
|
|
391
|
-
{
|
|
392
|
-
name: 'λI commutes with A',
|
|
393
|
-
ok: eqM(
|
|
394
|
-
mul(
|
|
395
|
-
[
|
|
396
|
-
[lambda, 0],
|
|
397
|
-
[0, lambda],
|
|
398
|
-
],
|
|
399
|
-
A,
|
|
400
|
-
),
|
|
401
|
-
mul(A, [
|
|
402
|
-
[lambda, 0],
|
|
403
|
-
[0, lambda],
|
|
404
|
-
]),
|
|
405
|
-
),
|
|
406
|
-
},
|
|
407
|
-
{ name: 'Diagonal S with diagonal T', ok: eqM(mul(S, T), mul(T, S)) },
|
|
408
|
-
];
|
|
409
|
-
const pass = cases.every((c) => c.ok);
|
|
410
|
-
const lines = cases.map((c) => `${c.name}: ${c.ok ? 'true' : 'false'}`);
|
|
411
|
-
document.getElementById('r4').innerHTML =
|
|
412
|
-
`<div class="${pass ? 'ok' : 'fail'}">${pass ? 'PASS' : 'FAIL'}</div><pre>${lines.join('\n')}</pre>`;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// ---------- Harness #5 ----------
|
|
416
|
-
function check5() {
|
|
417
|
-
const AB = mul(A, B),
|
|
418
|
-
BA = mul(B, A);
|
|
419
|
-
const comm = subM(AB, BA); // [A,B] = AB-BA
|
|
420
|
-
const nz = !eqM(comm, [
|
|
421
|
-
[0, 0],
|
|
422
|
-
[0, 0],
|
|
423
|
-
]);
|
|
424
|
-
const msg = `[A,B] = AB - BA = ${fmt(comm)}\nZero? ${!nz}`;
|
|
425
|
-
document.getElementById('r5').innerHTML =
|
|
426
|
-
`<div class="${nz ? 'ok' : 'fail'}">${nz ? 'PASS (nonzero commutator)' : 'FAIL'}</div><pre>${msg}</pre>`;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// ---------- Harness #6 ----------
|
|
430
|
-
function parseCSVSpec(text) {
|
|
431
|
-
// Expect: A: a,b; c,d\nB: e,f; g,h
|
|
432
|
-
const get = (k) => {
|
|
433
|
-
const m = (text || '').match(new RegExp(`^\\s*${k}\\s*:\\s*([^\\n]+)$`, 'mi'));
|
|
434
|
-
if (!m) return null;
|
|
435
|
-
const rows = m[1].split(';').map((s) => s.trim());
|
|
436
|
-
const nums = rows.map((r) => r.split(',').map((x) => parseFloat(x.trim())));
|
|
437
|
-
if (
|
|
438
|
-
nums.length !== 2 ||
|
|
439
|
-
nums[0].length !== 2 ||
|
|
440
|
-
nums[1].length !== 2 ||
|
|
441
|
-
nums.flat().some((x) => Number.isNaN(x))
|
|
442
|
-
)
|
|
443
|
-
return null;
|
|
444
|
-
return nums;
|
|
445
|
-
};
|
|
446
|
-
return { A: get('A'), B: get('B') };
|
|
447
|
-
}
|
|
448
|
-
function check6() {
|
|
449
|
-
const { A, B } = parseCSVSpec(document.getElementById('csv').value);
|
|
450
|
-
if (!A || !B) {
|
|
451
|
-
document.getElementById('r6').innerHTML =
|
|
452
|
-
`<div class="fail">FAIL</div><pre>Could not parse. Example:\nA: 1,2; 0,1\nB: 1,0; 3,1</pre>`;
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
const AB = mul(A, B),
|
|
456
|
-
BA = mul(B, A),
|
|
457
|
-
same = eqM(AB, BA);
|
|
458
|
-
const msg = `A = ${fmt(A)}\nB = ${fmt(B)}\nAB = ${fmt(AB)}\nBA = ${fmt(BA)}\nAB == BA ? ${same}`;
|
|
459
|
-
document.getElementById('r6').innerHTML =
|
|
460
|
-
`<div class="${!same ? 'ok' : 'fail'}">${!same ? 'PASS (non-commutative pair)' : 'Note: this pair commutes'}</div><pre>${msg}</pre>`;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// ---------- Harness #7 ----------
|
|
464
|
-
function apply(M, v) {
|
|
465
|
-
return [M[0][0] * v[0] + M[0][1] * v[1], M[1][0] * v[0] + M[1][1] * v[1]];
|
|
466
|
-
}
|
|
467
|
-
function check7() {
|
|
468
|
-
// Use the shear pair on a few points
|
|
469
|
-
const pts = [
|
|
470
|
-
[1, 0],
|
|
471
|
-
[0, 1],
|
|
472
|
-
[1, 1],
|
|
473
|
-
[2, -1],
|
|
474
|
-
];
|
|
475
|
-
const firstAB = pts.map((p) => apply(mul(A, B), p));
|
|
476
|
-
const firstBA = pts.map((p) => apply(mul(B, A), p));
|
|
477
|
-
const sameSets = JSON.stringify(firstAB) === JSON.stringify(firstBA);
|
|
478
|
-
const lines = ['Points (p), (AB)p, (BA)p:'];
|
|
479
|
-
for (let i = 0; i < pts.length; i++) {
|
|
480
|
-
lines.push(
|
|
481
|
-
`${JSON.stringify(pts[i])} → (AB)p=${JSON.stringify(firstAB[i])} ; (BA)p=${JSON.stringify(firstBA[i])}`,
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
document.getElementById('r7').innerHTML =
|
|
485
|
-
`<div class="${!sameSets ? 'ok' : 'fail'}">${!sameSets ? 'PASS' : 'FAIL'}</div><pre>${lines.join('\n')}</pre>`;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Autorun
|
|
489
|
-
window.addEventListener('DOMContentLoaded', () => {
|
|
490
|
-
const csv = document.getElementById('csv');
|
|
491
|
-
if (csv && !csv.value.trim()) csv.value = 'A: 1,2; 0,1\nB: 1,0; 3,1';
|
|
492
|
-
check1();
|
|
493
|
-
check2();
|
|
494
|
-
check3();
|
|
495
|
-
check4();
|
|
496
|
-
check5();
|
|
497
|
-
check6();
|
|
498
|
-
check7();
|
|
499
|
-
});
|
|
500
|
-
</script>
|
|
501
|
-
</body>
|
|
502
|
-
</html>
|