📦 Out of Context リポジトリ

リポジトリ / emssr / independent_common_compiler

r2
r2 emssr1
// scope の描き分け。どの関数も kernel が返した値をそのまま形にする。
r2 emssr2
// 計算はここに置かない。
r2 emssr3
//
r2 emssr4
// 座標と色は common_apps/joinrank/svg/draw.py と同じものを使う。
r2 emssr5
// 同じ図を同じ読み方で見せるため、別の流儀を持ち込まない。
r2 emssr6
r2 emssr7
const NS = 'http://www.w3.org/2000/svg';
r2 emssr8
r2 emssr9
function el(tag, attrs, text) {
r2 emssr10
  const n = document.createElementNS(NS, tag);
r2 emssr11
  for (const k in attrs) n.setAttribute(k, attrs[k]);
r2 emssr12
  if (text !== undefined) n.textContent = text;
r2 emssr13
  return n;
r2 emssr14
}
r2 emssr15
r2 emssr16
function svgBox(x, y, w, h) {
r2 emssr17
  const s = el('svg', { viewBox: `${x} ${y} ${w} ${h}`, width: w, height: h });
r2 emssr18
  s.style.maxWidth = '100%';
r2 emssr19
  return s;
r2 emssr20
}
r2 emssr21
r2 emssr22
function svg(w, h) { return svgBox(0, 0, w, h); }
r2 emssr23
r2 emssr24
function box(title, formula, node) {
r2 emssr25
  const d = document.createElement('div');
r2 emssr26
  d.className = 'scopebox';
r2 emssr27
  if (title) {
r2 emssr28
    const h = document.createElement('h2');
r2 emssr29
    h.textContent = title;
r2 emssr30
    d.appendChild(h);
r2 emssr31
  }
r2 emssr32
  if (formula) {
r2 emssr33
    const f = document.createElement('div');
r2 emssr34
    f.className = 'formula';
r2 emssr35
    f.textContent = formula;
r2 emssr36
    d.appendChild(f);
r2 emssr37
  }
r2 emssr38
  if (node) d.appendChild(node);
r2 emssr39
  return d;
r2 emssr40
}
r2 emssr41
r2 emssr42
function nothing(msg) {
r2 emssr43
  const p = document.createElement('p');
r2 emssr44
  p.className = 'empty';
r2 emssr45
  p.textContent = msg;
r2 emssr46
  return p;
r2 emssr47
}
r2 emssr48
r2 emssr49
function dyadText(num, d) {
r2 emssr50
  if (!d) return String(num);
r2 emssr51
  let n = num, k = d;
r2 emssr52
  while (k > 0 && n % 2 === 0) { n /= 2; k--; }
r2 emssr53
  return k === 0 ? String(n) : `${n}/${2 ** k}`;
r2 emssr54
}
r2 emssr55
r2 emssr56
// ===== 層と机(16 §2、19 §4.2)=====
r2 emssr57
//
r2 emssr58
// 層 r が行、机の位置 q が列。疎テンソル S(k,q,r) をそのまま平面へ置く。
r2 emssr59
// 箱は入口の区間 take を取り、出口の区間 give へ置く。ki は素通しの線になる。
r2 emssr60
r2 emssr61
function drawChamber(sc) {
r2 emssr62
  const rows = sc.frontier || [];
r2 emssr63
  if (!rows.length) return nothing('層がありません。');
r2 emssr64
r2 emssr65
  const COL = 46, ROW = 58, PAD = 40, TOP = 26;
r2 emssr66
  const wide = Math.max(1, ...rows.map(r => Math.max(r.in.length, r.out.length)));
r2 emssr67
  const W = PAD + wide * COL + 26, H = TOP + rows.length * ROW + 34;
r2 emssr68
  const s = svg(W, H);
r2 emssr69
  const x = q => PAD + q * COL + COL / 2;
r2 emssr70
r2 emssr71
  s.appendChild(el('text', { x: 4, y: TOP - 10, 'font-size': 9.5 }, 'q'));
r2 emssr72
  for (let q = 0; q < wide; q++) {
r2 emssr73
    s.appendChild(el('text', { x: x(q), y: TOP - 10, 'text-anchor': 'middle', 'font-size': 9.5 }, String(q)));
r2 emssr74
  }
r2 emssr75
r2 emssr76
  rows.forEach((row, i) => {
r2 emssr77
    const y0 = TOP + i * ROW, y1 = y0 + ROW;
r2 emssr78
    s.appendChild(el('line', { x1: PAD - 10, y1: y0, x2: W - 14, y2: y0, class: 'grid' }));
r2 emssr79
    s.appendChild(el('text', { x: 4, y: y0 + 16, class: 'lab', 'font-size': 10 }, 'Λ' + row.r));
r2 emssr80
r2 emssr81
    (row.put || []).forEach(p => {
r2 emssr82
      if (p.wire) {
r2 emssr83
        s.appendChild(el('path', {
r2 emssr84
          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 emssr85
          class: 'wire'
r2 emssr86
        }));
r2 emssr87
        return;
r2 emssr88
      }
r2 emssr89
      const hasIn = p.take[1] > p.take[0], hasOut = p.give[1] > p.give[0];
r2 emssr90
      const xs = [];
r2 emssr91
      if (hasIn) { xs.push(x(p.take[0]), x(p.take[1] - 1)); }
r2 emssr92
      if (hasOut) { xs.push(x(p.give[0]), x(p.give[1] - 1)); }
r2 emssr93
      const bx = Math.min(...xs) - 16, bw = Math.max(...xs) - Math.min(...xs) + 32;
r2 emssr94
      const by = y0 + 21, bh = 19;
r2 emssr95
      const col = `var(--${p.cls || 'frame'})`;
r2 emssr96
      for (let q = p.take[0]; q < p.take[1]; q++) {
r2 emssr97
        s.appendChild(el('line', { x1: x(q), y1: y0, x2: x(q), y2: by, class: 'wire' }));
r2 emssr98
      }
r2 emssr99
      for (let q = p.give[0]; q < p.give[1]; q++) {
r2 emssr100
        s.appendChild(el('line', { x1: x(q), y1: by + bh, x2: x(q), y2: y1, class: 'wire' }));
r2 emssr101
      }
r2 emssr102
      const r = el('rect', { x: bx, y: by, width: bw, height: bh, class: 'box' });
r2 emssr103
      r.setAttribute('stroke', col);
r2 emssr104
      s.appendChild(r);
r2 emssr105
      const t = el('text', { x: bx + bw / 2, y: by + 13.5, 'text-anchor': 'middle', 'font-size': 11 }, p.syl);
r2 emssr106
      t.setAttribute('fill', col);
r2 emssr107
      s.appendChild(t);
r2 emssr108
    });
r2 emssr109
  });
r2 emssr110
r2 emssr111
  const last = rows[rows.length - 1];
r2 emssr112
  s.appendChild(el('text', { x: 4, y: H - 12, class: 'lab', 'font-size': 10 }, 'Γ⁺'));
r2 emssr113
  last.out.forEach((w, q) =>
r2 emssr114
    s.appendChild(el('text', { x: x(q), y: H - 12, 'text-anchor': 'middle', 'font-size': 10 }, w)));
r2 emssr115
  return s;
r2 emssr116
}
r2 emssr117
r2 emssr118
// ===== 箱の DAG(19 §4.2、§4.3)=====
r2 emssr119
r2 emssr120
const KINDCLS = {
r2 emssr121
  Cst: 'dyad', Add: 'dyad', Mul: 'dyad', Neg: 'dyad',
r2 emssr122
  Load: 'store', Store: 'store', Ok: 'report', EqBr: 'bit', LtBr: 'bit'
r2 emssr123
};
r2 emssr124
r2 emssr125
function drawDag(sc) {
r2 emssr126
  const bs = sc.boxes || [];
r2 emssr127
  if (!bs.length) return nothing('箱がありません。');
r2 emssr128
  if (bs.length > 260) {
r2 emssr129
    const p = document.createElement('div');
r2 emssr130
    p.appendChild(nothing(`箱が ${bs.length} 個。名前を省き、種類の色だけで並べます。`));
r2 emssr131
    p.appendChild(drawSwarm(bs));
r2 emssr132
    p.appendChild(kindTable(bs));
r2 emssr133
    return p;
r2 emssr134
  }
r2 emssr135
r2 emssr136
  const h = bs.map(() => 0);
r2 emssr137
  bs.forEach(b => b.in.forEach(u => { if (u < b.i) h[b.i] = Math.max(h[b.i], h[u] + 1); }));
r2 emssr138
  const rows = [];
r2 emssr139
  bs.forEach(b => { (rows[h[b.i]] = rows[h[b.i]] || []).push(b); });
r2 emssr140
r2 emssr141
  const CW = 76, RH = 48, PAD = 10;
r2 emssr142
  const wide = Math.max(...rows.map(r => r.length));
r2 emssr143
  const W = PAD * 2 + wide * CW, H = PAD * 2 + rows.length * RH;
r2 emssr144
  const s = svg(W, H);
r2 emssr145
  const at = {};
r2 emssr146
  rows.forEach((row, r) => row.forEach((b, k) => {
r2 emssr147
    at[b.i] = { x: PAD + k * CW + CW / 2, y: PAD + r * RH + 13 };
r2 emssr148
  }));
r2 emssr149
r2 emssr150
  bs.forEach(b => b.in.forEach(u => {
r2 emssr151
    if (!at[u]) return;
r2 emssr152
    s.appendChild(el('path', {
r2 emssr153
      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 emssr154
      class: 'edge'
r2 emssr155
    }));
r2 emssr156
  }));
r2 emssr157
  bs.forEach(b => {
r2 emssr158
    if (b.to === undefined || !at[b.to]) return;
r2 emssr159
    const p = el('path', {
r2 emssr160
      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 emssr161
      class: 'edge'
r2 emssr162
    });
r2 emssr163
    p.setAttribute('stroke', 'var(--bit)');
r2 emssr164
    p.setAttribute('stroke-dasharray', '3 2');
r2 emssr165
    s.appendChild(p);
r2 emssr166
  });
r2 emssr167
  bs.forEach(b => {
r2 emssr168
    const g = at[b.i], col = `var(--${KINDCLS[b.kind] || 'frame'})`;
r2 emssr169
    const r = el('rect', { x: g.x - 22, y: g.y - 10, width: 44, height: 20, class: 'box' });
r2 emssr170
    r.setAttribute('stroke', col);
r2 emssr171
    s.appendChild(r);
r2 emssr172
    const t = el('text', { x: g.x, y: g.y + 3.5, 'text-anchor': 'middle', 'font-size': 10.5 },
r2 emssr173
      b.kind === 'Cst' ? String(b.num) : b.kind);
r2 emssr174
    t.setAttribute('fill', col);
r2 emssr175
    s.appendChild(t);
r2 emssr176
    s.appendChild(el('text', { x: g.x, y: g.y + 17, 'text-anchor': 'middle', 'font-size': 9 },
r2 emssr177
      b.v === undefined ? `#${b.i}` : `#${b.i}  ${dyadText(b.v, b.d)}`));
r2 emssr178
  });
r2 emssr179
  return s;
r2 emssr180
}
r2 emssr181
r2 emssr182
// 大きい DAG は名前を省き、種類の色の点だけで並べる。
r2 emssr183
function drawSwarm(bs) {
r2 emssr184
  const S = 6, GAP = 1.5, PER = 48;
r2 emssr185
  const rows = Math.ceil(bs.length / PER);
r2 emssr186
  const s = svg(PER * (S + GAP) + 4, rows * (S + GAP) + 4);
r2 emssr187
  bs.forEach((b, k) => {
r2 emssr188
    const r = el('rect', {
r2 emssr189
      x: 2 + (k % PER) * (S + GAP), y: 2 + Math.floor(k / PER) * (S + GAP),
r2 emssr190
      width: S, height: S
r2 emssr191
    });
r2 emssr192
    r.setAttribute('fill', `var(--${KINDCLS[b.kind] || 'frame'})`);
r2 emssr193
    r.setAttribute('opacity', '0.8');
r2 emssr194
    s.appendChild(r);
r2 emssr195
  });
r2 emssr196
  return s;
r2 emssr197
}
r2 emssr198
r2 emssr199
function kindTable(bs) {
r2 emssr200
  const c = {};
r2 emssr201
  bs.forEach(b => { c[b.kind] = (c[b.kind] || 0) + 1; });
r2 emssr202
  const t = document.createElement('table');
r2 emssr203
  t.innerHTML = '<tr><th class="l">種類</th><th>数</th></tr>' +
r2 emssr204
    Object.keys(c).sort().map(k =>
r2 emssr205
      `<tr><td class="l" style="color:var(--${KINDCLS[k] || 'frame'})">${k}</td><td>${c[k]}</td></tr>`).join('');
r2 emssr206
  return t;
r2 emssr207
}
r2 emssr208
r2 emssr209
// ===== ツイ数平面(幾何連合論理集合 Level 3〜5、6b、6d)=====
r2 emssr210
//
r2 emssr211
// 平面は O, ±1, i, j が張る有界な正方形になる。絶対値は 1 を越えない。
r2 emssr212
//
r2 emssr213
//   実軸  ω⁻ = (−1,0)  ―  O = (0,0)  ―  ω = (1,0)
r2 emssr214
//   ω 軸  j  = (0,−1)  ―  O          ―  i = (0,1)
r2 emssr215
//   角    ij = (1,1)   −ij = (−1,1)   ji = (1,−1)   −ji = (−1,−1)
r2 emssr216
//
r2 emssr217
// 整数は等間隔に並ばない。整数 1 を得た後、残余 (O, ω) に同じ構成を再帰的に
r2 emssr218
// 当てるので、各段が残余の半分を取る(Level 3 の 1/2 漸減性)。
r2 emssr219
//
r2 emssr220
//   t_n = 1 − 2^(−n)        t_1 = 1/2, t_2 = 3/4, t_3 = 7/8, … → ω
r2 emssr221
//
r2 emssr222
// この位置づけを Z[1/2] の全部へ伸ばすと、符号について奇な単調写像になる。
r2 emssr223
//
r2 emssr224
//   π(v) = sgn(v) · (1 − 2^(−|v|))        π(n) = t_n、π(±∞) = ω, ω⁻
r2 emssr225
//
r2 emssr226
// ω の非対称性(H⁺ 側は稠密、H⁻ 側は空)が i を生み、ij 対角線反転が
r2 emssr227
// i ↔ ω を対にする(6b)。だから実軸の点を ij 対角線で折り返すと ω 軸の
r2 emssr228
// 漸減列になり、i に収束する。縦に伸びるのはこの折り返しであって、深さではない。
r2 emssr229
r2 emssr230
function tsuiPos(v) {
r2 emssr231
  if (v === 0) return 0;
r2 emssr232
  const s = v < 0 ? -1 : 1;
r2 emssr233
  return s * (1 - Math.pow(2, -Math.abs(v)));
r2 emssr234
}
r2 emssr235
r2 emssr236
function drawTsui(sc) {
r2 emssr237
  const all = (sc.boxes || []).filter(b => b.v !== undefined && b.kind !== 'Ok'
r2 emssr238
                                           && Number.isFinite(b.v / 2 ** b.d));
r2 emssr239
  if (!all.length) return nothing('値を持つ箱がありません。');
r2 emssr240
r2 emssr241
  const outs = new Set((sc.gamma || []).map(g => g.box));
r2 emssr242
  const seen = new Map();
r2 emssr243
  for (const b of all) {
r2 emssr244
    const key = b.d + ':' + b.v;
r2 emssr245
    const cur = seen.get(key);
r2 emssr246
    if (!cur) seen.set(key, { v: b.v, d: b.d, x: b.v / 2 ** b.d, out: outs.has(b.i) });
r2 emssr247
    else cur.out = cur.out || outs.has(b.i);
r2 emssr248
  }
r2 emssr249
  const pts = [...seen.values()];
r2 emssr250
  const dmax = Math.max(...pts.map(p => p.d));
r2 emssr251
r2 emssr252
  const R = 168, PAD = 46;
r2 emssr253
  const s = svgBox(-R - PAD, -R - PAD - 12, 2 * (R + PAD), 2 * (R + PAD) + 24);
r2 emssr254
  const P = (u, v) => [R * u, -R * v];
r2 emssr255
r2 emssr256
  const line = (a, b, col, w, op, dash) => {
r2 emssr257
    const e = el('line', { x1: a[0], y1: a[1], x2: b[0], y2: b[1] });
r2 emssr258
    e.setAttribute('stroke', col);
r2 emssr259
    e.setAttribute('stroke-width', w || 1);
r2 emssr260
    if (op !== undefined) e.setAttribute('opacity', op);
r2 emssr261
    if (dash) e.setAttribute('stroke-dasharray', dash);
r2 emssr262
    s.appendChild(e);
r2 emssr263
  };
r2 emssr264
  const dot = (p, r, fill, stroke) => {
r2 emssr265
    const e = el('circle', { cx: p[0], cy: p[1], r: r });
r2 emssr266
    e.setAttribute('fill', fill || 'none');
r2 emssr267
    if (stroke) { e.setAttribute('stroke', stroke); e.setAttribute('stroke-width', 1.2); }
r2 emssr268
    s.appendChild(e);
r2 emssr269
  };
r2 emssr270
  const text = (p, str, col, size, anchor) => {
r2 emssr271
    const t = el('text', { x: p[0], y: p[1], 'font-size': size || 9.5,
r2 emssr272
                           'text-anchor': anchor || 'middle' }, str);
r2 emssr273
    t.setAttribute('fill', col);
r2 emssr274
    s.appendChild(t);
r2 emssr275
  };
r2 emssr276
r2 emssr277
  // ---- 正方形 (ij, −ij, −ji, ji) ----
r2 emssr278
  const ij = P(1, 1), mij = P(-1, 1), ji = P(1, -1), mji = P(-1, -1);
r2 emssr279
  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 emssr280
  sq.setAttribute('fill', 'none');
r2 emssr281
  sq.setAttribute('stroke', 'var(--ring)');
r2 emssr282
  sq.setAttribute('stroke-width', 1);
r2 emssr283
  s.appendChild(sq);
r2 emssr284
r2 emssr285
  // ---- 二本の対角線 ----
r2 emssr286
  line(mji, ij, 'var(--ring)', 1, 0.8, '4 4');
r2 emssr287
  line(mij, ji, 'var(--ring)', 1, 0.8, '4 4');
r2 emssr288
  text([ij[0] + 4, ij[1] - 6], 'ij', 'var(--label)', 10, 'start');
r2 emssr289
  text([mij[0] - 4, mij[1] - 6], '−ij', 'var(--label)', 10, 'end');
r2 emssr290
  text([ji[0] + 4, ji[1] + 13], 'ji', 'var(--label)', 10, 'start');
r2 emssr291
  text([mji[0] - 4, mji[1] + 13], '−ji', 'var(--label)', 10, 'end');
r2 emssr292
r2 emssr293
  // ---- 実軸と ω 軸 ----
r2 emssr294
  line(P(-1, 0), P(1, 0), 'var(--axis)', 1.2, 0.9);
r2 emssr295
  line(P(0, -1), P(0, 1), 'var(--axis)', 1.2, 0.9);
r2 emssr296
  dot(P(1, 0), 3.6, 'var(--acc)');
r2 emssr297
  dot(P(-1, 0), 3.6, 'var(--acc)');
r2 emssr298
  dot(P(0, 1), 3.6, 'var(--label)');
r2 emssr299
  dot(P(0, -1), 3.6, 'var(--label)');
r2 emssr300
  dot(P(0, 0), 2.6, 'var(--ink)');
r2 emssr301
  text([R + 8, 4], 'ω', 'var(--acc)', 11, 'start');
r2 emssr302
  text([-R - 8, 4], 'ω⁻', 'var(--acc)', 11, 'end');
r2 emssr303
  text([6, -R - 6], 'i', 'var(--label)', 11, 'start');
r2 emssr304
  text([6, R + 14], 'j', 'var(--label)', 11, 'start');
r2 emssr305
  text([-9, 13], 'O', 'var(--ink)', 10, 'end');
r2 emssr306
r2 emssr307
  // ---- Level 3 の整数。t_n = 1 − 2^(−n) で ω へ漸近する ----
r2 emssr308
  for (let n = 1; n <= 9; n++) {
r2 emssr309
    const t = 1 - Math.pow(2, -n);
r2 emssr310
    for (const sg of [1, -1]) {
r2 emssr311
      const p = P(sg * t, 0);
r2 emssr312
      line([p[0], p[1] - 5], [p[0], p[1] + 5], 'var(--acc)', 1, 0.8);
r2 emssr313
      if (n <= 4) text([p[0], p[1] + 16], String(sg * n), 'var(--acc)', 9);
r2 emssr314
    }
r2 emssr315
  }
r2 emssr316
  text([0, R + 32], '整数 n は t_n = 1 − 2^(−n)。等間隔ではなく ω へ漸近する',
r2 emssr317
       'var(--axis)', 9);
r2 emssr318
r2 emssr319
  // ---- ij 対角線反転。実軸の列を ω 軸へ写し、i へ収束させる(6b)----
r2 emssr320
  for (let n = 1; n <= 6; n++) {
r2 emssr321
    const t = 1 - Math.pow(2, -n);
r2 emssr322
    line(P(t, 0), P(0, t), 'var(--ring)', 0.7, 0.45);
r2 emssr323
    dot(P(0, t), 2.4, 'none', 'var(--label)');
r2 emssr324
  }
r2 emssr325
r2 emssr326
  // ---- 平文が作った値 ----
r2 emssr327
  const used = [];
r2 emssr328
  pts.sort((a, b) => a.x - b.x).forEach(p => {
r2 emssr329
    const u = tsuiPos(p.x);
r2 emssr330
    const q = P(u, 0);
r2 emssr331
    const col = p.out ? 'var(--warn)' : 'var(--mark)';
r2 emssr332
    line(P(u, 0), P(0, Math.abs(u)), 'var(--ring)', 0.7, 0.35);
r2 emssr333
    dot(P(0, Math.abs(u)), 2.2, 'none', col);
r2 emssr334
    dot(q, p.out ? 5 : 3.4, col);
r2 emssr335
    const label = dyadText(p.v, p.d) + (p.d ? '  δ' + p.d : '');
r2 emssr336
    const w = label.length * 5.2 + 6;
r2 emssr337
    let row = 0;
r2 emssr338
    while (used.some(r => r.row === row && Math.abs(r.x - q[0]) < (r.w + w) / 2)) row++;
r2 emssr339
    used.push({ row: row, x: q[0], w: w });
r2 emssr340
    const t = el('text', { x: q[0], y: q[1] - 12 - row * 12, 'text-anchor': 'middle',
r2 emssr341
                           'font-size': 9.5 }, label);
r2 emssr342
    t.setAttribute('fill', col);
r2 emssr343
    if (p.out) t.setAttribute('font-weight', '700');
r2 emssr344
    s.appendChild(t);
r2 emssr345
    line([q[0], q[1] - 7], [q[0], q[1] - 9 - row * 12], col, 0.7, 0.5);
r2 emssr346
  });
r2 emssr347
r2 emssr348
  const d = document.createElement('div');
r2 emssr349
  d.appendChild(s);
r2 emssr350
  const f = document.createElement('div');
r2 emssr351
  f.className = 'formula';
r2 emssr352
  f.textContent =
r2 emssr353
    '平面は O, ±1, i, j が張る有界な正方形。実軸も ω 軸も絶対値は 1 を越えない。\n' +
r2 emssr354
    '整数 1 の後、残余 (O, ω) へ同じ構成を再帰的に当てるので各段が残余の半分を取る。\n\n' +
r2 emssr355
    '    t_n = 1 − 2^(−n)          ω は整数列の収束先の境界点(Level 3)\n' +
r2 emssr356
    '    π(v) = sgn(v)(1 − 2^(−|v|))     π(n) = t_n、π(±∞) = ω, ω⁻\n\n' +
r2 emssr357
    '橙の目盛りが整数、青が平文の作った値、赤が出口境界 Γ⁺。\n' +
r2 emssr358
    'ω 軸へ渡る細線は ij 対角線反転(6b)。実軸の漸減列はこの折り返しで i へ収束する。' +
r2 emssr359
    (dmax === 0 ? '\nこの文は δ = 0。値は整数だけになる。'
r2 emssr360
                : '\n最も深い作図は δ = ' + dmax + '。値は x/2^δ で厳密に閉じる。');
r2 emssr361
  d.appendChild(f);
r2 emssr362
  return d;
r2 emssr363
}
r2 emssr364
r2 emssr365
// ===== lane64 の image(20 §7)=====
r2 emssr366
r2 emssr367
function drawLane(sc) {
r2 emssr368
  const im = sc.image;
r2 emssr369
  if (!im) return nothing('image がありません。');
r2 emssr370
  const d = document.createElement('div');
r2 emssr371
r2 emssr372
  const t = document.createElement('table');
r2 emssr373
  t.innerHTML = '<tr><th class="l">項目</th><th>値</th></tr>' +
r2 emssr374
    `<tr><td class="l">image</td><td>${im.bytes} byte</td></tr>` +
r2 emssr375
    `<tr><td class="l">命令</td><td>${im.insns}</td></tr>` +
r2 emssr376
    `<tr><td class="l">block</td><td>${im.blocks.length}</td></tr>` +
r2 emssr377
    Object.keys(im.kinds).sort().map(k => `<tr><td class="l">${k}</td><td>${im.kinds[k]}</td></tr>`).join('');
r2 emssr378
  d.appendChild(t);
r2 emssr379
r2 emssr380
  const bl = im.blocks, RH = 16, PAD = 6, LAB = 96, BARW = 250;
r2 emssr381
  const maxw = Math.max(1, ...bl.map(b => Math.max(1, ...(b.layers.length ? b.layers : [1]))));
r2 emssr382
  const s = svg(LAB + BARW + 10, PAD * 2 + bl.length * RH);
r2 emssr383
  bl.forEach((b, i) => {
r2 emssr384
    const y = PAD + i * RH;
r2 emssr385
    s.appendChild(el('text', { x: 2, y: y + 10, 'font-size': 9.5 }, 'pc ' + b.pc));
r2 emssr386
    const term = el('text', { x: 54, y: y + 10, 'font-size': 9.5 }, b.term);
r2 emssr387
    term.setAttribute('fill', 'var(--acc)');
r2 emssr388
    s.appendChild(term);
r2 emssr389
    let x = LAB;
r2 emssr390
    for (const w of b.layers) {
r2 emssr391
      const px = Math.max(3, (w / maxw) * 44);
r2 emssr392
      if (x + px > LAB + BARW) break;
r2 emssr393
      const r = el('rect', { x: x, y: y + 3, width: px, height: 9 });
r2 emssr394
      r.setAttribute('fill', 'var(--mark)');
r2 emssr395
      r.setAttribute('opacity', '0.6');
r2 emssr396
      s.appendChild(r);
r2 emssr397
      x += px + 2;
r2 emssr398
    }
r2 emssr399
  });
r2 emssr400
  const wrap = document.createElement('div');
r2 emssr401
  wrap.style.marginTop = '8px';
r2 emssr402
  wrap.appendChild(s);
r2 emssr403
  d.appendChild(box(null, '横棒 1 本が block の中の 1 層。並んだ棒は順序を入れ替えてよい箱の束。', wrap));
r2 emssr404
  return d;
r2 emssr405
}
r2 emssr406
r2 emssr407
// ===== 接合秩(Common-Lang/2 定義 4.1、Common-Lang/1 定理 4.1)=====
r2 emssr408
//
r2 emssr409
// 座標は common_apps/joinrank/svg/draw.py と同じ。
r2 emssr410
//
r2 emssr411
//   r(T)     = 1 − 2^(−|T|)                      次数が環の番号になる
r2 emssr412
//   θ(T)     = π Σ_{k∈T} 2^(−(k+1))              二進符号が偏角になる   上半平面 Π_i
r2 emssr413
//   y の点   r = 1/2, θ = −π/2                                          下半平面 Π_j
r2 emssr414
//   カット   θ_κ = π 2^(−κ) の直径が [n] = A ⊔ B を扇へ分ける
r2 emssr415
//
r2 emssr416
// 辺は二種類(Common-Lang/2 §3)。
r2 emssr417
//
r2 emssr418
//   E_star = {(T \ max T, T)}      経路を保持する。Π_i の中の弦。直線
r2 emssr419
//   E_plus = {(T, y) : a_T = 1}    経路を忘れる。実軸を一度だけ渡る弧
r2 emssr420
//
r2 emssr421
// 分離積は p_r と q_r の台へ細線を引き、二つの集約点の間に太い弦を一本渡す。
r2 emssr422
// カットを渡る太弦はちょうど ρ 本になる。
r2 emssr423
r2 emssr424
const TAU = Math.PI;
r2 emssr425
r2 emssr426
function popcount(t) { let c = 0; while (t) { c += t & 1; t >>= 1; } return c; }
r2 emssr427
function radiusOf(t) { return 1 - 2 ** (-popcount(t)); }
r2 emssr428
function angleOf(t, n) {
r2 emssr429
  let a = 0;
r2 emssr430
  for (let k = 0; k < n; k++) if ((t >> k) & 1) a += 2 ** (-(k + 1));
r2 emssr431
  return TAU * a;
r2 emssr432
}
r2 emssr433
function polar(r, th, R) { return [R * r * Math.cos(th), -R * r * Math.sin(th)]; }
r2 emssr434
function topBit(t, n) { let m = 0; for (let k = 0; k < n; k++) if ((t >> k) & 1) m = k; return m; }
r2 emssr435
r2 emssr436
function chainClosure(support, n) {
r2 emssr437
  const seen = new Set([0]);
r2 emssr438
  for (let t of support) {
r2 emssr439
    while (t && !seen.has(t)) { seen.add(t); t &= ~(1 << topBit(t, n)); }
r2 emssr440
  }
r2 emssr441
  return [...seen].sort((a, b) => a - b);
r2 emssr442
}
r2 emssr443
r2 emssr444
function monoLabel(t, n) {
r2 emssr445
  if (!t) return '1';
r2 emssr446
  let s = '';
r2 emssr447
  for (let k = 0; k < n; k++) if ((t >> k) & 1) s += 'x' + k;
r2 emssr448
  return s;
r2 emssr449
}
r2 emssr450
r2 emssr451
// 中点を水平へ寄せた制御点。B_y が t の一次式に落ちるので実軸との交点が一意に決まる。
r2 emssr452
function midCtrl(p0, p1, h) { return [(p0[0] + p1[0]) / 2 + h, (p0[1] + p1[1]) / 2]; }
r2 emssr453
r2 emssr454
function bez(p0, c, p1, t) {
r2 emssr455
  const u = 1 - t;
r2 emssr456
  return [u * u * p0[0] + 2 * t * u * c[0] + t * t * p1[0],
r2 emssr457
          u * u * p0[1] + 2 * t * u * c[1] + t * t * p1[1]];
r2 emssr458
}
r2 emssr459
r2 emssr460
// f は線形。f(B(t)) = a t² + b t + c の [0,1] の根の像を返す。
r2 emssr461
function crossAt(p0, c, p1, f) {
r2 emssr462
  const eps = 1e-9, f0 = f(p0), fc = f(c), f1 = f(p1);
r2 emssr463
  const a = f0 - 2 * fc + f1, b = 2 * (fc - f0), c0 = f0;
r2 emssr464
  let ts = [];
r2 emssr465
  if (Math.abs(a) < eps) { if (Math.abs(b) > eps) ts = [-c0 / b]; }
r2 emssr466
  else {
r2 emssr467
    const d = b * b - 4 * a * c0;
r2 emssr468
    if (d >= 0) { const s = Math.sqrt(d); ts = [(-b - s) / (2 * a), (-b + s) / (2 * a)]; }
r2 emssr469
  }
r2 emssr470
  ts = ts.filter(t => t >= -eps && t <= 1 + eps);
r2 emssr471
  return ts.length ? bez(p0, c, p1, Math.min(Math.max(ts[0], 0), 1)) : null;
r2 emssr472
}
r2 emssr473
r2 emssr474
function fAxis(p) { return p[1]; }
r2 emssr475
function fCut(th) {
r2 emssr476
  const dx = Math.cos(th), dy = -Math.sin(th);
r2 emssr477
  return p => p[0] * dy - p[1] * dx;
r2 emssr478
}
r2 emssr479
r2 emssr480
function meanAngle(sup, n, lo, hi) {
r2 emssr481
  if (!sup || !sup.length) return (lo + hi) / 2;
r2 emssr482
  const a = sup.reduce((s, t) => s + angleOf(t, n), 0) / sup.length;
r2 emssr483
  return Math.min(Math.max(a, lo + 0.08 * (hi - lo)), lo + 0.92 * (hi - lo));
r2 emssr484
}
r2 emssr485
r2 emssr486
function drawJoint(j) {
r2 emssr487
  if (!j) return nothing('このセルは入力の語を指定していません。notebook の cell に inputs と cut を書くと出ます。');
r2 emssr488
  if (!j.ok) return nothing(j.error || '接合秩を取れません。');
r2 emssr489
r2 emssr490
  const n = j.n, cut = j.cut, rho = j.rho, R = 150, PAD = 58;
r2 emssr491
  const support = j.anf || [];
r2 emssr492
  const nodes = chainClosure(support, n);
r2 emssr493
  const thc = TAU * 2 ** (-cut);
r2 emssr494
  const s = svgBox(-R - PAD, -R - PAD, 2 * (R + PAD), 2 * (R + PAD));
r2 emssr495
r2 emssr496
  const put = (tag, at, fill, stroke, w, op) => {
r2 emssr497
    const e = el(tag, at);
r2 emssr498
    if (fill) e.setAttribute('fill', fill); else e.setAttribute('fill', 'none');
r2 emssr499
    if (stroke) { e.setAttribute('stroke', stroke); e.setAttribute('stroke-width', w || 1); }
r2 emssr500
    if (op !== undefined) e.setAttribute('opacity', op);
r2 emssr501
    s.appendChild(e);
r2 emssr502
    return e;
r2 emssr503
  };
r2 emssr504
  const line = (p, q, col, w, op, dash) => {
r2 emssr505
    const e = put('line', { x1: p[0], y1: p[1], x2: q[0], y2: q[1] }, null, col, w, op);
r2 emssr506
    e.setAttribute('stroke-linecap', 'round');
r2 emssr507
    if (dash) e.setAttribute('stroke-dasharray', dash);
r2 emssr508
  };
r2 emssr509
  const text = (p, str, col, size, anchor, op) => {
r2 emssr510
    const t = el('text', { x: p[0], y: p[1], 'font-size': size || 9.5,
r2 emssr511
                           'text-anchor': anchor || 'middle' }, str);
r2 emssr512
    t.setAttribute('fill', col);
r2 emssr513
    if (op !== undefined) t.setAttribute('opacity', op);
r2 emssr514
    s.appendChild(t);
r2 emssr515
  };
r2 emssr516
  const wedge = (a0, a1, col) => {
r2 emssr517
    const p0 = polar(1, a0, R), p1 = polar(1, a1, R);
r2 emssr518
    const big = Math.abs(a1 - a0) > TAU ? 1 : 0;
r2 emssr519
    put('path', { d: `M 0 0 L ${p0[0]} ${p0[1]} A ${R} ${R} 0 ${big} 0 ${p1[0]} ${p1[1]} Z` },
r2 emssr520
      col, null, 0, 0.06);
r2 emssr521
  };
r2 emssr522
r2 emssr523
  // カットが分ける扇
r2 emssr524
  wedge(thc, TAU, 'var(--mark)');
r2 emssr525
  wedge(0, thc, 'var(--sub)');
r2 emssr526
r2 emssr527
  // 環。r = 1 − 2^(−|T|)
r2 emssr528
  for (let k = 0; k <= n; k++) {
r2 emssr529
    put('circle', { cx: 0, cy: 0, r: R * (1 - 2 ** (-k)) }, null, 'var(--ring)', 0.7, 0.55);
r2 emssr530
  }
r2 emssr531
  const om = put('circle', { cx: 0, cy: 0, r: R }, null, 'var(--ring)', 1.1, 0.9);
r2 emssr532
  om.setAttribute('stroke-dasharray', '5 4');
r2 emssr533
  text([R + 9, -3], 'ω', 'var(--ring)', 10, 'start');
r2 emssr534
r2 emssr535
  // 実軸。Π_i と Π_j の共通境界
r2 emssr536
  line([-R - 22, 0], [R + 22, 0], 'var(--axis)', 1, 0.85);
r2 emssr537
  text([-R - 26, -7], 'Π_i', 'var(--axis)', 9.5, 'start');
r2 emssr538
  text([-R - 26, 15], 'Π_j', 'var(--axis)', 9.5, 'start');
r2 emssr539
r2 emssr540
  // カットの直径
r2 emssr541
  const cp = polar(1.1, thc, R), cq = polar(1.1, thc + TAU, R);
r2 emssr542
  line(cp, cq, 'var(--acc)', 1.3, 0.95, '7 5');
r2 emssr543
  text([cp[0], cp[1] - 9], 'κ = ' + cut, 'var(--acc)', 10);
r2 emssr544
  text(polar(1.2, (thc + TAU) / 2, R), 'A', 'var(--mark)', 11);
r2 emssr545
  text(polar(1.2, thc / 2, R), 'B', 'var(--sub)', 11);
r2 emssr546
r2 emssr547
  const fac = new Set([].concat(...(j.psup || []), ...(j.qsup || [])));
r2 emssr548
  const raw = fac.size ? 0.26 : 1.0;
r2 emssr549
r2 emssr550
  // E_star。Π_i の中の弦
r2 emssr551
  for (const t of nodes) {
r2 emssr552
    if (!t) continue;
r2 emssr553
    const a = t & ~(1 << topBit(t, n));
r2 emssr554
    line(polar(radiusOf(a), angleOf(a, n), R), polar(radiusOf(t), angleOf(t, n), R),
r2 emssr555
      'var(--mark)', 1.3, raw);
r2 emssr556
  }
r2 emssr557
r2 emssr558
  // E_plus。実軸を一度だけ渡る弧と、その交点の丸
r2 emssr559
  const yout = polar(0.5, -TAU / 2, R);
r2 emssr560
  put('circle', { cx: yout[0], cy: yout[1], r: 6 }, 'var(--label)', null, 0, raw);
r2 emssr561
  text([yout[0], yout[1] + 20], 'y', 'var(--label)', 10, 'middle', raw);
r2 emssr562
  for (const t of support) {
r2 emssr563
    const pa = polar(radiusOf(t), angleOf(t, n), R);
r2 emssr564
    const ctl = midCtrl(pa, yout, pa[0] >= 0 ? 24 : -24);
r2 emssr565
    const e = put('path', { d: `M ${pa[0]} ${pa[1]} Q ${ctl[0]} ${ctl[1]} ${yout[0]} ${yout[1]}` },
r2 emssr566
      null, 'var(--sub)', 1.5, raw);
r2 emssr567
    e.setAttribute('stroke-linecap', 'round');
r2 emssr568
    const x = crossAt(pa, ctl, yout, fAxis);
r2 emssr569
    if (x) put('circle', { cx: x[0], cy: x[1], r: 2.8 }, 'var(--sub)', null, 0, raw);
r2 emssr570
  }
r2 emssr571
r2 emssr572
  // 単項式の点
r2 emssr573
  for (const t of nodes) {
r2 emssr574
    const p = polar(radiusOf(t), angleOf(t, n), R);
r2 emssr575
    const on = support.includes(t);
r2 emssr576
    const op = fac.has(t) ? 1 : raw;
r2 emssr577
    put('circle', { cx: p[0], cy: p[1], r: on ? 4.6 : 2.8 },
r2 emssr578
      on ? 'var(--ink)' : 'var(--bg)', 'var(--ink)', 1.1, op);
r2 emssr579
    const right = angleOf(t, n) < TAU / 2;
r2 emssr580
    text([p[0] + (right ? 10 : -10), p[1] - 8], monoLabel(t, n), 'var(--label)', 9,
r2 emssr581
      right ? 'start' : 'end', op);
r2 emssr582
  }
r2 emssr583
r2 emssr584
  // 分離積。カットを渡る太弦はちょうど ρ 本
r2 emssr585
  let ncross = 0;
r2 emssr586
  const fc = fCut(thc);
r2 emssr587
  for (let r = 0; r < rho; r++) {
r2 emssr588
    const rr = 0.40 + 0.22 * (r + 1) / (rho + 1);
r2 emssr589
    const ap = meanAngle((j.psup || [])[r], n, thc, TAU);
r2 emssr590
    const aq = meanAngle((j.qsup || [])[r], n, 0, thc);
r2 emssr591
    const pp = polar(rr, ap, R), pq = polar(rr, aq, R);
r2 emssr592
    for (const t of ((j.psup || [])[r] || [])) line(polar(radiusOf(t), angleOf(t, n), R), pp, 'var(--warn)', 0.8, 0.55);
r2 emssr593
    for (const t of ((j.qsup || [])[r] || [])) line(polar(radiusOf(t), angleOf(t, n), R), pq, 'var(--warn)', 0.8, 0.55);
r2 emssr594
    line(pq, pp, 'var(--warn)', 2.3, 1);
r2 emssr595
    const x = crossAt(pq, midCtrl(pq, pp, 0), pp, fc);
r2 emssr596
    if (x) { ncross++; put('circle', { cx: x[0], cy: x[1], r: 3.6 }, 'var(--acc)', 'var(--warn)', 1.2); }
r2 emssr597
    put('circle', { cx: pp[0], cy: pp[1], r: 5 }, 'var(--warn)');
r2 emssr598
    put('circle', { cx: pq[0], cy: pq[1], r: 5 }, 'var(--warn)');
r2 emssr599
    text([pp[0], pp[1] - 10], 'p' + r, 'var(--warn)', 9);
r2 emssr600
    text([pq[0], pq[1] - 10], 'q' + r, 'var(--warn)', 9);
r2 emssr601
  }
r2 emssr602
r2 emssr603
  const d = document.createElement('div');
r2 emssr604
  d.appendChild(s);
r2 emssr605
  const f = document.createElement('div');
r2 emssr606
  f.className = 'formula';
r2 emssr607
  f.textContent =
r2 emssr608
    `n = ${n}   A = {${j.inputs.slice(0, cut).join(', ')}}   B = {${j.inputs.slice(cut).join(', ')}}\n` +
r2 emssr609
    `ρ = rank_𝔽₂ F = ${rho}        カットを渡る太弦 ${ncross} 本\n` +
r2 emssr610
    `台 ${support.length} 点        環 ${n + 1} 本`;
r2 emssr611
  d.appendChild(f);
r2 emssr612
  d.appendChild(jointLegend());
r2 emssr613
  d.appendChild(jointTable(j));
r2 emssr614
  return d;
r2 emssr615
}
r2 emssr616
r2 emssr617
function jointLegend() {
r2 emssr618
  const rows = [
r2 emssr619
    ['ring', '環  r = 1 − 2^(−|T|)', '相互作用の次数 |T|'],
r2 emssr620
    ['mark', '青い弦  E_star', '経路の保持。角度差 π2^(−k) が x_k'],
r2 emssr621
    ['sub', '緑の弧  E_plus', '実軸を渡る合算。丸が経路を忘れる一点'],
r2 emssr622
    ['acc', '橙の直径', 'カット θ_κ = π 2^(−κ)'],
r2 emssr623
    ['warn', '赤の太弦', 'カットを渡る独立影響。本数が接合秩'],
r2 emssr624
  ];
r2 emssr625
  const t = document.createElement('table');
r2 emssr626
  t.innerHTML = rows.map(([c, a, b]) =>
r2 emssr627
    `<tr><td class="l" style="color:var(--${c})">${a}</td><td class="l">${b}</td></tr>`).join('');
r2 emssr628
  return t;
r2 emssr629
}
r2 emssr630
r2 emssr631
function jointTable(j) {
r2 emssr632
  const na = 1 << j.cut, nb = 1 << (j.n - j.cut);
r2 emssr633
  const t = document.createElement('table');
r2 emssr634
  let html = '<tr><th class="l">F</th>';
r2 emssr635
  for (let b = 0; b < nb; b++) html += `<th>β${b}</th>`;
r2 emssr636
  html += '</tr>';
r2 emssr637
  for (let a = 0; a < na; a++) {
r2 emssr638
    html += `<tr><th class="l">α${a}</th>`;
r2 emssr639
    for (let b = 0; b < nb; b++) html += `<td>${j.table[a + na * b]}</td>`;
r2 emssr640
    html += '</tr>';
r2 emssr641
  }
r2 emssr642
  t.innerHTML = html;
r2 emssr643
  const d = document.createElement('div');
r2 emssr644
  d.appendChild(t);
r2 emssr645
  const f = document.createElement('div');
r2 emssr646
  f.className = 'formula';
r2 emssr647
  f.textContent = 'f(x_A, x_B) = ⊕_{r<ρ} p_r(x_A) q_r(x_B)\n' +
r2 emssr648
    j.p.map((pr, r) => `  r=${r}   p = [${pr.join('')}]   q = [${j.q[r].join('')}]`).join('\n');
r2 emssr649
  d.appendChild(f);
r2 emssr650
  return d;
r2 emssr651
}
r2 emssr652
r2 emssr653
// ===== 音節と箱の表 =====
r2 emssr654
r2 emssr655
function drawText(sc) {
r2 emssr656
  const d = document.createElement('div');
r2 emssr657
  const w = document.createElement('table');
r2 emssr658
  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 emssr659
    sc.words.map(x => `<tr><td>${x.r}</td><td>${x.q}</td>` +
r2 emssr660
      `<td class="l" style="color:var(--${x.cls})">${x.syl}</td>` +
r2 emssr661
      `<td class="l">${x.sym}</td><td>${x.pin}</td><td>${x.pout}</td>` +
r2 emssr662
      `<td>${x.boxes === null ? '—' : x.boxes}</td></tr>`).join('');
r2 emssr663
  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 emssr664
r2 emssr665
  const b = document.createElement('table');
r2 emssr666
  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 emssr667
    sc.boxes.slice(0, 400).map(x =>
r2 emssr668
      `<tr><td>${x.i}</td>` +
r2 emssr669
      `<td class="l" style="color:var(--${KINDCLS[x.kind] || 'frame'})">${x.kind}${x.kind === 'Cst' ? ' ' + x.num : ''}</td>` +
r2 emssr670
      `<td class="l">${x.in.join(', ')}${x.to !== undefined ? ' → ' + x.to : ''}</td>` +
r2 emssr671
      `<td>${x.d}</td><td>${x.v === undefined ? '' : x.v}</td>` +
r2 emssr672
      `<td class="l">${x.v === undefined ? '' : dyadText(x.v, x.d)}</td></tr>`).join('');
r2 emssr673
  d.appendChild(box('箱', 'commonc の OPK / OPA / OPB / OPD / OPL をそのまま(19 §3.2)', b));
r2 emssr674
  return d;
r2 emssr675
}