{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
#if __GLASGOW_HASKELL__ >= 707
{-# LANGUAGE RoleAnnotations #-}
#endif
module System.Random.PCG
(
Gen, GenIO, GenST
, create, createSystemRandom, initialize
, withSystemRandom, withFrozen
, Variate (..)
, advance, retract
, FrozenGen
, save, restore, seed, initFrozen
, uniformW8, uniformW16, uniformW32, uniformW64
, uniformI8, uniformI16, uniformI32, uniformI64
, uniformF, uniformD, uniformBool
, uniformRW8, uniformRW16, uniformRW32, uniformRW64
, uniformRI8, uniformRI16, uniformRI32, uniformRI64
, uniformRF, uniformRD, uniformRBool
, uniformBW8, uniformBW16, uniformBW32, uniformBW64
, uniformBI8, uniformBI16, uniformBI32, uniformBI64
, uniformBF, uniformBD, uniformBBool
) where
import Control.Applicative
import Control.Monad.Primitive
import Control.Monad.ST
import Data.Data
import Foreign
import GHC.Generics
import System.IO.Unsafe
import System.Random
import System.Random.PCG.Class
data FrozenGen = FrozenGen {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
deriving (Show, Eq, Ord, Data, Typeable, Generic)
save :: PrimMonad m => Gen (PrimState m) -> m FrozenGen
save (Gen p) = unsafePrimToPrim (peek p)
{-# INLINE save #-}
restore :: PrimMonad m => FrozenGen -> m (Gen (PrimState m))
restore s = unsafePrimToPrim $ do
p <- malloc
poke p s
return (Gen p)
{-# INLINE restore #-}
seed :: FrozenGen
seed = FrozenGen 0x853c49e6748fea9b 0xda3e39cb94b95bdb
initFrozen :: Word64 -> Word64 -> FrozenGen
initFrozen w1 w2 = unsafeDupablePerformIO $ do
p <- malloc
pcg32_srandom_r p w1 w2
peek p <* free p
{-# INLINE initFrozen #-}
instance Storable FrozenGen where
sizeOf _ = 16
{-# INLINE sizeOf #-}
alignment _ = 8
{-# INLINE alignment #-}
poke ptr (FrozenGen x y) = poke ptr' x >> pokeElemOff ptr' 1 y
where ptr' = castPtr ptr
{-# INLINE poke #-}
peek ptr = FrozenGen <$> peek ptr' <*> peekElemOff ptr' 1
where ptr' = castPtr ptr
{-# INLINE peek #-}
newtype Gen s = Gen (Ptr FrozenGen)
deriving (Eq, Ord)
#if __GLASGOW_HASKELL__ >= 707
type role Gen representational
#endif
type GenIO = Gen RealWorld
type GenST s = Gen s
create :: PrimMonad m => m (Gen (PrimState m))
create = restore seed
initialize :: PrimMonad m => Word64 -> Word64 -> m (Gen (PrimState m))
initialize a b = unsafePrimToPrim $ do
p <- malloc
pcg32_srandom_r p a b
return (Gen p)
withSystemRandom :: (GenIO -> IO a) -> IO a
withSystemRandom f = do
w1 <- sysRandom
w2 <- sysRandom
initialize w1 w2 >>= f
withFrozen :: FrozenGen -> (forall s. Gen s -> ST s a) -> (a, FrozenGen)
withFrozen s f = runST $ restore s >>= \g -> liftA2 (,) (f g) (save g)
createSystemRandom :: IO GenIO
createSystemRandom = withSystemRandom (return :: GenIO -> IO GenIO)
advance :: PrimMonad m => Word64 -> Gen (PrimState m) -> m ()
advance u (Gen p) = unsafePrimToPrim $ pcg32_advance_r p u
{-# INLINE advance #-}
retract :: PrimMonad m => Word64 -> Gen (PrimState m) -> m ()
retract u g = advance (-u) g
{-# INLINE retract #-}
foreign import ccall unsafe "pcg_setseq_64_srandom_r"
pcg32_srandom_r :: Ptr FrozenGen -> Word64 -> Word64 -> IO ()
foreign import ccall unsafe "pcg_setseq_64_xsh_rr_32_random_r"
pcg32_random_r :: Ptr FrozenGen -> IO Word32
foreign import ccall unsafe "pcg_setseq_64_xsh_rr_32_boundedrand_r"
pcg32_boundedrand_r :: Ptr FrozenGen -> Word32 -> IO Word32
foreign import ccall unsafe "pcg_setseq_64_advance_r"
pcg32_advance_r :: Ptr FrozenGen -> Word64 -> IO ()
instance (PrimMonad m, s ~ PrimState m) => Generator (Gen s) m where
uniform1 f (Gen p) = unsafePrimToPrim $ f <$> pcg32_random_r p
{-# INLINE uniform1 #-}
uniform2 f (Gen p) = unsafePrimToPrim $ do
w1 <- pcg32_random_r p
w2 <- pcg32_random_r p
return $ f w1 w2
{-# INLINE uniform2 #-}
uniform1B f b (Gen p) = unsafePrimToPrim $ f <$> pcg32_boundedrand_r p b
{-# INLINE uniform1B #-}
instance RandomGen FrozenGen where
next s = unsafeDupablePerformIO $ do
p <- malloc
poke p s
w1 <- pcg32_random_r p
w2 <- pcg32_random_r p
s' <- peek p
free p
return (wordsTo64Bit w1 w2, s')
{-# INLINE next #-}
split s = unsafeDupablePerformIO $ do
p <- malloc
poke p s
w1 <- pcg32_random_r p
w2 <- pcg32_random_r p
w3 <- pcg32_random_r p
w4 <- pcg32_random_r p
w5 <- pcg32_random_r p
w6 <- pcg32_random_r p
w7 <- pcg32_random_r p
w8 <- pcg32_random_r p
pcg32_srandom_r p (wordsTo64Bit w1 w2) (wordsTo64Bit w3 w4)
s1 <- peek p
pcg32_srandom_r p (wordsTo64Bit w5 w6) (wordsTo64Bit w7 w8)
s2 <- peek p
free p
return (s1,s2)