RosettaCodeData/Task/Fermat-numbers/JavaScript/fermat-numbers.js

173 lines
3.9 KiB
JavaScript
Raw Permalink Normal View History

2026-04-30 12:34:36 -04:00
// Fermat Numbers Calculator and Factorizer
const TWO = 2n;
// Calculate Fermat number F[n] = 2^(2^n) + 1
function fermat(n) {
return TWO ** (2n ** BigInt(n)) + 1n;
}
// Pollard's Rho algorithm helper function
function pollardRhoG(x, n) {
return (x * x + 1n) % n;
}
// Fast variant of Pollard's Rho algorithm
// Performs 100 multiplications before computing GCD
function pollardRhoFast(n) {
const start = Date.now();
let x = 2n;
let y = 2n;
let d = 1n;
let count = 0;
let z = 1n;
while (true) {
x = pollardRhoG(x, n);
y = pollardRhoG(pollardRhoG(y, n), n);
d = (x - y > 0n ? x - y : y - x);
z = (z * d) % n;
count++;
if (count === 100) {
d = gcd(z, n);
if (d !== 1n) {
break;
}
z = 1n;
count = 0;
}
}
const end = Date.now();
console.log(` Pollard rho try factor ${n} elapsed time = ${end - start} ms (factor = ${d}).`);
if (d === n) {
return 0n;
}
return d;
}
// Calculate GCD using Euclidean algorithm
function gcd(a, b) {
a = a > 0n ? a : -a;
b = b > 0n ? b : -b;
while (b !== 0n) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}
// Miller-Rabin primality test
function isProbablePrime(n, iterations = 100) {
if (n < 2n) return false;
if (n === 2n || n === 3n) return true;
if (n % 2n === 0n) return false;
// Write n-1 as 2^r * d
let d = n - 1n;
let r = 0n;
while (d % 2n === 0n) {
d /= 2n;
r++;
}
// Witness loop
const witnesses = [2n, 3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n];
for (let i = 0; i < Math.min(iterations, witnesses.length); i++) {
const a = witnesses[i];
if (a >= n) continue;
let x = modPow(a, d, n);
if (x === 1n || x === n - 1n) continue;
let continueWitnessLoop = false;
for (let j = 0n; j < r - 1n; j++) {
x = (x * x) % n;
if (x === n - 1n) {
continueWitnessLoop = true;
break;
}
}
if (!continueWitnessLoop) return false;
}
return true;
}
// Modular exponentiation
function modPow(base, exp, mod) {
let result = 1n;
base = base % mod;
while (exp > 0n) {
if (exp % 2n === 1n) {
result = (result * base) % mod;
}
exp = exp / 2n;
base = (base * base) % mod;
}
return result;
}
// Known composite prefixes for large Fermat numbers
const COMPOSITE = {
9: "5529",
10: "6078",
11: "1037",
12: "5488",
13: "2884"
};
// Factor a Fermat number
function getFactors(fermatIndex, n) {
const factors = [];
while (true) {
if (isProbablePrime(n)) {
factors.push(n);
break;
} else {
if (COMPOSITE[fermatIndex]) {
const stop = COMPOSITE[fermatIndex];
if (n.toString().startsWith(stop)) {
factors.push(-BigInt(n.toString().length));
break;
}
}
const factor = pollardRhoFast(n);
if (factor === 0n) {
factors.push(n);
break;
} else {
factors.push(factor);
n = n / factor;
}
}
}
return factors;
}
// Format factors as a string
function getString(factors) {
if (factors.length === 1) {
return `${factors[0]} (PRIME)`;
}
return factors.map(v => {
const s = v.toString();
return s.startsWith('-') ? `(C${s.substring(1)})` : s;
}).join(' * ');
}
// Main execution
console.log("First 10 Fermat numbers:");
for (let i = 0; i < 10; i++) {
console.log(`F[${i}] = ${fermat(i)}`);
}
console.log("\nFirst 12 Fermat numbers factored:");
for (let i = 0; i < 13; i++) {
console.log(`F[${i}] = ${getString(getFactors(i, fermat(i)))}`);
}