UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

241 lines (240 loc) 7 kB
/** * Utility functions for working with `bigint` values. * * @module * * @example * * ```js * import { sum, abs, log2, bitLength, mask } from './module.f.ts' * * const total = sum([1n, 2n, 3n]) // 6n * const absoluteValue = abs(-42n) // 42n * const logValue = log2(8n) // 3n * const bitCount = bitLength(255n) // 8n * const bitmask = mask(5n) // 31n * const m = min(3n)(13n) // 3n * const c = combination([3n, 2n, 1n]) // 60n * ``` */ import { unsafeCmp } from "../function/compare/module.f.js"; import { reduce } from "../list/module.f.js"; /** * Adds two `bigint` values. * * @param a - The first bigint value. * @returns A function that takes the second bigint value and returns the sum. */ export const addition = a => b => a + b; /** * Calculates the sum of a list of `bigint` values. * * @param input - A list of bigint values. * @returns The sum of all values in the list. */ export const sum = reduce(addition)(0n); /** * Multiplies two `bigint` values. * * @param a - The first bigint value. * @returns A function that takes the second bigint value and returns the product. */ export const multiple = a => b => a * b; /** * Calculates the product of a list of `bigint` values. * * @param input - A list of bigint values. * @returns The product of all values in the list. */ export const product = reduce(multiple)(1n); /** * Calculates the absolute value of a `bigint`. * * @param a - The bigint value. * @returns The absolute value of the input bigint. */ export const abs = a => a >= 0 ? a : -a; /** * Determines the sign of a `bigint`. * @param a - The bigint value. * @returns `1` if positive, `-1` if negative, and `0` if zero. */ export const sign = (a) => unsafeCmp(a)(0n); /** * Serializes a `bigint` to a string representation. * * @param a - The bigint value. * @returns A string representation of the bigint (e.g., '123n'). */ export const serialize = (a) => `${a}n`; const { isFinite } = Number; const { log2: mathLog2 } = Math; /** * Calculates the base-2 logarithm (floor). * * This function returns the integer part of the logarithm. For example: * - `log2(1n)` returns `0n`, * - `log2(2n)` returns `1n`, * - `log2(15n)` returns `3n`. * * @param v - The input BigInt. * @returns The base-2 logarithm (floor) of the input BigInt, or `-1n` if the input is less than or equal to 0. * * @remarks * The function operates in two phases: * 1. **Fast Doubling Phase:** Uses exponential steps to quickly narrow down the range * of the most significant bit. * 2. **Binary Search Phase:** Refines the result by halving the step size and incrementally * determining the exact value of the logarithm. * 3. **Remainder Phase:** Using `Math.log2`. */ export const log2 = (v) => { if (v <= 0n) { return -1n; } // // 1. Fast Doubling. // let result = -1n; // `bigints` higher than 2**1023 may lead to `Inf` during conversion to `number`. // For example: `Number((1n << 1024n) - (1n << 970n)) === Inf`. let i = 0x400n; while (true) { const n = v >> i; if (n === 0n) { // overshot break; } v = n; result += i; i <<= 1n; } // // 2. Binary Search. // // We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0. // Because of this, We check if `i` is greater than 1023 before we divide it by 2. while (i !== 0x400n) { i >>= 1n; const n = v >> i; if (n !== 0n) { result += i; v = n; } } // // 3. Remainder Phase. // // We know that `v` is less than `1n << 1024` so we can calculate a remainder using // `Math.log2`. const nl = mathLog2(Number(v)); if (isFinite(nl)) { const rem = BigInt(nl | 0); // (v >> rem) is either `0` or `1`, and it's used as a correction for // Math.log2 rounding. return result + rem + (v >> rem); } else { // nl is Inf, it means log2(v) === 0x3FF and we add +1 to compensate for initial // `result = -1n`. return result + 0x400n; } }; /** * Calculates the bit length of a given BigInt. * * The bit length of a number is the number of bits required to represent its absolute value in binary, * excluding leading zeros. For example: * - `0n` has a bit length of 0 (it has no bits). * - `1n` (binary `1`) has a bit length of 1. * - `255n` (binary `11111111`) has a bit length of 8. * - `-255n` (absolute value `255`, binary `11111111`) also has a bit length of 8. * * The function handles both positive and negative numbers. For negative inputs, the bit length is calculated * based on the absolute value of the number. Zero has a bit length of 0. * * @param v - The input BigInt. * @returns The bit length of the input BigInt. * * @remark * The function uses the `log2` function to calculate the position of the most significant bit (MSB) * and adds `1n` to account for the MSB itself. For negative numbers, the absolute value is used. */ export const bitLength = (v) => { if (v <= 0n) { if (v === 0n) { return 0n; } v = -v; } return log2(v) + 1n; }; /** * Generates a bitmask with the specified number of bits set to 1. * * @param len - The number of bits to set in the mask. Must be a non-negative integer. * @returns A bigint representing the bitmask, where the least significant `len` bits are 1. * * @example * * ```js * const result = mask(3n) // 7n * ``` */ export const mask = (len) => (1n << len) - 1n; /** * Returns the smaller of two `bigint` values. * * @param a - The first bigint. * @returns A function that takes the second bigint and returns the smaller value. */ export const min = (a) => (b) => a < b ? a : b; /** * Returns the larger of two `bigint` values. * * @param a - The first bigint. * @returns A function that takes the second bigint and returns the larger value. */ export const max = (a) => (b) => a < b ? b : a; /** * Calculates the partial factorial `b!/a!`. * * @param a - The starting bigint value. * @returns A function that takes `b` and computes `b!/a!`. */ export const partialFactorial = (a) => (b) => { let result = b; while (true) { --b; if (b <= a) { return result; } result *= b; } }; /** * Calculates the factorial of a `bigint`. * * @param b - The bigint value. * @returns The factorial of the input. */ export const factorial = partialFactorial(1n); /** * Calculates the number of combinations for a list of `bigint` values. * * @param k - A list of bigint values. * @returns The number of combinations. */ export const combination = (...k) => { let s = 0n; let m = 1n; let p = 1n; for (let i of k) { s += i; if (i >= m) { [i, m] = [m, i]; } p *= factorial(i); } return partialFactorial(m)(s) / p; };