intel-aes-0.1.1: Codec/Encryption/BurtonRNGSlow.hs
{- |
This module includes two all-haskell implementations of Burton
Smith's algorithm for a statistically-sound binary tree of random
number generators. See the following thread:
<http://www.mail-archive.com/haskell-cafe@haskell.org/msg83901.html>
Generally, Codec.Crypto.IntelAES should be used in favor of this
module, but it is included for benchmarking purposes.
-}
module Codec.Encryption.BurtonRNGSlow
(
mkBurtonGen_reference,
mkBurtonGen
-- Plus, instances exported of course.
)
where
import System.Random (RandomGen, next, split)
import Crypto.Random.DRBG ()
import Codec.Encryption.AES (encrypt)
import Data.LargeWord
-- import Debug.Trace
--------------------------------------------------------------------------------
-- Reference implementation.
-- | Type of random number generators
-- This is a very simple but extremely inefficient vesion.
data RNG_ref = RNG_ref {-# UNPACK #-} !Word128 -- Seed
{-# UNPACK #-} !Word128 -- Counter
next128 (RNG_ref k c) = (encrypt k c, RNG_ref k (c+1))
-- | This instance is inefficient because it creates 128bits of
-- randomness but only uses an Int-sized (32 or 64 bit) subset of
-- them.
instance RandomGen RNG_ref where
next g = (fromIntegral n, g')
where (n,g') = next128 g
split g@(RNG_ref k c) = (g', mkBurtonGen_reference n)
where (n,g') = next128 g
-- | Extra slow reference implementation.
mkBurtonGen_reference :: Word128 -> RNG_ref
mkBurtonGen_reference seed = RNG_ref seed 0
--------------------------------------------------------------------------------
bits_in_int = round $ 1 + logBase 2 (fromIntegral (maxBound :: Int))
steps = 128 `quot` bits_in_int
-- | Type representing a more efficient random number generator that
-- | still uses an all-Haskell implementation.
data RNG = RNG {-# UNPACK #-} !Word128 -- Seed
{-# UNPACK #-} !Word128 -- Last batch of random bits generated.
{-# UNPACK #-} !Word128 -- Counter
{-# UNPACK #-} !Int -- Phase/step
-- | The idea with this one is that once we generate 128 bits of
-- randomness we parcel it out into two or four ints.
mkBurtonGen :: Word128 -> RNG
mkBurtonGen seed = RNG seed (encrypt seed 0) 1 0
next128' (RNG k _ c _) = RNG k (encrypt k c) (c+1) 0
instance RandomGen RNG where
-- In this scenario its time to generate a new batch of bits:
-- next g@(RNG k bits c s) | s == steps = next (next128' g)
-- FIXME: ASSUMES 64 BIT INTS -- NONPORTABLE:
-- This takes advantage the structure of the Word128 type:
next g@(RNG k bits c 0) = (fromIntegral (loHalf bits), RNG k bits c 1)
next g@(RNG k bits c 1) = (fromIntegral (hiHalf bits), next128' g)
-- We waste some random bits here:
split g@(RNG k bits c s) = (g', next128' g')
where g' = next128' g