Skip to content

Fermat Primality Test

In this task, for a given natural number

  1. generate a sequence of pseudorandom natural numbers such that ,
  2. 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

haskell
data LCG = LCG  Int Int Int Int deriving Show

where the four Ints in the LCG 4-tuple are , , , and w.r.t. . Implement the function

haskell
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

haskell
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.

haskell
> runState (generate_range 1 100) (LCG 513 1 1 1024)  
(20, LCG 513 514 1 1024)

Implement the primality test in the function

haskell
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:

haskell
> 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
haskell
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)