Fermat Primality Test
In this task, for a given natural number
- generate a sequence of pseudorandom natural numbers such that ,
- test whether is prime by checking whether the following equation holds for each generated pseudorandom number
If holds for all numbers , it is highly probable that is prime. This probabilistic primality test is known as the Fermat Primality Test. Note, the Carmichael numbers, which are composite yet pass the test for all relatively prime to the respective number, are avoided when testing your implementation of this task.
Pseudo-random Number Generation
To generate pseudorandom numbers in a given interval, use the Linear Congruential Generator (LCG)
where , and are constants. This equation generates the next pseudorandom number from the previous . The number is the seed.
The number drawn from can be transformed to the interval as
Your task is to implement the Fermat Primality Test in Haskell. There are two parts to this task: first, implement the LCG, and then the test itself.
Implementation
The LCG generator is represented as
data LCG = LCG Int Int Int Int deriving Show
where the four Int
s in the LCG
4-tuple are , , , and w.r.t. . Implement the function
generate_range :: Int -> Int -> State LCG Int
which is used to sample random integers where the lower and upper bounds are set in the first and second function input, respectively. The state of the LCG is kept using the State
monad. So start your code with
import Control.Monad.State
Note that it is desirable to follow and as closely as possible, since the tasks are tested with constant seed and thus are considered deterministic. The function is used as follows.
> runState (generate_range 1 100) (LCG 513 1 1 1024)
(20, LCG 513 514 1 1024)
Implement the primality test in the function
primality :: Int -> Int -> State LCG Bool
where the first input is the potential prime and the second is the number of repetitions of the test (i.e., the number of generated pseudorandom numbers). The function is used as follows:
> evalState (primality 17 1000) (LCG 513 1 1 1024)
True
> evalState (primality 42 1000) (LCG 513 1 1 1024)
False
> evalState (primality 9 0) (LCG 513 1 1 1024)
True
Hint
To prevent overflow of Int
, compute sequentially using the identity , i.e., sequentially multiplying by and applying modulo to each partial result.
Details
import Control.Monad.State
---- linear congruential generator
data LCG = LCG Int Int Int Int deriving Show -- A*x + C mod M
generate :: State LCG Int
generate = do (LCG a x c m) <- get
let x' = (a * x + c) `mod` m
put (LCG a x' c m)
return x'
generate_range :: Int -> Int -> State LCG Int -- a <= x < b
generate_range a b = do x <- generate
let x' = (x `mod` (b-a)) + a
return x'
modulo_power :: Int -> Int -> Int -> Int
modulo_power a n m = iter a n where
iter b 1 = b
iter b nn = iter (b*a `mod` m) (nn-1)
fermat_comp :: Int -> Int -> Int
fermat_comp p b = (modulo_power b (p-1) p)
fermat_check :: Int -> Int -> Int -> State LCG Bool
fermat_check p n 1 = primality p (n-1)
fermat_check _ _ _ = return False
primality :: Int -> Int -> State LCG Bool
primality p 0 = return True
primality p n = do b <- generate_range 1 p
fermat_check p n (fermat_comp p b)