// // PrimeTest.swift // CS.BigInt // // Created by Károly Lőrentey on 2016-01-04. // Copyright © 2016-2017 Károly Lőrentey. // /// The first several [prime numbers][primes]. /// /// [primes]: https://oeis.org/A000040 let primes: [CS.BigUInt.Word] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41] /// The ith element in this sequence is the smallest composite number that passes the strong probable prime test /// for all of the first (i+1) primes. /// /// This is sequence [A014233](http://oeis.org/A014233) on the [Online Encyclopaedia of Integer Sequences](http://oeis.org). let pseudoPrimes: [CS.BigUInt] = [ /* 2 */ 2_047, /* 3 */ 1_373_653, /* 5 */ 25_326_001, /* 7 */ 3_215_031_751, /* 11 */ 2_152_302_898_747, /* 13 */ 3_474_749_660_383, /* 17 */ 341_550_071_728_321, /* 19 */ 341_550_071_728_321, /* 23 */ 3_825_123_056_546_413_051, /* 29 */ 3_825_123_056_546_413_051, /* 31 */ 3_825_123_056_546_413_051, /* 37 */ "318665857834031151167461", /* 41 */ "3317044064679887385961981", ] extension CS.BigUInt { //MARK: Primality Testing /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base. /// /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime public func isStrongProbablePrime(_ base: CS.BigUInt) -> Bool { precondition(base > (1 as CS.BigUInt)) precondition(self > (0 as CS.BigUInt)) let dec = self - 1 let r = dec.trailingZeroBitCount let d = dec >> r var test = base.power(d, modulus: self) if test == 1 || test == dec { return true } if r > 0 { let shift = self.leadingZeroBitCount let normalized = self << shift for _ in 1 ..< r { test *= test test.formRemainder(dividingBy: normalized, normalizedBy: shift) if test == 1 { return false } if test == dec { return true } } } return false } /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime. /// /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations, /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default, /// but you may specify your own choice. /// /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before /// diving into (slower) Miller-Rabin testing. /// /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to /// return a correct result. /// /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test public func isPrime(rounds: Int = 10) -> Bool { if count <= 1 && self[0] < 2 { return false } if count == 1 && self[0] < 4 { return true } // Even numbers above 2 aren't prime. if self[0] & 1 == 0 { return false } // Quickly check for small primes. for i in 1 ..< primes.count { let p = primes[i] if self.count == 1 && self[0] == p { return true } if self.quotientAndRemainder(dividingByWord: p).remainder == 0 { return false } } /// Give an exact answer when we can. if self < pseudoPrimes.last! { for i in 0 ..< pseudoPrimes.count { guard isStrongProbablePrime(CS.BigUInt(primes[i])) else { break } if self < pseudoPrimes[i] { // `self` is below the lowest pseudoprime corresponding to the prime bases we tested. It's a prime! return true } } return false } /// Otherwise do as many rounds of random SPPT as required. for _ in 0 ..< rounds { let random = CS.BigUInt.randomInteger(lessThan: self - 2) + 2 guard isStrongProbablePrime(random) else { return false } } // Well, it smells primey to me. return true } } extension CS.BigInt { //MARK: Primality Testing /// Returns true iff this integer passes the [strong probable prime test][sppt] for the specified base. /// /// [sppt]: https://en.wikipedia.org/wiki/Probable_prime public func isStrongProbablePrime(_ base: CS.BigInt) -> Bool { precondition(base.sign == .plus) if self.sign == .minus { return false } return self.magnitude.isStrongProbablePrime(base.magnitude) } /// Returns true if this integer is probably prime. Returns false if this integer is definitely not prime. /// /// This function performs a probabilistic [Miller-Rabin Primality Test][mrpt], consisting of `rounds` iterations, /// each calculating the strong probable prime test for a random base. The number of rounds is 10 by default, /// but you may specify your own choice. /// /// To speed things up, the function checks if `self` is divisible by the first few prime numbers before /// diving into (slower) Miller-Rabin testing. /// /// Also, when `self` is less than 82 bits wide, `isPrime` does a deterministic test that is guaranteed to /// return a correct result. /// /// [mrpt]: https://en.wikipedia.org/wiki/Miller–Rabin_primality_test public func isPrime(rounds: Int = 10) -> Bool { if self.sign == .minus { return false } return self.magnitude.isPrime(rounds: rounds) } }