impl/typescript/rational.ts
1,727 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { gcdWide } from "./math_gcd_lcm.ts"; ← from math.gcd-lcm ^1.0.0 · built alongside by fune
import { type Rational } from "./math_rational_types.ts";
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
/**
* Reduce a wide fraction and bring it back into the exact number range. The
* arithmetic works on bigint products, which may be past 2^53, and only the
* reduced result has to fit.
*/
export function rationalFromWide(numerator: bigint, denominator: bigint): Rational {
if (denominator === 0n) {
throw new RangeError("denominator must not be zero");
}
const g = gcdWide(numerator, denominator);
let n = numerator / g;
let d = denominator / g;
if (d < 0n) {
n = -n;
d = -d;
}
if (n > MAX_SAFE || n < -MAX_SAFE || d > MAX_SAFE) {
throw new RangeError("rational overflow: the reduced result exceeds 2^53 - 1");
}
// Number(-0n) is 0, so a zero result never comes back as -0.
return { numerator: Number(n), denominator: Number(d) };
}
/** Build a reduced fraction with a positive denominator. */
export function rational(numerator: number, denominator: number): Rational {
if (!Number.isInteger(numerator) || !Number.isInteger(denominator)) {
throw new TypeError("numerator and denominator must be integers");
}
if (!Number.isSafeInteger(numerator) || !Number.isSafeInteger(denominator)) {
throw new RangeError("rational overflow: numerator and denominator must be within 2^53 - 1");
}
return rationalFromWide(BigInt(numerator), BigInt(denominator));
}
/** A checked, reduced operand as wide integers, ready to multiply without overflow. */
export function rationalParts(r: Rational): [bigint, bigint] {
const n = rational(r.numerator, r.denominator);
return [BigInt(n.numerator), BigInt(n.denominator)];
}