159 lines
3.5 KiB
TypeScript
159 lines
3.5 KiB
TypeScript
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);
|
|
// }
|