Functional Weave
Code in Rust

math.gcd-lcm@1.0.0

impl/typescript.ts

1,499 bytes · the TypeScript implementation · view raw

import { type GcdLcm } from "./math_gcd_lcm_types.ts";

function checkSafe(name: string, value: number): void {
  if (!Number.isInteger(value)) {
    throw new TypeError(`${name} must be an integer, received ${value}`);
  }
  if (!Number.isSafeInteger(value)) {
    throw new RangeError(`${name} is outside the safe integer range (±9007199254740991)`);
  }
}

/** Euclid's algorithm on wide integers. Never negative; gcdWide(0, 0) is 0. */
export function gcdWide(a: bigint, b: bigint): bigint {
  let x = a < 0n ? -a : a;
  let y = b < 0n ? -b : b;
  while (y !== 0n) {
    const r = x % y;
    x = y;
    y = r;
  }
  return x;
}

export function gcd(a: number, b: number): number {
  checkSafe("a", a);
  checkSafe("b", b);
  return Number(gcdWide(BigInt(a), BigInt(b)));
}

export function lcm(a: number, b: number): number {
  const g = gcd(a, b);
  if (g === 0) return 0;
  // Divide before multiplying: a * b overflows long before the lcm does.
  const result = (BigInt(Math.abs(a)) / BigInt(g)) * BigInt(Math.abs(b));
  if (result > BigInt(Number.MAX_SAFE_INTEGER)) {
    throw new RangeError(`the lcm of ${a} and ${b} exceeds 2^53 - 1`);
  }
  return Number(result);
}

/**
 * Greatest common divisor and least common multiple of two integers.
 *
 * Both are returned together because a caller needing one nearly always
 * needs the other (common denominators, repeating schedules).
 */
export function gcdLcm(a: number, b: number): GcdLcm {
  return { gcd: gcd(a, b), lcm: lcm(a, b) };
}