import { FSharpRef } from "./Types.ts"; import { int8, uint8, int16, uint16, int32, uint32, float16, float32, float64 } from "./Int32.ts"; import { decimal, fromParts, truncate } from "./Decimal.ts"; import { Exception, bigintHash } from "./Util.ts"; const isBigEndian = false; export type int64 = bigint; export type uint64 = bigint; export type int128 = bigint; export type uint128 = bigint; export type nativeint = bigint; export type unativeint = bigint; (BigInt.prototype as any).toJSON = function () { return `${this.toString()}`; }; const zero: bigint = 0n; const one: bigint = 1n; const two: bigint = 2n; const minusOne: bigint = -1n; export function isBigInt(x: any): boolean { return typeof x === "bigint"; } export function hash(x: bigint): int32 { return bigintHash(x); } export function equals(x: bigint, y: bigint): boolean { return x === y; } export function compare(x: bigint, y: bigint): int32 { return x < y ? -1 : x > y ? 1 : 0; } export function abs(x: bigint): bigint { return x < zero ? -x : x; } export function sign(x: bigint): int32 { return x < zero ? -1 : x > zero ? 1 : 0; } export function max(x: bigint, y: bigint): bigint { return x > y ? x : y; } export function min(x: bigint, y: bigint): bigint { return x < y ? x : y; } export function maxMagnitude(x: bigint, y: bigint): bigint { return abs(x) > abs(y) ? x : y; } export function minMagnitude(x: bigint, y: bigint): bigint { return abs(x) < abs(y) ? x : y; } export function clamp(x: bigint, min: bigint, max: bigint): bigint { return x < min ? min : x > max ? max : x; } export function add(x: bigint, y: bigint): bigint { return x + y } export function subtract(x: bigint, y: bigint): bigint { return x - y } export function multiply(x: bigint, y: bigint): bigint { return x * y } export function divide(x: bigint, y: bigint): bigint { return x / y } export function remainder(x: bigint, y: bigint): bigint { return x % y } export function negate(x: bigint): bigint { return -x } export function op_UnaryNegation(x: bigint): bigint { return -x; } export function op_LogicalNot(x: bigint): bigint { return ~x; } export function op_UnaryPlus(x: bigint): bigint { return x; } export function op_Addition(x: bigint, y: bigint): bigint { return x + y; } export function op_Subtraction(x: bigint, y: bigint): bigint { return x - y; } export function op_Multiply(x: bigint, y: bigint): bigint { return x * y; } export function op_Division(x: bigint, y: bigint): bigint { return x / y; } export function op_Modulus(x: bigint, y: bigint): bigint { return x % y; } export function op_RightShift(x: bigint, n: int32): bigint { return x >> BigInt(n); } export function op_LeftShift(x: bigint, n: int32): bigint { return x << BigInt(n); } export function op_BitwiseAnd(x: bigint, y: bigint): bigint { return x & y; } export function op_BitwiseOr(x: bigint, y: bigint): bigint { return x | y; } export function op_ExclusiveOr(x: bigint, y: bigint): bigint { return x ^ y; } export function op_LessThan(x: bigint, y: bigint): boolean { return x < y; } export function op_LessThanOrEqual(x: bigint, y: bigint): boolean { return x <= y; } export function op_GreaterThan(x: bigint, y: bigint): boolean { return x > y; } export function op_GreaterThanOrEqual(x: bigint, y: bigint): boolean { return x >= y; } export function op_Equality(x: bigint, y: bigint): boolean { return x === y; } export function op_Inequality(x: bigint, y: bigint): boolean { return x !== y; } export function get_Zero(): bigint { return zero; } export function get_One(): bigint { return one; } export function get_MinusOne(): bigint { return minusOne; } export function get_IsZero(x: bigint): boolean { return x === zero; } export function get_IsOne(x: bigint): boolean { return x === one; } export function get_IsEven(x: bigint): boolean { return isEvenInteger(x); } export function get_IsPowerOfTwo(x: bigint): boolean { return isPow2(x); } export function get_Sign(x: bigint): int32 { return sign(x); } export function isNegative(x: bigint): boolean { return x < zero; } export function isPositive(x: bigint): boolean { return x > zero; } export function isEvenInteger(x: bigint): boolean { return (x % two) === zero; } export function isOddInteger(x: bigint): boolean { return (x % two) !== zero; } export function isPow2(x: bigint): boolean { return (x & (x - one)) === zero } export function fromZero(): bigint { return zero; } export function fromOne(): bigint { return one; } export function fromInt8(n: int8): bigint { return BigInt(n); } export function fromUInt8(n: uint8): bigint { return BigInt(n); } export function fromInt16(n: int16): bigint { return BigInt(n); } export function fromUInt16(n: uint16): bigint { return BigInt(n); } export function fromInt32(n: int32): bigint { return BigInt(n); } export function fromUInt32(n: uint32): bigint { return BigInt(n); } export function fromInt64(n: int64): bigint { return n; } export function fromUInt64(n: uint64): bigint { return n; } export function fromInt128(n: int128): bigint { return n; } export function fromUInt128(n: uint128): bigint { return n; } export function fromNativeInt(n: nativeint): bigint { return n; } export function fromUNativeInt(n: unativeint): bigint { return n; } export function fromFloat16(n: float16): bigint { return BigInt(Math.trunc(n)); } export function fromFloat32(n: float32): bigint { return BigInt(Math.trunc(n)); } export function fromFloat64(n: float64): bigint { return BigInt(Math.trunc(n)); } export function fromDecimal(d: decimal): bigint { return BigInt(truncate(d).toString()); } export function fromBigInt(x: bigint): bigint { return x } export function fromBoolean(b: boolean): bigint { return BigInt(b); } export function fromChar(c: string): bigint { return BigInt(c.charCodeAt(0)); } export function fromString(s: string): bigint { return BigInt(s); } export function fromByteArray(bytes: ArrayLike): bigint { return fromSignedBytes(bytes, isBigEndian); } export function toByteArray(value: bigint): number[] { return toSignedBytes(value, isBigEndian) as any as number[]; } export function toIntN_unchecked(bits: number, x: bigint, signed: boolean): bigint { return signed ? BigInt.asIntN(bits, x) : BigInt.asUintN(bits, x); } export function toIntN(bits: number, x: bigint, signed: boolean): bigint { let higher_bits = abs(x) >> BigInt(bits); if (higher_bits !== 0n) { const s = signed ? "a signed" : "an unsigned"; throw new Exception(`Value was either too large or too small for ${s} ${bits}-bit integer.`); } return signed ? BigInt.asIntN(bits, x) : BigInt.asUintN(bits, x); } export function toInt8(x: bigint): int8 { return Number(toIntN(8, x, true)); } export function toUInt8(x: bigint): uint8 { return Number(toIntN(8, x, false)); } export function toInt16(x: bigint): int16 { return Number(toIntN(16, x, true)); } export function toUInt16(x: bigint): uint16 { return Number(toIntN(16, x, false)); } export function toInt32(x: bigint): int32 { return Number(toIntN(32, x, true)); } export function toUInt32(x: bigint): uint32 { return Number(toIntN(32, x, false)); } export function toInt64(x: bigint): int64 { return toIntN(64, x, true); } export function toUInt64(x: bigint): uint64 { return toIntN(64, x, false); } export function toInt128(x: bigint): int128 { return toIntN(128, x, true); } export function toUInt128(x: bigint): uint128 { return toIntN(128, x, false); } export function toNativeInt(x: bigint): nativeint { return toIntN(64, x, true); } export function toUNativeInt(x: bigint): unativeint { return toIntN(64, x, false); } export function toInt8_unchecked(x: bigint): int8 { return Number(toIntN_unchecked(8, x, true)); } export function toUInt8_unchecked(x: bigint): uint8 { return Number(toIntN_unchecked(8, x, false)); } export function toInt16_unchecked(x: bigint): int16 { return Number(toIntN_unchecked(16, x, true)); } export function toUInt16_unchecked(x: bigint): uint16 { return Number(toIntN_unchecked(16, x, false)); } export function toInt32_unchecked(x: bigint): int32 { return Number(toIntN_unchecked(32, x, true)); } export function toUInt32_unchecked(x: bigint): uint32 { return Number(toIntN_unchecked(32, x, false)); } export function toInt64_unchecked(x: bigint): int64 { return toIntN_unchecked(64, x, true); } export function toUInt64_unchecked(x: bigint): uint64 { return toIntN_unchecked(64, x, false); } export function toInt128_unchecked(x: bigint): int128 { return toIntN_unchecked(128, x, true); } export function toUInt128_unchecked(x: bigint): uint128 { return toIntN_unchecked(128, x, false); } export function toNativeInt_unchecked(x: bigint): nativeint { return toIntN_unchecked(64, x, true); } export function toUNativeInt_unchecked(x: bigint): unativeint { return toIntN_unchecked(64, x, false); } export function toFloat16(x: bigint): float16 { return Number(x); } export function toFloat32(x: bigint): float32 { return Number(x); } export function toFloat64(x: bigint): float64 { return Number(x); } export function toDecimal(x: bigint): decimal { const isNegative = x < zero; const bits = abs(x); const low = Number(BigInt.asUintN(32, bits)); const mid = Number(BigInt.asUintN(32, bits >> 32n)); const high = Number(BigInt.asUintN(32, bits >> 64n)); const scale = 0; return fromParts(low, mid, high, isNegative, scale) } export function toBigInt(x: bigint): bigint { return x; } export function toBoolean(x: bigint): boolean { return x !== zero; } export function toChar(x: bigint): string { return String.fromCharCode(toUInt16(x)) } export function toString(x: bigint): string { return x.toString(); } export function tryParse(s: string, res: FSharpRef): boolean { try { res.contents = BigInt(s); return true; } catch (err: any) { return false; } } export function parse(s: string): bigint { return BigInt(s); } export function pow(x: bigint, n: int32): bigint { return x ** BigInt(n); } export function modPow(x: bigint, e: bigint, m: bigint): bigint { return (x ** e) % m; } export function divRem(x: bigint, y: bigint): [bigint, bigint]; export function divRem(x: bigint, y: bigint, out: FSharpRef): bigint; export function divRem(x: bigint, y: bigint, out?: FSharpRef): bigint | [bigint, bigint] { const div = x / y; const rem = x % y; if (out === void 0) { return [div, rem]; } else { out.contents = rem; return div; } } export function greatestCommonDivisor(x: bigint, y: bigint): bigint { while (y > zero) { const q = x / y; const r = x - q * y; x = y; y = r; } return x; } export function getBitLength(x: bigint): int64 { return fromFloat64(x === zero ? 1 : log2(abs(x)) + 1); } export function log2(x: bigint): float64 { const n = Number(x); if (Number.isFinite(n)) return Math.log2(n); // fast path if (x < zero) return Number.NaN; let shift = one; while (x >= (one << shift)) { shift = shift << one; } let log = zero; while (shift > one) { shift = shift >> one; if (x >= (one << shift)) { log = log + shift; x = x >> shift; } } return Number(log); } export function log10(x: bigint): float64 { return log2(x) * Math.log10(2); } export function ln(x: bigint): float64 { return log2(x) * Math.log(2); } export function log(x: bigint, base: float64): float64 { return log2(x) / Math.log2(base); } export function ilog2(x: bigint): bigint { return BigInt(log2(x)); } // export function copySign // export function createChecked // export function createSaturating // export function createTruncating // export function getByteCount // export function leadingZeroCount // export function popCount // export function rotateLeft // export function rotateRight // export function trailingZeroCount // export function tryFormat // export function tryWriteBytes // ------------------------------------------------- // Binary serialization // ------------------------------------------------- const hexCodes = new Uint8Array([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102]); function fromHexCode(code: number): number { if (48 <= code && code <= 57) return code - 48; if (97 <= code && code <= 102) return code - 97 + 10; if (65 <= code && code <= 70) return code - 65 + 10; throw Error(`Invalid hex code: ${code}`); } function toSignedBytes(x: bigint, isBigEndian: boolean): Uint8Array { const isNeg = x < 0n; if (isNeg) { const len = log2(-x); const bits = len + (8 - len % 8); const pow2 = (1n << BigInt(bits)); x = x + pow2; // two's complement } const hex = x.toString(16); const len = hex.length; const odd = len % 2; const first = hex.charCodeAt(0); const isLow = 48 <= first && first <= 55; // 0..7 const start = (isNeg && isLow) || (!isNeg && !isLow) ? 1 : 0; const bytes = new Uint8Array(start + (len + odd) / 2); const inc = isBigEndian ? 1 : -1; let pos = isBigEndian ? 0 : bytes.length - 1; if (start > 0) { bytes[pos] = isNeg ? 255 : 0; pos += inc; } if (odd > 0) { bytes[pos] = fromHexCode(first); pos += inc; } for (let i = odd; i < len; i += 2, pos += inc) { const a = fromHexCode(hex.charCodeAt(i)); const b = fromHexCode(hex.charCodeAt(i + 1)); bytes[pos] = (a << 4) | b; } return bytes; } function fromSignedBytes(bytes: ArrayLike, isBigEndian: boolean) { if (bytes == null) { throw new Exception("bytes is null"); } const len = bytes.length; const first = isBigEndian ? 0 : len - 1; const isNeg = bytes[first] > 127; const codes = new Uint16Array(len * 2 + 2); codes[0] = 48; // 0 codes[1] = 120; // x const inc = isBigEndian ? 1 : -1; let pos = isBigEndian ? 0 : len - 1; for (let i = 0; i < bytes.length; i++, pos += inc) { const byte = bytes[pos]; codes[2 * i + 2] = hexCodes[byte >> 4]; codes[2 * i + 3] = hexCodes[byte & 15]; } const str = String.fromCharCode.apply(null, codes as any as number[]); let x = BigInt(str); if (isNeg) { const bits = len * 8; const pow2 = (1n << BigInt(bits)); x = x - pow2; // two's complement } return x; }