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,363 +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>Roots of Unity</title>
|
|
7
|
-
<style>
|
|
8
|
-
:root {
|
|
9
|
-
--bg: #f6f8fb;
|
|
10
|
-
--ink: #0f172a;
|
|
11
|
-
--sub: #334155;
|
|
12
|
-
--line: #d5dbe7;
|
|
13
|
-
--circle: #eaf3ff;
|
|
14
|
-
--edge: #4f79c8;
|
|
15
|
-
--pt: #0ea5e9;
|
|
16
|
-
--axis: #94a3b8;
|
|
17
|
-
--ok: #059669;
|
|
18
|
-
--bad: #dc2626;
|
|
19
|
-
}
|
|
20
|
-
html,
|
|
21
|
-
body {
|
|
22
|
-
margin: 0;
|
|
23
|
-
background: var(--bg);
|
|
24
|
-
color: var(--ink);
|
|
25
|
-
font-family:
|
|
26
|
-
system-ui,
|
|
27
|
-
Segoe UI,
|
|
28
|
-
Roboto,
|
|
29
|
-
Helvetica,
|
|
30
|
-
Arial,
|
|
31
|
-
sans-serif;
|
|
32
|
-
}
|
|
33
|
-
header {
|
|
34
|
-
padding: 18px 20px;
|
|
35
|
-
border-bottom: 1px solid var(--line);
|
|
36
|
-
}
|
|
37
|
-
h1 {
|
|
38
|
-
margin: 0 0 6px;
|
|
39
|
-
font-size: 22px;
|
|
40
|
-
}
|
|
41
|
-
h2 {
|
|
42
|
-
margin: 18px 0 10px;
|
|
43
|
-
font-size: 17px;
|
|
44
|
-
}
|
|
45
|
-
p,
|
|
46
|
-
li,
|
|
47
|
-
small {
|
|
48
|
-
line-height: 1.45;
|
|
49
|
-
}
|
|
50
|
-
main {
|
|
51
|
-
display: block;
|
|
52
|
-
padding: 18px 20px 40px;
|
|
53
|
-
}
|
|
54
|
-
.card {
|
|
55
|
-
background: #fff;
|
|
56
|
-
border: 1px solid var(--line);
|
|
57
|
-
border-radius: 12px;
|
|
58
|
-
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
|
|
59
|
-
margin: 0 0 18px;
|
|
60
|
-
}
|
|
61
|
-
.card header {
|
|
62
|
-
padding: 12px 14px;
|
|
63
|
-
border-bottom: 1px solid var(--line);
|
|
64
|
-
border-radius: 12px 12px 0 0;
|
|
65
|
-
}
|
|
66
|
-
.card section {
|
|
67
|
-
padding: 14px;
|
|
68
|
-
}
|
|
69
|
-
.muted {
|
|
70
|
-
color: var(--sub);
|
|
71
|
-
}
|
|
72
|
-
.mono {
|
|
73
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
74
|
-
}
|
|
75
|
-
.ok {
|
|
76
|
-
color: var(--ok);
|
|
77
|
-
}
|
|
78
|
-
.bad {
|
|
79
|
-
color: var(--bad);
|
|
80
|
-
}
|
|
81
|
-
svg {
|
|
82
|
-
max-width: 50%;
|
|
83
|
-
height: auto;
|
|
84
|
-
display: block;
|
|
85
|
-
margin: 0 auto;
|
|
86
|
-
shape-rendering: geometricPrecision;
|
|
87
|
-
}
|
|
88
|
-
</style>
|
|
89
|
-
</head>
|
|
90
|
-
<body>
|
|
91
|
-
<header>
|
|
92
|
-
<h1>Roots of Unity</h1>
|
|
93
|
-
</header>
|
|
94
|
-
|
|
95
|
-
<main>
|
|
96
|
-
<!-- WHAT THIS IS -->
|
|
97
|
-
<article class="card">
|
|
98
|
-
<header><h2>What this is?</h2></header>
|
|
99
|
-
<section>
|
|
100
|
-
<p>
|
|
101
|
-
An Argand diagram of the <em>n</em>-th roots of unity: points <em>z<sub>k</sub></em> = cos(2πk/n) + i
|
|
102
|
-
sin(2πk/n) for k = 0,…,n−1. They lie on the unit circle, equally spaced, forming a regular <em>n</em>-gon.
|
|
103
|
-
Multiplying by a unit complex number rotates; raising to the <em>n</em>-th power maps all points to 1.
|
|
104
|
-
</p>
|
|
105
|
-
</section>
|
|
106
|
-
</article>
|
|
107
|
-
|
|
108
|
-
<!-- ANSWER -->
|
|
109
|
-
<article class="card">
|
|
110
|
-
<header><h2>Answer — drawing</h2></header>
|
|
111
|
-
<section>
|
|
112
|
-
<svg id="fig" viewBox="0 0 240 240" aria-label="n-th roots of unity on the unit circle">
|
|
113
|
-
<g id="grid"></g>
|
|
114
|
-
<g id="axes"></g>
|
|
115
|
-
<g id="poly"></g>
|
|
116
|
-
<g id="points"></g>
|
|
117
|
-
</svg>
|
|
118
|
-
</section>
|
|
119
|
-
</article>
|
|
120
|
-
|
|
121
|
-
<!-- REASON -->
|
|
122
|
-
<article class="card">
|
|
123
|
-
<header><h2>Reason</h2></header>
|
|
124
|
-
<section>
|
|
125
|
-
<ol>
|
|
126
|
-
<li>
|
|
127
|
-
Solutions of z<sup>n</sup> = 1 are z<sub>k</sub> = cos(2πk/n) + i sin(2πk/n), k = 0,…,n−1 (De Moivre).
|
|
128
|
-
</li>
|
|
129
|
-
<li>All have |z<sub>k</sub>| = 1, arguments 2πk/n, hence equal spacing by 2π/n and a regular polygon.</li>
|
|
130
|
-
<li>
|
|
131
|
-
Sum of all roots is 0 and product is (−1)<sup>n−1</sup>. The polygon (circumradius 1) has area A = (n/2) ·
|
|
132
|
-
sin(2π/n).
|
|
133
|
-
</li>
|
|
134
|
-
</ol>
|
|
135
|
-
</section>
|
|
136
|
-
</article>
|
|
137
|
-
|
|
138
|
-
<!-- CHECK -->
|
|
139
|
-
<article class="card">
|
|
140
|
-
<header><h2>Check</h2></header>
|
|
141
|
-
<section>
|
|
142
|
-
<ul id="checks" class="mono"></ul>
|
|
143
|
-
<div id="numbers" class="mono" style="margin-top: 8px; white-space: pre-wrap"></div>
|
|
144
|
-
<small class="muted"
|
|
145
|
-
>We verify z<sup>n</sup>=1, unit modulus, equal angle steps, sum/product identities, chord length
|
|
146
|
-
2·sin(π/n), area formula, and DFT sums Σ z<sub>k</sub><sup>m</sup> = 0 for 1 ≤ m ≤ n−1.</small
|
|
147
|
-
>
|
|
148
|
-
</section>
|
|
149
|
-
</article>
|
|
150
|
-
</main>
|
|
151
|
-
|
|
152
|
-
<script>
|
|
153
|
-
(function () {
|
|
154
|
-
const checks = document.getElementById('checks');
|
|
155
|
-
const numbers = document.getElementById('numbers');
|
|
156
|
-
const ok = (t) => `<li><span class="ok">OK</span> — ${t}</li>`;
|
|
157
|
-
const bad = (t) => `<li><span class="bad">FAIL</span> — ${t}</li>`;
|
|
158
|
-
|
|
159
|
-
// ===== Parameters =====
|
|
160
|
-
const n = 12; // number of roots (change if you like)
|
|
161
|
-
const cx = 120,
|
|
162
|
-
cy = 120,
|
|
163
|
-
R = 100; // SVG center and radius (pixels)
|
|
164
|
-
const tol = 1e-10;
|
|
165
|
-
|
|
166
|
-
// ===== Build roots on the unit circle (math coords) =====
|
|
167
|
-
const roots = [];
|
|
168
|
-
for (let k = 0; k < n; k++) {
|
|
169
|
-
const theta = (2 * Math.PI * k) / n;
|
|
170
|
-
roots.push({ re: Math.cos(theta), im: Math.sin(theta), theta });
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ===== Drawing helpers (map unit circle -> SVG) =====
|
|
174
|
-
const X = (x) => cx + R * x;
|
|
175
|
-
const Y = (y) => cy - R * y; // flip y for SVG
|
|
176
|
-
|
|
177
|
-
const gGrid = document.getElementById('grid');
|
|
178
|
-
const gAxes = document.getElementById('axes');
|
|
179
|
-
const gPoly = document.getElementById('poly');
|
|
180
|
-
const gPts = document.getElementById('points');
|
|
181
|
-
|
|
182
|
-
// background ring and frame ticks
|
|
183
|
-
const ring = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
184
|
-
ring.setAttribute('cx', cx);
|
|
185
|
-
ring.setAttribute('cy', cy);
|
|
186
|
-
ring.setAttribute('r', R);
|
|
187
|
-
ring.setAttribute('fill', 'var(--circle)');
|
|
188
|
-
ring.setAttribute('stroke', '#c7d2e5');
|
|
189
|
-
ring.setAttribute('stroke-width', '1.2');
|
|
190
|
-
gGrid.appendChild(ring);
|
|
191
|
-
|
|
192
|
-
// axes
|
|
193
|
-
const axH = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
194
|
-
axH.setAttribute('x1', X(-1.1));
|
|
195
|
-
axH.setAttribute('y1', Y(0));
|
|
196
|
-
axH.setAttribute('x2', X(1.1));
|
|
197
|
-
axH.setAttribute('y2', Y(0));
|
|
198
|
-
axH.setAttribute('stroke', 'var(--axis)');
|
|
199
|
-
axH.setAttribute('stroke-width', '1.2');
|
|
200
|
-
gAxes.appendChild(axH);
|
|
201
|
-
|
|
202
|
-
const axV = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
203
|
-
axV.setAttribute('x1', X(0));
|
|
204
|
-
axV.setAttribute('y1', Y(-1.1));
|
|
205
|
-
axV.setAttribute('x2', X(0));
|
|
206
|
-
axV.setAttribute('y2', Y(1.1));
|
|
207
|
-
axV.setAttribute('stroke', 'var(--axis)');
|
|
208
|
-
axV.setAttribute('stroke-width', '1.2');
|
|
209
|
-
gAxes.appendChild(axV);
|
|
210
|
-
|
|
211
|
-
// polygon through the roots (in order)
|
|
212
|
-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
213
|
-
let d = `M ${X(roots[0].re)} ${Y(roots[0].im)}`;
|
|
214
|
-
for (let k = 1; k < n; k++) d += ` L ${X(roots[k].re)} ${Y(roots[k].im)}`;
|
|
215
|
-
d += ' Z';
|
|
216
|
-
path.setAttribute('d', d);
|
|
217
|
-
path.setAttribute('fill', 'none');
|
|
218
|
-
path.setAttribute('stroke', 'var(--edge)');
|
|
219
|
-
path.setAttribute('stroke-width', '1.8');
|
|
220
|
-
gPoly.appendChild(path);
|
|
221
|
-
|
|
222
|
-
// points
|
|
223
|
-
roots.forEach((z, k) => {
|
|
224
|
-
const c = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
225
|
-
c.setAttribute('cx', X(z.re));
|
|
226
|
-
c.setAttribute('cy', Y(z.im));
|
|
227
|
-
c.setAttribute('r', 2.4);
|
|
228
|
-
c.setAttribute('fill', 'white');
|
|
229
|
-
c.setAttribute('stroke', 'var(--pt)');
|
|
230
|
-
c.setAttribute('stroke-width', '1.4');
|
|
231
|
-
gPts.appendChild(c);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// ===== Checks =====
|
|
235
|
-
// 1) z^n = 1 and |z| = 1
|
|
236
|
-
let powOK = true,
|
|
237
|
-
modOK = true;
|
|
238
|
-
roots.forEach((z) => {
|
|
239
|
-
const r = Math.hypot(z.re, z.im);
|
|
240
|
-
if (Math.abs(r - 1) > 1e-12) modOK = false;
|
|
241
|
-
// z^n using angle
|
|
242
|
-
const zn = { re: Math.cos(n * Math.atan2(z.im, z.re)), im: Math.sin(n * Math.atan2(z.im, z.re)) };
|
|
243
|
-
if (Math.hypot(zn.re - 1, zn.im - 0) > 1e-9) powOK = false;
|
|
244
|
-
});
|
|
245
|
-
checks.insertAdjacentHTML(
|
|
246
|
-
'beforeend',
|
|
247
|
-
modOK ? ok('Every root has unit modulus |z| = 1') : bad('Some root |z| ≠ 1'),
|
|
248
|
-
);
|
|
249
|
-
checks.insertAdjacentHTML(
|
|
250
|
-
'beforeend',
|
|
251
|
-
powOK ? ok('Every root satisfies z^n = 1') : bad('Some root does not satisfy z^n = 1'),
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
// 2) Equal angle steps 2π/n
|
|
255
|
-
const ang = roots.map((z) => Math.atan2(z.im, z.re)).sort((a, b) => a - b);
|
|
256
|
-
// unwrap continuity
|
|
257
|
-
for (let i = 1; i < ang.length; i++) if (ang[i] - ang[i - 1] < -Math.PI) ang[i] += 2 * Math.PI;
|
|
258
|
-
const step = (2 * Math.PI) / n;
|
|
259
|
-
let spacingOK = true,
|
|
260
|
-
maxDev = 0;
|
|
261
|
-
for (let i = 0; i < n; i++) {
|
|
262
|
-
const a0 = ang[i],
|
|
263
|
-
a1 = ang[(i + 1) % n] + (i + 1 === n ? 2 * Math.PI : 0);
|
|
264
|
-
const dev = Math.abs(a1 - a0 - step);
|
|
265
|
-
if (dev > maxDev) maxDev = dev;
|
|
266
|
-
if (dev > 1e-10) spacingOK = false;
|
|
267
|
-
}
|
|
268
|
-
checks.insertAdjacentHTML(
|
|
269
|
-
'beforeend',
|
|
270
|
-
spacingOK
|
|
271
|
-
? ok('Equal angular spacing Δθ = 2π/n')
|
|
272
|
-
: bad(`Unequal spacing (max deviation ${maxDev.toExponential(2)})`),
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// 3) Sum of roots = 0
|
|
276
|
-
const sum = roots.reduce((a, z) => ({ re: a.re + z.re, im: a.im + z.im }), { re: 0, im: 0 });
|
|
277
|
-
const sumOK = Math.hypot(sum.re, sum.im) < 1e-9;
|
|
278
|
-
checks.insertAdjacentHTML(
|
|
279
|
-
'beforeend',
|
|
280
|
-
sumOK
|
|
281
|
-
? ok('Sum of all roots equals 0')
|
|
282
|
-
: bad(`Sum of roots not 0: |Σ| = ${Math.hypot(sum.re, sum.im).toExponential(2)}`),
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
// 4) Product of roots = (−1)^(n−1)
|
|
286
|
-
const prodArg = roots.reduce((a, z) => a + Math.atan2(z.im, z.re), 0); // magnitude is 1^n=1
|
|
287
|
-
// wrap to nearest of {0, π} mod 2π
|
|
288
|
-
let prodSign = Math.cos(prodArg) >= 0 ? 1 : -1;
|
|
289
|
-
const expectedSign = (n - 1) % 2 === 0 ? 1 : -1;
|
|
290
|
-
const prodOK = prodSign === expectedSign;
|
|
291
|
-
checks.insertAdjacentHTML(
|
|
292
|
-
'beforeend',
|
|
293
|
-
prodOK ? ok('Product of roots equals (−1)^(n−1)') : bad('Product check failed'),
|
|
294
|
-
);
|
|
295
|
-
|
|
296
|
-
// 5) Chord length between consecutive roots = 2 sin(π/n)
|
|
297
|
-
const chord = 2 * Math.sin(Math.PI / n);
|
|
298
|
-
let chordOK = true,
|
|
299
|
-
chordMaxDev = 0;
|
|
300
|
-
for (let k = 0; k < n; k++) {
|
|
301
|
-
const a = roots[k],
|
|
302
|
-
b = roots[(k + 1) % n];
|
|
303
|
-
const d = Math.hypot(a.re - b.re, a.im - b.im);
|
|
304
|
-
const dev = Math.abs(d - chord);
|
|
305
|
-
if (dev > chordMaxDev) chordMaxDev = dev;
|
|
306
|
-
if (dev > 1e-10) chordOK = false;
|
|
307
|
-
}
|
|
308
|
-
checks.insertAdjacentHTML(
|
|
309
|
-
'beforeend',
|
|
310
|
-
chordOK
|
|
311
|
-
? ok('Consecutive chord length is 2·sin(π/n)')
|
|
312
|
-
: bad(`Chord length mismatch (max deviation ${chordMaxDev.toExponential(2)})`),
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
// 6) Polygon area equals (n/2)·sin(2π/n)
|
|
316
|
-
function polyArea(pts) {
|
|
317
|
-
let s = 0;
|
|
318
|
-
for (let i = 0; i < pts.length; i++) {
|
|
319
|
-
const j = (i + 1) % pts.length;
|
|
320
|
-
s += pts[i].re * pts[j].im - pts[j].re * pts[i].im;
|
|
321
|
-
}
|
|
322
|
-
return Math.abs(s) / 2;
|
|
323
|
-
}
|
|
324
|
-
const A = polyArea(roots);
|
|
325
|
-
const Aexp = 0.5 * n * Math.sin((2 * Math.PI) / n);
|
|
326
|
-
const areaOK = Math.abs(A - Aexp) < 1e-10;
|
|
327
|
-
checks.insertAdjacentHTML(
|
|
328
|
-
'beforeend',
|
|
329
|
-
areaOK
|
|
330
|
-
? ok('Polygon area matches A = (n/2)·sin(2π/n)')
|
|
331
|
-
: bad(`Area mismatch: got ${A.toFixed(12)}, expected ${Aexp.toFixed(12)}`),
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// 7) DFT property: for 1 ≤ m ≤ n−1, Σ z_k^m = 0
|
|
335
|
-
let dftOK = true;
|
|
336
|
-
let firstBad = null;
|
|
337
|
-
for (let m = 1; m < n; m++) {
|
|
338
|
-
let acc = { re: 0, im: 0 };
|
|
339
|
-
for (const z of roots) {
|
|
340
|
-
const t = m * Math.atan2(z.im, z.re);
|
|
341
|
-
acc.re += Math.cos(t);
|
|
342
|
-
acc.im += Math.sin(t);
|
|
343
|
-
}
|
|
344
|
-
if (Math.hypot(acc.re, acc.im) > 1e-9) {
|
|
345
|
-
dftOK = false;
|
|
346
|
-
firstBad = m;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
checks.insertAdjacentHTML(
|
|
351
|
-
'beforeend',
|
|
352
|
-
dftOK ? ok('DFT sums vanish: Σ z_k^m = 0 for 1 ≤ m ≤ n−1') : bad(`DFT sum failed for m = ${firstBad}`),
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
numbers.textContent = `n = ${n}
|
|
356
|
-
Equal angle step = 2π/n = ${((2 * Math.PI) / n).toFixed(6)} rad
|
|
357
|
-
Chord length = ${chord.toFixed(6)}
|
|
358
|
-
Area (computed) = ${A.toFixed(12)}
|
|
359
|
-
Area (expected) = ${Aexp.toFixed(12)}`;
|
|
360
|
-
})();
|
|
361
|
-
</script>
|
|
362
|
-
</body>
|
|
363
|
-
</html>
|