Functional Weave
Code in TypeScript

math.big-integer@1.0.0

impl/typescript.ts

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. */
export const MAX_POWER_BITS = 65536;

/** Parse the registry's decimal integer form. "-0", "+1", "01" and " 1" are refused. */
export function parseBigInteger(text: string): bigint {
  if (typeof text !== "string" || !INTEGER.test(text) || text === "-0") {
    throw new 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.
 */
export function powerBigInteger(base: bigint, exponent: bigint): bigint {
  if (exponent < 0n) {
    throw new RangeError("exponent must not be negative");
  }
  const magnitude = base < 0n ? -base : base;
  if (magnitude >= 2n && BigInt(bitLength(magnitude)) * exponent > BigInt(MAX_POWER_BITS)) {
    throw new 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) return 1n;
    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).
 */
export function 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) throw new RangeError("division by zero");
      return (x / y).toString();
    case "remainder":
      if (y === 0n) throw new RangeError("division by zero");
      return (x % y).toString();
    case "power":
      return powerBigInteger(x, y).toString();
    default:
      throw new RangeError(`unknown operation "${op}"`);
  }
}