-- | -- This is a port of -- [xoshiro256**](http://xoshiro.di.unimi.it/xoshiro256starstar.c) from C to -- Haskell. The documentation and comments below come from the original C -- sources. -- -- Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) -- -- To the extent possible under law, the author has dedicated all copyright -- and related and neighboring rights to this software to the public domain -- worldwide. This software is distributed without any warranty. -- -- See <http://creativecommons.org/publicdomain/zero/1.0/>. module BingoSim.Prng ( -- * Seeding the generator State , mkState -- * Generating random numbers , next -- * Jumps , jump , longJump ) where import Data.Bits import Data.Word -- | The state of the generator. -- -- The state must be seeded so that it is not everywhere zero. If you have -- a 64-bit seed, we suggest to seed a splitmix64 generator and use its -- output to fill s. -- -- In Haskell, we've made this type opaque. See 'mkState' to construct a 'State'. data State = State {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64 instance Show State where show (State s0 s1 s2 s3) = "mkState " ++ (show s0) ++ " " ++ (show s1) ++ " " ++ (show s2) ++ " " ++ (show s3) -- | Create an initial state from a seed. -- -- Raises an exception if the initial seed is all zeros. -- -- >>> mkState 0 0 0 0 -- *** Exception: The state must be seeded so that it is not zero everywhere. -- >>> mkState 1 2 3 4 -- mkState 1 2 3 4 mkState :: Word64 -> Word64 -> Word64 -> Word64 -> State mkState 0 0 0 0 = error "The state must be seeded so that it is not zero everywhere." mkState s0 s1 s2 s3 = State s0 s1 s2 s3 -- | This is xoshiro256** 1.0, our all-purpose, rock-solid generator. It has -- excellent (sub-ns) speed, a state (256 bits) that is large enough for -- any parallel application, and it passes all tests we are aware of. -- -- For generating just floating-point numbers, xoshiro256+ is even faster. -- -- >>> let state = mkState 1 2 3 4 -- >>> next state (\rand _state' -> rand) -- 11520 -- -- Note: When porting to Haskell, we've explicitly chosen to make the API of -- 'next' in CPS-style (i.e., using a callback) to avoid always allocating a -- tuple for the result. -- -- If you'd rather just have a tuple, you can pass @(,)@ as the callback: -- -- >>> next state (,) -- (11520,mkState 7 0 262146 211106232532992) next :: State -> (Word64 -> State -> a) -> a next (State s0 s1 s2 s3) f = let result = ((s1 * 5) `rotateL` 7) * 9 t = s1 `unsafeShiftL` 17 s2' = s2 `xor` s0 s3' = s3 `xor` s1 s1' = s1 `xor` s2' s0' = s0 `xor` s3' s2'' = s2' `xor` t s3'' = s3' `rotateL` 45 in f result (State s0' s1' s2'' s3'') -- | This is the jump function for the generator. It is equivalent -- to 2^128 calls to next(); it can be used to generate 2^128 -- non-overlapping subsequences for parallel computations. -- -- Note: This function is not yet implemented in Haskell. jump :: State -> State jump (State _s0 _s1 _s2 _s3) = undefined -- | This is the long-jump function for the generator. It is equivalent to -- 2^192 calls to next(); it can be used to generate 2^64 starting points, -- from each of which jump() will generate 2^64 non-overlapping -- subsequences for parallel distributed computations. -- -- Note: This function is not yet implemented in Haskell. longJump :: State -> State longJump (State _s0 _s1 _s2 _s3) = undefined