{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE CPP #-}
#include "MachDeps.h"
module Hedgehog.Internal.Seed (
Seed(..)
, random
, from
, split
, nextInteger
, nextDouble
, goldenGamma
, nextWord64
, nextWord32
, mix64
, mix64variant13
, mix32
, mixGamma
) where
import Control.Monad.IO.Class (MonadIO(..))
import Data.Bifunctor (first)
import Data.Bits ((.|.), xor, shiftR, popCount)
#if (SIZEOF_HSINT == 8)
import Data.Int (Int64)
#else
import Data.Int (Int32)
#endif
import Data.Time.Clock.POSIX (getPOSIXTime)
import Data.IORef (IORef)
import qualified Data.IORef as IORef
import Data.Word (Word32, Word64)
import System.IO.Unsafe (unsafePerformIO)
import System.Random (RandomGen)
import qualified System.Random as Random
data Seed =
Seed {
seedValue :: !Word64
, seedGamma :: !Word64
} deriving (Eq, Ord)
instance Show Seed where
showsPrec p (Seed v g) =
showParen (p > 10) $
showString "Seed " .
showsPrec 11 v .
showChar ' ' .
showsPrec 11 g
instance Read Seed where
readsPrec p =
readParen (p > 10) $ \r0 -> do
("Seed", r1) <- lex r0
(v, r2) <- readsPrec 11 r1
(g, r3) <- readsPrec 11 r2
pure (Seed v g, r3)
global :: IORef Seed
global =
unsafePerformIO $ do
seconds <- getPOSIXTime
IORef.newIORef $ from (round (seconds * 1000))
{-# NOINLINE global #-}
random :: MonadIO m => m Seed
random =
liftIO $ IORef.atomicModifyIORef' global split
from :: Word64 -> Seed
from x =
Seed (mix64 x) (mixGamma (x + goldenGamma))
goldenGamma :: Word64
goldenGamma =
0x9e3779b97f4a7c15
next :: Seed -> (Word64, Seed)
next (Seed v0 g) =
let
v = v0 + g
in
(v, Seed v g)
split :: Seed -> (Seed, Seed)
split s0 =
let
(v0, s1) = next s0
(g0, s2) = next s1
in
(s2, Seed (mix64 v0) (mixGamma g0))
nextWord64 :: Seed -> (Word64, Seed)
nextWord64 s0 =
let
(v0, s1) = next s0
in
(mix64 v0, s1)
nextWord32 :: Seed -> (Word32, Seed)
nextWord32 s0 =
let
(v0, s1) = next s0
in
(mix32 v0, s1)
nextInteger :: Integer -> Integer -> Seed -> (Integer, Seed)
nextInteger lo hi =
Random.randomR (lo, hi)
nextDouble :: Double -> Double -> Seed -> (Double, Seed)
nextDouble lo hi =
Random.randomR (lo, hi)
mix64 :: Word64 -> Word64
mix64 x =
let
y = (x `xor` (x `shiftR` 33)) * 0xff51afd7ed558ccd
z = (y `xor` (y `shiftR` 33)) * 0xc4ceb9fe1a85ec53
in
z `xor` (z `shiftR` 33)
mix32 :: Word64 -> Word32
mix32 x =
let
y = (x `xor` (x `shiftR` 33)) * 0xff51afd7ed558ccd
z = (y `xor` (y `shiftR` 33)) * 0xc4ceb9fe1a85ec53
in
fromIntegral (z `shiftR` 32)
mix64variant13 :: Word64 -> Word64
mix64variant13 x =
let
y = (x `xor` (x `shiftR` 30)) * 0xbf58476d1ce4e5b9
z = (y `xor` (y `shiftR` 27)) * 0x94d049bb133111eb
in
z `xor` (z `shiftR` 31)
mixGamma :: Word64 -> Word64
mixGamma x =
let
y = mix64variant13 x .|. 1
n = popCount $ y `xor` (y `shiftR` 1)
in
if n < 24 then
y `xor` 0xaaaaaaaaaaaaaaaa
else
y
#if (SIZEOF_HSINT == 8)
instance RandomGen Seed where
next =
first fromIntegral . nextWord64
genRange _ =
(fromIntegral (minBound :: Int64), fromIntegral (maxBound :: Int64))
split =
split
#else
instance RandomGen Seed where
next =
first fromIntegral . nextWord32
genRange _ =
(fromIntegral (minBound :: Int32), fromIntegral (maxBound :: Int32))
split =
split
#endif