@typester/big-rational
Version:
BigRational is a ~1kB arbitrary precision rational number library powered by the native BigInt type.
179 lines (177 loc) • 5.88 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
BigRational: () => BigRational
});
module.exports = __toCommonJS(src_exports);
var re = (() => {
const inner = "[0-9]+(?:[.][0-9]*)?";
const nonNegativeDecimal = `(${inner})`;
const decimal = `(-?${inner})`;
const ws = "\\s*";
const make = (parts) => new RegExp(["^", ...parts, "$"].join(ws));
return [
make([decimal]),
make([decimal, "/", nonNegativeDecimal]),
make([decimal, nonNegativeDecimal, "/", nonNegativeDecimal])
];
})();
var BigRational = class _BigRational {
#numerator;
#denominator;
// Creates a new BigRational without simplifying the fraction or checking that
// the denominator is positive.
constructor(numerator, denominator) {
this.#numerator = numerator;
this.#denominator = denominator;
}
static from(numerator, denominator) {
if (denominator === 0n) throw new Error("denominator cannot be zero");
if (denominator === 1n) return new _BigRational(numerator, 1n);
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
}
const gcd_ = gcd(abs(numerator), denominator);
return new _BigRational(numerator / gcd_, denominator / gcd_);
}
static fromString(string) {
const match0 = string.match(re[0]);
if (match0) {
return parseOne(match0[1]);
}
const match1 = string.match(re[1]);
if (match1) {
return parseOne(match1[1]).divide(parseOne(match1[2]));
}
const match2 = string.match(re[2]);
if (match2) {
const whole = parseOne(match2[1]);
const fraction = parseOne(match2[2]).divide(parseOne(match2[3]));
return whole.#numerator >= 0n ? whole.add(fraction) : whole.subtract(fraction);
}
throw new Error(`Invalid BigRational: ${string}`);
}
numerator() {
return this.#numerator;
}
denominator() {
return this.#denominator;
}
add(other) {
return _BigRational.from(
this.#numerator * other.#denominator + other.#numerator * this.#denominator,
this.#denominator * other.#denominator
);
}
subtract(other) {
return _BigRational.from(
this.#numerator * other.#denominator - other.#numerator * this.#denominator,
this.#denominator * other.#denominator
);
}
multiply(other) {
return _BigRational.from(
this.#numerator * other.#numerator,
this.#denominator * other.#denominator
);
}
divide(other) {
return _BigRational.from(
this.#numerator * other.#denominator,
this.#denominator * other.#numerator
);
}
compare(other) {
const a = this.#numerator * other.#denominator;
const b = other.#numerator * this.#denominator;
return a === b ? 0 : a > b ? 1 : -1;
}
power(n) {
return _BigRational.from(this.#numerator ** n, this.#denominator ** n);
}
modulo(other) {
return this.subtract(
other.multiply(new _BigRational(this.divide(other).floor(), 1n))
);
}
// Round towards negative infinity.
floor() {
return (this.#numerator >= 0 ? this.#numerator : this.#numerator - this.#denominator + 1n) / this.#denominator;
}
// Round towards positive infinity.
ceil() {
return (this.#numerator >= 0 ? this.#numerator + this.#denominator - 1n : this.#numerator) / this.#denominator;
}
// Round towards zero.
truncate() {
return this.#numerator / this.#denominator;
}
negate() {
return new _BigRational(-this.#numerator, this.#denominator);
}
inverse() {
if (this.#numerator === 0n) throw new Error("cannot take inverse of zero");
return this.#numerator < 0n ? new _BigRational(-this.#denominator, -this.#numerator) : new _BigRational(this.#denominator, this.#numerator);
}
abs() {
return this.#numerator < 0n ? this.negate() : this;
}
toNumberApproximate() {
const whole = this.#numerator / this.#denominator;
const numerator = this.#numerator % this.#denominator;
return Number(whole) + Number(numerator) / Number(this.#denominator);
}
toFractionString() {
return this.#denominator === 1n ? `${this.#numerator}` : `${this.#numerator}/${this.#denominator}`;
}
toMixedString() {
const whole = this.#numerator / this.#denominator;
let numerator = this.#numerator % this.#denominator;
if (whole < 0) numerator = -numerator;
return numerator === 0n ? `${whole}` : whole === 0n ? `${numerator}/${this.#denominator}` : `${whole} ${numerator}/${this.#denominator}`;
}
toString() {
return this.toFractionString();
}
};
var abs = (a) => a >= 0n ? a : -a;
var gcd = (a, b) => {
while (b !== 0n) {
const t = b;
b = a % b;
a = t;
}
return a;
};
var parseOne = (string) => {
const indexOfDot = string.indexOf(".");
if (indexOfDot === -1) {
return BigRational.from(BigInt(string), 1n);
}
const before = string.slice(0, indexOfDot);
const after = string.slice(indexOfDot + 1);
return BigRational.from(BigInt(before + after), 10n ** BigInt(after.length));
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BigRational
});