-- |
-- Module      : Crypto.Number.Generate
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : Good

module Crypto.Number.Generate
    ( generateMax
    , generateBetween
    , generateOfSize
    ) where

import Crypto.Number.Serialize
import Crypto.Random.API
import qualified Data.ByteString as B
import Data.Bits ((.|.))

-- | generate a positive integer x, s.t. 0 <= x < m
--
-- Note that depending on m value, the number distribution
-- generated by this function is not necessarily uniform.
generateMax :: CPRG g => g -> Integer -> (Integer, g)
generateMax :: g -> Integer -> (Integer, g)
generateMax rng :: g
rng m :: Integer
m = g -> Int -> (ByteString -> Integer) -> (Integer, g)
forall g a. CPRG g => g -> Int -> (ByteString -> a) -> (a, g)
withRandomBytes g
rng (Integer -> Int
lengthBytes Integer
m) ((ByteString -> Integer) -> (Integer, g))
-> (ByteString -> Integer) -> (Integer, g)
forall a b. (a -> b) -> a -> b
$ \bs :: ByteString
bs ->
    ByteString -> Integer
os2ip ByteString
bs Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`mod` Integer
m

-- | generate a number between the inclusive bound [low,high].
generateBetween :: CPRG g => g -> Integer -> Integer -> (Integer, g)
generateBetween :: g -> Integer -> Integer -> (Integer, g)
generateBetween rng :: g
rng low :: Integer
low high :: Integer
high = (Integer
low Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
v, g
rng')
    where (v :: Integer
v, rng' :: g
rng') = g -> Integer -> (Integer, g)
forall g. CPRG g => g -> Integer -> (Integer, g)
generateMax g
rng (Integer
high Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
low Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ 1)

-- | generate a positive integer of a specific size in bits.
-- the number of bits need to be multiple of 8. It will always returns
-- an integer that is close to 2^(1+bits/8) by setting the 2 highest bits to 1.
generateOfSize :: CPRG g => g -> Int -> (Integer, g)
generateOfSize :: g -> Int -> (Integer, g)
generateOfSize rng :: g
rng bits :: Int
bits = g -> Int -> (ByteString -> Integer) -> (Integer, g)
forall g a. CPRG g => g -> Int -> (ByteString -> a) -> (a, g)
withRandomBytes g
rng (Int
bits Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 8) ((ByteString -> Integer) -> (Integer, g))
-> (ByteString -> Integer) -> (Integer, g)
forall a b. (a -> b) -> a -> b
$ \bs :: ByteString
bs ->
    ByteString -> Integer
os2ip (ByteString -> Integer) -> ByteString -> Integer
forall a b. (a -> b) -> a -> b
$ (Word8, ByteString) -> ByteString
forall a b. (a, b) -> b
snd ((Word8, ByteString) -> ByteString)
-> (Word8, ByteString) -> ByteString
forall a b. (a -> b) -> a -> b
$ (Word8 -> Word8 -> (Word8, Word8))
-> Word8 -> ByteString -> (Word8, ByteString)
forall acc.
(acc -> Word8 -> (acc, Word8))
-> acc -> ByteString -> (acc, ByteString)
B.mapAccumL (\acc :: Word8
acc w :: Word8
w -> (0, Word8
w Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.|. Word8
acc)) 0xc0 ByteString
bs