import { useMemo } from 'react'; import { ready } from '../../lib/mrly.js'; import { useQuery } from '../../lib/query.js'; import { mount, Page, Row, Pick, Slider, Check, Text, Btn, Stats, Stat, Note, Group } from '../../lib/app.jsx'; import { Picker, useSeeds } from '../../lib/select.jsx'; import { Pins, Ratios, Differences, Digits, Terms } from '../../lib/series.jsx'; const m = await ready(); const BUDGET = '500000'; const DEPTH = 6; const HEADS = 3; const LABELS = '4000'; const DIMS = [1, 2, 3, 4]; const BASES = [2, 3, 4, 5]; const MEASURES = [...m.ledger_measures()]; const OPS = [...m.blend_ops()]; const PAIRED = ['add', 'sub', 'hadamard', 'cauchy']; const VIEWS = [['pins', 'pin plot'], ['log', 'log plot'], ['ratios', 'ratios'], ['differences', 'difference triangle'], ['digits', 'digit heatmap']]; function listed(dimension, base) { try { return [...m.ledger_designs(dimension, base)]; } catch { return null; } } const SPACES = new Map(DIMS.map((d) => [d, BASES.filter((b) => listed(d, b))])); function gallery(dimension) { try { m.universe(dimension); return true; } catch { return false; } } const GALLERY = DIMS.filter(gallery); function digits(base) { return [...new Set([2, 3, 10, base])].sort((a, b) => a - b); } function shown(value) { return value === null || value === undefined ? 'none' : Number(value).toFixed(6); } function View({ row, view, dig, label, name }) { if (view === 'ratios') return ; if (view === 'differences') return ; if (view === 'digits') return ; return ; } function Rule({ row }) { return ( {row.order ?? 'none fits'} {row.recurrence || 'no linear rule fits these terms'} {row.polynomial || 'none'} {shown(row.root)} {shown(row.growth)} {shown(row.exponent)} {row.growth_from} ); } function App() { const s = useSeeds(); const [pick, set] = useQuery({ code: '7', dimension: 2, base: 2, measure: 'surface', axis: 'level', count: 12, view: 'pins', dig: 2, on: false, op: 'hadamard', arg: 2, scode: '3', smeasure: 'fills', }); const move = (patch) => { const dimension = patch.dimension ?? pick.dimension; const wanted = patch.base ?? pick.base; const bases = SPACES.get(dimension) ?? []; const base = bases.includes(wanted) ? wanted : bases[0] ?? wanted; const list = listed(dimension, base) ?? []; const keep = (code) => { try { m.name_of(code, dimension, base); return code; } catch { return list.at(-1) ?? code; } }; s.drop(); set({ ...patch, base, code: keep(pick.code.trim()), scode: keep(pick.scode) }); }; const read = useMemo(() => { try { return { row: JSON.parse(m.blend_series(pick.code.trim(), pick.dimension, pick.base, pick.measure, pick.axis, pick.count, BUDGET, DEPTH)), error: null }; } catch (error) { return { row: null, error }; } }, [pick.code, pick.dimension, pick.base, pick.measure, pick.axis, pick.count]); const family = useMemo(() => { try { return JSON.parse(m.blend_family(pick.dimension, pick.base, pick.smeasure, pick.axis, HEADS, LABELS)); } catch { return []; } }, [pick.dimension, pick.base, pick.smeasure, pick.axis]); const blend = useMemo(() => { if (!pick.on || !read.row) return { row: null, mate: null, error: null }; try { const mate = JSON.parse(m.blend_series(pick.scode.trim(), pick.dimension, pick.base, pick.smeasure, pick.axis, pick.count, BUDGET, DEPTH)); return { row: JSON.parse(m.blend_mix(read.row.terms, mate.terms, pick.op, pick.arg, DEPTH)), mate, error: null }; } catch (error) { return { row: null, mate: null, error }; } }, [pick.on, read.row, pick.scode, pick.smeasure, pick.op, pick.arg]); const row = read.row; const label = row ? `${row.axis === 'level' ? 'level L' : 'side k'} from ${row.start}` : ''; const mates = family.map((one) => [one.code, `${one.code} ยท ${one.terms.join(', ') || 'no terms'}`]); const options = family.some((one) => one.code === pick.scode) ? mates : [[pick.scode, pick.scode], ...mates]; const controls = ( <> {GALLERY.includes(pick.dimension) ? : ( { s.drop(); set({ code: v }); }} /> set({ code: m.random_code(pick.dimension, pick.base, s.next()) })}>Randomize )} move({ dimension: +v })} /> move({ base: +v })} /> set({ measure: v })} /> set({ axis: v })} /> set({ count: v })} /> set({ view: v })} /> {pick.view === 'digits' && set({ dig: +v })} />} set({ on: v })} /> {pick.on && ( set({ scode: v })} /> set({ smeasure: v })} /> set({ op: v })} /> set({ arg: v })} /> )} ); return ( A sequence is one design, one measure and one axis, the same key the ledger holds, so a row there links straight into this page. The pin plot stands every term on its index and the log plot lifts the tall ones back into view; the ratio strip colours each term against the one before, the difference triangle takes differences until the rows run out, and the heatmap spells every term in a chosen base. Under the drawing is the rule: the smallest linear constant-coefficient recurrence every term satisfies, hunted modulo three primes, rebuilt as exact fractions and verified on every term before it is shown, with its monic characteristic polynomial and the largest real root of that polynomial. The growth is that root where a rule fits and a least-squares slope of the log terms over the tail where none does, a fit and not a rule, and the exponent is the decades a term gains per step, which is the slope the log plot draws. Order and growth are honest only to the terms in hand: a rule needs twice its order plus two terms to be trusted, so a short read finds no rule where a longer one would. The mixer takes a second key from the same space and axis and runs a term operation over the two, adding, subtracting, multiplying term by term or convolving them, or dropping, thinning, differencing, summing and scaling the first alone; the mixed sequence carries its own rule, because sequences satisfying linear recurrences are closed under every one of these. The registry behind both pages is written up in the sequences note, and their formal census, with the fill law and the exposed-face recurrence, is the sequence-census paper. Every number on this page is computed in Rust; the page only draws.} controls={controls}>

The sequence {row ? row.name : 'nothing read'}

{row && } {row && } {row && ( {row.closed || 'none known'} {row.oeis ? {row.oeis} : 'none'} {row.tag || 'unmatched'} {row.capped ? `${row.terms.length}, to the budget` : row.terms.length} )} {row && }
{pick.on && (

The mix {blend.mate ? `${pick.op} of this and ${blend.mate.name}` : pick.op}

{blend.row && } {blend.row && } {blend.mate && ( {blend.mate.name} {blend.mate.closed || 'none known'} {shown(blend.mate.growth)} {PAIRED.includes(pick.op) ? 'unused' : pick.arg} )} {blend.row && }
)}
); } mount();