Miller–Rabin primality test: Difference between revisions

Content added Content deleted
Line 3,010: Line 3,010:
assert isPrime(492366587u32)
assert isPrime(492366587u32)
assert isPrime(1645333507u32)
assert isPrime(1645333507u32)
</lang>

This is a fast deterministic version for all integers < 2^64.

<lang nim>

# Compile as: $ nim c -d:release mrtest.nim
# Run using: $ ./mrtest

import math # for gcd and mod
import bitops # for countTrailingZeroBits
import strutils, typetraits # for number input
import times, os # for timing code execution

proc addmod*[T: SomeInteger](a, b, modulus: T): T =
## Modular addition
let a_m = if a < modulus: a else: a mod modulus
if b == 0.T: return a_m
let b_m = if b < modulus: b else: b mod modulus

# Avoid doing a + b that could overflow here
let b_from_m = modulus - b_m
if a_m >= b_from_m: return a_m - b_from_m
return a_m + b_m # safe to add here; a + b < modulus

proc mulmod*[T: SomeInteger](a, b, modulus: T): T =
## Modular multiplication
var a_m = if a < modulus: a else: a mod modulus
var b_m = if b < modulus: b else: b mod modulus
if b_m > a_m: swap(a_m, b_m)
while b_m > 0.T:
if (b_m and 1) == 1: result = addmod(result, a_m, modulus)
a_m = (a_m shl 1) - (if a_m >= (modulus - a_m): modulus else: 0)
b_m = b_m shr 1

proc expmod*[T: SomeInteger](base, exponent, modulus: T): T =
## Modular exponentiation
result = 1 # (exp 0 = 1)
var (e, b) = (exponent, base)
while e > 0.T:
if (e and 1) == 1: result = mulmod(result, b, modulus)
e = e shr 1
b = mulmod(b, b, modulus)
# Returns true if +self+ passes Miller-Rabin Test on witnesses +b+
proc miller_rabin_test[T: SomeInteger](num: T, witnesses: seq[uint64]): bool =
var d = num - 1
let (neg_one_mod, n) = (d, d)
d = d shr countTrailingZeroBits(d) # suck out factors of 2 from d
for b in witnesses: # do m-r test with each witness base
if b.T mod num == 0: continue # **skip base if a multiple of input**
var s = d
var y = expmod(b.T, d, num)
while s != n and y != 1 and y != neg_one_mod:
y = mulmod(y, y, num)
s = s shl 1
if y != neg_one_mod and (s and 1) != 1: return false
true

proc selectWitnesses[T: SomeInteger](num: T): seq[uint64] =
## Best known deterministic witnnesses for given range and number of bases
## https://miller-rabin.appspot.com/
## https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
if num < 341_531u:
result = @[9345883071009581737u64]
elif num < 1_050_535_501u:
result = @[336781006125u64, 9639812373923155u64]
elif num < 350_269_456_337u:
result = @[4230279247111683200u64, 14694767155120705706u64, 16641139526367750375u64]
elif num < 55_245_642_489_451u:
result = @[2u64, 141889084524735u64, 1199124725622454117u64, 11096072698276303650u64]
elif num < 7_999_252_175_582_851u:
result = @[2u64, 4130806001517u64, 149795463772692060u64, 186635894390467037u64, 3967304179347715805u64]
elif num < 585_226_005_592_931_977u:
result = @[2u64, 123635709730000u64, 9233062284813009u64, 43835965440333360u64, 761179012939631437u64, 1263739024124850375u64]
elif num.uint64 < 18_446_744_073_709_551_615u64:
result = @[2u64, 325, 9375, 28178, 450775, 9780504, 1795265022]
else:
result = @[2u64, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

proc primemr*[T: SomeInteger](n: T): bool =
let primes = @[2u64, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
if n <= 47: return (n in primes)
let modp47 = 614889782588491410u # => primes.product, largest < 2^64
if gcd(n, modp47) != 1: return false # eliminates 86.2% of all integers
let witnesses = selectWitnesses(n)
miller_rabin_test(n, witnesses)

echo "\nprimemr?"
echo("n = ", 1645333507u)
var te = epochTime()
echo primemr 1645333507u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 2147483647u)
te = epochTime()
echo primemr 2147483647u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 844674407370955389u)
te = epochTime()
echo primemr 844674407370955389u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 1844674407370954349u)
te = epochTime()
echo primemr 1844674407370954349u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 1844674407370954351u)
te = epochTime()
echo primemr 1844674407370954351u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 9223372036854775783u)
te = epochTime()
echo primemr 9223372036854775783u
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 9241386435364257883u64)
te = epochTime()
echo primemr 9241386435364257883u64
echo (epochTime()-te).formatFloat(ffDecimal, 6)

echo "\nprimemr?"
echo("n = ", 18446744073709551533u64, " largest prime < 2^64")
te = epochTime()
echo primemr 18446744073709551533u64
echo (epochTime()-te).formatFloat(ffDecimal, 6)
</lang>
</lang>