173 lines
3.9 KiB
JavaScript
173 lines
3.9 KiB
JavaScript
|
|
// 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)))}`);
|
||
|
|
}
|