\section{Probabalistic Primality Testing}
%$Log: Prime.lhs,v $
%Revision 1.1 2004/08/05 11:13:39 malcolm
%Add a regression testsuite for the nhc98 compiler. It isn't very good,
%but it is better than nothing. I've been using it for about four years
%on nightly builds, so it's about time it entered the repository! It
%includes a slightly altered version of the nofib suite.
%Instructions are in the README.
%
%Revision 1.2 1996/07/25 21:32:57 partain
%Bulk of final changes for 2.01
%
%Revision 1.1 1996/01/08 20:04:21 partain
%Initial revision
%
%Revision 1.2 92/06/30 18:34:22 dlester
%Bug fix; now works for 1.
%
%Revision 1.1 92/06/30 15:55:20 dlester
%Initial revision
%
> module Prime (multiTest) where
> import IntLib
> import Random
The function @multiTest@ tests the integer @n@ for primality @k@ times
using the random numbers supplied in @rs@; it returns a @Bool@ and a
depleted sequence of random numbers. When the @Bool@ is @True@ the
number is prime with probability $1 - 4^{-@k@}$; the number is
definitely composite when the @Bool@ is @False@.
> multiTest :: Int -> [Int] -> Integer -> (Bool, [Int])
> multiTest k rs n
> = if n <= 1 || even n then (n==2, rs) else mTest k rs
> where mTest 0 rs = (True, rs)
> mTest k rs = if t then mTest (k-1) rs' else (False, rs')
> where (t, rs') = singleTest n (findKQ n) rs
The function @findKQ@ takes an odd integer $n$ and returns the tuple
$(k,q)$ such that $n = q2^k+1$.
> findKQ :: Integer -> (Integer, Integer)
> findKQ n = f (0, (n-1))
> where f (k,q) = if r == 0 then f (k+1, d) else (k, q)
> where (d, r) = q `divMod` 2
The function @singleTest@ takes an odd, positive, @Integer@ @n@ and a
pair of @Integer@'s derived from @n@ by the @findKQ@ function
> singleTest :: Integer -> (Integer, Integer) -> [Int] -> (Bool, [Int])
> singleTest n kq rs
> = (singleTestX n kq (2+x), rs')
> where (x, rs') = random (n-2) rs
Given a value @x@ we can test whether we have a witness to @n@'s
compositeness using @singleTestX@.
> singleTestX n (k, q) x
> = t == 1 || t == n-1 || witness ts
> where (t:ts) = take (fromInteger k) (iterate square (powerMod x q n))
> witness [] = False
> witness (t:ts) = if t == n-1 then True else
> if t == 1 then False else
> witness ts
> square x = (x*x) `mod` n
The @random@ function takes a number @n@ and a list of pseudo-random
numbers @rs@ and returns a tuple consisting of an @Integer@ $x$ in the
range $0 \leq x < @n@$, and the remainder of the pseudo-random
numbers.
> random :: Integer -> [Int] -> (Integer, [Int])
> random n rs = (makeNumber 65536 (uniform ns rs1), rs2)
> where ns = chop 65536 n
> (rs1,rs2) = splitAt (length ns) rs
The @uniform@ function generates a sequence of @Integer@'s such that,
when considered as a sequence of digits, we generate a number uniform
in the range @0..ns@ from the random numbers @rs@.
> uniform :: [Integer] -> [Int] -> [Integer]
> uniform [n] [r] = [toInteger r `mod` n]
> uniform (n:ns) (r:rs) = if t == n then t: uniform ns rs
> else t: map ((`mod` 65536). toInteger) rs
> where t = toInteger r `mod` (n+1)
This concludes the Prime testing algorithm.
|