UNPKG

@sigma-js/primes

Version:

JavaScript library for efficient prime number detection and arithmetic, with customizable algorithms for large numbers.

377 lines (340 loc) 11.7 kB
module.exports = (function () { // Generate a list of prime numbers using an optimized Sieve of Eratosthenes. let minNum = 1; let maxNum = 8388607; let sieveType = "eratosthenes"; let primeNums = []; // Define message. const msgs = { // replace: {0}: Specified number/Starting number/Ending number/Decimal point position/MinNum/MaxNum, {1}: greater/less, {2}: 0/1/minNum/maxNum errNumericRange: "{0} must be {1} than or equal to {2}.", // replace: {0}: prime numbers, {1}: specified range errNoTarget: "There are no {0} in the {1}.", // replace: {0}: Multiplicative inverse errNotExist: "{0} does not exist.", // replace: {0}: Setting value errNotSpecify: "{0} is not specified.", // replace: {0}: sieveType, {1}: eratosthenes or atkin errInvalidSpecify: "Please specify {0} for {1}.", }; // Get messages. const getMsg = (key, repArr = null) => { const msg = msgs[key]; return repArr ? msg.replace(/{(\d+)}/g, (match, i) => { return repArr[i] !== undefined ? repArr[i] : match; }) : msg; }; // Get maximum number. const getMinNum = () => { return minNum; }; // Get minimum number. const getMaxNum = () => { return maxNum; }; // Get sieve type. const getSieveType = () => { return sieveType; }; // Generate list of prime numbers. const genPrimeNums = () => { primeNums = ( sieveType === "eratosthenes" ? genEratosthenesSieve() : genAtkinSieve() ).reduce((result, isPrime, index) => { if (isPrime) result.push(index + minNum); return result; }, []); }; // Generate a truth list of prime numbers in the target range using the Sieve of Eratosthenes. const genEratosthenesSieve = () => { const boolValues = new Array(maxNum - minNum + 1).fill(true); if (minNum === 1) boolValues[0] = false; for (let i = 2; i * i <= maxNum; i++) { for ( let j = Math.max(i * i, Math.ceil(minNum / i) * i); j <= maxNum; j += i ) { boolValues[j - minNum] = false; } } return boolValues; }; // Generate a truth list of prime numbers in the target range using the Sieve of Atkin. const genAtkinSieve = () => { const boolValues = new Array(maxNum - minNum + 1).fill(false); boolValues[2 - minNum] = boolValues[3 - minNum] = true; for (let x = 1; x * x <= maxNum; x++) { for (let y = 1; y * y <= maxNum; y++) { let n = 4 * x * x + y * y; if (n >= minNum && n <= maxNum && (n % 12 === 1 || n % 12 === 5)) { boolValues[n - minNum] = !boolValues[n - minNum]; } n = 3 * x * x + y * y; if (n >= minNum && n <= maxNum && n % 12 === 7) { boolValues[n - minNum] = !boolValues[n - minNum]; } n = 3 * x * x - y * y; if (x > y && n >= minNum && n <= maxNum && n % 12 === 11) { boolValues[n - minNum] = !boolValues[n - minNum]; } } } for (let x = 5; x * x <= maxNum; x++) { if (boolValues[x - minNum]) { for (let y = x * x; y <= maxNum; y += x * x) { boolValues[y - minNum] = false; } } } return boolValues; }; // Initialization process. const init = (config) => { if (config?.minNum === 0) throw new Error(getMsg("errNumericRange", ["MinNum", "greater", 1])); if (config?.maxNum === 0) throw new Error(getMsg("errNumericRange", ["MaxNum", "greater", 1])); if (!config?.maxNum && !config?.minNum && !config?.sieveType) throw new Error(getMsg("errNotSpecify", ["Setting value"])); if (config?.minNum) { if (config.minNum < 1) throw new Error(getMsg("errNumericRange", ["MinNum", "greater", 1])); if (config.minNum > config?.maxNum || !config?.maxNum && config.minNum > maxNum) throw new Error(getMsg("errNumericRange", ["MinNum", "less", maxNum])); minNum = config.minNum; } if (config?.maxNum) { if (config.maxNum < 1) throw new Error(getMsg("errNumericRange", ["MaxNum", "greater", 1])); if (!Number.isSafeInteger(config.maxNum)) throw new Error( getMsg("errNumericRange", ["MaxNum", "less", Number.MAX_SAFE_INTEGER]) ); maxNum = config.maxNum; } if (config?.sieveType) { if (config.sieveType !== "eratosthenes" && config.sieveType !== "atkin") throw new Error( getMsg("errInvalidSpecify", ["sieveType", "eratosthenes or atkin"]) ); sieveType = config.sieveType; } genPrimeNums(); }; // Get whether the specified number is prime. const isPrime = (num) => { if (num < minNum) throw new Error( getMsg("errNumericRange", ["Specified number", "greater", minNum]) ); if (num > maxNum) throw new Error( getMsg("errNumericRange", ["Specified number", "less", maxNum]) ); return primeNums.includes(num); }; // Get whether two integers are prime to each other. const isCoprime = (a, b) => { if (a < minNum || b < minNum) throw new Error( getMsg("errNumericRange", ["Specified number", "greater", minNum]) ); if (a > maxNum || b > maxNum) throw new Error( getMsg("errNumericRange", ["Specified number", "less", maxNum]) ); const gcd = (x, y) => { while (y !== 0) { const temp = y; y = x % y; x = temp; } return x; }; return gcd(a, b) === 1; }; // Get prime numbers in a specified range. const getPrimes = (start = minNum, end = maxNum) => { if (start < minNum) throw new Error( getMsg("errNumericRange", ["Starting number", "greater", minNum]) ); if (end > maxNum) throw new Error( getMsg("errNumericRange", ["Ending number", "less", maxNum]) ); if (start > end) throw new Error( getMsg("errNumericRange", ["Starting number", "less", "ending number"]) ); const primesInRange = primeNums.filter( (prime) => prime >= start && prime <= end ); return primesInRange; }; // Get the average of prime numbers within the specified range. const getPrimesAverage = (start = minNum, end = maxNum, places = 2) => { if (places < 0) throw new Error( getMsg("errNumericRange", ["Decimal point position", "greater", 0]) ); const primesInRange = getPrimes(start, end); const average = primesInRange.length ? primesInRange.reduce((acc, prime) => acc + prime, 0) / primesInRange.length : 0; return Number(average.toFixed(places)); }; // Get the number of prime numbers in the specified range. const getPrimesCount = (start = minNum, end = maxNum) => { const primesInRange = getPrimes(start, end); if (!primesInRange.length) throw new Error( getMsg("errNoTarget", ["prime numbers", "specified range"]) ); return primesInRange.length; }; // Get a function that retrieves the index of a prime number within a specified range. const getPrimesIndex = (num, start = minNum, end = maxNum) => { if (num < minNum) throw new Error( getMsg("errNumericRange", ["Specified number", "greater", minNum]) ); if (num > maxNum) throw new Error( getMsg("errNumericRange", ["Specified number", "less", maxNum]) ); const primesInRange = getPrimes(start, end); if (!primesInRange.length) throw new Error( getMsg("errNoTarget", ["prime numbers", "specified range"]) ); const position = primesInRange.indexOf(num); if (position === -1) throw new Error( getMsg("errNoTarget", ["prime numbers", "specified range"]) ); return position + 1; }; // Get the median of the prime numbers within the specified range. const getPrimesMedian = (start = minNum, end = maxNum) => { const primesInRange = getPrimes(start, end); const length = primesInRange.length; if (!length) return 0; if (length % 2 === 0) { const midIndex1 = length / 2 - 1; const midIndex2 = length / 2; return (primesInRange[midIndex1] + primesInRange[midIndex2]) / 2; } else { const midIndex = Math.floor(length / 2); return primesInRange[midIndex]; } }; // Get the sum of prime numbers within the specified range. const getPrimesSum = (start = minNum, end = maxNum) => { const primesInRange = getPrimes(start, end); return primesInRange.length ? primesInRange.reduce((acc, prime) => acc + prime, 0) : 0; }; // Get the twin prime numbers within the specified range const getPrimesTwins = (start = minNum, end = maxNum) => { const primesInRange = getPrimes(start, end); if (!primesInRange.length) throw new Error( getMsg("errNoTarget", ["prime numbers", "specified range"]) ); return primesInRange.reduce((twin, num, index, arr) => { if (index < arr.length - 1 && arr[index + 1] - num === 2) { twin.push([num, arr[index + 1]]); } return twin; }, []); }; // Get the prime factorization result of the specified number. const getFactors = (num) => { if (num < minNum) throw new Error( getMsg("errNumericRange", ["Specified number", "greater", minNum]) ); if (num > maxNum) throw new Error( getMsg("errNumericRange", ["Specified number", "less", maxNum]) ); const factors = []; let divisor = 2; while (num > 1 && divisor <= num) { if (primeNums.includes(divisor) && num % divisor === 0) { factors.push(divisor); num /= divisor; } else { divisor++; } } return factors; }; // Get the prime factorization result of the specified number using a formula. const getFactorsFormula = (num) => { const factors = getFactors(num); const exponents = {}; factors.forEach((factor) => { exponents[factor] = (exponents[factor] || 0) + 1; }); return Object.entries(exponents) .map(([factor, exponent]) => exponent === 1 ? factor : `${factor}^${exponent}` ) .join("*"); }; // Get the multiplicative inverse of the specified number. (Get x that becomes a*x≡1(mod m)) const getMultInverse = (a, m) => { if (a < minNum || m < minNum) throw new Error( getMsg("errNumericRange", ["Specified number", "greater", minNum]) ); if (a > maxNum || m > maxNum) throw new Error( getMsg("errNumericRange", ["Specified number", "less", maxNum]) ); if (!isCoprime(a, m)) // For relatively prime integers a and m, there always exists a multiplicative inverse. throw new Error(getMsg("errNotExist", ["Multiplicative inverse"])); return Array.from({ length: a * m }, (_, x) => x + 1).find( (x) => (a * x) % m === 1 ); }; // Get a random prime number within the specified range. const getRandomPrime = (start = minNum, end = maxNum) => { const primesInRange = getPrimes(start, end); if (!primesInRange.length) throw new Error( getMsg("errNoTarget", ["prime numbers", "specified range"]) ); return primesInRange[Math.floor(Math.random() * primesInRange.length)]; }; // Generate prime number list according to sieve type genPrimeNums(); return { getMsg, getMinNum, getMaxNum, getSieveType, init, isPrime, isCoprime, getPrimes, getPrimesAverage, getPrimesCount, getPrimesIndex, getPrimesMedian, getPrimesSum, getPrimesTwins, getFactors, getFactorsFormula, getMultInverse, getRandomPrime, }; })();