import { ready, ink } from '../../lib/mrly.js'; import { mount, Page, Row, Slider, Pick, Check, Btn, Stats, Stat, Note } from '../../lib/app.jsx'; import { Sketch } from '../../lib/draw.jsx'; import { board, line, rules, axis, tag } from '../../lib/chart.js'; import { useQuery } from '../../lib/query.js'; const m = await ready(); const BASES = [2, 3, 4, 5, 6, 7, 8, 9, 10]; const TOP = 64; const fixed = (value, places) => (Number.isFinite(value) ? value.toFixed(places) : 'none'); const full = (base) => (1 << base) - 1; const setOf = (mask, base) => Array.from({ length: base }, (_, d) => d).filter((d) => mask & (1 << d)); function span(rows) { let low = Infinity, high = -Infinity; for (const row of rows) for (const value of row) { if (value < low) low = value; if (value > high) high = value; } return [low, high]; } function Meter({ view }) { const draw = (canvas) => { const b = board(canvas, 260, { left: 44 }); const { logx, meter, echo, rest, read, split } = view; const drawn = split && read.sieve ? [meter, echo, rest] : [meter]; const [low, high] = span(drawn); const reach = Math.max(high - low, 1e-9); const first = logx[0], last = logx[logx.length - 1]; const wide = Math.max(last - first, 1e-9); const step = Math.max(1, Math.round(logx.length / 1400)); const place = (series) => { const out = []; for (let i = 0; i < series.length; i += step) out.push([(logx[i] - first) / wide, (series[i] - low) / reach]); return out; }; const ticks = []; const gap = Math.ceil(read.depth / 8); for (let j = 0; j <= read.depth; j += gap) { const at = (j * Math.log(read.base) - first) / wide; if (at >= 0 && at <= 1) ticks.push([at, `${read.base}^${j}`]); } axis(b, ticks, { wall: true }); b.ctx.strokeStyle = ink.line; b.ctx.lineWidth = 1; b.ctx.beginPath(); b.ctx.moveTo(b.x(0), b.y(-low / reach)); b.ctx.lineTo(b.x(1), b.y(-low / reach)); b.ctx.stroke(); if (drawn.length > 1) { line(b, place(echo), ink.blue, { width: 1.2 }); line(b, place(rest), ink.pink, { width: 1.2 }); } line(b, place(meter), ink.yellow, { width: 1.4 }); b.ctx.fillStyle = ink.dim; b.ctx.textAlign = 'right'; b.ctx.fillText(fixed(high, 3), b.left - 6, b.y(1) + 4); b.ctx.fillText(fixed(low, 3), b.left - 6, b.y(0) + 4); b.ctx.textAlign = 'left'; tag(b, 'the meter', ink.yellow); if (drawn.length > 1) { tag(b, 'the echo', ink.blue, 'left', b.x(0) + 76); tag(b, 'the residual', ink.pink, 'left', b.x(0) + 152); } tag(b, read.sieve ? '' : 'past the sieve cap, no echo here', ink.dim, 'right'); }; return ; } function Spectrum({ view }) { const draw = (canvas) => { const b = board(canvas, 260, { left: 44 }); const { gamma, score, read } = view; const at = (value) => value / TOP; let peak = 10; for (let i = 0; i < gamma.length; i += 1) if (gamma[i] > read.band[0] && gamma[i] < read.band[1] && score[i] > peak) peak = score[i]; const roof = Math.log10(peak); const lift = (value) => Math.log10(Math.max(value, 1)) / roof; rules(b, read.lattice.filter((g) => g < TOP).map(at), { color: ink.pink, dash: [3, 4] }); rules(b, read.zeros.filter((g) => g < TOP).map(at), { color: ink.yellow }); const points = []; for (let i = 0; i < gamma.length && gamma[i] <= TOP; i += 1) points.push([at(gamma[i]), lift(score[i])]); axis(b, [0, 10, 20, 30, 40, 50, 60].map((g) => [at(g), String(g)]), { wall: true }); line(b, [[0, lift(read.threshold)], [1, lift(read.threshold)]], ink.line, { width: 1, dash: [2, 4] }); line(b, points, ink.blue, { width: 1.2 }); b.ctx.fillStyle = ink.dim; b.ctx.textAlign = 'right'; b.ctx.fillText(fixed(peak, 1), b.left - 6, b.y(1) + 4); b.ctx.fillText('1', b.left - 6, b.y(0) + 4); b.ctx.textAlign = 'left'; tag(b, 'the zeta ordinates', ink.yellow); tag(b, `the pole lattice 2 pi j / log ${read.base}`, ink.pink, 'right'); }; return ; } function App() { const [pick, set] = useQuery({ base: 10, mask: 511, depth: 5, split: true, sub: false }); let error = null; let view = null; let caps = null; let depth = pick.depth; try { caps = JSON.parse(m.echo_caps(pick.base, pick.mask)); depth = Math.min(Math.max(pick.depth, caps.least), caps.deepest); const got = m.echo_read(pick.base, pick.mask, depth, pick.sub); view = { logx: got.logx, meter: got.meter, echo: got.echo, rest: got.rest, gamma: got.gamma, score: got.score, read: JSON.parse(got.read), split: pick.split, }; } catch (fault) { error = fault; } const read = view?.read; const digits = setOf(pick.mask, pick.base); const toggle = (d) => { const next = pick.mask ^ (1 << d); if (setOf(next, pick.base).filter((v) => v > 0).length < 1 || setOf(next, pick.base).length < 2) return; set({ mask: next }); }; const rebase = (base) => { const kept = pick.mask & full(base); const ok = setOf(kept, base).length >= 2 && setOf(kept, base).some((d) => d > 0); set({ base, mask: ok ? kept : full(base) }); }; const controls = ( <>

The design

[q, q])} onChange={(v) => rebase(+v)} /> set({ mask: full(pick.base) })}>full set {Array.from({ length: pick.base }, (_, d) => ( toggle(d)} /> ))}

