import { Constant } from "./const"; const BINARY_DIGITS = [ '0', '1', ] as const; const OCTAL_DIGITS = [ '0', '1', '2', '3', '4', '5', '6', '7', ] as const; const HEX_DIGITS = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', ] as const; export type IntegerLike = Integer | bigint | number | string; export class Integer extends Constant { public readonly value: bigint; constructor(value: IntegerLike) { super(); this.value = makeBigInt(value); } private static fromSystem(value: string, methodName: string, prefix: string, digits: readonly string[]): Integer { value = value.toLowerCase(); if (value.startsWith(prefix)) value = value.slice(prefix.length); if (!value.split('').every(c => digits.includes(c as any))) throw new Error(`Integer.${methodName}(...) called with an invalid value`); return new Integer(BigInt(prefix + value)); } static fromBinary(value: string): Integer { return Integer.fromSystem(value, 'fromBinary', '0b', BINARY_DIGITS); } static fromOctal(value: string): Integer { return Integer.fromSystem(value, 'fromOctal', '0o', OCTAL_DIGITS); } static fromHex(value: string): Integer { return Integer.fromSystem(value, 'fromHex', '0x', HEX_DIGITS); } public add(other: IntegerLike): Integer { return new Integer(this.value + makeBigInt(other)); } public sub(other: IntegerLike): Integer { return new Integer(this.value - makeBigInt(other)); } public mul(other: IntegerLike): Integer { return new Integer(this.value * makeBigInt(other)); } public div(other: IntegerLike): Integer { if (makeBigInt(other) === 0n) throw new Error('Integer.div(...) called with divisor of zero'); return new Integer(this.value / makeBigInt(other)); } public mod(other: IntegerLike): Integer { if (makeBigInt(other) === 0n) throw new Error('Integer.mod(...) called with divisor of zero'); return new Integer(this.value % makeBigInt(other)); } public pow(other: IntegerLike): Integer { if (makeBigInt(other) < 0n) throw new Error('Integer.pow(...) called with negative exponent'); return new Integer(this.value ** makeBigInt(other)); } public eq(other: IntegerLike): boolean { return this.value === makeBigInt(other); } public gt(other: IntegerLike): boolean { return this.value > makeBigInt(other); } public ge(other: IntegerLike): boolean { return this.value >= makeBigInt(other); } public lt(other: IntegerLike): boolean { return this.value < makeBigInt(other); } public le(other: IntegerLike): boolean { return this.value <= makeBigInt(other); } public gcd(other: IntegerLike): Integer { let a = this.value; let b = makeBigInt(other); if (a < 0n) a = -a; if (b < 0n) b = -b; if (a === 0n) return new Integer(b); if (b === 0n) return new Integer(a); let shift = 0n; while (((a | b) & 1n) == 0n) { shift++; a >>= 1n; b >>= 1n; } while (b != 0n) { while ((b & 1n) == 0n) b >>= 1n; if (a > b) [a, b] = [b, a]; b -= a; } let result = a << shift; if (result < 0n) result = -result; return new Integer(result); } public toString(): string { return this.value.toString(); } } export function makeBigInt(value: IntegerLike) { if (value instanceof Integer) return value.value; return BigInt(value); } // function makeInt(value: IntegerLike): Integer { // if (value instanceof Integer) return value; // return new Integer(value); // }