3

I just got into haskell. I'm trying to write a prime number generator that would return me a prime number depending on the number of bits I give it. Do I use some sort of Sieve of Eratosthenes? Would this be the quickest way? Currently, I already have a prime number checker for Miller-Rabin. Is there a correct way and a wrong way to do this? Also, I want to be able to generate large numbers very quickly.

Ex. generate a prime number that is of 32 bits

genp 32

Code so far.

import System.IO
import System.Random
import Data.List
import Control.Monad
import Control.Arrow


primesTo100 = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]

powerMod :: (Integral a, Integral b) => a -> a -> b -> a
powerMod m _ 0 =  1
powerMod m x n | n > 0 = join (flip f (n - 1)) x `rem` m where
  f _ 0 y = y
  f a d y = g a d where
    g b i | even i    = g (b*b `rem` m) (i `quot` 2)
          | otherwise = f b (i-1) (b*y `rem` m)

witns :: (Num a, Ord a, Random a) => Int -> a -> IO [a]
witns x y = do
     g <- newStdGen 
     let r = [9080191, 4759123141, 2152302898747, 3474749600383, 341550071728321]
         fs = [[31,73],[2,7,61],[2,3,5,7,11],[2,3,5,7,11,13],[2,3,5,7,11,13,17]]
     if  y >= 341550071728321
      then return $ take x $ randomRs (2,y-1) g
       else return $ snd.head.dropWhile ((<= y).fst) $ zip r fs

isMillerRabinPrime :: Integer -> IO Bool
isMillerRabinPrime n | n `elem` primesTo100 = return True
                     | otherwise = do
    let pn = pred n
        e = uncurry (++) . second(take 1) . span even . iterate (`div` 2) $ pn
        try = return . all (\a -> let c = map (powerMod n a) e in
                                  pn `elem` c || last c == 1)
    witns 100 n >>= try



type Prime = Integer

isProbablyPrime :: Prime -> Bool
isProbablyPrime n = isMillerRabinPrime n

pickFirstFrom :: Integer -> Prime
pickFirstFrom n = head $ filter isProbablyPrime [n..]

numBits = 1024
constantStdGen = mkStdGen 1234567 -- a random number

randomByBits n = fst $ randomR (2^(n-1), 2^n-1) constantStdGen

answer = pickFirstFrom (randomByBits numBits)

Using Library functions with RSA:

import Control.Monad.Fix 
import Math.NumberTheory.Primes

rndPrime :: Int -> IO Integer
rndPrime bits =
    fix $ \again -> do
        x <- fmap (.|. 1) $ randomRIO (2^(bits - 1), 2^bits - 1)
        if isPrime x then return x else again

rndPrimes :: Int -> IO (Integer, Integer)
rndPrimes bits = do
    p <- rndPrime bits
    fix $ \again -> do
        q <- rndPrime bits
        if p /= q then return (p, q) else again

Thanks guys, I really appreciate your help.

user805981
  • 9,979
  • 8
  • 44
  • 64

2 Answers2

16

For generating an odd random prime number of a specific bit size there is this de facto way of doing it, assuming a (usually probabilistic) primality test isPrime:

rndPrime :: Int -> IO Integer
rndPrime bits =
    fix $ \again -> do
        x <- fmap (.|. 1) $ randomRIO (2^(bits - 1), 2^bits - 1)
        if isPrime x then return x else again

For RSA:

rndPrimes :: Int -> IO (Integer, Integer)
rndPrimes bits = do
    p <- rndPrime bits
    fix $ \again -> do
        q <- rndPrime bits
        if p /= q then return (p, q) else again

You can find ready-made fast primality tests in the arithmoi package, including the current state of the art, the Baillie PSW test. Then just import the Math.NumberTheory.Primes module and the code above should work out of the box.

