ultra-mega-enumerator
Version:
Ultra Mega Enumerator is a lightweight library designed to enumerate various combinatorial objects.
425 lines • 14.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Numbers = void 0;
class Numbers {
static divides(k, n) {
return n % k === 0;
}
static factors(n) {
if (n < 1)
throw new Error("factors:: invalid n");
const factorsSet = new Set();
factorsSet.add(1);
factorsSet.add(n);
const u = Math.floor(Math.sqrt(n));
for (let i = 2; i <= u; i++) {
if (this.divides(i, n)) {
factorsSet.add(i);
factorsSet.add(n / i);
}
}
return factorsSet;
}
static isPowerOfTwo(n) {
return Math.round(Math.pow(2.0, Math.round(Math.log(n) / Math.log(2.0)))) === n;
}
static minDistMod12(a, b) {
let d1 = a - b;
if (d1 < 0)
d1 += 12;
let d2 = b - a;
if (d2 < 0)
d2 += 12;
return Math.min(d1, d2);
}
static correctMod(a, b) {
if (b < 0)
throw new Error("Number.CorrectMod: invalid parameters.");
if (a >= 0)
return a % b;
let a0 = a;
while (a0 < 0)
a0 += b;
return a0 % b;
}
static prime(n0) {
const n = Math.abs(n0);
if (n < 2) {
return false;
}
const s = Math.floor(Math.sqrt(n));
for (let i = 2; i <= s; i++) {
if (n % i === 0) {
return false;
}
}
return true;
}
static primeFactorization(n0) {
const tm = new Map();
let t = Math.abs(n0);
if (t < 2) {
throw new Error("primeFactorization: |n| < 2");
}
let p = 2;
while (t !== 1) {
if (t % p === 0) {
if (!tm.has(p)) {
tm.set(p, 0);
}
tm.set(p, tm.get(p) + 1);
t = Math.floor(t / p);
}
else {
do {
p++;
} while (!this.prime(p));
}
}
const keys = Array.from(tm.keys());
const values = Array.from(tm.values());
return [keys, values];
}
static totient(n) {
const [primes, counts] = this.primeFactorization(n);
let d = n;
const k = primes.length;
for (let i = 0; i < k; i++) {
d *= (1 - (1 / primes[i]));
}
return Math.round(d);
}
static gcd(a0, b0) {
let a = a0;
let b = b0;
let t = 0;
while (b !== 0) {
t = b;
b = a % b;
a = t;
}
return a;
}
static lcm(a, b) {
return (a * b) / this.gcd(a, b);
}
static catalan(n) {
if (n < 0) {
throw new Error("n must be non-negative.");
}
let result = 1;
for (let i = 0; i < n; i++) {
// Calculate the next value in the sequence
const next = (result * 2 * (2 * i + 1)) / (i + 2);
// Check for overflow
if (next > Number.MAX_SAFE_INTEGER) {
throw new Error("Overflow detected.");
}
result = next;
}
return result;
}
static bell(n) {
const bellTriangle = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0));
// Initialize the first row
bellTriangle[0][0] = 1;
// Build the Bell triangle
for (let i = 1; i <= n; i++) {
// Start the row with the rightmost element from the previous row
bellTriangle[i][0] = bellTriangle[i - 1][i - 1];
// Calculate the rest of the row
for (let j = 1; j <= i; j++) {
bellTriangle[i][j] = bellTriangle[i][j - 1] + bellTriangle[i - 1][j - 1];
}
}
// The Bell number is the leftmost element of the last row
return bellTriangle[n][0];
}
static binomial(n, k) {
if (n < 0) {
throw new Error("n must be non-negative.");
}
if (k < 0) {
throw new Error("k must be non-negative.");
}
if (k > n) {
throw new Error("k cannot be greater than n.");
}
if (k > n - k) {
k = n - k;
}
let result = 1;
for (let i = 0; i < k; i++) {
// Calculate the next value
const next = (result * (n - i)) / (i + 1);
// Check for overflow
if (next > Number.MAX_SAFE_INTEGER) {
throw new Error("Overflow detected.");
}
result = next;
}
return result;
}
static multinomial(n) {
if (n === null) {
throw new Error("Input cannot be null");
}
let sum = 0;
for (let value of n) {
if (value < 0) {
throw new Error("All elements must be non-negative");
}
sum += value;
}
let nf = this.factorial(sum);
for (let value of n) {
nf = nf / this.factorial(value);
}
return nf;
}
static factorial(n) {
if (n < 0) {
throw new Error("Invalid input: n cannot be negative.");
}
let o = 1;
for (let i = 2; i <= n; i++) {
o *= i;
}
return o;
}
static triangularNumber(n) {
return this.binomial(n + 1, 2);
}
static reverseTriangularNumber(n) {
return Math.floor((Math.sqrt(1 + 8 * n) - 1) / 2);
}
/**
* Expands the bits of the number `n` by inserting extra characters after each bit.
*
* When `filler` is '0', it inserts k zeros after each bit (dilating the bits).
* When `filler` is 'bit', it repeats the bit k times after the original bit (replicating the bit).
*
* @param n - The original number.
* @param k - The number of extra characters to insert after each bit.
* @param filler - Either '0' (to insert zeros) or 'bit' (to repeat the bit). Defaults to '0'.
* @returns The new number after the expansion.
*/
static expandBits(n, k, filler = '0') {
const binary = n.toString(2);
const expandedBinary = binary
.split('')
.map(bit => {
// If filler is 'bit', repeat the bit k additional times (total k+1 copies).
// If filler is '0', append k zeros after the original bit.
return filler === 'bit'
? bit.repeat(k + 1)
: bit + '0'.repeat(k);
})
.join('');
return parseInt(expandedBinary, 2);
}
static decodeCantor(v) {
const w = Math.floor((-1 + Math.sqrt(1 + 8 * v)) / 2);
const t = w * (w + 1) / 2;
const k2 = v - t; // s
const k1 = w - k2; // d'
return [k1, k2];
}
static decodeIntervals(iPrime) {
if (iPrime % 2 === 0) {
return iPrime / 2; // d >= 0
}
else {
return -((iPrime + 1) / 2); // d < 0
}
}
static CantorIntervalBinaryNumber(a, b) {
// Step 1: Determine segment length
const c = Math.abs(a);
const sign = a >= 0 ? 1 : -1;
// Step 2: Decode b into duration d and step s
let v = Math.abs(b); // Use absolute value of b for decoding
const [iPrime, s] = this.decodeCantor(v);
const i = this.decodeIntervals(iPrime);
// Step 3: Generate binary sequence
const binaryArray = new Array(c).fill(0);
if (i !== 0) {
const start = i > 0 ? 0 : c - 1; // Start from 0 or end based on d's sign
for (let j = 0; j < s; j++) {
const offset = (start + j * i) % c;
const adjustedOffset = (offset + c) % c; // Ensure non-negative index
binaryArray[adjustedOffset] = 1;
}
}
// Step 4: Invert bits if a is negative
if (sign < 0) {
for (let j = 0; j < c; j++) {
binaryArray[j] = 1 - binaryArray[j];
}
}
// Step 5: Convert binary array to decimal number
return parseInt(binaryArray.reverse().join(''), 2);
}
static getPermutation(n) {
if (n === 0)
return [0];
const absN = Math.abs(n);
// Find minimal k such that k! > absN
let k = 1;
let fact = 1;
while (fact <= absN) {
k++;
fact *= k;
}
k--; // overshot
const totalPerms = fact / (k + 1) * (k + 1); // (k+1)!
if (absN >= totalPerms) {
throw new Error(`n is too large to generate a permutation of size ${k + 1}`);
}
// Build Lehmer code
const lehmerCode = [];
let remaining = absN;
fact /= (k + 1); // Start with k! for k+1 elements
for (let i = k; i >= 1; i--) {
lehmerCode.push(Math.floor(remaining / fact));
remaining %= fact;
if (i > 1)
fact /= i;
}
// Build permutation from Lehmer code
const elements = [];
for (let i = 0; i <= k; i++)
elements.push(i);
const permutation = [];
for (const code of lehmerCode) {
permutation.push(elements.splice(code, 1)[0]);
}
permutation.push(elements[0]); // add last remaining element
// For negative n, reverse the permutation
if (n < 0)
permutation.reverse();
return permutation;
}
static permuteBits(a, b) {
let permutation = Numbers.getPermutation(b);
let permSize = permutation.length;
let numBits = 32;
// Extend the permutation cyclically
let permMap = [];
for (let i = 0; i < numBits; i++) {
permMap[i] = permutation[i % permSize] + Math.floor(i / permSize) * permSize;
}
let result = 0;
for (let i = 0; i < numBits; i++) {
let srcBit = (a >> i) & 1; // Extract bit i
let destBitPos = permMap[i] % numBits; // Keep within 32-bit range
result |= (srcBit << destBitPos); // Set bit in result
}
return result >>> 0; // Ensure unsigned 32-bit integer
}
static toBalancedTernary(n, nbdigits) {
const digitCount = Math.abs(nbdigits);
const digits = [];
let remaining = n;
// Process exactly 'digitCount' digits.
for (let i = 0; i < digitCount; i++) {
// Compute the remainder in a positive modulo sense.
let rem = ((remaining % 3) + 3) % 3;
// Adjust remainder: in balanced ternary we want digits -1, 0, 1.
if (rem === 2) {
// Represent "2" as -1 with a carry.
rem = -1;
remaining = Math.floor((remaining + 1) / 3);
}
else {
remaining = Math.floor(remaining / 3);
}
digits.push(rem);
}
// If k is positive, reverse to get big-endian order.
if (nbdigits > 0) {
digits.reverse();
}
return digits;
}
static fromBalancedTernary(trits) {
let o = 0;
for (let i = 0; i < trits.length; i++) {
o += trits[i] * Math.pow(3, (trits.length - 1 - i));
}
return o;
}
static toBinary(n, nbdigits) {
if (nbdigits == 0)
return [];
let sign = n < 0 ? -1 : 1;
n *= sign;
const binary = [];
let num = Math.abs(n);
while (num !== 0) {
const tmp = sign * (num % 2);
binary.push(tmp == 0 ? 0 : tmp);
num = Math.floor(num / 2);
}
if (binary.length === 0) {
binary.push(0);
}
while (binary.length < Math.abs(nbdigits)) {
binary.push(0);
}
if (binary.length > Math.abs(nbdigits)) {
binary.length = Math.abs(nbdigits);
}
if (nbdigits > 0) {
binary.reverse();
}
return binary;
}
static fromBinary(bits) {
let o = 0;
for (let i = 0; i < bits.length; i++) {
o += bits[i] * Math.pow(2, (bits.length - 1 - i));
}
return o;
}
static applyUnaryTritwise(n, tritOp) {
const absN = Math.abs(n);
const ndigits = absN === 0 ? 1 : Math.ceil(Math.log(2 * absN + 1) / Math.log(3));
const trits = Numbers.toBalancedTernary(n, ndigits);
const result = trits.map(t => tritOp[Numbers.tritIndex.get(t)]);
return Numbers.fromBalancedTernary(result);
}
static tritBuf(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.BUF); }
static tritNot(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.NOT); }
static tritPnot(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.PNOT); }
static tritNnot(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.NNOT); }
static tritAbs(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.ABS); }
static tritClu(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.CLU); }
static tritCld(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.CLD); }
static tritInc(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.INC); }
static tritDec(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.DEC); }
static tritRtu(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.RTU); }
static tritRtd(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.RTD); }
static tritIsp(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.ISP); }
static tritIsz(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.ISZ); }
static tritIsn(n) { return Numbers.applyUnaryTritwise(n, Numbers.unaryTritOps.ISN); }
}
exports.Numbers = Numbers;
Numbers.tritIndex = new Map([[-1, 0], [0, 1], [1, 2]]);
Numbers.unaryTritOps = {
BUF: [-1, 0, 1],
NOT: [1, 0, -1],
PNOT: [1, 1, -1],
NNOT: [1, -1, -1],
ABS: [1, 0, 1],
CLU: [0, 0, 1],
CLD: [-1, 0, 0],
INC: [0, 1, 1],
DEC: [-1, -1, 0],
RTU: [0, 1, -1],
RTD: [1, -1, 0],
ISP: [-1, -1, 1],
ISZ: [-1, 1, -1],
ISN: [1, -1, -1],
};
//# sourceMappingURL=Numbers.js.map