<p class="hint">Choose a dividend/divisor pair below. Each press of <b>Next step</b> multiplies the divisor by the new quotient term and subtracts it, exactly like long division on numbers.</p>
<div class="controls">
<label for="example">Example:</label>
<select id="example"></select>
<button id="next" type="button">Next step</button>
<button id="reset" type="button" class="ghost">Restart</button>
</div>
<div class="board">
<div class="row" id="quotientRow"><span class="rowlabel">Quotient</span><span id="quotientPoly" class="poly"></span></div>
<div class="row" id="workRow"><span class="rowlabel">This step</span><span id="workPoly" class="poly"></span></div>
<div class="row" id="remRow"><span class="rowlabel">Remainder</span><span id="remPoly" class="poly"></span></div>
</div>
<div class="status" id="status">Press Next step to begin.</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; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .8rem; }
.controls label { font-size: .85rem; font-weight: 600; color: #1d3557; }
select { font: 600 14px system-ui, sans-serif; padding: .4rem .5rem; border-radius: 8px; border: 1px solid #adb1b8; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .5; cursor: default; }
.board { background: #f4f6f8; border: 1px solid #dde3e8; border-radius: 10px; padding: .8rem 1rem; margin-bottom: .6rem; }
.row { display: flex; align-items: baseline; gap: .6rem; padding: .3rem 0; font: 700 17px ui-monospace, monospace; min-height: 1.6em; }
.rowlabel { font: 600 12px system-ui, sans-serif; color: #667; width: 92px; flex: none; }
.poly { color: #1d3557; word-break: break-word; }
.poly .fade { color: #adb1b8; }
.poly .neg { color: #c92f3c; }
#workRow .poly { color: #6a4c93; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Four sample (dividend, divisor) pairs, coefficients from highest degree to constant term.
const EXAMPLES = [
{ label: 'x³ − 2x² − 4 ÷ x − 3', dividend: [1, -2, 0, -4], divisor: [1, -3] },
{ label: '2x³ + 3x² − 4x + 5 ÷ x + 2', dividend: [2, 3, -4, 5], divisor: [1, 2] },
{ label: 'x⁴ − 1 ÷ x² + 1', dividend: [1, 0, 0, 0, -1], divisor: [1, 0, 1] },
{ label: '6x³ − 5x² + 4 ÷ 2x − 1', dividend: [6, -5, 0, 4], divisor: [2, -1] }
];
// Drop leading zero coefficients so the degree matches the true polynomial.
function trimLead(c) {
let i = 0;
while (i < c.length - 1 && c[i] === 0) i++;
return c.slice(i);
}
// Render a coefficient array as a human-readable polynomial in x.
function polyString(coeffs, baseDeg) {
const c = coeffs;
const n = c.length - 1;
const deg0 = (baseDeg === undefined) ? n : baseDeg;
const parts = [];
for (let i = 0; i <= n; i++) {
const power = deg0 - i;
const v = c[i];
if (v === 0 && n > 0) continue;
const sign = v < 0 ? '−' : (parts.length ? '+' : '');
const av = Math.abs(v);
let term;
if (power === 0) term = String(av);
else {
const coefPart = (av === 1) ? '' : String(av);
term = coefPart + 'x' + (power === 1 ? '' : supersc(power));
}
parts.push((sign ? sign + ' ' : '') + term);
}
if (!parts.length) return '0';
return parts.join(' ');
}
// Map single digits to their Unicode superscript character for exponents.
function supersc(n) {
const map = { '0':'⁰','1':'¹','2':'²','3':'³','4':'⁴','5':'⁵','6':'⁶','7':'⁷','8':'⁸','9':'⁹' };
return String(n).split('').map(d => map[d]).join('');
}
let dividend, divisor, dDeg, lead, rem, quotient, quotientDeg, done, stepCount;
function setup(ex) {
dividend = trimLead(ex.dividend.slice());
divisor = trimLead(ex.divisor.slice());
dDeg = divisor.length - 1;
lead = divisor[0];
rem = dividend.slice();
quotientDeg = (dividend.length - 1) - dDeg;
quotient = quotientDeg >= 0 ? new Array(quotientDeg + 1).fill(0) : [];
done = quotientDeg < 0;
stepCount = 0;
render();
setStatus(done ? 'The dividend’s degree is already below the divisor’s — quotient is 0.' : 'Press Next step to begin.', done ? 'ok' : '');
}
// One round of long division: take the remainder’s leading term, divide by the divisor’s leading term to get the next quotient term, then subtract that multiple of the divisor.
function step() {
if (done) return;
rem = trimLead(rem);
const curDeg = rem.length - 1;
if (curDeg < dDeg) { finish(); return; }
const factor = rem[0] / lead;
const shift = curDeg - dDeg;
quotient[quotientDeg - shift] = factor;
const sub = new Array(rem.length).fill(0);
for (let j = 0; j < divisor.length; j++) sub[j] += factor * divisor[j];
const newRem = rem.map((v, i) => v - sub[i]);
document.getElementById('workPoly').textContent =
i18nFmt('Subtracted %factor% × divisor: −(%sub%)', { factor: polyTermLabel(factor, shift), sub: polyString(trimLead(sub.slice())) });
rem = trimLead(newRem);
stepCount++;
if (rem.length - 1 < dDeg) finish(); else render();
setStatus(i18nFmt('Step %n% done — degree dropped by at least one.', { n: stepCount }), '');
}
function polyTermLabel(factor, shift) {
const av = Math.abs(factor);
const coefPart = (av === 1) ? '' : String(av);
const xPart = shift === 0 ? '' : ('x' + (shift === 1 ? '' : supersc(shift)));
const body = (coefPart + xPart) || '1';
return (factor < 0 ? '−' : '') + body;
}
function finish() {
done = true;
render();
setStatus(i18nFmt('Finished after %n% step(s) — remainder’s degree is now below the divisor’s.', { n: stepCount }), 'ok');
console.log('Final result:', 'Q=', polyString(quotient), 'R=', polyString(rem));
}
function render() {
document.getElementById('quotientPoly').textContent = quotient.length ? polyString(quotient) : '0';
document.getElementById('remPoly').textContent = polyString(rem);
document.getElementById('next').disabled = done;
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status ' + cls;
}
const sel = document.getElementById('example');
EXAMPLES.forEach((ex, i) => {
const o = document.createElement('option');
o.value = String(i);
o.textContent = ex.label;
sel.appendChild(o);
});
sel.addEventListener('change', () => setup(EXAMPLES[Number(sel.value)]));
document.getElementById('next').addEventListener('click', step);
document.getElementById('reset').addEventListener('click', () => setup(EXAMPLES[Number(sel.value)]));
setup(EXAMPLES[0]);
<p class="hint">Choose a dividend/divisor pair below. Each press of <b>Next step</b> multiplies the divisor by the new quotient term and subtracts it, exactly like long division on numbers.</p>
<div class="controls">
<label for="example">Example:</label>
<select id="example"></select>
<button id="next" type="button">Next step</button>
<button id="reset" type="button" class="ghost">Restart</button>
</div>
<div class="board">
<div class="row" id="quotientRow"><span class="rowlabel">Quotient</span><span id="quotientPoly" class="poly"></span></div>
<div class="row" id="workRow"><span class="rowlabel">This step</span><span id="workPoly" class="poly"></span></div>
<div class="row" id="remRow"><span class="rowlabel">Remainder</span><span id="remPoly" class="poly"></span></div>
</div>
<div class="status" id="status">Press Next step to begin.</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; }
.controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin-bottom: .8rem; }
.controls label { font-size: .85rem; font-weight: 600; color: #1d3557; }
select { font: 600 14px system-ui, sans-serif; padding: .4rem .5rem; border-radius: 8px; border: 1px solid #adb1b8; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .5; cursor: default; }
.board { background: #f4f6f8; border: 1px solid #dde3e8; border-radius: 10px; padding: .8rem 1rem; margin-bottom: .6rem; }
.row { display: flex; align-items: baseline; gap: .6rem; padding: .3rem 0; font: 700 17px ui-monospace, monospace; min-height: 1.6em; }
.rowlabel { font: 600 12px system-ui, sans-serif; color: #667; width: 92px; flex: none; }
.poly { color: #1d3557; word-break: break-word; }
.poly .fade { color: #adb1b8; }
.poly .neg { color: #c92f3c; }
#workRow .poly { color: #6a4c93; }
.status { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Four sample (dividend, divisor) pairs, coefficients from highest degree to constant term.
const EXAMPLES = [
{ label: 'x³ − 2x² − 4 ÷ x − 3', dividend: [1, -2, 0, -4], divisor: [1, -3] },
{ label: '2x³ + 3x² − 4x + 5 ÷ x + 2', dividend: [2, 3, -4, 5], divisor: [1, 2] },
{ label: 'x⁴ − 1 ÷ x² + 1', dividend: [1, 0, 0, 0, -1], divisor: [1, 0, 1] },
{ label: '6x³ − 5x² + 4 ÷ 2x − 1', dividend: [6, -5, 0, 4], divisor: [2, -1] }
];
// Drop leading zero coefficients so the degree matches the true polynomial.
function trimLead(c) {
let i = 0;
while (i < c.length - 1 && c[i] === 0) i++;
return c.slice(i);
}
// Render a coefficient array as a human-readable polynomial in x.
function polyString(coeffs, baseDeg) {
const c = coeffs;
const n = c.length - 1;
const deg0 = (baseDeg === undefined) ? n : baseDeg;
const parts = [];
for (let i = 0; i <= n; i++) {
const power = deg0 - i;
const v = c[i];
if (v === 0 && n > 0) continue;
const sign = v < 0 ? '−' : (parts.length ? '+' : '');
const av = Math.abs(v);
let term;
if (power === 0) term = String(av);
else {
const coefPart = (av === 1) ? '' : String(av);
term = coefPart + 'x' + (power === 1 ? '' : supersc(power));
}
parts.push((sign ? sign + ' ' : '') + term);
}
if (!parts.length) return '0';
return parts.join(' ');
}
// Map single digits to their Unicode superscript character for exponents.
function supersc(n) {
const map = { '0':'⁰','1':'¹','2':'²','3':'³','4':'⁴','5':'⁵','6':'⁶','7':'⁷','8':'⁸','9':'⁹' };
return String(n).split('').map(d => map[d]).join('');
}
let dividend, divisor, dDeg, lead, rem, quotient, quotientDeg, done, stepCount;
function setup(ex) {
dividend = trimLead(ex.dividend.slice());
divisor = trimLead(ex.divisor.slice());
dDeg = divisor.length - 1;
lead = divisor[0];
rem = dividend.slice();
quotientDeg = (dividend.length - 1) - dDeg;
quotient = quotientDeg >= 0 ? new Array(quotientDeg + 1).fill(0) : [];
done = quotientDeg < 0;
stepCount = 0;
render();
setStatus(done ? 'The dividend’s degree is already below the divisor’s — quotient is 0.' : 'Press Next step to begin.', done ? 'ok' : '');
}
// One round of long division: take the remainder’s leading term, divide by the divisor’s leading term to get the next quotient term, then subtract that multiple of the divisor.
function step() {
if (done) return;
rem = trimLead(rem);
const curDeg = rem.length - 1;
if (curDeg < dDeg) { finish(); return; }
const factor = rem[0] / lead;
const shift = curDeg - dDeg;
quotient[quotientDeg - shift] = factor;
const sub = new Array(rem.length).fill(0);
for (let j = 0; j < divisor.length; j++) sub[j] += factor * divisor[j];
const newRem = rem.map((v, i) => v - sub[i]);
document.getElementById('workPoly').textContent =
i18nFmt('Subtracted %factor% × divisor: −(%sub%)', { factor: polyTermLabel(factor, shift), sub: polyString(trimLead(sub.slice())) });
rem = trimLead(newRem);
stepCount++;
if (rem.length - 1 < dDeg) finish(); else render();
setStatus(i18nFmt('Step %n% done — degree dropped by at least one.', { n: stepCount }), '');
}
function polyTermLabel(factor, shift) {
const av = Math.abs(factor);
const coefPart = (av === 1) ? '' : String(av);
const xPart = shift === 0 ? '' : ('x' + (shift === 1 ? '' : supersc(shift)));
const body = (coefPart + xPart) || '1';
return (factor < 0 ? '−' : '') + body;
}
function finish() {
done = true;
render();
setStatus(i18nFmt('Finished after %n% step(s) — remainder’s degree is now below the divisor’s.', { n: stepCount }), 'ok');
console.log('Final result:', 'Q=', polyString(quotient), 'R=', polyString(rem));
}
function render() {
document.getElementById('quotientPoly').textContent = quotient.length ? polyString(quotient) : '0';
document.getElementById('remPoly').textContent = polyString(rem);
document.getElementById('next').disabled = done;
}
function setStatus(t, cls) {
const s = document.getElementById('status');
s.textContent = t;
s.className = 'status ' + cls;
}
const sel = document.getElementById('example');
EXAMPLES.forEach((ex, i) => {
const o = document.createElement('option');
o.value = String(i);
o.textContent = ex.label;
sel.appendChild(o);
});
sel.addEventListener('change', () => setup(EXAMPLES[Number(sel.value)]));
document.getElementById('next').addEventListener('click', step);
document.getElementById('reset').addEventListener('click', () => setup(EXAMPLES[Number(sel.value)]));
setup(EXAMPLES[0]);
Every click does one round of "multiply and subtract." Once the remainder's degree is smaller than the divisor's, there is nothing left to bring down — the algorithm halts on its own, and what remains is the final remainder.
Comments
Loading comments...