splitmix 0.0.1 → 0.0.2
raw patch · 4 files changed
+121/−49 lines, 4 filesdep ~basedep ~base-compat-batteriesdep ~bytestring
Dependency ranges changed: base, base-compat-batteries, bytestring, containers, deepseq, process, random, time, vector
Files
- Changelog.md +5/−0
- splitmix.cabal +36/−42
- src/System/Random/SplitMix.hs +63/−5
- tests/Dieharder.hs +17/−2
Changelog.md view
@@ -1,3 +1,8 @@+# 0.0.2++- Support back to GHC-7.0+- Add `Read SMGen` instance+ # 0.0.1 - Add `NFData SMGen` instance
splitmix.cabal view
@@ -1,8 +1,7 @@-cabal-version: >= 1.10-name: splitmix-version: 0.0.1--synopsis: Fast Splittable PRNG+cabal-version: >=1.10+name: splitmix+version: 0.0.2+synopsis: Fast Splittable PRNG description: Pure Haskell implementation of SplitMix described in .@@ -27,19 +26,14 @@ (the mixing functions are easily inverted, and two successive outputs suffice to reconstruct the internal state). -license: BSD3-license-file: LICENSE-maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>-bug-reports: https://github.com/phadej/splitmix#issues-category: System-build-type: Simple+license: BSD3+license-file: LICENSE+maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>+bug-reports: https://github.com/phadej/splitmix#issues+category: System+build-type: Simple tested-with:- GHC==7.6.3,- GHC==7.8.4,- GHC==7.10.3,- GHC==8.0.2,- GHC==8.2.2,- GHC==8.4.2+ GHC ==8.6.4 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2 || ==7.2.2 || ==7.0.4 extra-source-files: README.md@@ -49,18 +43,18 @@ default-language: Haskell2010 ghc-options: -Wall hs-source-dirs: src- exposed-modules:- System.Random.SplitMix- build-depends:- base >=4.5 && <4.12,- deepseq >=1.3.0.1 && <1.5,- time >=1.4.0.1 && <1.9,- random >=1.1 && <1.2+ exposed-modules: System.Random.SplitMix -- dump-core -- build-depends: dump-core -- ghc-options: -fplugin=DumpCore -fplugin-opt DumpCore:core-html + build-depends:+ base >=4.3 && <4.13+ , deepseq >=1.3.0.0 && <1.5+ , random >=1.0 && <1.2+ , time >=1.2.0.3 && <1.9+ source-repository head type: git location: https://github.com/phadej/splitmix.git@@ -72,12 +66,12 @@ hs-source-dirs: bench main-is: Bench.hs build-depends:- base,- splitmix,- random,- containers >=0.5 && <0.6,- tf-random >=0.5 && <0.6,- criterion >=1.1.0.0 && <1.5+ base+ , containers >=0.4.2.1 && <0.7+ , criterion >=1.1.0.0 && <1.6+ , random+ , splitmix+ , tf-random >=0.5 && <0.6 test-suite montecarlo-pi type: exitcode-stdio-1.0@@ -86,8 +80,8 @@ hs-source-dirs: tests main-is: SplitMixPi.hs build-depends:- base,- splitmix+ base+ , splitmix test-suite splitmix-dieharder type: exitcode-stdio-1.0@@ -96,13 +90,13 @@ hs-source-dirs: tests main-is: Dieharder.hs build-depends:- base,- splitmix,- random,- deepseq,- async >=2.2.1 && <2.3,- vector >=0.12.0.1 && <0.13,- base-compat-batteries >=0.10.1 && <0.11,- bytestring >=0.10.4.0 && <0.11,- process >=1.1.0.2 && <1.7,- tf-random >=0.5 && <0.6+ async >=2.2.1 && <2.3+ , base+ , base-compat-batteries >=0.10.5 && <0.11+ , bytestring >=0.9.1.8 && <0.11+ , deepseq+ , process >=1.0.1.5 && <1.7+ , random+ , splitmix+ , tf-random >=0.5 && <0.6+ , vector >=0.11.0.0 && <0.13
src/System/Random/SplitMix.hs view
@@ -23,7 +23,16 @@ -- because generated sequences of pseudorandom values are too predictable -- (the mixing functions are easily inverted, and two successive outputs -- suffice to reconstruct the internal state).+--+-- Note: This module supports all GHCs since GHC-7.0.4,+-- but GHC-7.0 and GHC-7.2 have slow implementation, as there+-- are no native 'popCount'.+--+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+#if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-}+#endif module System.Random.SplitMix ( SMGen, nextWord64,@@ -40,7 +49,7 @@ ) where import Control.DeepSeq (NFData (..))-import Data.Bits (popCount, shiftL, shiftR, xor, (.|.))+import Data.Bits (shiftL, shiftR, xor, (.|.)) import Data.IORef (IORef, atomicModifyIORef, newIORef) import Data.Time.Clock.POSIX (getPOSIXTime) import Data.Word (Word32, Word64)@@ -49,25 +58,62 @@ import qualified System.Random as R +#if MIN_VERSION_base(4,5,0)+import Data.Bits (popCount)+#else+import Data.Bits ((.&.))+popCount :: Word64 -> Int+popCount = go 0+ where+ go !c 0 = c+ go c w = go (c+1) (w .&. (w - 1)) -- clear the least significant+#endif++-- $setup+-- >>> import Text.Read (readMaybe)+-- >>> import Data.List (unfoldr)+-- >>> import Text.Printf (printf)+ ------------------------------------------------------------------------------- -- Generator ------------------------------------------------------------------------------- -- | SplitMix generator state.-data SMGen = SMGen- { _seed :: !Word64- , _gamma :: !Word64 -- ^ always odd- }+data SMGen = SMGen !Word64 !Word64 -- seed and gamma; gamma is odd deriving Show instance NFData SMGen where rnf (SMGen _ _) = () +-- |+--+-- >>> readMaybe "SMGen 1 1" :: Maybe SMGen+-- Just (SMGen 1 1)+--+-- >>> readMaybe "SMGen 1 2" :: Maybe SMGen+-- Nothing+--+-- >>> readMaybe (show (mkSMGen 42)) :: Maybe SMGen+-- Just (SMGen 9297814886316923340 13679457532755275413)+--+instance Read SMGen where+ readsPrec d r = readParen (d > 10) (\r0 ->+ [ (SMGen seed gamma, r3)+ | ("SMGen", r1) <- lex r0+ , (seed, r2) <- readsPrec 11 r1+ , (gamma, r3) <- readsPrec 11 r2+ , odd gamma+ ]) r+ ------------------------------------------------------------------------------- -- Operations ------------------------------------------------------------------------------- -- | Generate a 'Word64'.+--+-- >>> take 3 $ map (printf "%x") $ unfoldr (Just . nextWord64) (mkSMGen 1337) :: [String]+-- ["b5c19e300e8b07b3","d600e0e216c0ac76","c54efc3b3cc5af29"]+-- nextWord64 :: SMGen -> (Word64, SMGen) nextWord64 (SMGen seed gamma) = (mix64 seed', SMGen seed' gamma) where@@ -79,6 +125,10 @@ (w64, g') -> (fromIntegral w64, g') -- | Generate a 'Double' in @[0, 1)@ range.+--+-- >>> take 8 $ map (printf "%0.3f") $ unfoldr (Just . nextDouble) (mkSMGen 1337) :: [String]+-- ["0.710","0.836","0.771","0.409","0.297","0.527","0.589","0.067"]+-- nextDouble :: SMGen -> (Double, SMGen) nextDouble g = case nextWord64 g of (w64, g') -> (fromIntegral (w64 `shiftR` 11) * doubleUlp, g')@@ -144,6 +194,10 @@ ------------------------------------------------------------------------------- -- | Create 'SMGen' using seed and gamma.+--+-- >>> seedSMGen 2 2+-- SMGen 2 3+-- seedSMGen :: Word64 -- ^ seed -> Word64 -- ^ gamma@@ -159,6 +213,10 @@ unseedSMGen (SMGen seed gamma) = (seed, gamma) -- | Preferred way to deterministically construct 'SMGen'.+--+-- >>> mkSMGen 42+-- SMGen 9297814886316923340 13679457532755275413+-- mkSMGen :: Word64 -> SMGen mkSMGen s = SMGen (mix64 s) (mixGamma (s + goldenGamma))
tests/Dieharder.hs view
@@ -9,6 +9,7 @@ import Control.Concurrent.QSem import Control.DeepSeq (force) import Data.Bits (shiftL, (.|.))+import Data.Char (isSpace) import Data.List (isInfixOf, unfoldr) import Data.Maybe (fromMaybe) import Data.Word (Word64)@@ -17,8 +18,7 @@ import GHC.IO.Exception (IOErrorType (..), IOException (..)) import System.Environment (getArgs) import System.IO (Handle, hGetContents)-import Text.Read (readMaybe)-import Text.Printf (printf)+import Text.Printf (printf) import qualified Control.Concurrent.Async as A import qualified Control.Exception as E@@ -166,6 +166,21 @@ else return gen' {-# INLINE generate #-} +-------------------------------------------------------------------------------+-- readMaybe+-------------------------------------------------------------------------------++readEither :: Read a => String -> Either String a+readEither s =+ case [ x | (x,rest) <- reads s, all isSpace rest ] of+ [x] -> Right x+ [] -> Left "Prelude.read: no parse"+ _ -> Left "Prelude.read: ambiguous parse"++readMaybe :: Read a => String -> Maybe a+readMaybe s = case readEither s of+ Left _ -> Nothing+ Right a -> Just a ------------------------------------------------------------------------------- -- Do it yourself command line parsing -------------------------------------------------------------------------------