Skill: factoring-gcf
GCF Factoring Calculator
Find the GCF of monomials, factor it out of any polynomial, or factor a four-term cubic by grouping — with every step shown.
GCF of two monomials
Type monomials like 12x^3. The tool takes the gcd of the coefficients and the smaller exponent.
Factor out the GCF
Enter any polynomial. The tool finds the GCF of all terms and writes the complete factorization.
Factor by grouping
Enter a four-term polynomial (usually a cubic). The tool pairs terms, factors each pair, and pulls out the common binomial.
/* ============================================================================ FiboPoly — polynomial / rational-expression math core for FiboTutors skill ecosystems. Pure JavaScript, no DOM access. Free of the double-ampersand operator by construction (nested ifs / De Morgan). Exposed as FiboPoly (browser) or module.exports (node). ============================================================================ */ (function (root, factory) { 'use strict'; var api = factory(); if (typeof module === 'undefined') { root.FiboPoly = api; } else if (module.exports) { module.exports = api; } else { root.FiboPoly = api; } })(typeof self !== 'undefined' ? self : this, function () { 'use strict';
/* ================= integers ================= */ function igcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b) { var t = a % b; a = b; b = t; } return a || 1; }
/* ================= fractions {n,d}, d > 0, reduced ================= */ function fred(n, d) { if (d === 0) throw new Error('fred: zero denominator'); if (d < 0) { n = -n; d = -d; } if (n === 0) return { n: 0, d: 1 }; var g = igcd(n, d); return { n: n / g, d: d / g }; } function fi(k) { return { n: k, d: 1 }; } function fadd(a, b) { return fred(a.n * b.d + b.n * a.d, a.d * b.d); } function fsub(a, b) { return fred(a.n * b.d - b.n * a.d, a.d * b.d); } function fmul(a, b) { return fred(a.n * b.n, a.d * b.d); } function fdiv(a, b) { if (b.n === 0) throw new Error('fdiv: division by zero'); return fred(a.n * b.d, a.d * b.n); } function fneg(a) { return { n: -a.n, d: a.d }; } function feq(a, b) { return !(a.n !== b.n || a.d !== b.d); } function fis0(a) { return a.n === 0; } function ffmt(f) { return f.d === 1 ? String(f.n) : f.n + '/' + f.d; } /* parse "5", "-3", "3/4", "0.75" (also unicode minus) -> fraction or null */ function fparse(str) { var s = String(str).replace(/−/g, '-').replace(/\s+/g, ''); if (!s) return null; var m = /^([+-]?\d+)\/([+-]?\d+)$/.exec(s); if (m) { var d = parseInt(m[2], 10); if (d === 0) return null; return fred(parseInt(m[1], 10), d); } m = /^([+-]?(?:\d+\.?\d*|\.\d+))$/.exec(s); if (m) { var body = m[1].replace(/^[+-]/, ''); var dot = body.indexOf('.'); var places = dot < 0 ? 0 : body.length - dot - 1; if (places > 9) places = 9; var k = Math.pow(10, places); var num = Math.round(parseFloat(m[1]) * k); if (!isFinite(num)) return null; return fred(num, k); } return null; }
/* ================= polys: arrays of fractions, index = degree ================= */ function pnorm(p) { var q = p.slice(); while (q.length > 1) { if (!fis0(q[q.length - 1])) break; q.pop(); } return q; } function pzero() { return [{ n: 0, d: 1 }]; } function pconst(k) { return pnorm([fi(k)]); } function pconstF(f) { return pnorm([{ n: f.n, d: f.d }]); } function pvar() { return [{ n: 0, d: 1 }, { n: 1, d: 1 }]; } function pdeg(p) { return pnorm(p).length - 1; } function pis0(p) { var q = pnorm(p); if (q.length !== 1) return false; return fis0(q[0]); } function padd(a, b) { var n = Math.max(a.length, b.length), r = [], i; for (i = 0; i < n; i++) { var x = i < a.length ? a[i] : fi(0); var y = i < b.length ? b[i] : fi(0); r.push(fadd(x, y)); } return pnorm(r); } function psub(a, b) { var n = Math.max(a.length, b.length), r = [], i; for (i = 0; i < n; i++) { var x = i < a.length ? a[i] : fi(0); var y = i < b.length ? b[i] : fi(0); r.push(fsub(x, y)); } return pnorm(r); } function pmul(a, b) { var r = [], i, j; for (i = 0; i < a.length + b.length - 1; i++) r.push(fi(0)); for (i = 0; i < a.length; i++) { for (j = 0; j < b.length; j++) { r[i + j] = fadd(r[i + j], fmul(a[i], b[j])); } } return pnorm(r); } function pmulC(a, k) { return pnorm(a.map(function (c) { return fmul(c, k); })); } function ppow(p, e) { var r = pconst(1); for (var i = 0; i < e; i++) r = pmul(r, p); return r; } function peq(a, b) { a = pnorm(a); b = pnorm(b); if (a.length !== b.length) return false; for (var i = 0; i < a.length; i++) if (!feq(a[i], b[i])) return false; return true; } function peval(p, x) { var acc = fi(0); for (var i = p.length - 1; i >= 0; i--) acc = fadd(fmul(acc, x), p[i]); return acc; } /* synthetic division by (x - r), r a fraction. returns {q, rem} */ function pdivLin(p, r) { var a = pnorm(p).slice(), n = a.length - 1, i; if (n < 1) return { q: pzero(), rem: a[0] || fi(0) }; var q = new Array(n), carry = fi(0); for (i = n; i >= 1; i--) { var c = fadd(a[i], carry); q[i - 1] = c; carry = fmul(c, r); } return { q: pnorm(q), rem: fadd(a[0], carry) }; } /* scale to integer coefficients: returns {p:[ints], k} where p = k * orig */ function pscaleInt(p) { p = pnorm(p); var L = 1, i; for (i = 0; i < p.length; i++) { var d = p[i].d; L = (L / igcd(L, d)) * d; } var ints = p.map(function (c) { return (c.n * L) / c.d; }); return { p: ints, k: L }; } /* gcd of every integer coefficient of num and den (after scaling). Uses a zero-safe gcd so leading zeros do not corrupt the accumulation. */ function pgcdAll(numP, denP) { function g0(x, y) { x = Math.abs(x); y = Math.abs(y); while (y) { var t = x % y; x = y; y = t; } return x; } var a = pscaleInt(numP).p, b = pscaleInt(denP).p, g = 0, i; for (i = 0; i < a.length; i++) g = g0(g, a[i]); for (i = 0; i < b.length; i++) g = g0(g, b[i]); return g === 0 ? 1 : g; } /* ================= parsing ================= */ function tokenize(s) { var toks = [], i = 0, n = s.length, c, j; while (i < n) { c = s[i]; if (c === ' ' || c === '\t' || c === '\n' || c === '\r') { i++; continue; } if (c === '+' || c === '-' || c === '*' || c === '^' || c === '(' || c === ')') { toks.push({ t: c }); i++; continue; } if (c === 'x' || c === 'X') { toks.push({ t: 'x' }); i++; continue; } var isDig = (c >= '0'); if (isDig) isDig = (c <= '9'); if (isDig || c === '.') { j = i; while (j < n) { var cj = s[j]; var jd = (cj >= '0'); if (jd) jd = (cj <= '9'); if (jd || cj === '.') { j++; continue; } break; } toks.push({ t: 'num', v: s.slice(i, j) }); i = j; continue; } return null; } return toks; } function decFrac(v) { var m = /^(\d*)\.?(\d*)$/.exec(v); if (!m) return null; var ip = m[1] || '0', fp = m[2] || ''; if (ip === '0') { if (fp === '') return { n: 0, d: 1 }; } if (!/^\d+$/.test(ip)) return null; if (fp !== '') { if (!/^\d+$/.test(fp)) return null; } var places = fp.length; var k = Math.pow(10, Math.min(places, 9)); var num; if (places > 9) { num = Math.round(parseFloat(v) * k); } else { num = parseInt(ip, 10) * k + (fp === '' ? 0 : parseInt(fp, 10)); } return fred(num, k); } /* parsePoly(str) -> Poly or null. Supports + - * ^ ( ) x, decimals, implicit multiplication (2x, 2(x+1), (x+1)(x+2), x(x+3)), x^2, unicode. */ function parsePoly(str) { var s = String(str).replace(/−/g, '-').replace(/²/g, '^2').replace(/³/g, '^3').replace(/×/g, '*'); var toks = tokenize(s); if (!toks || toks.length === 0) return null; var t2 = [], i; for (i = 0; i < toks.length; i++) { t2.push(toks[i]); if (i + 1 < toks.length) { var a = toks[i].t, b = toks[i + 1].t; var left = (a === 'num' || a === 'x' || a === ')'); var right = (b === 'num' || b === 'x' || b === '('); if (left) { if (right) { if (a !== 'num' || b !== 'num') t2.push({ t: '*' }); } } } } var pos = 0; function peek() { return pos < t2.length ? t2[pos] : null; } function parseIntExp() { var k = peek(); if (!k || k.t !== 'num') return null; if (!/^\d+$/.test(k.v)) return null; pos++; return parseInt(k.v, 10); } function parseExpr() { var p = parseTerm(); if (!p) return null; for (;;) { var k = peek(); if (!k) break; if (k.t === '+') { pos++; var q = parseTerm(); if (!q) return null; p = padd(p, q); } else if (k.t === '-') { pos++; var q2 = parseTerm(); if (!q2) return null; p = psub(p, q2); } else break; } return p; } function parseTerm() { var p = parseFactor(); if (!p) return null; for (;;) { var k = peek(); if (!k || k.t !== '*') break; pos++; var q = parseFactor(); if (!q) return null; p = pmul(p, q); } return p; } function parseFactor() { var neg = false, k = peek(); if (k) { if (k.t === '+' || k.t === '-') { neg = (k.t === '-'); pos++; } } k = peek(); if (!k) return null; var p = null; if (k.t === 'num') { pos++; var f = decFrac(k.v); if (!f) return null; p = [f]; var kk = peek(); if (kk) { if (kk.t === '^') { pos++; var e = parseIntExp(); if (e === null || e > 9) return null; p = ppow(p, e); } } } else if (k.t === 'x') { pos++; p = pvar(); var kk2 = peek(); if (kk2) { if (kk2.t === '^') { pos++; var e2 = parseIntExp(); if (e2 === null || e2 > 9) return null; p = ppow(pvar(), e2); } } } else if (k.t === '(') { pos++; p = parseExpr(); if (!p) return null; k = peek(); if (!k || k.t !== ')') return null; pos++; var kk3 = peek(); if (kk3) { if (kk3.t === '^') { pos++; var e3 = parseIntExp(); if (e3 === null || e3 > 4) return null; p = ppow(p, e3); } } } else { return null; } if (neg) p = pmulC(p, fi(-1)); return p; } var res = parseExpr(); if (!res || pos !== t2.length) return null; return pnorm(res); } /* parseRational(str) -> {num, den} or null. Splits on one top-level '/'. */ function parseRational(str) { var s = String(str).replace(/−/g, '-').replace(/²/g, '^2').replace(/³/g, '^3').replace(/×/g, '*'); var depth = 0, idx = -1, i; for (i = 0; i < s.length; i++) { var c = s[i]; if (c === '(') depth++; else if (c === ')') { depth--; if (depth < 0) return null; } else if (c === '/') { if (depth === 0) { if (idx >= 0) return null; idx = i; } } } if (depth !== 0) return null; var num, den; if (idx < 0) { num = parsePoly(s); den = pconst(1); } else { num = parsePoly(s.slice(0, idx)); den = parsePoly(s.slice(idx + 1)); } if (!num || !den) return null; if (pis0(den)) return null; return { num: num, den: den }; } /* parse a list of excluded values: "0, -1", "x≠0,-1", "x != 0" -> [frac] or null */ function parseDomainList(str) { var s = String(str).replace(/−/g, '-').replace(/≠/g, '').replace(/!/g, '') .replace(/=/g, '').replace(/x/gi, '').replace(/\s+/g, ''); if (!s) return null; var parts = s.split(/[,;]+/), out = [], i; for (i = 0; i < parts.length; i++) { if (parts[i] === '') continue; var f = fparse(parts[i]); if (!f) return null; out.push(f); } if (out.length === 0) return null; return out; } function domainSetEq(a, b) { if (a.length !== b.length) return false; var used = [], i, j; for (i = 0; i < a.length; i++) used.push(false); for (i = 0; i < a.length; i++) { var found = false; for (j = 0; j < b.length; j++) { if (!used[j]) { if (feq(a[i], b[j])) { used[j] = true; found = true; break; } } } if (!found) return false; } return true; } /* ================= formatting ================= */ function coefMagText(c) { var a = Math.abs(c.n); return c.d === 1 ? String(a) : a + '/' + c.d; } /* canonical plain text: "4x^2-5x-1" */ function pfmtText(p) { p = pnorm(p); if (pis0(p)) return '0'; var s = '', d; for (d = p.length - 1; d >= 0; d--) { var c = p[d]; if (fis0(c)) continue; var neg = c.n < 0, mag; var one = !(Math.abs(c.n) !== 1 || c.d !== 1); if (d === 0) mag = coefMagText(c); else if (d === 1) mag = one ? 'x' : coefMagText(c) + 'x'; else mag = one ? 'x^' + d : coefMagText(c) + 'x^' + d; if (s === '') s = (neg ? '-' : '') + mag; else s += (neg ? '-' : '+') + mag; } return s; } /* pretty HTML with exponents and proper minus signs */ function pfmtHTML(p) { p = pnorm(p); if (pis0(p)) return '0'; var s = '', d; for (d = p.length - 1; d >= 0; d--) { var c = p[d]; if (fis0(c)) continue; var neg = c.n < 0, mag; var one = !(Math.abs(c.n) !== 1 || c.d !== 1); if (d === 0) mag = coefMagText(c); else if (d === 1) mag = one ? 'x' : coefMagText(c) + 'x'; else mag = one ? 'x' + d + '' : coefMagText(c) + 'x' + d + ''; if (s === '') s = (neg ? '−' : '') + mag; else s += (neg ? ' − ' : ' + ') + mag; } return s; } /* factored-form HTML from factorQuad-style linear factors */ function linFactorHTML(d, e) { /* (dx + e) with integers d, e */ var s = '('; if (d === 1) s += 'x'; else if (d === -1) s += '−x'; else s += String(d).replace('-', '−') + 'x'; if (e > 0) s += ' + ' + e; else if (e < 0) s += ' − ' + Math.abs(e); return s + ')'; } function escHTML(s) { return String(s).replace(/\x26/g, '&').replace(//g, '>'); }
/* ================= factoring ax^2 + bx + c over integers ================= */ function divisors(n) { n = Math.abs(n); var d = [], i; for (i = 1; i * i <= n; i++) { if (n % i === 0) { d.push(i); if (i * i !== n) d.push(n / i); } } return d.sort(function (x, y) { return x - y; }); } /* factorQuad(a,b,c): integers. Returns a result object (see below). */ function factorQuad(a, b, c) { if (a === 0) return { ok: false, error: 'not quadratic (a = 0)' }; var neg = false; if (a < 0) { neg = true; a = -a; b = -b; c = -c; } var g = igcd(igcd(a, b), c); var A = a / g, B = b / g, C = c / g; var factors = []; var res = { ok: true, neg: neg, gcf: g, A: A, B: B, C: C, cZero: false, factors: factors }; if (C === 0) { res.cZero = true; if (B === 0) { if (A !== 1) factors.push(pconst(A)); factors.push(pvar()); factors.push(pvar()); res.bZero = true; } else { var g2 = igcd(A, B); var f1 = A / g2, g1 = B / g2; if (g2 > 1) factors.push(pconst(g2)); factors.push(pvar()); factors.push(pnorm([fi(g1), fi(f1)])); res.lin = { f: f1, g: g1 }; } } else { var da = divisors(A), dc = divisors(C), sol = null, i, j, k; var done = false; for (i = 0; i < da.length; i++) { if (done) break; var d = da[i], f = A / d; for (j = 0; j < dc.length; j++) { if (done) break; var e0 = dc[j], g0 = C / e0; var pairs = [[e0, g0], [-e0, -g0]]; for (k = 0; k < pairs.length; k++) { var e = pairs[k][0], gg = pairs[k][1]; if (d * gg + e * f === B) { sol = { d: d, e: e, f: f, g: gg }; done = true; break; } } } } if (!sol) { res.ok = false; res.error = 'does not factor over the integers'; return res; } res.sol = sol; res.hunt = { p: sol.d * sol.g, q: sol.e * sol.f }; factors.push(pnorm([fi(sol.e), fi(sol.d)])); factors.push(pnorm([fi(sol.g), fi(sol.f)])); } if (g > 1) factors.unshift(pconst(g)); if (neg) factors.unshift(pconst(-1)); return res; } /* verify a factorQuad result by expansion; origA,origB,origC are the ORIGINAL coeffs */ function factorQuadVerify(r, oa, ob, oc) { if (!r.ok) return false; var acc = pconst(1), i; for (i = 0; i < r.factors.length; i++) acc = pmul(acc, r.factors[i]); return peq(acc, pnorm([fi(oc), fi(ob), fi(oa)])); } /* ================= rational expressions ================= */ /* integer/rational roots of a poly (deg <= 2); {ok, roots:[frac]} or {ok:false, approx:[...]} */ /* exact rational roots of an integer-coefficient quadratic; {ok, roots:[frac]} or {ok:false, approx:[numbers]} */ function intRootsDeg2(c) { var roots = [], i; var r = factorQuad(c[2], c[1], c[0]); if (!r.ok) { var D = c[1] * c[1] - 4 * c[2] * c[0], approx = []; if (D >= 0) { var sq = Math.sqrt(D); approx.push((-c[1] + sq) / (2 * c[2])); approx.push((-c[1] - sq) / (2 * c[2])); } return { ok: false, approx: approx }; } for (i = 0; i < r.factors.length; i++) { var f = pnorm(r.factors[i]); if (f.length === 2) { if (!fis0(f[1])) roots.push(fdiv(fneg(f[0]), f[1])); } } return { ok: true, roots: roots }; } /* candidate rational roots p/q for an integer-coefficient array c (const term nonzero) */ function ratRootCandidates(c) { var lead = Math.abs(c[c.length - 1]), con = Math.abs(c[0]); var ps = divisors(con), qs = divisors(lead), out = [], seen = {}, i, j; for (i = 0; i < ps.length; i++) { for (j = 0; j < qs.length; j++) { var k1 = ps[i] + '/' + qs[j]; if (!seen[k1]) { seen[k1] = 1; out.push(fred(ps[i], qs[j])); } var k2 = (-ps[i]) + '/' + qs[j]; if (!seen[k2]) { seen[k2] = 1; out.push(fred(-ps[i], qs[j])); } } } return out; } function intRootsOfPoly(p) { var s = pscaleInt(p), c = s.p, roots = [], i; var deg = c.length - 1; if (deg <= 0) return { ok: true, roots: [] }; if (deg === 1) { if (c[1] === 0) return { ok: false }; return { ok: true, roots: [fred(-c[0], c[1])] }; } if (deg === 2) return intRootsDeg2(c); /* deg >= 3: peel off rational roots one by one, finish with the quadratic case */ var cc = c.map(fi), guard = 0; for (;;) { guard++; if (guard > 64) break; var dd = cc.length - 1; if (dd < 2) break; while (!(!(cc.length > 1) || !fis0(cc[0]))) { roots.push(fi(0)); cc = cc.slice(1); } dd = cc.length - 1; if (dd < 2) break; if (fis0(cc[dd])) break; var ci = pscaleInt(cc).p, found = null, cands = ratRootCandidates(ci), k; for (k = 0; k < cands.length; k++) { if (fis0(peval(cc, cands[k]))) { found = cands[k]; break; } } if (found === null) break; var dv = pdivLin(cc, found); if (!fis0(dv.rem)) break; roots.push(found); cc = dv.q; } var rest = cc.length - 1; if (rest === 2) { var q2 = intRootsDeg2(pscaleInt(cc).p); if (!q2.ok) return { ok: false, approx: q2.approx }; return { ok: true, roots: roots.concat(q2.roots) }; } if (rest === 1) { if (!fis0(cc[1])) roots.push(fdiv(fneg(cc[0]), cc[1])); return { ok: true, roots: roots }; } if (rest <= 0) return { ok: true, roots: roots }; return { ok: false }; } /* rationalSimplify(numP, denP) -> simplified form + domain. Domain roots are exact fractions when the denominator factors over rationals. */ function rationalSimplify(numP, denP) { if (pis0(denP)) return { ok: false, error: 'The denominator cannot be zero.' }; var dn = intRootsOfPoly(denP); if (!dn.ok) { return { ok: false, error: 'The denominator does not factor over the rationals.', approx: dn.approx || [] }; } var domain = dn.roots.slice(); var nq = pnorm(numP), dq = pnorm(denP), canceled = [], i; var seen = {}; for (i = 0; i < domain.length; i++) { var r = domain[i]; var key = r.n + '/' + r.d; if (seen[key]) continue; seen[key] = 1; for (;;) { var dvn = pdivLin(nq, r); if (!fis0(dvn.rem)) break; var dvd = pdivLin(dq, r); if (!fis0(dvd.rem)) break; nq = dvn.q; dq = dvd.q; canceled.push(r); } } var G = pgcdAll(nq, dq); if (G > 1) { nq = pnorm(nq.map(function (cc) { return fdiv(cc, fi(G)); })); dq = pnorm(dq.map(function (cc) { return fdiv(cc, fi(G)); })); } var lc = dq[dq.length - 1]; if (lc.n < 0) { nq = pmulC(nq, fi(-1)); dq = pmulC(dq, fi(-1)); } return { ok: true, num: nq, den: dq, canceled: canceled, domain: domain, constGcd: G, simplest: !(canceled.length !== 0 || G > 1) }; } /* (n1/d1) op (n2/d2); op in 'add','sub','mul','div'. Domain from ORIGINAL dens. */ function rationalOp(n1, d1, op, n2, d2) { var r1 = intRootsOfPoly(d1), r2 = intRootsOfPoly(d2); if (!r1.ok || !r2.ok) return { ok: false, error: 'A denominator does not factor over the rationals.' }; var domain = r1.roots.concat(r2.roots); var num, den; if (op === 'mul') { num = pmul(n1, n2); den = pmul(d1, d2); } else if (op === 'div') { if (pis0(n2)) return { ok: false, error: 'Cannot divide by zero.' }; var rn2 = intRootsOfPoly(n2); if (!rn2.ok) return { ok: false, error: 'The divisor does not factor over the rationals.' }; /* dividing by n2/d2 also excludes the zeros of n2 */ domain = domain.concat(rn2.roots); num = pmul(n1, d2); den = pmul(d1, n2); } else if (op === 'add') { num = padd(pmul(n1, d2), pmul(n2, d1)); den = pmul(d1, d2); } else if (op === 'sub') { num = psub(pmul(n1, d2), pmul(n2, d1)); den = pmul(d1, d2); } else return { ok: false, error: 'Unknown operation.' }; var s = rationalSimplify(num, den); if (!s.ok) return s; s.domain = domain; return s; } /* pretty domain text: "x ≠ −3" / "x ≠ 0, −1" */ function fmtDomain(domain) { if (!domain.length) return 'no excluded values'; var parts = domain.map(function (f) { return ffmt(f).replace('-', '−'); }); return 'x ≠ ' + parts.join(', '); } /* simplified rational as HTML: stacked fraction when needed */ function rationalHTML(num, den) { var denOne = (den.length === 1); if (denOne) denOne = feq(den[0], fi(1)); if (denOne) return pfmtHTML(num); return '' + pfmtHTML(num) + '' + pfmtHTML(den) + ''; } function rationalText(num, den) { var denOne = (den.length === 1); if (denOne) denOne = feq(den[0], fi(1)); if (denOne) return pfmtText(num); return '(' + pfmtText(num) + ')/(' + pfmtText(den) + ')'; }
/* ================= seeded RNG for generators ================= */ function mulberry32(seed) { var a = (seed >>> 0) || 1; return function () { a |= 0; a = (a + 0x6D2B79F5) | 0; var t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } function rint(rand, lo, hi) { return lo + Math.floor(rand() * (hi - lo + 1)); } function rpick(rand, arr) { return arr[Math.floor(rand() * arr.length)]; } function rpickNZ(rand, lo, hi) { var v = 0, guard = 0; while (v === 0) { if (guard >= 50) break; v = rint(rand, lo, hi); guard++; } return v === 0 ? 1 : v; } function rshuffle(rand, arr) { var a = arr.slice(), i, j, t; for (i = a.length - 1; i > 0; i--) { j = Math.floor(rand() * (i + 1)); t = a[i]; a[i] = a[j]; a[j] = t; } return a; }
return { igcd: igcd, fred: fred, fi: fi, fadd: fadd, fsub: fsub, fmul: fmul, fdiv: fdiv, fneg: fneg, feq: feq, fis0: fis0, ffmt: ffmt, fparse: fparse, pnorm: pnorm, pzero: pzero, pconst: pconst, pconstF: pconstF, pvar: pvar, pdeg: pdeg, pis0: pis0, padd: padd, psub: psub, pmul: pmul, pmulC: pmulC, ppow: ppow, peq: peq, peval: peval, pdivLin: pdivLin, pscaleInt: pscaleInt, pgcdAll: pgcdAll, parsePoly: parsePoly, parseRational: parseRational, parseDomainList: parseDomainList, domainSetEq: domainSetEq, pfmtText: pfmtText, pfmtHTML: pfmtHTML, linFactorHTML: linFactorHTML, escHTML: escHTML, divisors: divisors, factorQuad: factorQuad, factorQuadVerify: factorQuadVerify, intRootsOfPoly: intRootsOfPoly, rationalSimplify: rationalSimplify, rationalOp: rationalOp, fmtDomain: fmtDomain, rationalHTML: rationalHTML, rationalText: rationalText, mulberry32: mulberry32, rint: rint, rpick: rpick, rpickNZ: rpickNZ, rshuffle: rshuffle }; });
/*__TOOL_SRC__*/ /* Tool JS: factoring-gcf (key gcf). Pure compute between TOOL_CORE markers, DOM glue outside. Expects global FiboPoly. Double-ampersand-free. */ 'use strict'; /*__TOOL_CORE__*/ var GcfTool = (function () { var FP = FiboPoly; function fi(k) { return FP.fi(k); } function igcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b) { var t = a % b; a = b; b = t; } return a || 1; } function frac2(n, d) { var g = igcd(n, d); return { n: n / g, d: d / g }; } /* GCF monomial of an integer-coefficient poly */ function gcfOfPoly(p) { var g = 0, d = 99, i; for (i = 0; i < p.length; i++) { if (!FP.fis0(p[i])) { g = igcd(g, p[i].n); if (i < d) d = i; } } if (d === 99) d = 0; var arr = [], k; for (k = 0; k < d; k++) arr.push(fi(0)); arr.push(fi(g)); return FP.pnorm(arr); } /* exact division of integer poly p by its GCF monomial g */ function divByGcf(p, g) { var gd = FP.pdeg(g), gc = g[gd].n, rr = [], i; for (i = 0; i < p.length; i++) { if (i < gd) { if (!FP.fis0(p[i])) return null; } else { rr.push(frac2(p[i].n, gc)); } } return FP.pnorm(rr); } function termCount(p) { var n = 0, i; for (i = 0; i < p.length; i++) if (!FP.fis0(p[i])) n++; return n; } function gcfMonomials(m1T, m2T) { var m1 = FP.parsePoly(m1T), m2 = FP.parsePoly(m2T); if (!m1) return { ok: false, error: 'Could not read the first monomial.' }; if (!m2) return { ok: false, error: 'Could not read the second monomial.' }; if (termCount(m1) !== 1) return { ok: false, error: 'The first input is not a monomial.' }; if (termCount(m2) !== 1) return { ok: false, error: 'The second input is not a monomial.' }; var c1 = m1[FP.pdeg(m1)].n, c2 = m2[FP.pdeg(m2)].n; var d1 = FP.pdeg(m1), d2 = FP.pdeg(m2); var g = igcd(c1, c2), d = d1 < d2 ? d1 : d2; var ans = FP.pnorm((function () { var arr = [], k; for (k = 0; k < d; k++) arr.push(fi(0)); arr.push(fi(g)); return arr; })()); return { ok: true, answerText: FP.pfmtText(ans), steps: [ 'Coefficients: gcd(' + Math.abs(c1) + ', ' + Math.abs(c2) + ') = ' + g + '.', 'Variables: min(x^' + d1 + ', x^' + d2 + ') = x^' + d + ' (smaller exponent wins).', 'GCF = ' + FP.pfmtText(ans) + '.' ] }; } function factorGCF(pT) { var p = FP.parsePoly(pT); if (!p) return { ok: false, error: 'Could not read the polynomial.' }; var g = gcfOfPoly(p); if (FP.pdeg(g) === 0) { if (g[0].n === 1) { return { ok: true, answerText: FP.pfmtText(p), steps: [ 'gcd of the coefficients is 1 and no x divides every term.', 'The GCF is 1 — nothing to factor out.', 'This polynomial is already fully factored (over the integers).' ] }; } } var rest = divByGcf(p, g); if (!rest) return { ok: false, error: 'Internal error dividing by the GCF.' }; var gT = FP.pfmtText(g), rT = FP.pfmtText(rest); return { ok: true, answerText: gT + '(' + rT + ')', steps: [ 'GCF of all terms: ' + gT + ' (gcd of coefficients, smallest x-exponent).', 'Divide each term by ' + gT + ': ' + rT + '.', 'Factored: ' + gT + '(' + rT + ').', 'Check: ' + gT + ' x ' + rT + ' multiplies back to ' + FP.pfmtText(p) + '.' ] }; } function groupFactor(pT) { var p = FP.parsePoly(pT); if (!p) return { ok: false, error: 'Could not read the polynomial.' }; var terms = [], i; for (i = 0; i < p.length; i++) { if (!FP.fis0(p[i])) { var arr = [], k; for (k = 0; k < i; k++) arr.push(fi(0)); arr.push({ n: p[i].n, d: p[i].d }); terms.push(FP.pnorm(arr)); } } if (terms.length !== 4) { return { ok: false, error: 'Grouping needs exactly four terms (got ' + terms.length + ').' }; } var pairings = [[[0, 1], [2, 3]], [[0, 2], [1, 3]], [[0, 3], [1, 2]]]; for (var pi = 0; pi < pairings.length; pi++) { var A = pairings[pi][0], Bc = pairings[pi][1]; var pair1 = FP.padd(terms[A[0]], terms[A[1]]); var pair2 = FP.padd(terms[Bc[0]], terms[Bc[1]]); var g1 = gcfOfPoly(pair1), g2 = gcfOfPoly(pair2); var h1 = divByGcf(pair1, g1), h2 = divByGcf(pair2, g2); if (!h1 || !h2) continue; if (FP.peq(h1, h2)) { var outer = FP.padd(g1, g2); var oT = FP.pfmtText(outer), hT = FP.pfmtText(h1); return { ok: true, answerText: '(' + oT + ')(' + hT + ')', steps: [ 'Pair the terms: (' + FP.pfmtText(pair1) + ') + (' + FP.pfmtText(pair2) + ').', 'Factor each pair: ' + FP.pfmtText(g1) + '(' + hT + ') + ' + FP.pfmtText(g2) + '(' + hT + ').', 'Common binomial (' + hT + ') appears — factor it out.', 'Result: (' + oT + ')(' + hT + ').', 'Check: multiply back to get ' + FP.pfmtText(p) + '.' ] }; } } return { ok: false, error: 'No pairing produced a common binomial — this polynomial may not factor by grouping.' }; } return { gcfMonomials: gcfMonomials, factorGCF: factorGCF, groupFactor: groupFactor }; })(); /*__TOOL_CORE_END__*/ /* DOM glue */ (function () { 'use strict'; function esc(s) { return String(s).replace(/\x26/g, '&').replace(//g, '>'); } function mathify(s) { return esc(s).replace(/x\^(\d+)/g, 'x$1'); } function show(errId, resId, ansId, stepsId, r) { var err = document.getElementById(errId); var res = document.getElementById(resId); if (!r.ok) { err.textContent = r.error; res.style.display = 'none'; return; } err.textContent = ''; res.style.display = 'block'; document.getElementById(ansId).innerHTML = '' + mathify(r.answerText) + ''; var ol = document.getElementById(stepsId); ol.innerHTML = ''; r.steps.forEach(function (st) { var li = document.createElement('li'); li.innerHTML = '' + mathify(st) + ''; ol.appendChild(li); }); } function bind() { var tabs = document.querySelectorAll('#fiboToolTabs .ttab'); var panels = document.querySelectorAll('.fibo-toolwrap .panel'); tabs.forEach(function (t) { t.addEventListener('click', function () { tabs.forEach(function (x) { x.classList.remove('active'); }); panels.forEach(function (x) { x.classList.remove('active'); }); t.classList.add('active'); document.getElementById(t.getAttribute('data-tab')).classList.add('active'); }); }); document.getElementById('calc1').addEventListener('click', function () { show('err1', 'res1', 'ans1', 'steps1', GcfTool.gcfMonomials(document.getElementById('gm1').value, document.getElementById('gm2').value)); }); document.getElementById('calc2').addEventListener('click', function () { show('err2', 'res2', 'ans2', 'steps2', GcfTool.factorGCF(document.getElementById('fp').value)); }); document.getElementById('calc3').addEventListener('click', function () { show('err3', 'res3', 'ans3', 'steps3', GcfTool.groupFactor(document.getElementById('gp').value)); }); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind(); })();
/*__TOOL_SRC_END__*/