| 'use strict'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| if (typeof BigInt === 'undefined') BigInt = function (n) { if (isNaN(n)) throw new Error(""); return n; }; |
|
|
| const C_ZERO = BigInt(0); |
| const C_ONE = BigInt(1); |
| const C_TWO = BigInt(2); |
| const C_THREE = BigInt(3); |
| const C_FIVE = BigInt(5); |
| const C_TEN = BigInt(10); |
| const MAX_INTEGER = BigInt(Number.MAX_SAFE_INTEGER); |
|
|
| |
| |
| |
| const MAX_CYCLE_LEN = 2000; |
|
|
| |
| const P = { |
| "s": C_ONE, |
| "n": C_ZERO, |
| "d": C_ONE |
| }; |
|
|
| function assign(n, s) { |
|
|
| try { |
| n = BigInt(n); |
| } catch (e) { |
| throw InvalidParameter(); |
| } |
| return n * s; |
| } |
|
|
| function ifloor(x) { |
| return typeof x === 'bigint' ? x : Math.floor(x); |
| } |
|
|
| |
| function newFraction(n, d) { |
|
|
| if (d === C_ZERO) { |
| throw DivisionByZero(); |
| } |
|
|
| const f = Object.create(Fraction.prototype); |
| f["s"] = n < C_ZERO ? -C_ONE : C_ONE; |
|
|
| n = n < C_ZERO ? -n : n; |
|
|
| const a = gcd(n, d); |
|
|
| f["n"] = n / a; |
| f["d"] = d / a; |
| return f; |
| } |
|
|
| const FACTORSTEPS = [C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO, C_TWO * C_TWO, C_TWO * C_THREE, C_TWO, C_TWO * C_THREE]; |
| function factorize(n) { |
|
|
| const factors = Object.create(null); |
| if (n <= C_ONE) { |
| factors[n] = C_ONE; |
| return factors; |
| } |
|
|
| const add = (p) => { factors[p] = (factors[p] || C_ZERO) + C_ONE; }; |
|
|
| while (n % C_TWO === C_ZERO) { add(C_TWO); n /= C_TWO; } |
| while (n % C_THREE === C_ZERO) { add(C_THREE); n /= C_THREE; } |
| while (n % C_FIVE === C_ZERO) { add(C_FIVE); n /= C_FIVE; } |
|
|
| |
| |
| for (let si = 0, p = C_TWO + C_FIVE; p * p <= n;) { |
| while (n % p === C_ZERO) { add(p); n /= p; } |
| p += FACTORSTEPS[si]; |
| si = (si + 1) & 7; |
| } |
| if (n > C_ONE) add(n); |
| return factors; |
| } |
|
|
| const parse = function (p1, p2) { |
|
|
| let n = C_ZERO, d = C_ONE, s = C_ONE; |
|
|
| if (p1 === undefined || p1 === null) { |
| |
| } else if (p2 !== undefined) { |
|
|
| if (typeof p1 === "bigint") { |
| n = p1; |
| } else if (isNaN(p1)) { |
| throw InvalidParameter(); |
| } else if (p1 % 1 !== 0) { |
| throw NonIntegerParameter(); |
| } else { |
| n = BigInt(p1); |
| } |
|
|
| if (typeof p2 === "bigint") { |
| d = p2; |
| } else if (isNaN(p2)) { |
| throw InvalidParameter(); |
| } else if (p2 % 1 !== 0) { |
| throw NonIntegerParameter(); |
| } else { |
| d = BigInt(p2); |
| } |
|
|
| s = n * d; |
|
|
| } else if (typeof p1 === "object") { |
| if ("d" in p1 && "n" in p1) { |
| n = BigInt(p1["n"]); |
| d = BigInt(p1["d"]); |
| if ("s" in p1) |
| n *= BigInt(p1["s"]); |
| } else if (0 in p1) { |
| n = BigInt(p1[0]); |
| if (1 in p1) |
| d = BigInt(p1[1]); |
| } else if (typeof p1 === "bigint") { |
| n = p1; |
| } else { |
| throw InvalidParameter(); |
| } |
| s = n * d; |
| } else if (typeof p1 === "number") { |
|
|
| if (isNaN(p1)) { |
| throw InvalidParameter(); |
| } |
|
|
| if (p1 < 0) { |
| s = -C_ONE; |
| p1 = -p1; |
| } |
|
|
| if (p1 % 1 === 0) { |
| n = BigInt(p1); |
| } else { |
|
|
| let z = 1; |
|
|
| let A = 0, B = 1; |
| let C = 1, D = 1; |
|
|
| let N = 10000000; |
|
|
| if (p1 >= 1) { |
| z = 10 ** Math.floor(1 + Math.log10(p1)); |
| p1 /= z; |
| } |
|
|
| |
|
|
| while (B <= N && D <= N) { |
| let M = (A + C) / (B + D); |
|
|
| if (p1 === M) { |
| if (B + D <= N) { |
| n = A + C; |
| d = B + D; |
| } else if (D > B) { |
| n = C; |
| d = D; |
| } else { |
| n = A; |
| d = B; |
| } |
| break; |
|
|
| } else { |
|
|
| if (p1 > M) { |
| A += C; |
| B += D; |
| } else { |
| C += A; |
| D += B; |
| } |
|
|
| if (B > N) { |
| n = C; |
| d = D; |
| } else { |
| n = A; |
| d = B; |
| } |
| } |
| } |
| n = BigInt(n) * BigInt(z); |
| d = BigInt(d); |
| } |
|
|
| } else if (typeof p1 === "string") { |
|
|
| let ndx = 0; |
|
|
| let v = C_ZERO, w = C_ZERO, x = C_ZERO, y = C_ONE, z = C_ONE; |
|
|
| let match = p1.replace(/_/g, '').match(/\d+|./g); |
|
|
| if (match === null) |
| throw InvalidParameter(); |
|
|
| if (match[ndx] === '-') { |
| s = -C_ONE; |
| ndx++; |
| } else if (match[ndx] === '+') { |
| ndx++; |
| } |
|
|
| if (match.length === ndx + 1) { |
| w = assign(match[ndx++], s); |
| } else if (match[ndx + 1] === '.' || match[ndx] === '.') { |
|
|
| if (match[ndx] !== '.') { |
| v = assign(match[ndx++], s); |
| } |
| ndx++; |
|
|
| |
| if (ndx + 1 === match.length || match[ndx + 1] === '(' && match[ndx + 3] === ')' || match[ndx + 1] === "'" && match[ndx + 3] === "'") { |
| w = assign(match[ndx], s); |
| y = C_TEN ** BigInt(match[ndx].length); |
| ndx++; |
| } |
|
|
| |
| if (match[ndx] === '(' && match[ndx + 2] === ')' || match[ndx] === "'" && match[ndx + 2] === "'") { |
| x = assign(match[ndx + 1], s); |
| z = C_TEN ** BigInt(match[ndx + 1].length) - C_ONE; |
| ndx += 3; |
| } |
|
|
| } else if (match[ndx + 1] === '/' || match[ndx + 1] === ':') { |
| w = assign(match[ndx], s); |
| y = assign(match[ndx + 2], C_ONE); |
| ndx += 3; |
| } else if (match[ndx + 3] === '/' && match[ndx + 1] === ' ') { |
| v = assign(match[ndx], s); |
| w = assign(match[ndx + 2], s); |
| y = assign(match[ndx + 4], C_ONE); |
| ndx += 5; |
| } |
|
|
| if (match.length <= ndx) { |
| d = y * z; |
| s = |
| n = x + d * v + z * w; |
| } else { |
| throw InvalidParameter(); |
| } |
|
|
| } else if (typeof p1 === "bigint") { |
| n = p1; |
| s = p1; |
| d = C_ONE; |
| } else { |
| throw InvalidParameter(); |
| } |
|
|
| if (d === C_ZERO) { |
| throw DivisionByZero(); |
| } |
|
|
| P["s"] = s < C_ZERO ? -C_ONE : C_ONE; |
| P["n"] = n < C_ZERO ? -n : n; |
| P["d"] = d < C_ZERO ? -d : d; |
| }; |
|
|
| function modpow(b, e, m) { |
|
|
| let r = C_ONE; |
| for (; e > C_ZERO; b = (b * b) % m, e >>= C_ONE) { |
|
|
| if (e & C_ONE) { |
| r = (r * b) % m; |
| } |
| } |
| return r; |
| } |
|
|
| function cycleLen(n, d) { |
|
|
| for (; d % C_TWO === C_ZERO; |
| d /= C_TWO) { |
| } |
|
|
| for (; d % C_FIVE === C_ZERO; |
| d /= C_FIVE) { |
| } |
|
|
| if (d === C_ONE) |
| return C_ZERO; |
|
|
| |
| |
| |
| |
|
|
| let rem = C_TEN % d; |
| let t = 1; |
|
|
| for (; rem !== C_ONE; t++) { |
| rem = rem * C_TEN % d; |
|
|
| if (t > MAX_CYCLE_LEN) |
| return C_ZERO; |
| } |
| return BigInt(t); |
| } |
|
|
| function cycleStart(n, d, len) { |
|
|
| let rem1 = C_ONE; |
| let rem2 = modpow(C_TEN, len, d); |
|
|
| for (let t = 0; t < 300; t++) { |
| |
|
|
| if (rem1 === rem2) |
| return BigInt(t); |
|
|
| rem1 = rem1 * C_TEN % d; |
| rem2 = rem2 * C_TEN % d; |
| } |
| return 0; |
| } |
|
|
| function gcd(a, b) { |
|
|
| if (!a) |
| return b; |
| if (!b) |
| return a; |
|
|
| while (1) { |
| a %= b; |
| if (!a) |
| return b; |
| b %= a; |
| if (!b) |
| return a; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| function Fraction(a, b) { |
|
|
| parse(a, b); |
|
|
| if (this instanceof Fraction) { |
| a = gcd(P["d"], P["n"]); |
| this["s"] = P["s"]; |
| this["n"] = P["n"] / a; |
| this["d"] = P["d"] / a; |
| } else { |
| return newFraction(P['s'] * P['n'], P['d']); |
| } |
| } |
|
|
| const DivisionByZero = function () { return new Error("Division by Zero"); }; |
| const InvalidParameter = function () { return new Error("Invalid argument"); }; |
| const NonIntegerParameter = function () { return new Error("Parameters must be integer"); }; |
|
|
| Fraction.prototype = { |
|
|
| "s": C_ONE, |
| "n": C_ZERO, |
| "d": C_ONE, |
|
|
| |
| |
| |
| |
| |
| "abs": function () { |
|
|
| return newFraction(this["n"], this["d"]); |
| }, |
|
|
| |
| |
| |
| |
| |
| "neg": function () { |
|
|
| return newFraction(-this["s"] * this["n"], this["d"]); |
| }, |
|
|
| |
| |
| |
| |
| |
| "add": function (a, b) { |
|
|
| parse(a, b); |
| return newFraction( |
| this["s"] * this["n"] * P["d"] + P["s"] * this["d"] * P["n"], |
| this["d"] * P["d"] |
| ); |
| }, |
|
|
| |
| |
| |
| |
| |
| "sub": function (a, b) { |
|
|
| parse(a, b); |
| return newFraction( |
| this["s"] * this["n"] * P["d"] - P["s"] * this["d"] * P["n"], |
| this["d"] * P["d"] |
| ); |
| }, |
|
|
| |
| |
| |
| |
| |
| "mul": function (a, b) { |
|
|
| parse(a, b); |
| return newFraction( |
| this["s"] * P["s"] * this["n"] * P["n"], |
| this["d"] * P["d"] |
| ); |
| }, |
|
|
| |
| |
| |
| |
| |
| "div": function (a, b) { |
|
|
| parse(a, b); |
| return newFraction( |
| this["s"] * P["s"] * this["n"] * P["d"], |
| this["d"] * P["n"] |
| ); |
| }, |
|
|
| |
| |
| |
| |
| |
| "clone": function () { |
| return newFraction(this['s'] * this['n'], this['d']); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| "mod": function (a, b) { |
|
|
| if (a === undefined) { |
| return newFraction(this["s"] * this["n"] % this["d"], C_ONE); |
| } |
|
|
| parse(a, b); |
| if (C_ZERO === P["n"] * this["d"]) { |
| throw DivisionByZero(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return newFraction( |
| this["s"] * (P["d"] * this["n"]) % (P["n"] * this["d"]), |
| P["d"] * this["d"]); |
| }, |
|
|
| |
| |
| |
| |
| |
| "gcd": function (a, b) { |
|
|
| parse(a, b); |
|
|
| |
| |
|
|
| return newFraction(gcd(P["n"], this["n"]) * gcd(P["d"], this["d"]), P["d"] * this["d"]); |
| }, |
|
|
| |
| |
| |
| |
| |
| "lcm": function (a, b) { |
|
|
| parse(a, b); |
|
|
| |
| |
|
|
| if (P["n"] === C_ZERO && this["n"] === C_ZERO) { |
| return newFraction(C_ZERO, C_ONE); |
| } |
| return newFraction(P["n"] * this["n"], gcd(P["n"], this["n"]) * gcd(P["d"], this["d"])); |
| }, |
|
|
| |
| |
| |
| |
| |
| "inverse": function () { |
| return newFraction(this["s"] * this["d"], this["n"]); |
| }, |
|
|
| |
| |
| |
| |
| |
| "pow": function (a, b) { |
|
|
| parse(a, b); |
|
|
| |
|
|
| if (P['d'] === C_ONE) { |
|
|
| if (P['s'] < C_ZERO) { |
| return newFraction((this['s'] * this["d"]) ** P['n'], this["n"] ** P['n']); |
| } else { |
| return newFraction((this['s'] * this["n"]) ** P['n'], this["d"] ** P['n']); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if (this['s'] < C_ZERO) return null; |
|
|
| |
| let N = factorize(this['n']); |
| let D = factorize(this['d']); |
|
|
| |
| let n = C_ONE; |
| let d = C_ONE; |
| for (let k in N) { |
| if (k === '1') continue; |
| if (k === '0') { |
| n = C_ZERO; |
| break; |
| } |
| N[k] *= P['n']; |
|
|
| if (N[k] % P['d'] === C_ZERO) { |
| N[k] /= P['d']; |
| } else return null; |
| n *= BigInt(k) ** N[k]; |
| } |
|
|
| for (let k in D) { |
| if (k === '1') continue; |
| D[k] *= P['n']; |
|
|
| if (D[k] % P['d'] === C_ZERO) { |
| D[k] /= P['d']; |
| } else return null; |
| d *= BigInt(k) ** D[k]; |
| } |
|
|
| if (P['s'] < C_ZERO) { |
| return newFraction(d, n); |
| } |
| return newFraction(n, d); |
| }, |
|
|
| |
| |
| |
| |
| |
| "log": function (a, b) { |
|
|
| parse(a, b); |
|
|
| if (this['s'] <= C_ZERO || P['s'] <= C_ZERO) return null; |
|
|
| const allPrimes = Object.create(null); |
|
|
| const baseFactors = factorize(P['n']); |
| const T1 = factorize(P['d']); |
|
|
| const numberFactors = factorize(this['n']); |
| const T2 = factorize(this['d']); |
|
|
| for (const prime in T1) { |
| baseFactors[prime] = (baseFactors[prime] || C_ZERO) - T1[prime]; |
| } |
| for (const prime in T2) { |
| numberFactors[prime] = (numberFactors[prime] || C_ZERO) - T2[prime]; |
| } |
|
|
| for (const prime in baseFactors) { |
| if (prime === '1') continue; |
| allPrimes[prime] = true; |
| } |
| for (const prime in numberFactors) { |
| if (prime === '1') continue; |
| allPrimes[prime] = true; |
| } |
|
|
| let retN = null; |
| let retD = null; |
|
|
| |
| for (const prime in allPrimes) { |
|
|
| const baseExponent = baseFactors[prime] || C_ZERO; |
| const numberExponent = numberFactors[prime] || C_ZERO; |
|
|
| if (baseExponent === C_ZERO) { |
| if (numberExponent !== C_ZERO) { |
| return null; |
| } |
| continue; |
| } |
|
|
| |
| let curN = numberExponent; |
| let curD = baseExponent; |
|
|
| |
| const gcdValue = gcd(curN, curD); |
| curN /= gcdValue; |
| curD /= gcdValue; |
|
|
| |
| if (retN === null && retD === null) { |
| retN = curN; |
| retD = curD; |
| } else if (curN * retD !== retN * curD) { |
| return null; |
| } |
| } |
|
|
| return retN !== null && retD !== null |
| ? newFraction(retN, retD) |
| : null; |
| }, |
|
|
| |
| |
| |
| |
| |
| "equals": function (a, b) { |
|
|
| parse(a, b); |
| return this["s"] * this["n"] * P["d"] === P["s"] * P["n"] * this["d"]; |
| }, |
|
|
| |
| |
| |
| |
| |
| "lt": function (a, b) { |
|
|
| parse(a, b); |
| return this["s"] * this["n"] * P["d"] < P["s"] * P["n"] * this["d"]; |
| }, |
|
|
| |
| |
| |
| |
| |
| "lte": function (a, b) { |
|
|
| parse(a, b); |
| return this["s"] * this["n"] * P["d"] <= P["s"] * P["n"] * this["d"]; |
| }, |
|
|
| |
| |
| |
| |
| |
| "gt": function (a, b) { |
|
|
| parse(a, b); |
| return this["s"] * this["n"] * P["d"] > P["s"] * P["n"] * this["d"]; |
| }, |
|
|
| |
| |
| |
| |
| |
| "gte": function (a, b) { |
|
|
| parse(a, b); |
| return this["s"] * this["n"] * P["d"] >= P["s"] * P["n"] * this["d"]; |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| "compare": function (a, b) { |
|
|
| parse(a, b); |
| let t = this["s"] * this["n"] * P["d"] - P["s"] * P["n"] * this["d"]; |
|
|
| return (C_ZERO < t) - (t < C_ZERO); |
| }, |
|
|
| |
| |
| |
| |
| |
| "ceil": function (places) { |
|
|
| places = C_TEN ** BigInt(places || 0); |
|
|
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + |
| (places * this["n"] % this["d"] > C_ZERO && this["s"] >= C_ZERO ? C_ONE : C_ZERO), |
| places); |
| }, |
|
|
| |
| |
| |
| |
| |
| "floor": function (places) { |
|
|
| places = C_TEN ** BigInt(places || 0); |
|
|
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) - |
| (places * this["n"] % this["d"] > C_ZERO && this["s"] < C_ZERO ? C_ONE : C_ZERO), |
| places); |
| }, |
|
|
| |
| |
| |
| |
| |
| "round": function (places) { |
|
|
| places = C_TEN ** BigInt(places || 0); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| return newFraction(ifloor(this["s"] * places * this["n"] / this["d"]) + |
| this["s"] * ((this["s"] >= C_ZERO ? C_ONE : C_ZERO) + C_TWO * (places * this["n"] % this["d"]) > this["d"] ? C_ONE : C_ZERO), |
| places); |
| }, |
|
|
| |
| |
| |
| |
| |
| "roundTo": function (a, b) { |
|
|
| |
| |
| |
| |
| |
| |
|
|
| parse(a, b); |
|
|
| const n = this['n'] * P['d']; |
| const d = this['d'] * P['n']; |
| const r = n % d; |
|
|
| |
| let k = ifloor(n / d); |
| if (r + r >= d) { |
| k++; |
| } |
| return newFraction(this['s'] * k * P['n'], P['d']); |
| }, |
|
|
| |
| |
| |
| |
| |
| "divisible": function (a, b) { |
|
|
| parse(a, b); |
| if (P['n'] === C_ZERO) return false; |
| return (this['n'] * P['d']) % (P['n'] * this['d']) === C_ZERO; |
| }, |
|
|
| |
| |
| |
| |
| |
| 'valueOf': function () { |
| |
| return Number(this['s'] * this['n']) / Number(this['d']); |
| |
| }, |
|
|
| |
| |
| |
| |
| |
| 'toString': function (dec = 15) { |
|
|
| let N = this["n"]; |
| let D = this["d"]; |
|
|
| let cycLen = cycleLen(N, D); |
| let cycOff = cycleStart(N, D, cycLen); |
|
|
| let str = this['s'] < C_ZERO ? "-" : ""; |
|
|
| |
| str += ifloor(N / D); |
|
|
| N %= D; |
| N *= C_TEN; |
|
|
| if (N) |
| str += "."; |
|
|
| if (cycLen) { |
|
|
| for (let i = cycOff; i--;) { |
| str += ifloor(N / D); |
| N %= D; |
| N *= C_TEN; |
| } |
| str += "("; |
| for (let i = cycLen; i--;) { |
| str += ifloor(N / D); |
| N %= D; |
| N *= C_TEN; |
| } |
| str += ")"; |
| } else { |
| for (let i = dec; N && i--;) { |
| str += ifloor(N / D); |
| N %= D; |
| N *= C_TEN; |
| } |
| } |
| return str; |
| }, |
|
|
| |
| |
| |
| |
| |
| 'toFraction': function (showMixed = false) { |
|
|
| let n = this["n"]; |
| let d = this["d"]; |
| let str = this['s'] < C_ZERO ? "-" : ""; |
|
|
| if (d === C_ONE) { |
| str += n; |
| } else { |
| const whole = ifloor(n / d); |
| if (showMixed && whole > C_ZERO) { |
| str += whole; |
| str += " "; |
| n %= d; |
| } |
|
|
| str += n; |
| str += '/'; |
| str += d; |
| } |
| return str; |
| }, |
|
|
| |
| |
| |
| |
| |
| 'toLatex': function (showMixed = false) { |
|
|
| let n = this["n"]; |
| let d = this["d"]; |
| let str = this['s'] < C_ZERO ? "-" : ""; |
|
|
| if (d === C_ONE) { |
| str += n; |
| } else { |
| const whole = ifloor(n / d); |
| if (showMixed && whole > C_ZERO) { |
| str += whole; |
| n %= d; |
| } |
|
|
| str += "\\frac{"; |
| str += n; |
| str += '}{'; |
| str += d; |
| str += '}'; |
| } |
| return str; |
| }, |
|
|
| |
| |
| |
| |
| |
| 'toContinued': function () { |
|
|
| let a = this['n']; |
| let b = this['d']; |
| const res = []; |
|
|
| while (b) { |
| res.push(ifloor(a / b)); |
| const t = a % b; |
| a = b; |
| b = t; |
| } |
| return res; |
| }, |
|
|
| "simplify": function (eps = 1e-3) { |
|
|
| |
| |
| |
|
|
| const ieps = BigInt(Math.ceil(1 / eps)); |
|
|
| const thisABS = this['abs'](); |
| const cont = thisABS['toContinued'](); |
|
|
| for (let i = 1; i < cont.length; i++) { |
|
|
| let s = newFraction(cont[i - 1], C_ONE); |
| for (let k = i - 2; k >= 0; k--) { |
| s = s['inverse']()['add'](cont[k]); |
| } |
|
|
| let t = s['sub'](thisABS); |
| if (t['n'] * ieps < t['d']) { |
| return s['mul'](this['s']); |
| } |
| } |
| return this; |
| } |
| }; |
| export { |
| Fraction as default, Fraction |
| }; |
|
|