ertes
  • 176
  • 2
  • @user805981 import `Control.Monad.Fix` – Philip JF Nov 09 '12 at 04:58
  • @PhilipJF `Couldn't match expected type `Integer' with actual type `(Integer, Integer)' Expected type: IO (Integer, Integer) Actual type: IO ((Integer, Integer), (Integer, Integer)) In a stmt of a 'do' block: fix $ \ again -> do { q <- rndPrimes bits; if p /= q then return ... else again } In the expression: do { p <- rndPrimes bits; fix $ \ again -> do { ... } } Failed, modules loaded: none. ` – user805981 Nov 09 '12 at 06:29
  • 1
    @user805981 Compiles without a problem here. What is the exact code you're trying to compile? – Daniel Fischer Nov 09 '12 at 09:33
  • 1
    @user805981: You're calling `rndPrimes` instead of `rndPrime`. – hammar Nov 09 '12 at 12:13
  • @hammar `prime.hs:7:20: Not in scope: `.|.' prime.hs:7:29: Not in scope: `randomRIO'` I'm now able to compile the number library. And my formatting seems to be correct. What does this mean? My code for it is currently updated into the original post. – user805981 Nov 09 '12 at 13:06
  • 1
    @user805981: `.|.` is from the `Data.Bits` module, and `randomRIO` from `System.Random`. You'll need to import those modules. – hammar Nov 09 '12 at 13:23
  • you should honestly be my professor instead xD. It works beautifully now. :) – user805981 Nov 09 '12 at 17:22
  • The test `p /= q` should not be practically relevant for non-trivial bitsizes, as the likelihood to get the same primes here is roughly the same of accidentally randomly getting the DNSSEC root key, isn’t it? – Joachim Breitner Mar 16 '17 at 21:09
2

You're in luck mister. The prime numbers are appearing so frequently that you just can try to enumerate all numbers from a given start number and pick the first one that is prime.

The solution would be something like this

import System.Random

type Prime = Integer

isProbablyPrime :: Prime -> Bool
isProbablyPrime n = error "insert miller rabin here"

pickFirstFrom :: Integer -> Prime
pickFirstFrom n = head $ filter isProbablyPrime [n..]

numBits = 1024
constantStdGen = mkStdGen 1234567 -- a random number

randomByBits n = fst $ randomR (2^(n-1), 2^n-1) constantStdGen

answer = pickFirstFrom (randomByBits numBits)

Here answer will be as you specified in your comments. Note however that you have to modify this code to allow more than one constant seed!

Tarrasch
  • 10,199
  • 6
  • 41
  • 57
  • hmmm... strange its giving me the following error `Couldn't match expected type `Integer' with actual type `(a0, StdGen)' In the return type of a call of `randomByBits' In the first argument of `pickFirstFrom', namely `(randomByBits numBits)' In the expression: pickFirstFrom (randomByBits numBits) Failed, modules loaded: none. ` – user805981 Nov 09 '12 at 01:51
  • 2
    @user805981 You need a `fst`, `randomByBits n = fst $ randomR ...`. Small mistake in the answer. – Daniel Fischer Nov 09 '12 at 02:00
  • Thanks Dan. I'm getting the following issue now `Couldn't match expected type `Bool' with actual type `IO Bool' In the return type of a call of `isMillerRabinPrime' In the expression: isMillerRabinPrime n In an equation for `isProbablyPrime': isProbablyPrime n = isMillerRabinPrime n Failed, modules loaded: none.` – user805981 Nov 09 '12 at 02:36
  • Yea, your version is `:: Integer -> IO Bool` but mine is `:: Integer -> Bool`. Try to use `constantStdGen` in order to avoid the `IO` parts (in particular `newStdGen`). – Tarrasch Nov 09 '12 at 04:41
  • @Tarrasch Even changing would not work for me. The boolean does not match the returned expression, I guess... This is what I got from it. ` Couldn't match expected type `Bool' with actual type `m0 a0' In the return type of a call of `return' In the expression: return True In an equation for `isMillerRabinPrime': isMillerRabinPrime n | n `elem` primesTo100 = return True | otherwise = do { let pn = ... ....; witns 100 n >>= try } Failed, modules loaded: none.` – user805981 Nov 09 '12 at 06:26