"how numbers are stored and used in computers"
The Pocklington-Lehmer primality test is a deterministic primality test that can prove a number is prime by finding a partial factorization of n - 1. It's particularly useful when n - 1 has a large prime factor.
The test is based on the following theorem:
Let
Then
The time complexity depends on the factorization of
Here's an implementation of the Pocklington-Lehmer test.
code.js1function modPow(base, exponent, modulus) { 2 if (modulus === 1n) return 0n; 3 4 let result = 1n; 5 base = base % modulus; 6 7 while (exponent > 0n) { 8 if (exponent % 2n === 1n) { 9 result = (result * base) % modulus; 10 } 11 base = (base * base) % modulus; 12 exponent = exponent / 2n; 13 } 14 15 return result; 16} 17 18function gcd(a, b) { 19 a = BigInt(Math.abs(Number(a))); 20 b = BigInt(Math.abs(Number(b))); 21 22 while (b) { 23 [a, b] = [b, a % b]; 24 } 25 26 return a; 27} 28 29function findPrimeFactors(n) { 30 const factors = []; 31 let num = n; 32 33 // Handle factors of 2 34 while (num % 2n === 0n) { 35 factors.push(2n); 36 num = num / 2n; 37 } 38 39 // Handle odd factors 40 for (let i = 3n; i * i <= num; i += 2n) { 41 while (num % i === 0n) { 42 factors.push(i); 43 num = num / i; 44 } 45 } 46 47 // If num > 1, it's prime 48 if (num > 1n) { 49 factors.push(num); 50 } 51 52 return [...new Set(factors)]; // Return unique factors 53} 54 55function pocklingtonLehmer(n) { 56 // Convert to BigInt and handle small cases 57 n = BigInt(n); 58 if (n <= 1n) return false; 59 if (n <= 3n) return true; 60 if (n % 2n === 0n) return false; 61 62 const nMinus1 = n - 1n; 63 const sqrt = BigInt(Math.floor(Math.sqrt(Number(n)))); 64 65 // Find prime factors of n-1 66 const primeFactors = findPrimeFactors(nMinus1); 67 68 // Try to find suitable F and R 69 for (let i = 0n; i < primeFactors.length; i++) { 70 const F = nMinus1 / primeFactors[i]; 71 72 if (F > sqrt - 1n) { 73 // Try different bases 74 for (let a = 2n; a < 20n; a++) { 75 const powerMod = modPow(a, nMinus1, n); 76 if (powerMod !== 1n) continue; 77 78 const factor = modPow(a, F, n); 79 if (gcd(factor - 1n, n) === 1n) { 80 return true; 81 } 82 } 83 } 84 } 85 86 return false; 87} 88 89// Example usage: 90console.log(pocklingtonLehmer(17)); // true 91console.log(pocklingtonLehmer(561)); // false (Carmichael number)
Here's a more comprehensive version that includes additional checks:
code.js1function extendedPocklingtonLehmer(n) { 2 // Handle small cases 3 if (n <= 1n) return false; 4 if (n <= 3n) return true; 5 if (n % 2n === 0n) return false; 6 7 // Trial division by small primes 8 const smallPrimes = [3n, 5n, 7n, 11n, 13n, 17n, 19n, 23n, 29n, 31n]; 9 for (const p of smallPrimes) { 10 if (n === p) return true; 11 if (n % p === 0n) return false; 12 } 13 14 const nMinus1 = n - 1n; 15 const sqrt = BigInt(Math.floor(Math.sqrt(Number(n)))); 16 17 // Find complete factorization of n-1 18 const factors = findPrimeFactors(nMinus1); 19 20 // Try different factorizations 21 for (let mask = 1; mask < (1 << factors.length); mask++) { 22 let F = 1n; 23 for (let i = 0; i < factors.length; i++) { 24 if (mask & (1 << i)) { 25 F *= factors[i]; 26 } 27 } 28 29 if (F > sqrt - 1n) { 30 // Try different bases 31 for (let a = 2n; a < 20n; a++) { 32 let isValid = true; 33 34 // Check first condition 35 if (modPow(a, nMinus1, n) !== 1n) { 36 continue; 37 } 38 39 // Check second condition for each prime factor 40 for (const q of factors) { 41 const power = nMinus1 / q; 42 const value = modPow(a, power, n); 43 if (gcd(value - 1n, n) !== 1n) { 44 isValid = false; 45 break; 46 } 47 } 48 49 if (isValid) return true; 50 } 51 } 52 } 53 54 return false; 55}
The Pocklington-Lehmer test is useful in several contexts:
When implementing the test:
code.js1function optimizedModPow(base, exponent, modulus) { 2 if (modulus === 1n) return 0n; 3 4 const bits = exponent.toString(2); 5 let result = 1n; 6 base = base % modulus; 7 8 for (const bit of bits) { 9 result = (result * result) % modulus; 10 if (bit === '1') { 11 result = (result * base) % modulus; 12 } 13 } 14 15 return result; 16}
code.js1function binaryGCD(a, b) { 2 if (b === 0n) return a; 3 4 let shift = 0n; 5 while ((a | b) & 1n === 0n) { 6 a >>= 1n; 7 b >>= 1n; 8 shift++; 9 } 10 11 while (a & 1n === 0n) a >>= 1n; 12 13 while (b !== 0n) { 14 while (b & 1n === 0n) b >>= 1n; 15 if (a > b) [a, b] = [b, a]; 16 b -= a; 17 } 18 19 return a << shift; 20}
The test is important because:
For practical applications, this test is often combined with other methods or used in specific cases where n-1 has a known factorization.