{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
#if __GLASGOW_HASKELL__ >= 801
{-# LANGUAGE TypeInType #-}
#endif
{-# LANGUAGE UnboxedTuples #-}
#if __GLASGOW_HASKELL__ >= 707
{-# LANGUAGE RoleAnnotations #-}
#endif
module System.Random.PCG.Fast.Pure
(
Gen, GenIO, GenST
, create, createSystemRandom, initialize, withSystemRandom
, 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.Monad.Primitive
import Data.Bits
import Data.Data
import Data.Primitive.ByteArray
import Data.Primitive.Types
import GHC.Generics
import GHC.Word
import System.Random
import System.Random.PCG.Class
newtype FrozenGen = F Word64
deriving (Show, Eq, Ord, Prim, Typeable, Data, Generic)
newtype Gen s = G (MutableByteArray s)
deriving Typeable
type GenIO = Gen RealWorld
type GenST = Gen
data Pair = P {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word32
deriving Show
fastMultiplier :: Word64
fastMultiplier = 6364136223846793005
state :: Word64 -> Word64
state s = s * fastMultiplier
output :: Word64 -> Word32
output s = fromIntegral $
((s `shiftR` 22) `xor` s) `unsafeShiftR` (fromIntegral (s `shiftR` 61) + 22)
pair :: Word64 -> Pair
pair s = P (state s) (output s)
bounded :: Word32 -> Word64 -> Pair
bounded b s0 = go s0
where
t = negate b `mod` b
go !s | r >= t = P s' (r `mod` b)
| otherwise = go s'
where P s' r = pair s
{-# INLINE bounded #-}
advancing
:: Word64
-> Word64
-> Word64
-> Word64
-> Word64
advancing d0 s m0 p0 = go d0 m0 p0 1 0
where
go d cm cp am ap
| d <= 0 = am * s + ap
| odd d = go d' cm' cp' (am * cm) (ap * cm + cp)
| otherwise = go d' cm' cp' am ap
where
cm' = cm * cm
cp' = (cm + 1) * cp
d' = d `div` 2
advanceFast :: Word64 -> FrozenGen -> FrozenGen
advanceFast d (F s) = F $ advancing d s fastMultiplier 0
save :: PrimMonad m => Gen (PrimState m) -> m FrozenGen
save (G a) = readByteArray a 0
{-# INLINE save #-}
restore :: PrimMonad m => FrozenGen -> m (Gen (PrimState m))
restore f = do
a <- newByteArray 8
writeByteArray a 0 f
return $! G a
{-# INLINE restore #-}
initFrozen :: Word64 -> FrozenGen
initFrozen w = F (w .|. 1)
seed :: FrozenGen
seed = F 0xcafef00dd15ea5e5
create :: PrimMonad m => m (Gen (PrimState m))
create = restore seed
initialize :: PrimMonad m => Word64 -> m (Gen (PrimState m))
initialize a = restore (initFrozen a)
withSystemRandom :: (GenIO -> IO a) -> IO a
withSystemRandom f = do
w <- sysRandom
initialize w >>= f
createSystemRandom :: IO GenIO
createSystemRandom = withSystemRandom (return :: GenIO -> IO GenIO)
advance :: PrimMonad m => Word64 -> Gen (PrimState m) -> m ()
advance u (G a) = do
s <- readByteArray a 0
let s' = advanceFast u s
writeByteArray a 0 s'
{-# INLINE advance #-}
retract :: PrimMonad m => Word64 -> Gen (PrimState m) -> m ()
retract u g = advance (-u) g
{-# INLINE retract #-}
instance (PrimMonad m, s ~ PrimState m) => Generator (Gen s) m where
uniform1 f (G a) = do
s <- readByteArray a 0
let P s' r = pair s
writeByteArray a 0 s'
return $! f r
{-# INLINE uniform1 #-}
uniform2 f (G a) = do
s <- readByteArray a 0
let s' = state s
writeByteArray a 0 (state s')
return $! f (output s) (output s')
{-# INLINE uniform2 #-}
uniform1B f b (G a) = do
s <- readByteArray a 0
let P s' r = bounded b s
writeByteArray a 0 s'
return $! f r
{-# INLINE uniform1B #-}
instance RandomGen FrozenGen where
next (F s) = (wordsTo64Bit w1 w2, F s'')
where
P s' w1 = pair s
P s'' w2 = pair s'
{-# INLINE next #-}
split (F s) = (mk w1 w2, mk w3 w4)
where
mk a b = initFrozen $! wordsTo64Bit a b
P s1 w1 = pair s
P s2 w2 = pair s1
P s3 w3 = pair s2
w4 = output s3
{-# INLINE split #-}