リポジトリ / emssr / independent_common_compiler
r2
| r2 emssr | 1 | // scope の描き分け。どの関数も kernel が返した値をそのまま形にする。 |
| r2 emssr | 2 | // 計算はここに置かない。 |
| r2 emssr | 3 | // |
| r2 emssr | 4 | // 座標と色は common_apps/joinrank/svg/draw.py と同じものを使う。 |
| r2 emssr | 5 | // 同じ図を同じ読み方で見せるため、別の流儀を持ち込まない。 |
| r2 emssr | 6 | |
| r2 emssr | 7 | const NS = 'http://www.w3.org/2000/svg'; |
| r2 emssr | 8 | |
| r2 emssr | 9 | function el(tag, attrs, text) { |
| r2 emssr | 10 | const n = document.createElementNS(NS, tag); |
| r2 emssr | 11 | for (const k in attrs) n.setAttribute(k, attrs[k]); |
| r2 emssr | 12 | if (text !== undefined) n.textContent = text; |
| r2 emssr | 13 | return n; |
| r2 emssr | 14 | } |
| r2 emssr | 15 | |
| r2 emssr | 16 | function svgBox(x, y, w, h) { |
| r2 emssr | 17 | const s = el('svg', { viewBox: `${x} ${y} ${w} ${h}`, width: w, height: h }); |
| r2 emssr | 18 | s.style.maxWidth = '100%'; |
| r2 emssr | 19 | return s; |
| r2 emssr | 20 | } |
| r2 emssr | 21 | |
| r2 emssr | 22 | function svg(w, h) { return svgBox(0, 0, w, h); } |
| r2 emssr | 23 | |
| r2 emssr | 24 | function box(title, formula, node) { |
| r2 emssr | 25 | const d = document.createElement('div'); |
| r2 emssr | 26 | d.className = 'scopebox'; |
| r2 emssr | 27 | if (title) { |
| r2 emssr | 28 | const h = document.createElement('h2'); |
| r2 emssr | 29 | h.textContent = title; |
| r2 emssr | 30 | d.appendChild(h); |
| r2 emssr | 31 | } |
| r2 emssr | 32 | if (formula) { |
| r2 emssr | 33 | const f = document.createElement('div'); |
| r2 emssr | 34 | f.className = 'formula'; |
| r2 emssr | 35 | f.textContent = formula; |
| r2 emssr | 36 | d.appendChild(f); |
| r2 emssr | 37 | } |
| r2 emssr | 38 | if (node) d.appendChild(node); |
| r2 emssr | 39 | return d; |
| r2 emssr | 40 | } |
| r2 emssr | 41 | |
| r2 emssr | 42 | function nothing(msg) { |
| r2 emssr | 43 | const p = document.createElement('p'); |
| r2 emssr | 44 | p.className = 'empty'; |
| r2 emssr | 45 | p.textContent = msg; |
| r2 emssr | 46 | return p; |
| r2 emssr | 47 | } |
| r2 emssr | 48 | |
| r2 emssr | 49 | function dyadText(num, d) { |
| r2 emssr | 50 | if (!d) return String(num); |
| r2 emssr | 51 | let n = num, k = d; |
| r2 emssr | 52 | while (k > 0 && n % 2 === 0) { n /= 2; k--; } |
| r2 emssr | 53 | return k === 0 ? String(n) : `${n}/${2 ** k}`; |
| r2 emssr | 54 | } |
| r2 emssr | 55 | |
| r2 emssr | 56 | // ===== 層と机(16 §2、19 §4.2)===== |
| r2 emssr | 57 | // |
| r2 emssr | 58 | // 層 r が行、机の位置 q が列。疎テンソル S(k,q,r) をそのまま平面へ置く。 |
| r2 emssr | 59 | // 箱は入口の区間 take を取り、出口の区間 give へ置く。ki は素通しの線になる。 |
| r2 emssr | 60 | |
| r2 emssr | 61 | function drawChamber(sc) { |
| r2 emssr | 62 | const rows = sc.frontier || []; |
| r2 emssr | 63 | if (!rows.length) return nothing('層がありません。'); |
| r2 emssr | 64 | |
| r2 emssr | 65 | const COL = 46, ROW = 58, PAD = 40, TOP = 26; |
| r2 emssr | 66 | const wide = Math.max(1, ...rows.map(r => Math.max(r.in.length, r.out.length))); |
| r2 emssr | 67 | const W = PAD + wide * COL + 26, H = TOP + rows.length * ROW + 34; |
| r2 emssr | 68 | const s = svg(W, H); |
| r2 emssr | 69 | const x = q => PAD + q * COL + COL / 2; |
| r2 emssr | 70 | |
| r2 emssr | 71 | s.appendChild(el('text', { x: 4, y: TOP - 10, 'font-size': 9.5 }, 'q')); |
| r2 emssr | 72 | for (let q = 0; q < wide; q++) { |
| r2 emssr | 73 | s.appendChild(el('text', { x: x(q), y: TOP - 10, 'text-anchor': 'middle', 'font-size': 9.5 }, String(q))); |
| r2 emssr | 74 | } |
| r2 emssr | 75 | |
| r2 emssr | 76 | rows.forEach((row, i) => { |
| r2 emssr | 77 | const y0 = TOP + i * ROW, y1 = y0 + ROW; |
| r2 emssr | 78 | s.appendChild(el('line', { x1: PAD - 10, y1: y0, x2: W - 14, y2: y0, class: 'grid' })); |
| r2 emssr | 79 | s.appendChild(el('text', { x: 4, y: y0 + 16, class: 'lab', 'font-size': 10 }, 'Λ' + row.r)); |
| r2 emssr | 80 | |
| r2 emssr | 81 | (row.put || []).forEach(p => { |
| r2 emssr | 82 | if (p.wire) { |
| r2 emssr | 83 | s.appendChild(el('path', { |
| r2 emssr | 84 | d: `M ${x(p.take[0])} ${y0} C ${x(p.take[0])} ${y0 + 22}, ${x(p.give[0])} ${y1 - 22}, ${x(p.give[0])} ${y1}`, |
| r2 emssr | 85 | class: 'wire' |
| r2 emssr | 86 | })); |
| r2 emssr | 87 | return; |
| r2 emssr | 88 | } |
| r2 emssr | 89 | const hasIn = p.take[1] > p.take[0], hasOut = p.give[1] > p.give[0]; |
| r2 emssr | 90 | const xs = []; |
| r2 emssr | 91 | if (hasIn) { xs.push(x(p.take[0]), x(p.take[1] - 1)); } |
| r2 emssr | 92 | if (hasOut) { xs.push(x(p.give[0]), x(p.give[1] - 1)); } |
| r2 emssr | 93 | const bx = Math.min(...xs) - 16, bw = Math.max(...xs) - Math.min(...xs) + 32; |
| r2 emssr | 94 | const by = y0 + 21, bh = 19; |
| r2 emssr | 95 | const col = `var(--${p.cls || 'frame'})`; |
| r2 emssr | 96 | for (let q = p.take[0]; q < p.take[1]; q++) { |
| r2 emssr | 97 | s.appendChild(el('line', { x1: x(q), y1: y0, x2: x(q), y2: by, class: 'wire' })); |
| r2 emssr | 98 | } |
| r2 emssr | 99 | for (let q = p.give[0]; q < p.give[1]; q++) { |
| r2 emssr | 100 | s.appendChild(el('line', { x1: x(q), y1: by + bh, x2: x(q), y2: y1, class: 'wire' })); |
| r2 emssr | 101 | } |
| r2 emssr | 102 | const r = el('rect', { x: bx, y: by, width: bw, height: bh, class: 'box' }); |
| r2 emssr | 103 | r.setAttribute('stroke', col); |
| r2 emssr | 104 | s.appendChild(r); |
| r2 emssr | 105 | const t = el('text', { x: bx + bw / 2, y: by + 13.5, 'text-anchor': 'middle', 'font-size': 11 }, p.syl); |
| r2 emssr | 106 | t.setAttribute('fill', col); |
| r2 emssr | 107 | s.appendChild(t); |
| r2 emssr | 108 | }); |
| r2 emssr | 109 | }); |
| r2 emssr | 110 | |
| r2 emssr | 111 | const last = rows[rows.length - 1]; |
| r2 emssr | 112 | s.appendChild(el('text', { x: 4, y: H - 12, class: 'lab', 'font-size': 10 }, 'Γ⁺')); |
| r2 emssr | 113 | last.out.forEach((w, q) => |
| r2 emssr | 114 | s.appendChild(el('text', { x: x(q), y: H - 12, 'text-anchor': 'middle', 'font-size': 10 }, w))); |
| r2 emssr | 115 | return s; |
| r2 emssr | 116 | } |
| r2 emssr | 117 | |
| r2 emssr | 118 | // ===== 箱の DAG(19 §4.2、§4.3)===== |
| r2 emssr | 119 | |
| r2 emssr | 120 | const KINDCLS = { |
| r2 emssr | 121 | Cst: 'dyad', Add: 'dyad', Mul: 'dyad', Neg: 'dyad', |
| r2 emssr | 122 | Load: 'store', Store: 'store', Ok: 'report', EqBr: 'bit', LtBr: 'bit' |
| r2 emssr | 123 | }; |
| r2 emssr | 124 | |
| r2 emssr | 125 | function drawDag(sc) { |
| r2 emssr | 126 | const bs = sc.boxes || []; |
| r2 emssr | 127 | if (!bs.length) return nothing('箱がありません。'); |
| r2 emssr | 128 | if (bs.length > 260) { |
| r2 emssr | 129 | const p = document.createElement('div'); |
| r2 emssr | 130 | p.appendChild(nothing(`箱が ${bs.length} 個。名前を省き、種類の色だけで並べます。`)); |
| r2 emssr | 131 | p.appendChild(drawSwarm(bs)); |
| r2 emssr | 132 | p.appendChild(kindTable(bs)); |
| r2 emssr | 133 | return p; |
| r2 emssr | 134 | } |
| r2 emssr | 135 | |
| r2 emssr | 136 | const h = bs.map(() => 0); |
| r2 emssr | 137 | bs.forEach(b => b.in.forEach(u => { if (u < b.i) h[b.i] = Math.max(h[b.i], h[u] + 1); })); |
| r2 emssr | 138 | const rows = []; |
| r2 emssr | 139 | bs.forEach(b => { (rows[h[b.i]] = rows[h[b.i]] || []).push(b); }); |
| r2 emssr | 140 | |
| r2 emssr | 141 | const CW = 76, RH = 48, PAD = 10; |
| r2 emssr | 142 | const wide = Math.max(...rows.map(r => r.length)); |
| r2 emssr | 143 | const W = PAD * 2 + wide * CW, H = PAD * 2 + rows.length * RH; |
| r2 emssr | 144 | const s = svg(W, H); |
| r2 emssr | 145 | const at = {}; |
| r2 emssr | 146 | rows.forEach((row, r) => row.forEach((b, k) => { |
| r2 emssr | 147 | at[b.i] = { x: PAD + k * CW + CW / 2, y: PAD + r * RH + 13 }; |
| r2 emssr | 148 | })); |
| r2 emssr | 149 | |
| r2 emssr | 150 | bs.forEach(b => b.in.forEach(u => { |
| r2 emssr | 151 | if (!at[u]) return; |
| r2 emssr | 152 | s.appendChild(el('path', { |
| r2 emssr | 153 | d: `M ${at[u].x} ${at[u].y + 10} C ${at[u].x} ${at[u].y + 28}, ${at[b.i].x} ${at[b.i].y - 20}, ${at[b.i].x} ${at[b.i].y - 10}`, |
| r2 emssr | 154 | class: 'edge' |
| r2 emssr | 155 | })); |
| r2 emssr | 156 | })); |
| r2 emssr | 157 | bs.forEach(b => { |
| r2 emssr | 158 | if (b.to === undefined || !at[b.to]) return; |
| r2 emssr | 159 | const p = el('path', { |
| r2 emssr | 160 | d: `M ${at[b.i].x + 23} ${at[b.i].y} L ${at[b.i].x + 34} ${at[b.i].y} L ${at[b.to].x + 34} ${at[b.to].y} L ${at[b.to].x + 23} ${at[b.to].y}`, |
| r2 emssr | 161 | class: 'edge' |
| r2 emssr | 162 | }); |
| r2 emssr | 163 | p.setAttribute('stroke', 'var(--bit)'); |
| r2 emssr | 164 | p.setAttribute('stroke-dasharray', '3 2'); |
| r2 emssr | 165 | s.appendChild(p); |
| r2 emssr | 166 | }); |
| r2 emssr | 167 | bs.forEach(b => { |
| r2 emssr | 168 | const g = at[b.i], col = `var(--${KINDCLS[b.kind] || 'frame'})`; |
| r2 emssr | 169 | const r = el('rect', { x: g.x - 22, y: g.y - 10, width: 44, height: 20, class: 'box' }); |
| r2 emssr | 170 | r.setAttribute('stroke', col); |
| r2 emssr | 171 | s.appendChild(r); |
| r2 emssr | 172 | const t = el('text', { x: g.x, y: g.y + 3.5, 'text-anchor': 'middle', 'font-size': 10.5 }, |
| r2 emssr | 173 | b.kind === 'Cst' ? String(b.num) : b.kind); |
| r2 emssr | 174 | t.setAttribute('fill', col); |
| r2 emssr | 175 | s.appendChild(t); |
| r2 emssr | 176 | s.appendChild(el('text', { x: g.x, y: g.y + 17, 'text-anchor': 'middle', 'font-size': 9 }, |
| r2 emssr | 177 | b.v === undefined ? `#${b.i}` : `#${b.i} ${dyadText(b.v, b.d)}`)); |
| r2 emssr | 178 | }); |
| r2 emssr | 179 | return s; |
| r2 emssr | 180 | } |
| r2 emssr | 181 | |
| r2 emssr | 182 | // 大きい DAG は名前を省き、種類の色の点だけで並べる。 |
| r2 emssr | 183 | function drawSwarm(bs) { |
| r2 emssr | 184 | const S = 6, GAP = 1.5, PER = 48; |
| r2 emssr | 185 | const rows = Math.ceil(bs.length / PER); |
| r2 emssr | 186 | const s = svg(PER * (S + GAP) + 4, rows * (S + GAP) + 4); |
| r2 emssr | 187 | bs.forEach((b, k) => { |
| r2 emssr | 188 | const r = el('rect', { |
| r2 emssr | 189 | x: 2 + (k % PER) * (S + GAP), y: 2 + Math.floor(k / PER) * (S + GAP), |
| r2 emssr | 190 | width: S, height: S |
| r2 emssr | 191 | }); |
| r2 emssr | 192 | r.setAttribute('fill', `var(--${KINDCLS[b.kind] || 'frame'})`); |
| r2 emssr | 193 | r.setAttribute('opacity', '0.8'); |
| r2 emssr | 194 | s.appendChild(r); |
| r2 emssr | 195 | }); |
| r2 emssr | 196 | return s; |
| r2 emssr | 197 | } |
| r2 emssr | 198 | |
| r2 emssr | 199 | function kindTable(bs) { |
| r2 emssr | 200 | const c = {}; |
| r2 emssr | 201 | bs.forEach(b => { c[b.kind] = (c[b.kind] || 0) + 1; }); |
| r2 emssr | 202 | const t = document.createElement('table'); |
| r2 emssr | 203 | t.innerHTML = '<tr><th class="l">種類</th><th>数</th></tr>' + |
| r2 emssr | 204 | Object.keys(c).sort().map(k => |
| r2 emssr | 205 | `<tr><td class="l" style="color:var(--${KINDCLS[k] || 'frame'})">${k}</td><td>${c[k]}</td></tr>`).join(''); |
| r2 emssr | 206 | return t; |
| r2 emssr | 207 | } |
| r2 emssr | 208 | |
| r2 emssr | 209 | // ===== ツイ数平面(幾何連合論理集合 Level 3〜5、6b、6d)===== |
| r2 emssr | 210 | // |
| r2 emssr | 211 | // 平面は O, ±1, i, j が張る有界な正方形になる。絶対値は 1 を越えない。 |
| r2 emssr | 212 | // |
| r2 emssr | 213 | // 実軸 ω⁻ = (−1,0) ― O = (0,0) ― ω = (1,0) |
| r2 emssr | 214 | // ω 軸 j = (0,−1) ― O ― i = (0,1) |
| r2 emssr | 215 | // 角 ij = (1,1) −ij = (−1,1) ji = (1,−1) −ji = (−1,−1) |
| r2 emssr | 216 | // |
| r2 emssr | 217 | // 整数は等間隔に並ばない。整数 1 を得た後、残余 (O, ω) に同じ構成を再帰的に |
| r2 emssr | 218 | // 当てるので、各段が残余の半分を取る(Level 3 の 1/2 漸減性)。 |
| r2 emssr | 219 | // |
| r2 emssr | 220 | // t_n = 1 − 2^(−n) t_1 = 1/2, t_2 = 3/4, t_3 = 7/8, … → ω |
| r2 emssr | 221 | // |
| r2 emssr | 222 | // この位置づけを Z[1/2] の全部へ伸ばすと、符号について奇な単調写像になる。 |
| r2 emssr | 223 | // |
| r2 emssr | 224 | // π(v) = sgn(v) · (1 − 2^(−|v|)) π(n) = t_n、π(±∞) = ω, ω⁻ |
| r2 emssr | 225 | // |
| r2 emssr | 226 | // ω の非対称性(H⁺ 側は稠密、H⁻ 側は空)が i を生み、ij 対角線反転が |
| r2 emssr | 227 | // i ↔ ω を対にする(6b)。だから実軸の点を ij 対角線で折り返すと ω 軸の |
| r2 emssr | 228 | // 漸減列になり、i に収束する。縦に伸びるのはこの折り返しであって、深さではない。 |
| r2 emssr | 229 | |
| r2 emssr | 230 | function tsuiPos(v) { |
| r2 emssr | 231 | if (v === 0) return 0; |
| r2 emssr | 232 | const s = v < 0 ? -1 : 1; |
| r2 emssr | 233 | return s * (1 - Math.pow(2, -Math.abs(v))); |
| r2 emssr | 234 | } |
| r2 emssr | 235 | |
| r2 emssr | 236 | function drawTsui(sc) { |
| r2 emssr | 237 | const all = (sc.boxes || []).filter(b => b.v !== undefined && b.kind !== 'Ok' |
| r2 emssr | 238 | && Number.isFinite(b.v / 2 ** b.d)); |
| r2 emssr | 239 | if (!all.length) return nothing('値を持つ箱がありません。'); |
| r2 emssr | 240 | |
| r2 emssr | 241 | const outs = new Set((sc.gamma || []).map(g => g.box)); |
| r2 emssr | 242 | const seen = new Map(); |
| r2 emssr | 243 | for (const b of all) { |
| r2 emssr | 244 | const key = b.d + ':' + b.v; |
| r2 emssr | 245 | const cur = seen.get(key); |
| r2 emssr | 246 | if (!cur) seen.set(key, { v: b.v, d: b.d, x: b.v / 2 ** b.d, out: outs.has(b.i) }); |
| r2 emssr | 247 | else cur.out = cur.out || outs.has(b.i); |
| r2 emssr | 248 | } |
| r2 emssr | 249 | const pts = [...seen.values()]; |
| r2 emssr | 250 | const dmax = Math.max(...pts.map(p => p.d)); |
| r2 emssr | 251 | |
| r2 emssr | 252 | const R = 168, PAD = 46; |
| r2 emssr | 253 | const s = svgBox(-R - PAD, -R - PAD - 12, 2 * (R + PAD), 2 * (R + PAD) + 24); |
| r2 emssr | 254 | const P = (u, v) => [R * u, -R * v]; |
| r2 emssr | 255 | |
| r2 emssr | 256 | const line = (a, b, col, w, op, dash) => { |
| r2 emssr | 257 | const e = el('line', { x1: a[0], y1: a[1], x2: b[0], y2: b[1] }); |
| r2 emssr | 258 | e.setAttribute('stroke', col); |
| r2 emssr | 259 | e.setAttribute('stroke-width', w || 1); |
| r2 emssr | 260 | if (op !== undefined) e.setAttribute('opacity', op); |
| r2 emssr | 261 | if (dash) e.setAttribute('stroke-dasharray', dash); |
| r2 emssr | 262 | s.appendChild(e); |
| r2 emssr | 263 | }; |
| r2 emssr | 264 | const dot = (p, r, fill, stroke) => { |
| r2 emssr | 265 | const e = el('circle', { cx: p[0], cy: p[1], r: r }); |
| r2 emssr | 266 | e.setAttribute('fill', fill || 'none'); |
| r2 emssr | 267 | if (stroke) { e.setAttribute('stroke', stroke); e.setAttribute('stroke-width', 1.2); } |
| r2 emssr | 268 | s.appendChild(e); |
| r2 emssr | 269 | }; |
| r2 emssr | 270 | const text = (p, str, col, size, anchor) => { |
| r2 emssr | 271 | const t = el('text', { x: p[0], y: p[1], 'font-size': size || 9.5, |
| r2 emssr | 272 | 'text-anchor': anchor || 'middle' }, str); |
| r2 emssr | 273 | t.setAttribute('fill', col); |
| r2 emssr | 274 | s.appendChild(t); |
| r2 emssr | 275 | }; |
| r2 emssr | 276 | |
| r2 emssr | 277 | // ---- 正方形 (ij, −ij, −ji, ji) ---- |
| r2 emssr | 278 | const ij = P(1, 1), mij = P(-1, 1), ji = P(1, -1), mji = P(-1, -1); |
| r2 emssr | 279 | const sq = el('path', { d: `M ${ij[0]} ${ij[1]} L ${mij[0]} ${mij[1]} L ${mji[0]} ${mji[1]} L ${ji[0]} ${ji[1]} Z` }); |
| r2 emssr | 280 | sq.setAttribute('fill', 'none'); |
| r2 emssr | 281 | sq.setAttribute('stroke', 'var(--ring)'); |
| r2 emssr | 282 | sq.setAttribute('stroke-width', 1); |
| r2 emssr | 283 | s.appendChild(sq); |
| r2 emssr | 284 | |
| r2 emssr | 285 | // ---- 二本の対角線 ---- |
| r2 emssr | 286 | line(mji, ij, 'var(--ring)', 1, 0.8, '4 4'); |
| r2 emssr | 287 | line(mij, ji, 'var(--ring)', 1, 0.8, '4 4'); |
| r2 emssr | 288 | text([ij[0] + 4, ij[1] - 6], 'ij', 'var(--label)', 10, 'start'); |
| r2 emssr | 289 | text([mij[0] - 4, mij[1] - 6], '−ij', 'var(--label)', 10, 'end'); |
| r2 emssr | 290 | text([ji[0] + 4, ji[1] + 13], 'ji', 'var(--label)', 10, 'start'); |
| r2 emssr | 291 | text([mji[0] - 4, mji[1] + 13], '−ji', 'var(--label)', 10, 'end'); |
| r2 emssr | 292 | |
| r2 emssr | 293 | // ---- 実軸と ω 軸 ---- |
| r2 emssr | 294 | line(P(-1, 0), P(1, 0), 'var(--axis)', 1.2, 0.9); |
| r2 emssr | 295 | line(P(0, -1), P(0, 1), 'var(--axis)', 1.2, 0.9); |
| r2 emssr | 296 | dot(P(1, 0), 3.6, 'var(--acc)'); |
| r2 emssr | 297 | dot(P(-1, 0), 3.6, 'var(--acc)'); |
| r2 emssr | 298 | dot(P(0, 1), 3.6, 'var(--label)'); |
| r2 emssr | 299 | dot(P(0, -1), 3.6, 'var(--label)'); |
| r2 emssr | 300 | dot(P(0, 0), 2.6, 'var(--ink)'); |
| r2 emssr | 301 | text([R + 8, 4], 'ω', 'var(--acc)', 11, 'start'); |
| r2 emssr | 302 | text([-R - 8, 4], 'ω⁻', 'var(--acc)', 11, 'end'); |
| r2 emssr | 303 | text([6, -R - 6], 'i', 'var(--label)', 11, 'start'); |
| r2 emssr | 304 | text([6, R + 14], 'j', 'var(--label)', 11, 'start'); |
| r2 emssr | 305 | text([-9, 13], 'O', 'var(--ink)', 10, 'end'); |
| r2 emssr | 306 | |
| r2 emssr | 307 | // ---- Level 3 の整数。t_n = 1 − 2^(−n) で ω へ漸近する ---- |
| r2 emssr | 308 | for (let n = 1; n <= 9; n++) { |
| r2 emssr | 309 | const t = 1 - Math.pow(2, -n); |
| r2 emssr | 310 | for (const sg of [1, -1]) { |
| r2 emssr | 311 | const p = P(sg * t, 0); |
| r2 emssr | 312 | line([p[0], p[1] - 5], [p[0], p[1] + 5], 'var(--acc)', 1, 0.8); |
| r2 emssr | 313 | if (n <= 4) text([p[0], p[1] + 16], String(sg * n), 'var(--acc)', 9); |
| r2 emssr | 314 | } |
| r2 emssr | 315 | } |
| r2 emssr | 316 | text([0, R + 32], '整数 n は t_n = 1 − 2^(−n)。等間隔ではなく ω へ漸近する', |
| r2 emssr | 317 | 'var(--axis)', 9); |
| r2 emssr | 318 | |
| r2 emssr | 319 | // ---- ij 対角線反転。実軸の列を ω 軸へ写し、i へ収束させる(6b)---- |
| r2 emssr | 320 | for (let n = 1; n <= 6; n++) { |
| r2 emssr | 321 | const t = 1 - Math.pow(2, -n); |
| r2 emssr | 322 | line(P(t, 0), P(0, t), 'var(--ring)', 0.7, 0.45); |
| r2 emssr | 323 | dot(P(0, t), 2.4, 'none', 'var(--label)'); |
| r2 emssr | 324 | } |
| r2 emssr | 325 | |
| r2 emssr | 326 | // ---- 平文が作った値 ---- |
| r2 emssr | 327 | const used = []; |
| r2 emssr | 328 | pts.sort((a, b) => a.x - b.x).forEach(p => { |
| r2 emssr | 329 | const u = tsuiPos(p.x); |
| r2 emssr | 330 | const q = P(u, 0); |
| r2 emssr | 331 | const col = p.out ? 'var(--warn)' : 'var(--mark)'; |
| r2 emssr | 332 | line(P(u, 0), P(0, Math.abs(u)), 'var(--ring)', 0.7, 0.35); |
| r2 emssr | 333 | dot(P(0, Math.abs(u)), 2.2, 'none', col); |
| r2 emssr | 334 | dot(q, p.out ? 5 : 3.4, col); |
| r2 emssr | 335 | const label = dyadText(p.v, p.d) + (p.d ? ' δ' + p.d : ''); |
| r2 emssr | 336 | const w = label.length * 5.2 + 6; |
| r2 emssr | 337 | let row = 0; |
| r2 emssr | 338 | while (used.some(r => r.row === row && Math.abs(r.x - q[0]) < (r.w + w) / 2)) row++; |
| r2 emssr | 339 | used.push({ row: row, x: q[0], w: w }); |
| r2 emssr | 340 | const t = el('text', { x: q[0], y: q[1] - 12 - row * 12, 'text-anchor': 'middle', |
| r2 emssr | 341 | 'font-size': 9.5 }, label); |
| r2 emssr | 342 | t.setAttribute('fill', col); |
| r2 emssr | 343 | if (p.out) t.setAttribute('font-weight', '700'); |
| r2 emssr | 344 | s.appendChild(t); |
| r2 emssr | 345 | line([q[0], q[1] - 7], [q[0], q[1] - 9 - row * 12], col, 0.7, 0.5); |
| r2 emssr | 346 | }); |
| r2 emssr | 347 | |
| r2 emssr | 348 | const d = document.createElement('div'); |
| r2 emssr | 349 | d.appendChild(s); |
| r2 emssr | 350 | const f = document.createElement('div'); |
| r2 emssr | 351 | f.className = 'formula'; |
| r2 emssr | 352 | f.textContent = |
| r2 emssr | 353 | '平面は O, ±1, i, j が張る有界な正方形。実軸も ω 軸も絶対値は 1 を越えない。\n' + |
| r2 emssr | 354 | '整数 1 の後、残余 (O, ω) へ同じ構成を再帰的に当てるので各段が残余の半分を取る。\n\n' + |
| r2 emssr | 355 | ' t_n = 1 − 2^(−n) ω は整数列の収束先の境界点(Level 3)\n' + |
| r2 emssr | 356 | ' π(v) = sgn(v)(1 − 2^(−|v|)) π(n) = t_n、π(±∞) = ω, ω⁻\n\n' + |
| r2 emssr | 357 | '橙の目盛りが整数、青が平文の作った値、赤が出口境界 Γ⁺。\n' + |
| r2 emssr | 358 | 'ω 軸へ渡る細線は ij 対角線反転(6b)。実軸の漸減列はこの折り返しで i へ収束する。' + |
| r2 emssr | 359 | (dmax === 0 ? '\nこの文は δ = 0。値は整数だけになる。' |
| r2 emssr | 360 | : '\n最も深い作図は δ = ' + dmax + '。値は x/2^δ で厳密に閉じる。'); |
| r2 emssr | 361 | d.appendChild(f); |
| r2 emssr | 362 | return d; |
| r2 emssr | 363 | } |
| r2 emssr | 364 | |
| r2 emssr | 365 | // ===== lane64 の image(20 §7)===== |
| r2 emssr | 366 | |
| r2 emssr | 367 | function drawLane(sc) { |
| r2 emssr | 368 | const im = sc.image; |
| r2 emssr | 369 | if (!im) return nothing('image がありません。'); |
| r2 emssr | 370 | const d = document.createElement('div'); |
| r2 emssr | 371 | |
| r2 emssr | 372 | const t = document.createElement('table'); |
| r2 emssr | 373 | t.innerHTML = '<tr><th class="l">項目</th><th>値</th></tr>' + |
| r2 emssr | 374 | `<tr><td class="l">image</td><td>${im.bytes} byte</td></tr>` + |
| r2 emssr | 375 | `<tr><td class="l">命令</td><td>${im.insns}</td></tr>` + |
| r2 emssr | 376 | `<tr><td class="l">block</td><td>${im.blocks.length}</td></tr>` + |
| r2 emssr | 377 | Object.keys(im.kinds).sort().map(k => `<tr><td class="l">${k}</td><td>${im.kinds[k]}</td></tr>`).join(''); |
| r2 emssr | 378 | d.appendChild(t); |
| r2 emssr | 379 | |
| r2 emssr | 380 | const bl = im.blocks, RH = 16, PAD = 6, LAB = 96, BARW = 250; |
| r2 emssr | 381 | const maxw = Math.max(1, ...bl.map(b => Math.max(1, ...(b.layers.length ? b.layers : [1])))); |
| r2 emssr | 382 | const s = svg(LAB + BARW + 10, PAD * 2 + bl.length * RH); |
| r2 emssr | 383 | bl.forEach((b, i) => { |
| r2 emssr | 384 | const y = PAD + i * RH; |
| r2 emssr | 385 | s.appendChild(el('text', { x: 2, y: y + 10, 'font-size': 9.5 }, 'pc ' + b.pc)); |
| r2 emssr | 386 | const term = el('text', { x: 54, y: y + 10, 'font-size': 9.5 }, b.term); |
| r2 emssr | 387 | term.setAttribute('fill', 'var(--acc)'); |
| r2 emssr | 388 | s.appendChild(term); |
| r2 emssr | 389 | let x = LAB; |
| r2 emssr | 390 | for (const w of b.layers) { |
| r2 emssr | 391 | const px = Math.max(3, (w / maxw) * 44); |
| r2 emssr | 392 | if (x + px > LAB + BARW) break; |
| r2 emssr | 393 | const r = el('rect', { x: x, y: y + 3, width: px, height: 9 }); |
| r2 emssr | 394 | r.setAttribute('fill', 'var(--mark)'); |
| r2 emssr | 395 | r.setAttribute('opacity', '0.6'); |
| r2 emssr | 396 | s.appendChild(r); |
| r2 emssr | 397 | x += px + 2; |
| r2 emssr | 398 | } |
| r2 emssr | 399 | }); |
| r2 emssr | 400 | const wrap = document.createElement('div'); |
| r2 emssr | 401 | wrap.style.marginTop = '8px'; |
| r2 emssr | 402 | wrap.appendChild(s); |
| r2 emssr | 403 | d.appendChild(box(null, '横棒 1 本が block の中の 1 層。並んだ棒は順序を入れ替えてよい箱の束。', wrap)); |
| r2 emssr | 404 | return d; |
| r2 emssr | 405 | } |
| r2 emssr | 406 | |
| r2 emssr | 407 | // ===== 接合秩(Common-Lang/2 定義 4.1、Common-Lang/1 定理 4.1)===== |
| r2 emssr | 408 | // |
| r2 emssr | 409 | // 座標は common_apps/joinrank/svg/draw.py と同じ。 |
| r2 emssr | 410 | // |
| r2 emssr | 411 | // r(T) = 1 − 2^(−|T|) 次数が環の番号になる |
| r2 emssr | 412 | // θ(T) = π Σ_{k∈T} 2^(−(k+1)) 二進符号が偏角になる 上半平面 Π_i |
| r2 emssr | 413 | // y の点 r = 1/2, θ = −π/2 下半平面 Π_j |
| r2 emssr | 414 | // カット θ_κ = π 2^(−κ) の直径が [n] = A ⊔ B を扇へ分ける |
| r2 emssr | 415 | // |
| r2 emssr | 416 | // 辺は二種類(Common-Lang/2 §3)。 |
| r2 emssr | 417 | // |
| r2 emssr | 418 | // E_star = {(T \ max T, T)} 経路を保持する。Π_i の中の弦。直線 |
| r2 emssr | 419 | // E_plus = {(T, y) : a_T = 1} 経路を忘れる。実軸を一度だけ渡る弧 |
| r2 emssr | 420 | // |
| r2 emssr | 421 | // 分離積は p_r と q_r の台へ細線を引き、二つの集約点の間に太い弦を一本渡す。 |
| r2 emssr | 422 | // カットを渡る太弦はちょうど ρ 本になる。 |
| r2 emssr | 423 | |
| r2 emssr | 424 | const TAU = Math.PI; |
| r2 emssr | 425 | |
| r2 emssr | 426 | function popcount(t) { let c = 0; while (t) { c += t & 1; t >>= 1; } return c; } |
| r2 emssr | 427 | function radiusOf(t) { return 1 - 2 ** (-popcount(t)); } |
| r2 emssr | 428 | function angleOf(t, n) { |
| r2 emssr | 429 | let a = 0; |
| r2 emssr | 430 | for (let k = 0; k < n; k++) if ((t >> k) & 1) a += 2 ** (-(k + 1)); |
| r2 emssr | 431 | return TAU * a; |
| r2 emssr | 432 | } |
| r2 emssr | 433 | function polar(r, th, R) { return [R * r * Math.cos(th), -R * r * Math.sin(th)]; } |
| r2 emssr | 434 | function topBit(t, n) { let m = 0; for (let k = 0; k < n; k++) if ((t >> k) & 1) m = k; return m; } |
| r2 emssr | 435 | |
| r2 emssr | 436 | function chainClosure(support, n) { |
| r2 emssr | 437 | const seen = new Set([0]); |
| r2 emssr | 438 | for (let t of support) { |
| r2 emssr | 439 | while (t && !seen.has(t)) { seen.add(t); t &= ~(1 << topBit(t, n)); } |
| r2 emssr | 440 | } |
| r2 emssr | 441 | return [...seen].sort((a, b) => a - b); |
| r2 emssr | 442 | } |
| r2 emssr | 443 | |
| r2 emssr | 444 | function monoLabel(t, n) { |
| r2 emssr | 445 | if (!t) return '1'; |
| r2 emssr | 446 | let s = ''; |
| r2 emssr | 447 | for (let k = 0; k < n; k++) if ((t >> k) & 1) s += 'x' + k; |
| r2 emssr | 448 | return s; |
| r2 emssr | 449 | } |
| r2 emssr | 450 | |
| r2 emssr | 451 | // 中点を水平へ寄せた制御点。B_y が t の一次式に落ちるので実軸との交点が一意に決まる。 |
| r2 emssr | 452 | function midCtrl(p0, p1, h) { return [(p0[0] + p1[0]) / 2 + h, (p0[1] + p1[1]) / 2]; } |
| r2 emssr | 453 | |
| r2 emssr | 454 | function bez(p0, c, p1, t) { |
| r2 emssr | 455 | const u = 1 - t; |
| r2 emssr | 456 | return [u * u * p0[0] + 2 * t * u * c[0] + t * t * p1[0], |
| r2 emssr | 457 | u * u * p0[1] + 2 * t * u * c[1] + t * t * p1[1]]; |
| r2 emssr | 458 | } |
| r2 emssr | 459 | |
| r2 emssr | 460 | // f は線形。f(B(t)) = a t² + b t + c の [0,1] の根の像を返す。 |
| r2 emssr | 461 | function crossAt(p0, c, p1, f) { |
| r2 emssr | 462 | const eps = 1e-9, f0 = f(p0), fc = f(c), f1 = f(p1); |
| r2 emssr | 463 | const a = f0 - 2 * fc + f1, b = 2 * (fc - f0), c0 = f0; |
| r2 emssr | 464 | let ts = []; |
| r2 emssr | 465 | if (Math.abs(a) < eps) { if (Math.abs(b) > eps) ts = [-c0 / b]; } |
| r2 emssr | 466 | else { |
| r2 emssr | 467 | const d = b * b - 4 * a * c0; |
| r2 emssr | 468 | if (d >= 0) { const s = Math.sqrt(d); ts = [(-b - s) / (2 * a), (-b + s) / (2 * a)]; } |
| r2 emssr | 469 | } |
| r2 emssr | 470 | ts = ts.filter(t => t >= -eps && t <= 1 + eps); |
| r2 emssr | 471 | return ts.length ? bez(p0, c, p1, Math.min(Math.max(ts[0], 0), 1)) : null; |
| r2 emssr | 472 | } |
| r2 emssr | 473 | |
| r2 emssr | 474 | function fAxis(p) { return p[1]; } |
| r2 emssr | 475 | function fCut(th) { |
| r2 emssr | 476 | const dx = Math.cos(th), dy = -Math.sin(th); |
| r2 emssr | 477 | return p => p[0] * dy - p[1] * dx; |
| r2 emssr | 478 | } |
| r2 emssr | 479 | |
| r2 emssr | 480 | function meanAngle(sup, n, lo, hi) { |
| r2 emssr | 481 | if (!sup || !sup.length) return (lo + hi) / 2; |
| r2 emssr | 482 | const a = sup.reduce((s, t) => s + angleOf(t, n), 0) / sup.length; |
| r2 emssr | 483 | return Math.min(Math.max(a, lo + 0.08 * (hi - lo)), lo + 0.92 * (hi - lo)); |
| r2 emssr | 484 | } |
| r2 emssr | 485 | |
| r2 emssr | 486 | function drawJoint(j) { |
| r2 emssr | 487 | if (!j) return nothing('このセルは入力の語を指定していません。notebook の cell に inputs と cut を書くと出ます。'); |
| r2 emssr | 488 | if (!j.ok) return nothing(j.error || '接合秩を取れません。'); |
| r2 emssr | 489 | |
| r2 emssr | 490 | const n = j.n, cut = j.cut, rho = j.rho, R = 150, PAD = 58; |
| r2 emssr | 491 | const support = j.anf || []; |
| r2 emssr | 492 | const nodes = chainClosure(support, n); |
| r2 emssr | 493 | const thc = TAU * 2 ** (-cut); |
| r2 emssr | 494 | const s = svgBox(-R - PAD, -R - PAD, 2 * (R + PAD), 2 * (R + PAD)); |
| r2 emssr | 495 | |
| r2 emssr | 496 | const put = (tag, at, fill, stroke, w, op) => { |
| r2 emssr | 497 | const e = el(tag, at); |
| r2 emssr | 498 | if (fill) e.setAttribute('fill', fill); else e.setAttribute('fill', 'none'); |
| r2 emssr | 499 | if (stroke) { e.setAttribute('stroke', stroke); e.setAttribute('stroke-width', w || 1); } |
| r2 emssr | 500 | if (op !== undefined) e.setAttribute('opacity', op); |
| r2 emssr | 501 | s.appendChild(e); |
| r2 emssr | 502 | return e; |
| r2 emssr | 503 | }; |
| r2 emssr | 504 | const line = (p, q, col, w, op, dash) => { |
| r2 emssr | 505 | const e = put('line', { x1: p[0], y1: p[1], x2: q[0], y2: q[1] }, null, col, w, op); |
| r2 emssr | 506 | e.setAttribute('stroke-linecap', 'round'); |
| r2 emssr | 507 | if (dash) e.setAttribute('stroke-dasharray', dash); |
| r2 emssr | 508 | }; |
| r2 emssr | 509 | const text = (p, str, col, size, anchor, op) => { |
| r2 emssr | 510 | const t = el('text', { x: p[0], y: p[1], 'font-size': size || 9.5, |
| r2 emssr | 511 | 'text-anchor': anchor || 'middle' }, str); |
| r2 emssr | 512 | t.setAttribute('fill', col); |
| r2 emssr | 513 | if (op !== undefined) t.setAttribute('opacity', op); |
| r2 emssr | 514 | s.appendChild(t); |
| r2 emssr | 515 | }; |
| r2 emssr | 516 | const wedge = (a0, a1, col) => { |
| r2 emssr | 517 | const p0 = polar(1, a0, R), p1 = polar(1, a1, R); |
| r2 emssr | 518 | const big = Math.abs(a1 - a0) > TAU ? 1 : 0; |
| r2 emssr | 519 | put('path', { d: `M 0 0 L ${p0[0]} ${p0[1]} A ${R} ${R} 0 ${big} 0 ${p1[0]} ${p1[1]} Z` }, |
| r2 emssr | 520 | col, null, 0, 0.06); |
| r2 emssr | 521 | }; |
| r2 emssr | 522 | |
| r2 emssr | 523 | // カットが分ける扇 |
| r2 emssr | 524 | wedge(thc, TAU, 'var(--mark)'); |
| r2 emssr | 525 | wedge(0, thc, 'var(--sub)'); |
| r2 emssr | 526 | |
| r2 emssr | 527 | // 環。r = 1 − 2^(−|T|) |
| r2 emssr | 528 | for (let k = 0; k <= n; k++) { |
| r2 emssr | 529 | put('circle', { cx: 0, cy: 0, r: R * (1 - 2 ** (-k)) }, null, 'var(--ring)', 0.7, 0.55); |
| r2 emssr | 530 | } |
| r2 emssr | 531 | const om = put('circle', { cx: 0, cy: 0, r: R }, null, 'var(--ring)', 1.1, 0.9); |
| r2 emssr | 532 | om.setAttribute('stroke-dasharray', '5 4'); |
| r2 emssr | 533 | text([R + 9, -3], 'ω', 'var(--ring)', 10, 'start'); |
| r2 emssr | 534 | |
| r2 emssr | 535 | // 実軸。Π_i と Π_j の共通境界 |
| r2 emssr | 536 | line([-R - 22, 0], [R + 22, 0], 'var(--axis)', 1, 0.85); |
| r2 emssr | 537 | text([-R - 26, -7], 'Π_i', 'var(--axis)', 9.5, 'start'); |
| r2 emssr | 538 | text([-R - 26, 15], 'Π_j', 'var(--axis)', 9.5, 'start'); |
| r2 emssr | 539 | |
| r2 emssr | 540 | // カットの直径 |
| r2 emssr | 541 | const cp = polar(1.1, thc, R), cq = polar(1.1, thc + TAU, R); |
| r2 emssr | 542 | line(cp, cq, 'var(--acc)', 1.3, 0.95, '7 5'); |
| r2 emssr | 543 | text([cp[0], cp[1] - 9], 'κ = ' + cut, 'var(--acc)', 10); |
| r2 emssr | 544 | text(polar(1.2, (thc + TAU) / 2, R), 'A', 'var(--mark)', 11); |
| r2 emssr | 545 | text(polar(1.2, thc / 2, R), 'B', 'var(--sub)', 11); |
| r2 emssr | 546 | |
| r2 emssr | 547 | const fac = new Set([].concat(...(j.psup || []), ...(j.qsup || []))); |
| r2 emssr | 548 | const raw = fac.size ? 0.26 : 1.0; |
| r2 emssr | 549 | |
| r2 emssr | 550 | // E_star。Π_i の中の弦 |
| r2 emssr | 551 | for (const t of nodes) { |
| r2 emssr | 552 | if (!t) continue; |
| r2 emssr | 553 | const a = t & ~(1 << topBit(t, n)); |
| r2 emssr | 554 | line(polar(radiusOf(a), angleOf(a, n), R), polar(radiusOf(t), angleOf(t, n), R), |
| r2 emssr | 555 | 'var(--mark)', 1.3, raw); |
| r2 emssr | 556 | } |
| r2 emssr | 557 | |
| r2 emssr | 558 | // E_plus。実軸を一度だけ渡る弧と、その交点の丸 |
| r2 emssr | 559 | const yout = polar(0.5, -TAU / 2, R); |
| r2 emssr | 560 | put('circle', { cx: yout[0], cy: yout[1], r: 6 }, 'var(--label)', null, 0, raw); |
| r2 emssr | 561 | text([yout[0], yout[1] + 20], 'y', 'var(--label)', 10, 'middle', raw); |
| r2 emssr | 562 | for (const t of support) { |
| r2 emssr | 563 | const pa = polar(radiusOf(t), angleOf(t, n), R); |
| r2 emssr | 564 | const ctl = midCtrl(pa, yout, pa[0] >= 0 ? 24 : -24); |
| r2 emssr | 565 | const e = put('path', { d: `M ${pa[0]} ${pa[1]} Q ${ctl[0]} ${ctl[1]} ${yout[0]} ${yout[1]}` }, |
| r2 emssr | 566 | null, 'var(--sub)', 1.5, raw); |
| r2 emssr | 567 | e.setAttribute('stroke-linecap', 'round'); |
| r2 emssr | 568 | const x = crossAt(pa, ctl, yout, fAxis); |
| r2 emssr | 569 | if (x) put('circle', { cx: x[0], cy: x[1], r: 2.8 }, 'var(--sub)', null, 0, raw); |
| r2 emssr | 570 | } |
| r2 emssr | 571 | |
| r2 emssr | 572 | // 単項式の点 |
| r2 emssr | 573 | for (const t of nodes) { |
| r2 emssr | 574 | const p = polar(radiusOf(t), angleOf(t, n), R); |
| r2 emssr | 575 | const on = support.includes(t); |
| r2 emssr | 576 | const op = fac.has(t) ? 1 : raw; |
| r2 emssr | 577 | put('circle', { cx: p[0], cy: p[1], r: on ? 4.6 : 2.8 }, |
| r2 emssr | 578 | on ? 'var(--ink)' : 'var(--bg)', 'var(--ink)', 1.1, op); |
| r2 emssr | 579 | const right = angleOf(t, n) < TAU / 2; |
| r2 emssr | 580 | text([p[0] + (right ? 10 : -10), p[1] - 8], monoLabel(t, n), 'var(--label)', 9, |
| r2 emssr | 581 | right ? 'start' : 'end', op); |
| r2 emssr | 582 | } |
| r2 emssr | 583 | |
| r2 emssr | 584 | // 分離積。カットを渡る太弦はちょうど ρ 本 |
| r2 emssr | 585 | let ncross = 0; |
| r2 emssr | 586 | const fc = fCut(thc); |
| r2 emssr | 587 | for (let r = 0; r < rho; r++) { |
| r2 emssr | 588 | const rr = 0.40 + 0.22 * (r + 1) / (rho + 1); |
| r2 emssr | 589 | const ap = meanAngle((j.psup || [])[r], n, thc, TAU); |
| r2 emssr | 590 | const aq = meanAngle((j.qsup || [])[r], n, 0, thc); |
| r2 emssr | 591 | const pp = polar(rr, ap, R), pq = polar(rr, aq, R); |
| r2 emssr | 592 | for (const t of ((j.psup || [])[r] || [])) line(polar(radiusOf(t), angleOf(t, n), R), pp, 'var(--warn)', 0.8, 0.55); |
| r2 emssr | 593 | for (const t of ((j.qsup || [])[r] || [])) line(polar(radiusOf(t), angleOf(t, n), R), pq, 'var(--warn)', 0.8, 0.55); |
| r2 emssr | 594 | line(pq, pp, 'var(--warn)', 2.3, 1); |
| r2 emssr | 595 | const x = crossAt(pq, midCtrl(pq, pp, 0), pp, fc); |
| r2 emssr | 596 | if (x) { ncross++; put('circle', { cx: x[0], cy: x[1], r: 3.6 }, 'var(--acc)', 'var(--warn)', 1.2); } |
| r2 emssr | 597 | put('circle', { cx: pp[0], cy: pp[1], r: 5 }, 'var(--warn)'); |
| r2 emssr | 598 | put('circle', { cx: pq[0], cy: pq[1], r: 5 }, 'var(--warn)'); |
| r2 emssr | 599 | text([pp[0], pp[1] - 10], 'p' + r, 'var(--warn)', 9); |
| r2 emssr | 600 | text([pq[0], pq[1] - 10], 'q' + r, 'var(--warn)', 9); |
| r2 emssr | 601 | } |
| r2 emssr | 602 | |
| r2 emssr | 603 | const d = document.createElement('div'); |
| r2 emssr | 604 | d.appendChild(s); |
| r2 emssr | 605 | const f = document.createElement('div'); |
| r2 emssr | 606 | f.className = 'formula'; |
| r2 emssr | 607 | f.textContent = |
| r2 emssr | 608 | `n = ${n} A = {${j.inputs.slice(0, cut).join(', ')}} B = {${j.inputs.slice(cut).join(', ')}}\n` + |
| r2 emssr | 609 | `ρ = rank_𝔽₂ F = ${rho} カットを渡る太弦 ${ncross} 本\n` + |
| r2 emssr | 610 | `台 ${support.length} 点 環 ${n + 1} 本`; |
| r2 emssr | 611 | d.appendChild(f); |
| r2 emssr | 612 | d.appendChild(jointLegend()); |
| r2 emssr | 613 | d.appendChild(jointTable(j)); |
| r2 emssr | 614 | return d; |
| r2 emssr | 615 | } |
| r2 emssr | 616 | |
| r2 emssr | 617 | function jointLegend() { |
| r2 emssr | 618 | const rows = [ |
| r2 emssr | 619 | ['ring', '環 r = 1 − 2^(−|T|)', '相互作用の次数 |T|'], |
| r2 emssr | 620 | ['mark', '青い弦 E_star', '経路の保持。角度差 π2^(−k) が x_k'], |
| r2 emssr | 621 | ['sub', '緑の弧 E_plus', '実軸を渡る合算。丸が経路を忘れる一点'], |
| r2 emssr | 622 | ['acc', '橙の直径', 'カット θ_κ = π 2^(−κ)'], |
| r2 emssr | 623 | ['warn', '赤の太弦', 'カットを渡る独立影響。本数が接合秩'], |
| r2 emssr | 624 | ]; |
| r2 emssr | 625 | const t = document.createElement('table'); |
| r2 emssr | 626 | t.innerHTML = rows.map(([c, a, b]) => |
| r2 emssr | 627 | `<tr><td class="l" style="color:var(--${c})">${a}</td><td class="l">${b}</td></tr>`).join(''); |
| r2 emssr | 628 | return t; |
| r2 emssr | 629 | } |
| r2 emssr | 630 | |
| r2 emssr | 631 | function jointTable(j) { |
| r2 emssr | 632 | const na = 1 << j.cut, nb = 1 << (j.n - j.cut); |
| r2 emssr | 633 | const t = document.createElement('table'); |
| r2 emssr | 634 | let html = '<tr><th class="l">F</th>'; |
| r2 emssr | 635 | for (let b = 0; b < nb; b++) html += `<th>β${b}</th>`; |
| r2 emssr | 636 | html += '</tr>'; |
| r2 emssr | 637 | for (let a = 0; a < na; a++) { |
| r2 emssr | 638 | html += `<tr><th class="l">α${a}</th>`; |
| r2 emssr | 639 | for (let b = 0; b < nb; b++) html += `<td>${j.table[a + na * b]}</td>`; |
| r2 emssr | 640 | html += '</tr>'; |
| r2 emssr | 641 | } |
| r2 emssr | 642 | t.innerHTML = html; |
| r2 emssr | 643 | const d = document.createElement('div'); |
| r2 emssr | 644 | d.appendChild(t); |
| r2 emssr | 645 | const f = document.createElement('div'); |
| r2 emssr | 646 | f.className = 'formula'; |
| r2 emssr | 647 | f.textContent = 'f(x_A, x_B) = ⊕_{r<ρ} p_r(x_A) q_r(x_B)\n' + |
| r2 emssr | 648 | j.p.map((pr, r) => ` r=${r} p = [${pr.join('')}] q = [${j.q[r].join('')}]`).join('\n'); |
| r2 emssr | 649 | d.appendChild(f); |
| r2 emssr | 650 | return d; |
| r2 emssr | 651 | } |
| r2 emssr | 652 | |
| r2 emssr | 653 | // ===== 音節と箱の表 ===== |
| r2 emssr | 654 | |
| r2 emssr | 655 | function drawText(sc) { |
| r2 emssr | 656 | const d = document.createElement('div'); |
| r2 emssr | 657 | const w = document.createElement('table'); |
| r2 emssr | 658 | w.innerHTML = '<tr><th>r</th><th>q</th><th class="l">音節</th><th class="l">生成元</th><th>pin</th><th>pout</th><th>箱</th></tr>' + |
| r2 emssr | 659 | sc.words.map(x => `<tr><td>${x.r}</td><td>${x.q}</td>` + |
| r2 emssr | 660 | `<td class="l" style="color:var(--${x.cls})">${x.syl}</td>` + |
| r2 emssr | 661 | `<td class="l">${x.sym}</td><td>${x.pin}</td><td>${x.pout}</td>` + |
| r2 emssr | 662 | `<td>${x.boxes === null ? '—' : x.boxes}</td></tr>`).join(''); |
| r2 emssr | 663 | d.appendChild(box('音節 → 生成元', 'idx(cv) = 5 ord(c) + ord(v) C = (k s t n h m y r w g z d b p), V = (a i u e o)', w)); |
| r2 emssr | 664 | |
| r2 emssr | 665 | const b = document.createElement('table'); |
| r2 emssr | 666 | b.innerHTML = '<tr><th>#</th><th class="l">種類</th><th class="l">引数</th><th>δ</th><th>語 64+w</th><th class="l">x/2^δ</th></tr>' + |
| r2 emssr | 667 | sc.boxes.slice(0, 400).map(x => |
| r2 emssr | 668 | `<tr><td>${x.i}</td>` + |
| r2 emssr | 669 | `<td class="l" style="color:var(--${KINDCLS[x.kind] || 'frame'})">${x.kind}${x.kind === 'Cst' ? ' ' + x.num : ''}</td>` + |
| r2 emssr | 670 | `<td class="l">${x.in.join(', ')}${x.to !== undefined ? ' → ' + x.to : ''}</td>` + |
| r2 emssr | 671 | `<td>${x.d}</td><td>${x.v === undefined ? '' : x.v}</td>` + |
| r2 emssr | 672 | `<td class="l">${x.v === undefined ? '' : dyadText(x.v, x.d)}</td></tr>`).join(''); |
| r2 emssr | 673 | d.appendChild(box('箱', 'commonc の OPK / OPA / OPB / OPD / OPL をそのまま(19 §3.2)', b)); |
| r2 emssr | 674 | return d; |
| r2 emssr | 675 | } |