export function modPow(base, exp, mod) {
base %= mod;
let r = 1n;
while (exp > 0n) {
if (exp & 1n) r = (r * base) % mod;
base = (base * base) % mod;
exp >>= 1n;
}
return r;
}
export function gcd(a, b) {
while (b) {
[a, b] = [b, a % b];
}
return a < 0n ? -a : a;
}
const MR_BASES = [2n, 3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n, 31n, 37n];
const TWO64 = 1n << 64n;
function millerRabinWitness(n, a) {
let d = n - 1n;
let s = 0n;
while ((d & 1n) === 0n) {
d >>= 1n;
s++;
}
let x = modPow(a % n, d, n);
if (x === 1n || x === n - 1n) return true;
for (let i = 1n; i < s; i++) {
x = (x * x) % n;
if (x === n - 1n) return true;
if (x === 1n) return false;
}
return false;
}
function jacobi(a, n) {
a %= n;
if (a < 0n) a += n;
let result = 1;
while (a !== 0n) {
while ((a & 1n) === 0n) {
a >>= 1n;
const n8 = n & 7n;
if (n8 === 3n || n8 === 5n) result = -result;
}
[a, n] = [n, a];
if ((a & 3n) === 3n && (n & 3n) === 3n) result = -result;
a %= n;
}
return n === 1n ? result : 0;
}
function strongLucasSelfridge(n) {
let magnitude = 5n;
let positive = true;
let D;
for (;;) {
D = positive ? magnitude : -magnitude;
const symbol = jacobi(D, n);
if (symbol === -1) break;
if (symbol === 0) return false;
magnitude += 2n;
positive = !positive;
}
const Q = (1n - D) / 4n;
let odd = n + 1n;
let s = 0n;
while ((odd & 1n) === 0n) {
odd >>= 1n;
s++;
}
const mod = (x) => ((x % n) + n) % n;
const half = (x) => {
x = mod(x);
return (x & 1n) === 0n ? x >> 1n : (x + n) >> 1n;
};
let U = 1n;
let V = 1n;
let Qk = mod(Q);
const bits = odd.toString(2);
for (let i = 1; i < bits.length; i++) {
U = mod(U * V);
V = mod(V * V - 2n * Qk);
Qk = mod(Qk * Qk);
if (bits[i] === "1") {
const oldU = U;
const oldV = V;
U = half(oldU + oldV);
V = half(D * oldU + oldV);
Qk = mod(Qk * Q);
}
}
if (U === 0n || V === 0n) return true;
for (let r = 1n; r < s; r++) {
V = mod(V * V - 2n * Qk);
Qk = mod(Qk * Qk);
if (V === 0n) return true;
}
return false;
}
export function isPrime(n) {
if (n < 2n) return false;
for (const p of MR_BASES) {
if (n === p) return true;
if (n % p === 0n) return false;
}
if (n >= TWO64) {
const root = integerRoot(n, 2);
if (root * root === n) return false;
if (!millerRabinWitness(n, 2n) || !strongLucasSelfridge(n)) return false;
}
let d = n - 1n;
let s = 0n;
while ((d & 1n) === 0n) {
d >>= 1n;
s++;
}
witness: for (const a of MR_BASES) {
let x = modPow(a, d, n);
if (x === 1n || x === n - 1n) continue;
for (let i = 1n; i < s; i++) {
x = (x * x) % n;
if (x === n - 1n) continue witness;
}
return false;
}
return true;
}
export function integerRoot(n, k) {
if (n < 2n) return n;
let lo = 1n;
let hi = 1n << (BigInt(n.toString(2).length) / BigInt(k) + 1n);
while (lo < hi) {
const mid = (lo + hi + 1n) >> 1n;
if (mid ** BigInt(k) <= n) lo = mid;
else hi = mid - 1n;
}
return lo;
}
export function perfectPower(n) {
const bits = n.toString(2).length;
for (let k = 2; k <= bits; k++) {
const r = integerRoot(n, k);
if (r ** BigInt(k) === n) return { base: r, k };
}
return null;
}
export function trialDivide(n, out, limit = 100000n) {
for (let p = 2n; p <= limit && p * p <= n; p += p === 2n ? 1n : 2n) {
while (n % p === 0n) {
out.push(p);
n /= p;
}
}
return n;
}
export function pollardBrent(n, budget = 1 << 20) {
if (n % 2n === 0n) return 2n;
let steps = 0;
for (let c = 1n; c < 32n; c++) {
let y = 2n;
let r = 1n;
let q = 1n;
let g = 1n;
let x = 0n;
let ys = 0n;
const f = (v) => (v * v + c) % n;
while (g === 1n && steps < budget) {
x = y;
for (let i = 0n; i < r; i++) y = f(y);
let k = 0n;
while (k < r && g === 1n && steps < budget) {
ys = y;
const lim = r - k < 128n ? r - k : 128n;
for (let i = 0n; i < lim; i++) {
y = f(y);
const d = x > y ? x - y : y - x;
if (d !== 0n) q = (q * d) % n;
steps++;
}
g = gcd(q, n);
k += lim;
}
r <<= 1n;
}
if (g === n) {
do {
ys = f(ys);
g = gcd(x > ys ? x - ys : ys - x, n);
} while (g === 1n && steps++ < budget);
}
if (g > 1n && g < n) return g;
if (steps >= budget) return null;
}
return null;
}
export const bitLength = (n) => (n <= 0n ? 0 : n.toString(2).length);
function randomOdd(bits) {
const bytes = new Uint8Array((bits + 7) >> 3);
crypto.getRandomValues(bytes);
let n = 0n;
for (const b of bytes) n = (n << 8n) | BigInt(b);
n &= (1n << BigInt(bits)) - 1n; n |= (1n << BigInt(bits - 1)) | (1n << BigInt(bits - 2)); return n | 1n; }
const SMALL_PRIMES = [3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n, 31n, 37n, 41n, 43n, 47n];
export function randomPrime(bits) {
for (;;) {
let n = randomOdd(bits);
for (let tries = 0; tries < 20000; tries++, n += 2n) {
if (SMALL_PRIMES.some((p) => n % p === 0n)) continue;
if (isPrime(n)) return n;
}
}
}
export function rsaNumber(bits) {
const pbits = bits >> 1;
const qbits = bits - pbits;
let p, q;
do {
p = randomPrime(pbits);
q = randomPrime(qbits);
} while (p === q);
return p * q;
}
export function groupFactors(primes) {
primes.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
const grouped = [];
for (const p of primes) {
const last = grouped[grouped.length - 1];
if (last && last.prime === p) last.exponent++;
else grouped.push({ prime: p, exponent: 1 });
}
return grouped;
}