Skill: exponential-logarithmic
Exponential & Log Calculator
Evaluate b^x, solve b^x = v exactly, evaluate log_b(x), and solve log_b(x) = e — with the reasoning shown.
Evaluate b^x
Enter a base b and an integer exponent x (negatives allowed).
Solve b^x = v
Enter base b and value v. Solves exactly when v is an integer power of b.
Logarithms
Evaluate log_b(x), or solve log_b(x) = e.
/* ============================================================================ 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 }; });
/* ============================================================================ Alg2 — extra math core for wave-2 algebra skills (quadratics, radicals, rational exponents, functions, exponential/logarithmic). Pure JavaScript, no DOM. Expects global FiboPoly (FP). Double-ampersand-free by construction: every logical-and is written !( !a || !b ). ============================================================================ */ var Alg2 = (function () { 'use strict'; var FP = (typeof FiboPoly !== 'undefined') ? FiboPoly : null; function reqFP(){ if (!FP) throw new Error('Alg2 needs FiboPoly'); } function fi(k){ reqFP(); return FP.fi(k); } function fr(n, d){ reqFP(); return FP.fred(n, d); } function fadd(a, b){ reqFP(); return FP.fadd(a, b); } function fsub(a, b){ reqFP(); return FP.fsub(a, b); } function fmul(a, b){ reqFP(); return FP.fmul(a, b); } function fdiv(a, b){ reqFP(); return FP.fdiv(a, b); } function fneg(a){ reqFP(); return FP.fneg(a); } function feq(a, b){ reqFP(); return FP.feq(a, b); } function ffmt(f){ reqFP(); return FP.ffmt(f); }
/* ---------------- integers ---------------- */ function isSq(n){ if (n < 0) return false; var r = Math.round(Math.sqrt(n)); return r * r === n; } function isqrt(n){ var r = Math.round(Math.sqrt(n)); return (r * r === n) ? r : -1; } /* integer n-th root when exact, else -1 */ function iroot(n, k){ if (k <= 0) return -1; if (n < 0) { if (k % 2 === 0) return -1; var rr = iroot(-n, k); return rr < 0 ? -1 : -rr; } var r = Math.round(Math.pow(n, 1 / k)), i; for (i = -2; i <= 2; i++){ var t = r + i, p = 1, j; for (j = 0; j < k; j++) p *= t; if (p === n) return t; } return -1; } /* ---------------- quadratics: ax^2 + bx + c = 0 (integers) ---------------- */ function qdisc(a, b, c){ return b * b - 4 * a * c; } /* exact rational roots when the discriminant is a perfect square >= 0 */ function qsolve(a, b, c){ if (a === 0) return { kind: 'linear', root: fr(-c, b) }; var D = qdisc(a, b, c); if (D < 0) return { kind: 'none', D: D }; var s = isqrt(D); if (s < 0) return { kind: 'surd', D: D }; var r1 = fr(-b + s, 2 * a), r2 = fr(-b - s, 2 * a); if (D === 0) return { kind: 'double', D: D, r1: r1, r2: r1 }; return { kind: 'two', D: D, r1: r1, r2: r2 }; } /* vertex of y = ax^2 + bx + c as fractions; axis x = h */ function qvertex(a, b, c){ var h = fr(-b, 2 * a); var hh = fdiv(fi(-b), fi(2 * a)); var k = fadd(fadd(fmul(fi(a), fmul(hh, hh)), fmul(fi(b), hh)), fi(c)); return { h: hh, k: k }; } /* completing the square: returns vertex form data a(x-h)^2 + k */ function qCompleteSquare(a, b, c){ return qvertex(a, b, c); } /* factor form check: does (x-r1)(x-r2) expand to ax^2+bx+c up to leading coeff */ function qCheckRoots(a, b, c, r1, r2){ /* substitute each root: a r^2 + b r + c == 0 */ function sub(r){ var v = fadd(fadd(fmul(fi(a), fmul(r, r)), fmul(fi(b), r)), fi(c)); return v.n === 0; } var ok1 = sub(r1), ok2 = sub(r2); return !( !ok1 || !ok2 ); } /* quadratic formula display parts (for tool steps) */ function qFormulaParts(a, b, c){ var D = qdisc(a, b, c); return { a: a, b: b, c: c, D: D, sq: isSq(D) ? isqrt(D) : -1 }; } /* ---------------- rational exponents & radicals ---------------- */ /* exact value of base^(p/q) when it comes out rational; else null */ function ratPow(base, p, q){ if (q <= 0) return null; if (p === 0) return fi(1); var neg = p < 0, ap = Math.abs(p); var num = iroot(Math.pow(Math.abs(base), ap), q); if (base < 0) { if (q % 2 === 0) return null; var r2 = iroot(Math.pow(-base, ap), q); if (r2 < 0) return null; num = -r2; } else { if (num < 0) return null; } var v = fi(num); return neg ? fdiv(fi(1), v) : v; } /* simplify sqrt(n) -> {c, r} meaning c*sqrt(r), r squarefree */ function simpSqrt(n){ if (n < 0) return null; var c = 1, r = n, d; for (d = 2; d * d <= r; d++){ while (r % (d * d) === 0){ r = r / (d * d); c *= d; } } return { c: c, r: r }; } /* solve sqrt(a*x + b) = c (a != 0). Returns {kind:'one', x:frac} | {kind:'none'} */ function solveSqrtLin(a, b, c){ if (c < 0) return { kind: 'none', why: 'sqrt >= 0' }; var x = fr(c * c - b, a); /* verify in original: a*x + b >= 0 */ var inside = fadd(fmul(fi(a), x), fi(b)); if (inside.n < 0) return { kind: 'none', why: 'extraneous' }; return { kind: 'one', x: x }; } /* solve cbrt(a*x + b) = c -> always valid */ function solveCbrtLin(a, b, c){ return { kind: 'one', x: fr(c * c * c - b, a) }; } /* solve x + sqrt(x) style not needed; solve sqrt(x)+a = b */ function solveSqrtShift(a, b){ /* sqrt(x) = b - a ... caller handles */ return solveSqrtLin(1, 0, b - a); }
/* ---------------- rational equations ---------------- */ /* solve (x + p)/(x + q) = r (x != -q) */ function solveRat1(p, q, r){ if (r === 1) return { kind: 'none', why: 'identity-or-none' }; var x = fr(-(p + r * q), 1 - r); /* hmm: x + p = r(x + q) -> x - r x = r q - p -> x(1-r) = rq - p */ x = fr(r * q - p, 1 - r); if (feq(x, fi(-q))) return { kind: 'none', why: 'extraneous' }; return { kind: 'one', x: x }; } /* solve a/x = b (a != 0) */ function solveRatSimple(a, b){ if (b === 0) return { kind: 'none', why: 'a/x=0 impossible' }; return { kind: 'one', x: fr(a, b) }; }
/* ---------------- exponential / logarithmic ---------------- */ /* exact log_base(x) when x is an integer power of base; else null */ function logExact(base, x){ if (base <= 0 || base === 1 || x <= 0) return null; if (x === 1) return fi(0); var e = 0, v = 1; while (v < x){ v *= base; e++; if (e > 60) return null; } if (v === x) return fi(e); /* negative powers */ v = 1; e = 0; while (e > -60){ v = v / base; e--; if (Math.abs(v - x) < 1e-12) return fi(e); if (v < x) break; } return null; } /* exact base^x for integer x */ function expInt(base, x){ var r = 1, i; if (x >= 0){ for (i = 0; i < x; i++) r *= base; return fi(r); } for (i = 0; i < -x; i++) r /= base; return fr(1, Math.pow(base, -x)); } /* solve b^x = v exactly when v is an integer power of b; else null */ function solveExpInt(base, v){ var e = logExact(base, v); return e === null ? null : { kind: 'one', x: e }; } /* solve log_b(x) = e -> x = b^e (check domain x > 0) */ function solveLogEq(base, e){ var x = expInt(base, e); return { kind: 'one', x: x }; } /* condense/expand check helpers operate on strings in templates */
/* ---------------- functions ---------------- */ function funcEvalLinear(m, b, x){ return fadd(fmul(fi(m), x), fi(b)); } function funcEvalQuad(a, b, c, x){ return fadd(fadd(fmul(fi(a), fmul(x, x)), fmul(fi(b), x)), fi(c)); }
return { isSq: isSq, isqrt: isqrt, iroot: iroot, qdisc: qdisc, qsolve: qsolve, qvertex: qvertex, qCompleteSquare: qCompleteSquare, qCheckRoots: qCheckRoots, qFormulaParts: qFormulaParts, ratPow: ratPow, simpSqrt: simpSqrt, solveSqrtLin: solveSqrtLin, solveCbrtLin: solveCbrtLin, solveSqrtShift: solveSqrtShift, solveRat1: solveRat1, solveRatSimple: solveRatSimple, logExact: logExact, expInt: expInt, solveExpInt: solveExpInt, solveLogEq: solveLogEq, funcEvalLinear: funcEvalLinear, funcEvalQuad: funcEvalQuad, fi: fi, fr: fr, fadd: fadd, fsub: fsub, fmul: fmul, fdiv: fdiv, fneg: fneg, feq: feq, ffmt: ffmt }; })(); if (typeof module === 'undefined') { } else { if (module.exports) module.exports = Alg2; }
/*__TOOL_SRC__*/ /* Tool JS: exponential-logarithmic (key explog). Pure compute between TOOL_CORE markers, DOM glue outside. Expects globals FiboPoly, Alg2. Double-ampersand-free. */ 'use strict'; /*__TOOL_CORE__*/ var ExpLogTool = (function () { var FP = FiboPoly, A2 = Alg2; function num(t) { var v = parseFloat(String(t).trim()); return isNaN(v) ? null : v; } function isInt(x) { return Math.abs(x - Math.round(x)) < 1e-9; } function fmtN(x) { return String(Math.round(x * 1000000) / 1000000); } function evalPow(bT, xT) { var b = num(bT), x = num(xT); if (b === null || x === null) return { ok: false, error: 'Enter numbers for b and x.' }; if (!isInt(x)) return { ok: false, error: 'Integer exponents only here.' }; var xi = Math.round(x); var v = A2.expInt(b, xi); var steps = x >= 0 ? [b + '^' + xi + ': multiply ' + b + ' by itself ' + xi + ' times = ' + FP.ffmt(v) + '.'] : [b + '^' + xi + ' = 1/' + b + '^' + (-xi) + ' = ' + FP.ffmt(v) + '.']; return { ok: true, answerText: b + '^' + xi + ' = ' + FP.ffmt(v), steps: steps }; }
function solveExp(bT, vT) { var b = num(bT), v = num(vT); if (b === null || v === null) return { ok: false, error: 'Enter numbers for b and v.' }; if (b <= 0 || b === 1) return { ok: false, error: 'Base must be positive and not 1.' }; if (v <= 0) return { ok: false, error: 'b^x is always positive — no solution for v ≤ 0.' }; var sol = A2.solveExpInt(b, v); if (!sol) return { ok: true, answerText: 'x = log_' + b + '(' + v + ') (not an integer power)', steps: [ 'x = log_' + b + '(' + v + ').', v + ' is not an integer power of ' + b + ', so the exact integer answer does not exist.', 'A calculator gives x ≈ ' + fmtN(Math.log(v) / Math.log(b)) + '.' ] }; var e = sol.x.n / sol.x.d; return { ok: true, answerText: 'x = ' + FP.ffmt(sol.x), steps: [ 'Ask: "' + b + ' to which power is ' + v + '?"', v + ' = ' + b + '^' + FP.ffmt(sol.x) + ', so x = ' + FP.ffmt(sol.x) + '.', 'Check: ' + b + '^' + FP.ffmt(sol.x) + ' = ' + v + '.' ] }; } function evalLog(bT, xT) { var b = num(bT), x = num(xT); if (b === null || x === null) return { ok: false, error: 'Enter numbers for b and x.' }; if (b <= 0 || b === 1) return { ok: false, error: 'Base must be positive and not 1.' }; if (x <= 0) return { ok: false, error: 'log needs x > 0 — undefined here.' }; var e = A2.logExact(b, x); if (e === null) return { ok: true, answerText: 'log_' + b + '(' + x + ') ≈ ' + fmtN(Math.log(x) / Math.log(b)), steps: ['log_' + b + '(' + x + '): ' + x + ' is not an integer power of ' + b + '.', 'Approximate value ≈ ' + fmtN(Math.log(x) / Math.log(b)) + '.'] }; return { ok: true, answerText: 'log_' + b + '(' + x + ') = ' + FP.ffmt(e), steps: [ 'Ask: "' + b + ' to which power is ' + x + '?"', x + ' = ' + b + '^' + FP.ffmt(e) + ', so log_' + b + '(' + x + ') = ' + FP.ffmt(e) + '.' ] }; }
function solveLog(bT, eT) { var b = num(bT), e = num(eT); if (b === null || e === null) return { ok: false, error: 'Enter numbers for b and e.' }; if (b <= 0 || b === 1) return { ok: false, error: 'Base must be positive and not 1.' }; if (!isInt(e)) return { ok: false, error: 'Integer e only here.' }; var x = A2.solveLogEq(b, Math.round(e)); return { ok: true, answerText: 'x = ' + FP.ffmt(x.x), steps: [ 'Flip to exponential form: x = ' + b + '^' + e + '.', 'x = ' + FP.ffmt(x.x) + ' (positive, so the log domain is satisfied).' ] }; } return { evalPow: evalPow, solveExp: solveExp, evalLog: evalLog, solveLog: solveLog }; })(); /*__TOOL_CORE_END__*/ /* DOM glue */ (function () { 'use strict'; 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).textContent = r.answerText; var ol = document.getElementById(stepsId); ol.innerHTML = ''; r.steps.forEach(function (st) { var li = document.createElement('li'); li.textContent = 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', ExpLogTool.evalPow(document.getElementById('pb').value, document.getElementById('px').value)); }); document.getElementById('calc2').addEventListener('click', function () { show('err2', 'res2', 'ans2', 'steps2', ExpLogTool.solveExp(document.getElementById('sb').value, document.getElementById('sv').value)); }); document.getElementById('calc3').addEventListener('click', function () { show('err3', 'res3', 'ans3', 'steps3', ExpLogTool.evalLog(document.getElementById('lb').value, document.getElementById('lx').value)); }); document.getElementById('calc4').addEventListener('click', function () { show('err3', 'res3', 'ans3', 'steps3', ExpLogTool.solveLog(document.getElementById('lb').value, document.getElementById('le').value)); }); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind(); })(); /*__TOOL_SRC_END__*/