plsm-ts/src/const/integer.ts
2025-03-04 20:54:46 +01:00

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);
// }