import { IComparable } from "./Util.ts"; export const symbol = Symbol("numeric"); export interface CustomNumeric extends IComparable { [symbol](): { multiply(y: Numeric): Numeric, toPrecision(sd?: number): string, toExponential(dp?: number): string, toFixed(dp?: number): string, toHex(): string, } } export type Numeric = number | bigint | CustomNumeric; export function isNumeric(x: any) { return typeof x === "number" || typeof x === "bigint" || x?.[symbol]; } export function isIntegral(x: Numeric) { // Not perfect, because in JS we can't distinguish between 1.0 and 1 return typeof x === "number" && Number.isInteger(x) || typeof x === "bigint"; } export function compare(x: Numeric, y: number) { if (typeof x === "number") { return x < y ? -1 : (x > y ? 1 : 0); } else if (typeof x === "bigint") { return x < y ? -1 : (x > y ? 1 : 0); } else { return x.CompareTo(y); } } export function multiply(x: Numeric, y: number) { if (typeof x === "number") { return x * y; } else if (typeof x === "bigint") { return x * BigInt(y); } else { return x[symbol]().multiply(y); } } export function toFixed(x: Numeric, dp?: number) { if (typeof x === "number") { return x.toFixed(dp); } else if (typeof x === "bigint") { return x.toString(); } else { return x[symbol]().toFixed(dp); } } export function toPrecision(x: Numeric, sd?: number) { if (typeof x === "number") { return x.toPrecision(sd); } else if (typeof x === "bigint") { return x.toString(); } else { return x[symbol]().toPrecision(sd); } } export function toExponential(x: Numeric, dp?: number) { if (typeof x === "number") { return x.toExponential(dp); } else if (typeof x === "bigint") { return x; } else { return x[symbol]().toExponential(dp); } } export function toHex(x: Numeric) { if (typeof x === "number") { return (Number(x) >>> 0).toString(16); } else if (typeof x === "bigint") { // TODO: properly handle other bit sizes return BigInt.asUintN(64, x).toString(16); } else { return x[symbol]().toHex(); } }