Try it. Type a number — or pick one of the presets — and hit Factor. The demo uses trial division: test 2, then 3, then 5, 7, 9… dividing out every factor it finds. Watch the step counter.
<p class="hint">Type a number — or pick a preset — and hit <b>Factor</b>. The demo divides out primes one by one and counts every test it makes. Watch how the <b>step count</b> explodes for a <i>semiprime</i> (a product of two big primes).</p>
<div class="row">
<input id="num" type="text" inputmode="numeric" value="8051" autocomplete="off" />
<button id="go" type="button">Factor</button>
</div>
<div class="presets">
<button class="chip" data-n="360" type="button">360 <span>easy</span></button>
<button class="chip" data-n="8051" type="button">8051 <span>83 × 97</span></button>
<button class="chip" data-n="99400891" type="button">99,400,891 <span>4-digit primes</span></button>
<button class="chip" data-n="560008500011" type="button">560,008,500,011 <span>6-digit primes</span></button>
</div>
<div id="out" class="out"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.row { display: flex; gap: .5rem; margin-bottom: .6rem; }
#num { flex: 1; min-width: 0; font: 700 18px ui-monospace, monospace; padding: .5rem .7rem; border: 1px solid #bbb; border-radius: 8px; color: #1d3557; }
button { font: 600 14px system-ui, sans-serif; padding: .5rem 1rem; border: 1px solid #457b9d; background: #457b9d; color: #fff; border-radius: 8px; cursor: pointer; }
.presets { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .8rem; }
.chip { background: #fff; color: #457b9d; border: 1px solid #cdd9e2; font-weight: 600; padding: .35rem .6rem; }
.chip span { color: #888; font-weight: 500; font-size: .8em; margin-left: .25rem; }
.chip:hover { background: #f0f4f8; }
.out { min-height: 3em; }
.factv { font: 800 22px ui-monospace, monospace; color: #1d3557; word-break: break-word; line-height: 1.35; margin: .2rem 0 .5rem; }
.factv .x { color: #999; font-weight: 600; margin: 0 .15rem; }
.factv .p { color: #2a9d8f; }
.meta { font-size: .92rem; color: #444; line-height: 1.5; }
.meta b { color: #c0392b; }
.steps { font-weight: 800; color: #c0392b; }
.tag { display: inline-block; font-size: .8rem; font-weight: 700; padding: .1rem .5rem; border-radius: 99px; margin-left: .4rem; }
.tag.prime { background: #fde8e8; color: #c0392b; }
.tag.semi { background: #fff4e0; color: #b9770e; }
.tag.smooth { background: #e6f6ec; color: #0a7d33; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Integer factorization by trial division — and a live step counter.
// The point: small numbers fall apart instantly; a semiprime (two big primes multiplied) forces the loop almost all the way to sqrt(n).
const STEP_CAP = 80000000; // safety bound so the browser never freezes
function factor(n) {
const factors = [];
let steps = 0;
let capped = false;
// divide out 2s first
while (n % 2 === 0) { factors.push(2); n /= 2; steps++; }
// then odd candidates up to sqrt(n)
let d = 3;
while (d * d <= n) {
steps++;
if (steps > STEP_CAP) { capped = true; break; }
if (n % d === 0) { factors.push(d); n /= d; }
else { d += 2; }
}
if (!capped && n > 1) factors.push(n); // leftover prime
return { factors, steps, capped, remainder: n };
}
function classify(f) {
if (f.length === 1) return ['prime', 'prime'];
if (f.length === 2 && f[0] > 3 && f[1] > 3) return ['semi', 'semiprime'];
return ['smooth', 'composite'];
}
function group(n) { return n.toLocaleString('en-US'); }
function run() {
const raw = (document.getElementById('num').value || '').replace(/[ ,]/g, '');
const out = document.getElementById('out');
const n = Number(raw);
if (!Number.isInteger(n) || n < 2 || n > 9e15) {
out.innerHTML = '<p class="meta"><b>Enter a whole number between 2 and 9,000,000,000,000,000.</b></p>';
return;
}
const res = factor(n);
if (res.capped) {
out.innerHTML = fmt('<p class="meta">Still searching after <span class="steps"><span class="steps">%group%</span></span> steps — gave up. Smallest factor (if any) is above %group2%. This is exactly why big semiprimes are hard: trial division would need to climb all the way to the square root.</p>', { group: group(STEP_CAP), group2: group(Math.floor(Math.sqrt(n))) });
return;
}
const [cls, name] = classify(res.factors);
const eq = res.factors.map(p => '<span class="p">' + group(p) + '</span>').join('<span class="x">×</span>');
let line;
if (res.factors.length === 1) line = fmt('<span class="p">%group%</span> is prime', { group: group(n) });
else line = group(n) + '<span class="x">=</span>' + eq;
out.innerHTML =
fmt('<div class="factv">%line%<span class="tag %cls%">%name%</span></div><p class="meta">Found in <span class="steps">%group%</span> %v% %v2%</p>', { line: line, cls: cls, name: name, group: group(res.steps), v: res.steps === 1 ? 'step.' : 'steps.', v2: cls === 'semi'
? 'A <b>semiprime</b>: the loop had to climb near √n before the first factor appeared. Add a few digits and the steps multiply.'
: cls === 'prime'
? 'No factor below √n, so it is prime — but we only know that after testing every candidate.'
: 'Lots of small factors, so it fell apart fast.' });
}
document.getElementById('go').addEventListener('click', run);
document.getElementById('num').addEventListener('keydown', e => { if (e.key === 'Enter') run(); });
document.querySelectorAll('.chip').forEach(c =>
c.addEventListener('click', () => { document.getElementById('num').value = c.dataset.n; run(); }));
run();
<p class="hint">Type a number — or pick a preset — and hit <b>Factor</b>. The demo divides out primes one by one and counts every test it makes. Watch how the <b>step count</b> explodes for a <i>semiprime</i> (a product of two big primes).</p>
<div class="row">
<input id="num" type="text" inputmode="numeric" value="8051" autocomplete="off" />
<button id="go" type="button">Factor</button>
</div>
<div class="presets">
<button class="chip" data-n="360" type="button">360 <span>easy</span></button>
<button class="chip" data-n="8051" type="button">8051 <span>83 × 97</span></button>
<button class="chip" data-n="99400891" type="button">99,400,891 <span>4-digit primes</span></button>
<button class="chip" data-n="560008500011" type="button">560,008,500,011 <span>6-digit primes</span></button>
</div>
<div id="out" class="out"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .7rem; line-height: 1.45; }
.row { display: flex; gap: .5rem; margin-bottom: .6rem; }
#num { flex: 1; min-width: 0; font: 700 18px ui-monospace, monospace; padding: .5rem .7rem; border: 1px solid #bbb; border-radius: 8px; color: #1d3557; }
button { font: 600 14px system-ui, sans-serif; padding: .5rem 1rem; border: 1px solid #457b9d; background: #457b9d; color: #fff; border-radius: 8px; cursor: pointer; }
.presets { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .8rem; }
.chip { background: #fff; color: #457b9d; border: 1px solid #cdd9e2; font-weight: 600; padding: .35rem .6rem; }
.chip span { color: #888; font-weight: 500; font-size: .8em; margin-left: .25rem; }
.chip:hover { background: #f0f4f8; }
.out { min-height: 3em; }
.factv { font: 800 22px ui-monospace, monospace; color: #1d3557; word-break: break-word; line-height: 1.35; margin: .2rem 0 .5rem; }
.factv .x { color: #999; font-weight: 600; margin: 0 .15rem; }
.factv .p { color: #2a9d8f; }
.meta { font-size: .92rem; color: #444; line-height: 1.5; }
.meta b { color: #c0392b; }
.steps { font-weight: 800; color: #c0392b; }
.tag { display: inline-block; font-size: .8rem; font-weight: 700; padding: .1rem .5rem; border-radius: 99px; margin-left: .4rem; }
.tag.prime { background: #fde8e8; color: #c0392b; }
.tag.semi { background: #fff4e0; color: #b9770e; }
.tag.smooth { background: #e6f6ec; color: #0a7d33; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Integer factorization by trial division — and a live step counter.
// The point: small numbers fall apart instantly; a semiprime (two big primes multiplied) forces the loop almost all the way to sqrt(n).
const STEP_CAP = 80000000; // safety bound so the browser never freezes
function factor(n) {
const factors = [];
let steps = 0;
let capped = false;
// divide out 2s first
while (n % 2 === 0) { factors.push(2); n /= 2; steps++; }
// then odd candidates up to sqrt(n)
let d = 3;
while (d * d <= n) {
steps++;
if (steps > STEP_CAP) { capped = true; break; }
if (n % d === 0) { factors.push(d); n /= d; }
else { d += 2; }
}
if (!capped && n > 1) factors.push(n); // leftover prime
return { factors, steps, capped, remainder: n };
}
function classify(f) {
if (f.length === 1) return ['prime', 'prime'];
if (f.length === 2 && f[0] > 3 && f[1] > 3) return ['semi', 'semiprime'];
return ['smooth', 'composite'];
}
function group(n) { return n.toLocaleString('en-US'); }
function run() {
const raw = (document.getElementById('num').value || '').replace(/[ ,]/g, '');
const out = document.getElementById('out');
const n = Number(raw);
if (!Number.isInteger(n) || n < 2 || n > 9e15) {
out.innerHTML = '<p class="meta"><b>Enter a whole number between 2 and 9,000,000,000,000,000.</b></p>';
return;
}
const res = factor(n);
if (res.capped) {
out.innerHTML = fmt('<p class="meta">Still searching after <span class="steps"><span class="steps">%group%</span></span> steps — gave up. Smallest factor (if any) is above %group2%. This is exactly why big semiprimes are hard: trial division would need to climb all the way to the square root.</p>', { group: group(STEP_CAP), group2: group(Math.floor(Math.sqrt(n))) });
return;
}
const [cls, name] = classify(res.factors);
const eq = res.factors.map(p => '<span class="p">' + group(p) + '</span>').join('<span class="x">×</span>');
let line;
if (res.factors.length === 1) line = fmt('<span class="p">%group%</span> is prime', { group: group(n) });
else line = group(n) + '<span class="x">=</span>' + eq;
out.innerHTML =
fmt('<div class="factv">%line%<span class="tag %cls%">%name%</span></div><p class="meta">Found in <span class="steps">%group%</span> %v% %v2%</p>', { line: line, cls: cls, name: name, group: group(res.steps), v: res.steps === 1 ? 'step.' : 'steps.', v2: cls === 'semi'
? 'A <b>semiprime</b>: the loop had to climb near √n before the first factor appeared. Add a few digits and the steps multiply.'
: cls === 'prime'
? 'No factor below √n, so it is prime — but we only know that after testing every candidate.'
: 'Lots of small factors, so it fell apart fast.' });
}
document.getElementById('go').addEventListener('click', run);
document.getElementById('num').addEventListener('keydown', e => { if (e.key === 'Enter') run(); });
document.querySelectorAll('.chip').forEach(c =>
c.addEventListener('click', () => { document.getElementById('num').value = c.dataset.n; run(); }));
run();
Small numbers fall apart instantly. But the semiprimes — products of two primes close in size — are the cruel case: trial division has to climb almost all the way to the square root before it finds the first factor. Bump the size up a few digits and the steps multiply. Now imagine the number is 600 digits long.
Comments
Loading comments...