The depth

set({ depth: v })} />

The split

set({ split: v })} /> set({ sub: v })} />
); return ( Every number here is computed in Rust and the page only draws. The elements of S_F are the whole numbers whose digits in the base all lie in the set, the meter is M_F(x), the sum of mu(n) over those elements up to x, and the yardstick is x^(alpha/2) at alpha = log_base fill. The spectrum is that series resampled on 4096 points uniform in log x, mean-removed, Hann-windowed and read as gamma = 2 pi j over the log range against a 101-bin running median floor, so a peak is a power over its own neighbourhood and the threshold is 8. Resolution comes from the log range and not from the element count, so the bin is 2 pi / (level log base) and a browser that stops at base^level < 2^27 stops at a bin near a third. Read the hit counts against the chance rates beside them: with 13 ordinates and 20 lattice lines in the band a peak lands on one by luck often enough that a single peak proves nothing, which is why the full set is here as the control and the residual as the null. The echo needs the Mobius values of every whole number up to base^level, so it is refused past 2^24 and the page says so. The lab reads the same designs four digits deeper, where base 3 {'{0, 1}'} carries ten peaks and its pole lattice scores below its own null; at the depth a browser affords, that design is still under the floor and the base-10 designs are not. The critical line itself is zeta, the Mertens sum against the square root is the Mertens function page, and the mathematics is on the Mobius page.}>

The meter M_F(x) over x^(alpha/2), drawn against log x

{view && } {read && fixed(read.alpha, 6)} {`{${digits.join(', ')}}`} {read?.count} {read?.last} {read?.peak} {read && fixed(read.theta, 4)} {read && (read.share === null ? 'past the cap' : fixed(read.share, 4))} {read && (read.residual === null ? 'past the cap' : fixed(read.residual, 4))} {read && `x^${fixed(read.rate, 6)}`}

The spectrum {pick.sub ? 'the residual, echo taken out' : 'the meter'} over its local median floor, yellow the zeta ordinates, pink the pole lattice

{view && } {read && fixed(read.span, 4)} {read && fixed(read.bin, 4)} {read?.found} {read && `${read.hits} of ${read.peaks.length}`} {read && fixed(read.chance, 3)} {read && `${read.lines} of ${read.peaks.length}`} {read && fixed(read.chanceLines, 3)}
{read?.peaks.map((row) => ( {fixed(row.gamma, 3)} {fixed(row.score, 1)} {`zeta ${fixed(row.zeta, 3)}`} {`lattice ${fixed(row.lattice, 3)}`} ))} {read?.peaks.length === 0 && no peak clears the floor at this depth}
{caps && `depth ${caps.least} to ${caps.deepest} here; the echo is sieved through depth ${caps.sieved}, past which base^level leaves 2^24.`}
); } mount();