RosettaCodeData/Task/Home-primes/JavaScript/home-primes.js
2026-04-30 12:34:36 -04:00

240 lines
6.2 KiB
JavaScript

/**
* Home Primes calculator - JavaScript version
* Finds the home prime for a given number by repeatedly factorizing and concatenating
*/
const CERTAINTY_LEVEL = 20;
const PRIME_LIMIT = 10000;
class HomePrimes {
constructor() {
this.primes = [];
this.listPrimes(PRIME_LIMIT);
}
/**
* Main function to calculate home primes for test values
*/
main() {
const values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 65];
for (const value of values) {
let number = BigInt(value);
const previousNumbers = [number];
let searching = true;
while (searching) {
number = this.concatenate(this.primeFactors(number));
previousNumbers.push(number);
if (this.isProbablePrime(number, CERTAINTY_LEVEL)) {
const lastIndex = previousNumbers.length - 1;
let output = '';
for (let k = lastIndex; k >= 1; k--) {
output += `HP${previousNumbers[lastIndex - k]}(${k}) = `;
}
output += previousNumbers[lastIndex];
console.log(output);
searching = false;
}
}
}
}
/**
* Get prime factors of a number using trial division or Pollard's Rho
*/
primeFactors(num) {
if (num <= BigInt(PRIME_LIMIT * PRIME_LIMIT)) {
return this.smallPrimeFactors(num);
}
if (this.isProbablePrime(num, CERTAINTY_LEVEL)) {
return [num];
}
const divisor = this.pollardRho(num);
const result = [
...this.primeFactors(divisor),
...this.primeFactors(num / divisor)
];
return result.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
}
/**
* Trial division for smaller numbers
*/
smallPrimeFactors(num) {
let number = Number(num);
const result = [];
for (let i = 0; i < this.primes.length && number > 1; i++) {
while (number % this.primes[i] === 0) {
result.push(BigInt(this.primes[i]));
number = Math.floor(number / this.primes[i]);
}
}
if (number > 1) {
result.push(BigInt(number));
}
return result;
}
/**
* Pollard's Rho algorithm for integer factorization
*/
pollardRho(num) {
// Check if even
if ((num & BigInt(1)) === BigInt(0)) {
return BigInt(2);
}
const bitLength = num.toString(2).length;
const constant = this.randomBigInt(bitLength);
let x = this.randomBigInt(bitLength);
let y = x;
let divisor = BigInt(1);
while (divisor === BigInt(1)) {
x = (x * x + constant) % num;
y = (y * y + constant) % num;
y = (y * y + constant) % num;
divisor = this.gcd(x > y ? x - y : y - x, num);
}
return divisor;
}
/**
* Concatenate list of BigInts into a single BigInt
*/
concatenate(list) {
const str = list.map(n => n.toString()).join('');
return BigInt(str);
}
/**
* Sieve of Eratosthenes to generate primes up to limit
*/
listPrimes(limit) {
const sieve = new Array(limit + 1).fill(true);
sieve[0] = false;
sieve[1] = false;
for (let i = 2; i * i <= limit; i++) {
if (sieve[i]) {
for (let j = i * i; j <= limit; j += i) {
sieve[j] = false;
}
}
}
this.primes = [];
for (let i = 2; i <= limit; i++) {
if (sieve[i]) {
this.primes.push(i);
}
}
}
/**
* Miller-Rabin primality test
*/
isProbablePrime(n, k) {
if (n === BigInt(2) || n === BigInt(3)) return true;
if (n <= BigInt(1) || (n & BigInt(1)) === BigInt(0)) return false;
// Write n-1 as d * 2^r
let d = n - BigInt(1);
let r = 0;
while ((d & BigInt(1)) === BigInt(0)) {
d >>= BigInt(1);
r++;
}
// Witness loop
for (let i = 0; i < k; i++) {
const a = this.randomBigIntRange(BigInt(2), n - BigInt(2));
let x = this.modPow(a, d, n);
if (x === BigInt(1) || x === n - BigInt(1)) continue;
let composite = true;
for (let j = 0; j < r - 1; j++) {
x = this.modPow(x, BigInt(2), n);
if (x === n - BigInt(1)) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
/**
* Modular exponentiation: (base^exp) % mod
*/
modPow(base, exp, mod) {
let result = BigInt(1);
base = base % mod;
while (exp > BigInt(0)) {
if ((exp & BigInt(1)) === BigInt(1)) {
result = (result * base) % mod;
}
exp >>= BigInt(1);
base = (base * base) % mod;
}
return result;
}
/**
* Euclidean algorithm for GCD
*/
gcd(a, b) {
a = a < BigInt(0) ? -a : a;
b = b < BigInt(0) ? -b : b;
while (b !== BigInt(0)) {
const temp = b;
b = a % b;
a = temp;
}
return a;
}
/**
* Generate random BigInt with specified bit length
*/
randomBigInt(bits) {
const bytes = Math.ceil(bits / 8);
const hex = Array(bytes)
.fill(0)
.map(() => Math.floor(Math.random() * 256).toString(16).padStart(2, '0'))
.join('');
const num = BigInt('0x' + hex);
return num >> BigInt(bytes * 8 - bits); // Trim to exact bit length
}
/**
* Generate random BigInt in range [min, max]
*/
randomBigIntRange(min, max) {
const range = max - min + BigInt(1);
const bits = range.toString(2).length;
let result;
do {
result = this.randomBigInt(bits);
} while (result >= range);
return result + min;
}
}
// Run the program
const hp = new HomePrimes();
hp.main();