UNPKG

big-rational

Version:

An arbitrary length rational number library for Javascript

292 lines (265 loc) 10.4 kB
; var bigRat = (function (bigInt) { "use strict"; function BigRational(num, denom) { // Alias properties kept for backwards compatability if (denom.isZero()) throw "Denominator cannot be 0."; this.numerator = this.num = num; this.denominator = this.denom = denom; } var gcd = bigInt.gcd, lcm = bigInt.lcm; function reduce(n, d) { var divisor = gcd(n, d), num = n.over(divisor), denom = d.over(divisor); if (denom.isNegative()) { return new BigRational(num.negate(), denom.negate()); } return new BigRational(num, denom); } BigRational.prototype.add = function (n, d) { var v = interpret(n, d), multiple = lcm(this.denom, v.denom), a = multiple.divide(this.denom), b = multiple.divide(v.denom); a = this.num.times(a); b = v.num.times(b); return reduce(a.add(b), multiple); }; BigRational.prototype.plus = BigRational.prototype.add; BigRational.prototype.subtract = function (n, d) { var v = interpret(n, d); return this.add(v.negate()); }; BigRational.prototype.minus = BigRational.prototype.subtract; BigRational.prototype.multiply = function (n, d) { var v = interpret(n, d); return reduce(this.num.times(v.num), this.denom.times(v.denom)); }; BigRational.prototype.times = BigRational.prototype.multiply; BigRational.prototype.divide = function (n, d) { var v = interpret(n, d); return reduce(this.num.times(v.denom), this.denom.times(v.num)); }; BigRational.prototype.over = BigRational.prototype.divide; BigRational.prototype.reciprocate = function () { return new BigRational(this.denom, this.num); }; BigRational.prototype.mod = function (n, d) { var v = interpret(n, d); return this.minus(v.times(this.over(v).floor())); }; BigRational.prototype.pow = function (n) { var v = bigInt(n); var num = this.num.pow(v), denom = this.denom.pow(v); return reduce(num, denom); }; BigRational.prototype.floor = function (toBigInt) { var divmod = this.num.divmod(this.denom), floor; if (divmod.remainder.isZero() || !divmod.quotient.isNegative()) { floor = divmod.quotient; } else floor = divmod.quotient.prev(); if (toBigInt) return floor; return new BigRational(floor, bigInt[1]); }; BigRational.prototype.ceil = function (toBigInt) { var divmod = this.num.divmod(this.denom), ceil; if (divmod.remainder.isZero() || divmod.quotient.isNegative()) { ceil = divmod.quotient; } else ceil = divmod.quotient.next(); if (toBigInt) return ceil; return new BigRational(ceil, bigInt[1]); }; BigRational.prototype.round = function (toBigInt) { return this.add(1, 2).floor(toBigInt); }; BigRational.prototype.compareAbs = function (n, d) { var v = interpret(n, d); if (this.denom.equals(v.denom)) { return this.num.compareAbs(v.num); } return this.num.times(v.denom).compareAbs(v.num.times(this.denom)); }; BigRational.prototype.compare = function (n, d) { var v = interpret(n, d); if (this.denom.equals(v.denom)) { return this.num.compare(v.num); } var comparison = this.denom.isNegative() === v.denom.isNegative() ? 1 : -1; return comparison * this.num.times(v.denom).compare(v.num.times(this.denom)); }; BigRational.prototype.compareTo = BigRational.prototype.compare; BigRational.prototype.equals = function (n, d) { return this.compare(n, d) === 0; }; BigRational.prototype.eq = BigRational.prototype.equals; BigRational.prototype.notEquals = function (n, d) { return this.compare(n, d) !== 0; }; BigRational.prototype.neq = BigRational.prototype.notEquals; BigRational.prototype.lesser = function (n, d) { return this.compare(n, d) < 0; }; BigRational.prototype.lt = BigRational.prototype.lesser; BigRational.prototype.lesserOrEquals = function (n, d) { return this.compare(n, d) <= 0; }; BigRational.prototype.leq = BigRational.prototype.lesserOrEquals; BigRational.prototype.greater = function (n, d) { return this.compare(n, d) > 0; }; BigRational.prototype.gt = BigRational.prototype.greater; BigRational.prototype.greaterOrEquals = function (n, d) { return this.compare(n, d) >= 0; }; BigRational.prototype.geq = BigRational.prototype.greaterOrEquals; BigRational.prototype.abs = function () { if (this.isPositive()) return this; return this.negate(); }; BigRational.prototype.negate = function () { if (this.denom.isNegative()) { return new BigRational(this.num, this.denom.negate()); } return new BigRational(this.num.negate(), this.denom); }; BigRational.prototype.isNegative = function () { return this.num.isNegative() !== this.denom.isNegative() && !this.num.isZero(); }; BigRational.prototype.isPositive = function () { return this.num.isNegative() === this.denom.isNegative() && !this.num.isZero(); }; BigRational.prototype.isZero = function () { return this.num.isZero(); }; BigRational.prototype.toDecimal = function (digits) { digits = typeof digits === "number" ? digits : 10; var n = this.num.divmod(this.denom); var intPart = n.quotient.abs().toString(); var remainder = parse(n.remainder.abs(), this.denom); var shiftedRemainder = remainder.times(bigInt("1e" + digits)); var decPart = shiftedRemainder.num.over(shiftedRemainder.denom).toString(); if (decPart.length < digits) { decPart = new Array(digits - decPart.length + 1).join("0") + decPart; } if (shiftedRemainder.num.mod(shiftedRemainder.denom).isZero()) { while (decPart.slice(-1) === "0") { decPart = decPart.slice(0, -1); } } if (digits < 1) decPart = ""; if (this.isNegative()) { intPart = "-" + intPart; } if (decPart === "") { return intPart; } return intPart + "." + decPart; }; BigRational.prototype.toString = function () { return String(this.num) + "/" + String(this.denom); }; BigRational.prototype.valueOf = function () { if (!isFinite(+this.num) || !isFinite(+this.denom)) { return +this.toDecimal(64); } return this.num / this.denom; }; function interpret(n, d) { return parse(n, d); } function parseDecimal(n) { var parts = n.split(/e/i); if (parts.length > 2) { throw new Error("Invalid input: too many 'e' tokens"); } if (parts.length > 1) { var isPositive = true; if (parts[1][0] === "-") { parts[1] = parts[1].slice(1); isPositive = false; } if (parts[1][0] === "+") { parts[1] = parts[1].slice(1); } var significand = parseDecimal(parts[0]); var exponent = new BigRational(bigInt(10).pow(parts[1]), bigInt[1]); if (isPositive) { return significand.times(exponent); } else { return significand.over(exponent); } } parts = n.trim().split("."); if (parts.length > 2) { throw new Error("Invalid input: too many '.' tokens"); } if (parts.length > 1) { var isNegative = parts[0][0] === '-'; if (isNegative) parts[0] = parts[0].slice(1); var intPart = new BigRational(bigInt(parts[0]), bigInt[1]); var length = parts[1].length; while (parts[1][0] === "0") { parts[1] = parts[1].slice(1); } var exp = "1" + Array(length + 1).join("0"); var decPart = reduce(bigInt(parts[1]), bigInt(exp)); intPart = intPart.add(decPart); if (isNegative) intPart = intPart.negate(); return intPart; } return new BigRational(bigInt(n), bigInt[1]); } function parse(a, b) { if (!a) { return new BigRational(bigInt(0), bigInt[1]); } if (b) { return reduce(bigInt(a), bigInt(b)); } if (bigInt.isInstance(a)) { return new BigRational(a, bigInt[1]); } if (a instanceof BigRational) return a; var num; var denom; var text = String(a); var texts = text.split("/"); if (texts.length > 2) { throw new Error("Invalid input: too many '/' tokens"); } if (texts.length > 1) { var parts = texts[0].split("_"); if (parts.length > 2) { throw new Error("Invalid input: too many '_' tokens"); } if (parts.length > 1) { var isPositive = parts[0][0] !== "-"; num = bigInt(parts[0]).times(texts[1]); if (isPositive) { num = num.add(parts[1]); } else { num = num.subtract(parts[1]); } denom = bigInt(texts[1]); return reduce(num, denom); } return reduce(bigInt(texts[0]), bigInt(texts[1])); } return parseDecimal(text); } parse.zero = parse(0); parse.one = parse(1); parse.minusOne = parse(-1); return parse; })(typeof bigInt !== "undefined" ? bigInt : require("big-integer")); if (typeof module !== "undefined") { if (module.hasOwnProperty("exports")) { module.exports = bigRat; } }