2,388 bytes · the TypeScript implementation · view raw
import { type BigIntegerOp } from"./math_big_integer_types.ts";
const INTEGER = /^-?(0|[1-9][0-9]*)$/;
/** The largest result, in bits, that power will build: about 19,700 decimal digits. */exportconst MAX_POWER_BITS = 65536;
/** Parse the registry's decimal integer form. "-0", "+1", "01" and " 1" are refused. */exportfunction parseBigInteger(text: string): bigint {
if (typeof text !== "string" || !INTEGER.test(text) || text === "-0") {
thrownew RangeError(`not a whole number in decimal: "${text}"`);
}
return BigInt(text);
}
function bitLength(value: bigint): number {
return value === 0n ? 0 : (value < 0n ? -value : value).toString(2).length;
}
/** * Raise to a power, refusing results beyond MAX_POWER_BITS before building * them, so a typo cannot hang the process on a million-digit number. */exportfunction powerBigInteger(base: bigint, exponent: bigint): bigint {
if (exponent < 0n) {
thrownew RangeError("exponent must not be negative");
}
const magnitude = base < 0n ? -base : base;
if (magnitude >= 2n && BigInt(bitLength(magnitude)) * exponent > BigInt(MAX_POWER_BITS)) {
thrownew RangeError(`result too large: power is limited to ${MAX_POWER_BITS} bits`);
}
if (magnitude <= 1n) {
// 0^0 is 1, as in every language's integer power; (-1)^n alternates.if (exponent === 0n) return1n;
if (base === -1n) return exponent % 2n === 0n ? 1n : -1n;
return base;
}
return base ** exponent;
}
/** * Exact integer arithmetic on decimal strings. Division truncates towards * zero and the remainder takes the sign of the dividend, as in TypeScript, * Rust, C and Java (Python floors, and is adjusted to agree). */exportfunction calculateBigInteger(a: string, op: BigIntegerOp, b: string): string {
const x = parseBigInteger(a);
const y = parseBigInteger(b);
switch (op) {
case"add":
return (x + y).toString();
case"subtract":
return (x - y).toString();
case"multiply":
return (x * y).toString();
case"divide":
if (y === 0n) thrownew RangeError("division by zero");
return (x / y).toString();
case"remainder":
if (y === 0n) thrownew RangeError("division by zero");
return (x % y).toString();
case"power":
return powerBigInteger(x, y).toString();
default:
thrownew RangeError(`unknown operation "${op}"`);
}
}