pcg-random-0.1.1.0: src/System/Random/PCG/Unique.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MultiParamTypeClasses #-}
--------------------------------------------------------------------
-- |
-- Module : System.Random.PCG.Unique
-- Copyright : Copyright (c) 2014-2015, Christopher Chalmers <c.chalmers@me.com>
-- License : BSD3
-- Maintainer : Christopher Chalmers <c.chalmers@me.com>
-- Stability : experimental
-- Portability: CPP, FFI
--
-- Unique variant of the PCG random number generator. Guarantees the
-- sequence to be unique by using the pointer address to select the
-- output sequence.
--
-- There is no way to freeze the state because then it wouldn't be
-- unique anymore. Also, generators can't be initialized in ST because
-- we don't know what pointer reference they'll get.
--
-- See <http://www.pcg-random.org> for details.
--
-- @
-- import System.Random.PCG.Unique
--
-- three :: IO [Double]
-- three = do
-- g <- create
-- a <- uniform g
-- b <- uniform g
-- c <- uniform g
-- return [a,b,c]
-- @
module System.Random.PCG.Unique
( -- * Gen
Gen
, create, createSystemRandom, initialize, withSystemRandom
-- * Getting random numbers
, Variate (..)
, advance, retract
-- * Type restricted versions
-- ** uniform
, uniformW8, uniformW16, uniformW32, uniformW64
, uniformI8, uniformI16, uniformI32, uniformI64
, uniformF, uniformD, uniformBool
-- ** uniformR
, uniformRW8, uniformRW16, uniformRW32, uniformRW64
, uniformRI8, uniformRI16, uniformRI32, uniformRI64
, uniformRF, uniformRD, uniformRBool
-- ** uniformB
, uniformBW8, uniformBW16, uniformBW32, uniformBW64
, uniformBI8, uniformBI16, uniformBI32, uniformBI64
, uniformBF, uniformBD, uniformBBool
) where
import Data.Functor
import Foreign
import System.Random.PCG.Class
-- | Standard initial seed.
seed :: Word64
seed = 0x4d595df4d0f33173
-- | Create a 'Gen' from a fixed initial seed.
create :: IO Gen
create = initialize seed
-- do
-- p <- malloc
-- poke p seed
-- return (Gen p)
-- Note that this does produce a unique sequence but if two generators
-- are created in sequence they'll have the similar pointer references
-- and the first couple of numbers are likely to be the same. Since
-- this is undesirable we run initialise to randomise it.
------------------------------------------------------------------------
-- Generator
------------------------------------------------------------------------
-- | State of the random number generator
newtype Gen = Gen (Ptr Word64)
deriving (Eq, Ord)
-- | Create a generator from two words. Note: this is not the same as the
-- two words in a 'Seed'.
initialize :: Word64 -> IO Gen
initialize a = do
p <- malloc
pcg32u_srandom_r p a
return (Gen p)
-- | Seed with system random number. (\"@\/dev\/urandom@\" on Unix-like
-- systems, time otherwise).
withSystemRandom :: (Gen -> IO a) -> IO a
withSystemRandom f = sysRandom >>= initialize >>= f
-- | Seed a PRNG with data from the system's fast source of pseudo-random
-- numbers. All the caveats of 'withSystemRandom' apply here as well.
createSystemRandom :: IO Gen
createSystemRandom = withSystemRandom return
-- -- | Generate a uniform 'Word32' bounded above by the given bound.
-- uniformB :: Word32 -> UGen -> IO Word32
-- uniformB u (UGen p) = unsafePrimToPrim $ pcg32u_boundedrand_r p u
-- {-# INLINE uniformB #-}
-- | Advance the given generator n steps in log(n) time.
advance :: Word64 -> Gen -> IO ()
advance u (Gen p) = pcg32u_advance_r p u
{-# INLINE advance #-}
-- | Retract the given generator n steps in log(2^64-n) time. This
-- is just @advance (-n)@.
retract :: Word64 -> Gen -> IO ()
retract u g = advance (-u) g
{-# INLINE retract #-}
------------------------------------------------------------------------
-- Foreign calls
------------------------------------------------------------------------
foreign import ccall unsafe "pcg_unique_64_srandom_r"
pcg32u_srandom_r :: Ptr Word64 -> Word64 -> IO ()
foreign import ccall unsafe "pcg_unique_64_xsh_rs_32_random_r"
pcg32u_random_r :: Ptr Word64 -> IO Word32
foreign import ccall unsafe "pcg_unique_64_xsh_rs_32_boundedrand_r"
pcg32u_boundedrand_r :: Ptr Word64 -> Word32 -> IO Word32
foreign import ccall unsafe "pcg_unique_64_advance_r"
pcg32u_advance_r :: Ptr Word64 -> Word64 -> IO ()
------------------------------------------------------------------------
-- Instances
------------------------------------------------------------------------
instance Generator Gen IO where
uniform1 f (Gen p) = f <$> pcg32u_random_r p
{-# INLINE uniform1 #-}
uniform2 f (Gen p) = do
w1 <- pcg32u_random_r p
w2 <- pcg32u_random_r p
return $ f w1 w2
{-# INLINE uniform2 #-}
uniform1B f b (Gen p) = f <$> pcg32u_boundedrand_r p b
{-# INLINE uniform1B #-}