Miller-Rabin primality test
% Remco Bloemen % 2014-02-27, last updated 2014-03-04
As an exercise I implemented the Miller-Rabin primality test in plain C++. It turns out this algorithms lends itself for a true festival of operators, so I couldn’t resist making the code very dense:
bool
That’s it! Now, the algorithm will overflow for n \geq 2^{32} but I intend to use it to find 64-bit primes, so I’ll have to work on that.
Interestingly, the "primes page" claims that (source):
If n < 4,759,123,141 is a 2, 7 and 61-SPRP, then n is prime.
(A number n is called “k-SPRP” or “strong probable-prime base k” if it passes Miller Rabin with this k)
Since 4 759 123 141 is greater that 2^{32} we can use this to implement a fast and exact function to determine whether some 32 bit number is prime. I special cased the first several primes to improve performance and made the whole thing into an incomprehensible operator soup, just like the Miller-Rabin implementation:
bool
64-bits
In the previous post I showed an implementation of Miller-Rabin that overflowed when the number was more that 32 bits. In this post I present a way to fix this.
I could remember an instruction I used before, mulq which does 128 bit
multiplication. It multiplies two 64 bit number and stores the least
significant 64 bits in rax and the significant half in rdx.
Now we still need to reduce this number modulo m. Originally I planned
to create a table of 2^n \mod m, use the bits of rdx to add them and
then add rax % m. But after consulting this x86-64
manual
I found out about divq, which takes a 128 bit number stored in
rdx:rax, a 64 bit number m and produces the quotient and remainder.
The whole thing can be coded as:
/// Modular multiplication
/// @param a The first factor, a < m
/// @param a The second factor, b < m
/// @param m The modulus
/// @return The reduced product, a b mod m < m
uint64
This should work fast, but I still need to look into Montgomery reduction. Perhaps I should benchmark the two.
Modular exponentiation can now be implemented using exponentiation by squaring:
/// Modular exponentiation
/// @param b The base, b < m
/// @param e The exponent
/// @param m The modulus
/// @returns The reduced power of a, a^b mod m
uint64
And the Miller-Rabin test becomes:
/// Miller-Rabin probabilistic primality testing
/// @param n The number to test for primality
/// @param k The witness for primality
/// @returns True iff when n is a k-stong pseudoprime
bool
Together with the prime sieve I created earlier a fast probablilistic prime checking procedure can be developed:
const vector<uint64> small_primes = ;
/// Miller-Rabin probabilistic primality testing
/// @param n The number to test for primality
/// @returns False when n is not a prime
bool
This algorithm correctly identifies the first ten primes less than 2^{64}. But then again, so does a single Miller-Rabin round with K= 17. Perhaps I should implement the Baillie-PSW algorithm, there are rumours that it has no false positives for 64 bits!