setting up the skeleton for a 1.3 mostly compatible release
authorCarter Tazio Schonwald <carter.schonwald@gmail.com>
Tue, 26 Dec 2017 01:12:23 +0000 (20:12 -0500)
committerCarter Tazio Schonwald <carter.schonwald@gmail.com>
Tue, 26 Dec 2017 01:12:23 +0000 (20:12 -0500)
random.cabal
src/Data/Random/Utils.hs
src/System/Random.hs [new file with mode: 0644]
src/System/Random/PCG32/Internal.hs [moved from src/System/Random/PCG/Internal.hs with 99% similarity]
src/System/Random/SplitMix/Internal.hs
src/System/Random/SplitMix/Internal/Splitting.hs [new file with mode: 0644]

index 9b81aa6..2d4f18d 100644 (file)
@@ -10,7 +10,7 @@ name:                random
 -- PVP summary:      +-+------- breaking API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             2.0.0.0
+version:             1.3.0.0
 
 -- A short (one-line) description of the package.
 synopsis:            Random number generation library for haskell
@@ -57,8 +57,10 @@ cabal-version:       >=1.10
 library
   -- Modules exported` by the library.
   exposed-modules:
+    System.Random
     System.Random.SplitMix.Internal
-    System.Random.PCG.Internal
+    System.Random.SplitMix.Internal.Splitting
+    System.Random.PCG32.Internal
     Data.Distribution.FloatingInterval
     Data.Distribution.Normal
     Data.Distribution.Permutation
index 560b333..4bd2ec1 100644 (file)
@@ -13,7 +13,7 @@ module Data.Random.Utils(
     ,castDoubleToWord64
     ,castWord32ToFloat
     ,castFloatToWord32
-    ,CastIEEE(..)) where
+    ,RandomCastIEEE(..)) where
 import GHC.Word(Word32(..),Word64(..))
 import GHC.Prim (Word#,Float#,Double#)
 import GHC.Types
@@ -23,21 +23,25 @@ aa206346e6f12c9f88fdf051185741761ea88fbb
 of the ghc git repo, due for inclusion in ghc 8.4
 
 this should be move out of random into its own micro package for pre ghc 8.4 compat
+with conversion facilities  in ghc >= 8.4
+
+this copy has name mangling at the CMM layer for happy linking
+plus Random prefixing the class so it should be low headache
 -}
 
 
 
 
-class CastIEEE word ieee | word -> ieee , ieee -> word where
+class RandomCastIEEE word ieee | word -> ieee , ieee -> word where
     toIEEE :: word -> ieee
     fromIEEE :: ieee -> word
 
-instance CastIEEE Word32 Float where
+instance RandomCastIEEE Word32 Float where
   {-# INLINE toIEEE #-}
   {-# INLINE fromIEEE #-}
   toIEEE = castWord32ToFloat
   fromIEEE = castFloatToWord32
-instance CastIEEE Word64 Double where
+instance RandomCastIEEE Word64 Double where
   {-# INLINE toIEEE #-}
   {-# INLINE fromIEEE #-}
   toIEEE = castWord64ToDouble
diff --git a/src/System/Random.hs b/src/System/Random.hs
new file mode 100644 (file)
index 0000000..f6bde7f
--- /dev/null
@@ -0,0 +1,525 @@
+{-# LANGUAGE CPP #-}
+#if __GLASGOW_HASKELL__ >= 701
+{-# LANGUAGE Trustworthy #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Random
+-- Copyright   :  (c) The University of Glasgow 2001
+-- License     :  BSD-style (see the file LICENSE in the 'random' repository)
+--
+-- Maintainer  :  libraries@haskell.org
+-- Stability   :  stable
+-- Portability :  portable
+--
+-- This library deals with the common task of pseudo-random number
+-- generation. The library makes it possible to generate repeatable
+-- results, by starting with a specified initial random number generator,
+-- or to get different results on each run by using the system-initialised
+-- generator or by supplying a seed from some other source.
+--
+-- The library is split into two layers:
+--
+-- * A core /random number generator/ provides a supply of bits.
+--   The class 'RandomGen' provides a common interface to such generators.
+--   The library provides one instance of 'RandomGen', the abstract
+--   data type 'StdGen'.  Programmers may, of course, supply their own
+--   instances of 'RandomGen'.
+--
+-- * The class 'Random' provides a way to extract values of a particular
+--   type from a random number generator.  For example, the 'Float'
+--   instance of 'Random' allows one to generate random values of type
+--   'Float'.
+--
+-- This implementation uses the Portable Combined Generator of L'Ecuyer
+-- ["System.Random\#LEcuyer"] for 32-bit computers, transliterated by
+-- Lennart Augustsson.  It has a period of roughly 2.30584e18.
+--
+-----------------------------------------------------------------------------
+
+#include "MachDeps.h"
+
+module System.Random
+        (
+
+        -- $intro
+
+        -- * Random number generators
+
+
+          RandomGen(next, genRange)
+        , SplittableGen(split)
+
+        -- ** Standard random number generators
+        --, StdGen
+        --, mkStdGen
+
+        -- ** The global random number generator
+
+        -- $globalrng
+
+        --, getStdRandom
+        --, getStdGen
+        --, setStdGen
+        --, newStdGen
+
+        -- * Random values of various types
+        , Random ( random,   randomR,
+                   randoms,  randomRs,
+                   randomIO, randomRIO )
+
+        -- * References
+        -- $references
+
+        ) where
+
+import Prelude
+
+import Data.Bits
+import Data.Int
+import Data.Word
+import Foreign.C.Types
+
+
+
+--import Data.Ratio       ( numerator, denominator )
+
+--import Data.Char        ( isSpace, chr, ord )
+--import System.IO.Unsafe ( unsafePerformIO )
+--import Data.IORef       ( IORef, newIORef, readIORef, writeIORef )
+#if MIN_VERSION_base (4,6,0)
+--import Data.IORef       ( atomicModifyIORef' )
+#else
+import Data.IORef       ( atomicModifyIORef )
+#endif
+--import Numeric          ( readDec )
+
+#ifdef __GLASGOW_HASKELL__
+import GHC.Exts         ( build )
+#else
+-- | A dummy variant of build without fusion.
+{-# INLINE build #-}
+build :: ((a -> [a] -> [a]) -> [a] -> [a]) -> [a]
+build g = g (:) []
+#endif
+
+#if !MIN_VERSION_base (4,6,0)
+atomicModifyIORef' :: IORef a -> (a -> (a,b)) -> IO b
+atomicModifyIORef' ref f = do
+    b <- atomicModifyIORef ref
+            (\x -> let (a, b) = f x
+                    in (a, a `seq` b))
+    b `seq` return b
+#endif
+
+
+
+-- | The class 'RandomGen' provides a common interface to random number
+-- generators.
+--
+-- Minimal complete definition: 'next'.
+
+class RandomGen g where
+
+   -- |The 'next' operation returns an 'Int' that is uniformly distributed
+   -- in the range returned by 'genRange' (including both end points),
+   -- and a new generator.
+   next     :: g -> (Word64, g)
+
+   -- |The 'genRange' operation yields the range of values returned by
+   -- the generator.
+   --
+   -- It is required that:
+   --
+   -- * If @(a,b) = 'genRange' g@, then @a < b@.
+   --
+   -- * 'genRange' always returns a pair of defined 'Int's.
+   --
+   -- The second condition ensures that 'genRange' cannot examine its
+   -- argument, and hence the value it returns can be determined only by the
+   -- instance of 'RandomGen'.  That in turn allows an implementation to make
+   -- a single call to 'genRange' to establish a generator's range, without
+   -- being concerned that the generator returned by (say) 'next' might have
+   -- a different range to the generator passed to 'next'.
+   --
+   -- The default definition spans the full range of 'Int'.
+   genRange :: g -> (Word64,Word64)
+
+   -- default method
+   genRange _ = (minBound, maxBound)
+
+
+-- | The class 'SplittableGen' proivides a way to specify a random number
+--   generator that can be split into two new generators.
+class SplittableGen g where
+
+   -- |The 'split' operation allows one to obtain two distinct random number
+   -- generators. This is very useful in functional programs (for example, when
+   -- passing a random number generator down to recursive calls), but very
+   -- little work has been done on statistically robust implementations of
+   -- 'split' (["System.Random\#Burton", "System.Random\#Hellekalek"]
+   -- are the only examples we know of).
+   split    :: g -> (g, g)
+
+{- |
+The 'StdGen' instance of 'RandomGen' has a 'genRange' of at least 30 bits.
+
+The result of repeatedly using 'next' should be at least as statistically
+robust as the /Minimal Standard Random Number Generator/ described by
+["System.Random\#Park", "System.Random\#Carta"].
+Until more is known about implementations of 'split', all we require is
+that 'split' deliver generators that are (a) not identical and
+(b) independently robust in the sense just given.
+
+The 'Show' and 'Read' instances of 'StdGen' provide a primitive way to save the
+state of a random number generator.
+It is required that @'read' ('show' g) == g@.
+
+In addition, 'reads' may be used to map an arbitrary string (not necessarily one
+produced by 'show') onto a value of type 'StdGen'. In general, the 'Read'
+instance of 'StdGen' has the following properties:
+
+* It guarantees to succeed on any string.
+
+* It guarantees to consume only a finite portion of the string.
+
+* Different argument strings are likely to result in different results.
+
+-}
+
+--data StdGen
+-- = StdGen !Int32 !Int32
+{-
+instance RandomGen StdGen where
+  next  = stdNext
+  genRange _ = stdRange
+
+
+instance SplittableGen StdGen where
+  split = stdSplit
+
+instance Show StdGen where
+  showsPrec p (StdGen s1 s2) =
+     showsPrec p s1 .
+     showChar ' ' .
+     showsPrec p s2
+
+instance Read StdGen where
+  readsPrec _p = \ r ->
+     case try_read r of
+       r'@[_] -> r'
+       _   -> [stdFromString r] -- because it shouldn't ever fail.
+    where
+      try_read r = do
+         (s1, r1) <- readDec (dropWhile isSpace r)
+         (s2, r2) <- readDec (dropWhile isSpace r1)
+         return (StdGen s1 s2, r2)
+
+{-
+ If we cannot unravel the StdGen from a string, create
+ one based on the string given.
+-}
+stdFromString         :: String -> (StdGen, String)
+stdFromString s        = (mkStdGen num, rest)
+        where (cs, rest) = splitAt 6 s
+              num        = foldl (\a x -> x + 3 * a) 1 (map ord cs)
+
+
+ |
+The function 'mkStdGen' provides an alternative way of producing an initial
+generator, by mapping an 'Int' into a generator. Again, distinct arguments
+should be likely to produce distinct generators.
+
+mkStdGen :: Int -> StdGen -- why not Integer ?
+mkStdGen s = mkStdGen32 $ fromIntegral s
+
+{-
+From ["System.Random\#LEcuyer"]: "The integer variables s1 and s2 ... must be
+initialized to values in the range [1, 2147483562] and [1, 2147483398]
+respectively."
+-}
+mkStdGen32 :: Int32 -> StdGen
+mkStdGen32 sMaybeNegative = StdGen (s1+1) (s2+1)
+      where
+        -- We want a non-negative number, but we can't just take the abs
+        -- of sMaybeNegative as -minBound == minBound.
+        s       = sMaybeNegative .&. maxBound
+        (q, s1) = s `divMod` 2147483562
+        s2      = q `mod` 2147483398
+
+createStdGen :: Integer -> StdGen
+createStdGen s = mkStdGen32 $ fromIntegral s
+-}
+
+{- |
+With a source of random number supply in hand, the 'Random' class allows the
+programmer to extract random values of a variety of types.
+
+Minimal complete definition: 'randomR' and 'random'.
+
+-}
+
+class Random a where
+  -- | Takes a range /(lo,hi)/ and a random number generator
+  -- /g/, and returns a random value uniformly distributed in the closed
+  -- interval /[lo,hi]/, together with a new generator. It is unspecified
+  -- what happens if /lo>hi/. For continuous types there is no requirement
+  -- that the values /lo/ and /hi/ are ever produced, but they may be,
+  -- depending on the implementation and the interval.
+  randomR :: RandomGen g => (a,a) -> g -> (a,g)
+
+  -- | The same as 'randomR', but using a default range determined by the type:
+  --
+  -- * For bounded types (instances of 'Bounded', such as 'Char'),
+  --   the range is normally the whole type.
+  --
+  -- * For fractional types, the range is normally the semi-closed interval
+  -- @[0,1)@.
+  --
+  -- * For 'Integer', the range is (arbitrarily) the range of 'Int'.
+  random  :: RandomGen g => g -> (a, g)
+
+  -- | Plural variant of 'randomR', producing an infinite list of
+  -- random values instead of returning a new generator.
+  {-# INLINE randomRs #-}
+  randomRs :: RandomGen g => (a,a) -> g -> [a]
+  randomRs ival g = build (\cons _nil -> buildRandoms cons (randomR ival) g)
+
+  -- | Plural variant of 'random', producing an infinite list of
+  -- random values instead of returning a new generator.
+  {-# INLINE randoms #-}
+  randoms  :: RandomGen g => g -> [a]
+  randoms  g      = build (\cons _nil -> buildRandoms cons random g)
+
+  -- | A variant of 'randomR' that uses the global random number generator
+  -- (see "System.Random#globalrng").
+  randomRIO :: (a,a) -> IO a
+  --randomRIO range  = getStdRandom (randomR range)
+
+  -- | A variant of 'random' that uses the global random number generator
+  -- (see "System.Random#globalrng").
+  randomIO  :: IO a
+  --randomIO         = getStdRandom random
+
+-- | Produce an infinite list-equivalent of random values.
+{-# INLINE buildRandoms #-}
+buildRandoms :: RandomGen g
+             => (a -> as -> as)  -- ^ E.g. '(:)' but subject to fusion
+             -> (g -> (a,g))     -- ^ E.g. 'random'
+             -> g                -- ^ A 'RandomGen' instance
+             -> as
+buildRandoms cons rand = go
+  where
+    -- The seq fixes part of #4218 and also makes fused Core simpler.
+    go g = x `seq` (x `cons` go g') where (x,g') = rand g
+
+
+instance Random Integer where
+  randomR ival g = randomIvalInteger ival g
+  random g       = randomR (toInteger (minBound::Int), toInteger (maxBound::Int)) g
+
+instance Random Int        where randomR = randomIvalIntegral; random = randomBounded
+instance Random Int8       where randomR = randomIvalIntegral; random = randomBounded
+instance Random Int16      where randomR = randomIvalIntegral; random = randomBounded
+instance Random Int32      where randomR = randomIvalIntegral; random = randomBounded
+instance Random Int64      where randomR = randomIvalIntegral; random = randomBounded
+
+
+
+instance Random Word       where randomR = randomIvalIntegral; random = randomBounded
+instance Random Word8      where randomR = randomIvalIntegral; random = randomBounded
+instance Random Word16     where randomR = randomIvalIntegral; random = randomBounded
+instance Random Word32     where randomR = randomIvalIntegral; random = randomBounded
+instance Random Word64     where randomR = randomIvalIntegral; random = randomBounded
+
+instance Random CChar      where randomR = randomIvalIntegral; random = randomBounded
+instance Random CSChar     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CUChar     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CShort     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CUShort    where randomR = randomIvalIntegral; random = randomBounded
+instance Random CInt       where randomR = randomIvalIntegral; random = randomBounded
+instance Random CUInt      where randomR = randomIvalIntegral; random = randomBounded
+instance Random CLong      where randomR = randomIvalIntegral; random = randomBounded
+instance Random CULong     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CPtrdiff   where randomR = randomIvalIntegral; random = randomBounded
+instance Random CSize      where randomR = randomIvalIntegral; random = randomBounded
+instance Random CWchar     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CSigAtomic where randomR = randomIvalIntegral; random = randomBounded
+instance Random CLLong     where randomR = randomIvalIntegral; random = randomBounded
+instance Random CULLong    where randomR = randomIvalIntegral; random = randomBounded
+instance Random CIntPtr    where randomR = randomIvalIntegral; random = randomBounded
+instance Random CUIntPtr   where randomR = randomIvalIntegral; random = randomBounded
+instance Random CIntMax    where randomR = randomIvalIntegral; random = randomBounded
+instance Random CUIntMax   where randomR = randomIvalIntegral; random = randomBounded
+
+instance Random Char where
+  --randomR (a,b) g =
+  --     case (randomIvalInteger (toInteger (ord a), toInteger (ord b)) g) of
+  --       (x,g') -> (chr x, g')
+  --random g        = randomR (minBound,maxBound) g
+
+instance Random Bool where
+  randomR (a,b) g =
+      case (randomIvalInteger (bool2Int a, bool2Int b) g) of
+        (x, g') -> (int2Bool x, g')
+       where
+         bool2Int :: Bool -> Integer
+         bool2Int False = 0
+         bool2Int True  = 1
+
+         int2Bool :: Int -> Bool
+         int2Bool 0     = False
+         int2Bool _     = True
+
+  random g        = randomR (minBound,maxBound) g
+
+{-# INLINE randomRFloating #-}
+randomRFloating :: (Fractional a, Num a, Ord a, Random a, RandomGen g) => (a, a) -> g -> (a, g)
+randomRFloating (l,h) g
+    | l>h       = randomRFloating (h,l) g
+    | otherwise = let (coef,g') = random g in
+                  (2.0 * (0.5*l + coef * (0.5*h - 0.5*l)), g')  -- avoid overflow
+
+instance Random Double where
+  randomR = randomRFloating
+  random rng     =
+    case random rng of
+      (x,rng') ->
+          -- We use 53 bits of randomness corresponding to the 53 bit significand:
+          ((fromIntegral (mask53 .&. (x::Int64)) :: Double)
+           /  fromIntegral twoto53, rng')
+   where
+    twoto53 = (2::Int64) ^ (53::Int64)
+    mask53 = twoto53 - 1
+
+instance Random Float where
+  randomR = randomRFloating
+  random rng =
+    -- TODO: Faster to just use 'next' IF it generates enough bits of randomness.
+    case random rng of
+      (x,rng') ->
+          -- We use 24 bits of randomness corresponding to the 24 bit significand:
+          ((fromIntegral (mask24 .&. (x::Int32)) :: Float)
+           /  fromIntegral twoto24, rng')
+         -- Note, encodeFloat is another option, but I'm not seeing slightly
+         --  worse performance with the following [2011.06.25]:
+--         (encodeFloat rand (-24), rng')
+   where
+     mask24 = twoto24 - 1
+     twoto24 = (2::Int32) ^ (24::Int32)
+
+-- CFloat/CDouble are basically the same as a Float/Double:
+instance Random CFloat where
+  randomR = randomRFloating
+  random rng = case random rng of
+                 (x,rng') -> (realToFrac (x::Float), rng')
+
+instance Random CDouble where
+  --randomR = randomRFloating
+  -- A MYSTERY:
+  -- Presently, this is showing better performance than the Double instance:
+  -- (And yet, if the Double instance uses randomFrac then its performance is much worse!)
+  --random  = randomFrac
+  -- random rng = case random rng of
+  --             (x,rng') -> (realToFrac (x::Double), rng')
+
+
+
+randomBounded :: (RandomGen g, Random a, Bounded a) => g -> (a, g)
+randomBounded = randomR (minBound, maxBound)
+
+-- The two integer functions below take an [inclusive,inclusive] range.
+randomIvalIntegral :: (RandomGen g, Integral a) => (a, a) -> g -> (a, g)
+randomIvalIntegral (l,h) = randomIvalInteger (toInteger l, toInteger h)
+
+-- {-# SPECIALIZE randomIvalInteger :: (Num a) =>
+--     (Integer, Integer) -> StdGen -> (a, StdGen) #-}
+
+randomIvalInteger :: (RandomGen g, Num a) => (Integer, Integer) -> g -> (a, g)
+randomIvalInteger (l,h) rng
+ | l > h     = randomIvalInteger (h,l) rng
+ | otherwise = case (f 1 0 rng) of (v, rng') -> (fromInteger (l + v `mod` k), rng')
+     where
+       (genlo, genhi) = genRange rng
+       b = fromIntegral genhi - fromIntegral genlo + 1
+
+       -- Probabilities of the most likely and least likely result
+       -- will differ at most by a factor of (1 +- 1/q).  Assuming the RandomGen
+       -- is uniform, of course
+
+       -- On average, log q / log b more random values will be generated
+       -- than the minimum
+       q = 1000
+       k = h - l + 1
+       magtgt = k * q
+
+       -- generate random values until we exceed the target magnitude
+       f mag v g | mag >= magtgt = (v, g)
+                 | otherwise = v' `seq`f (mag*b) v' g' where
+                        (x,g') = next g
+                        v' = (v * b + (fromIntegral x - fromIntegral genlo))
+
+
+{--- The continuous functions on the other hand take an [inclusive,exclusive) range.
+randomFrac :: (RandomGen g, Fractional a) => g -> (a, g)
+randomFrac = randomIvalDouble (0::Double,1) realToFrac-}
+
+--randomIvalDouble :: (RandomGen g, Fractional a) => (Double, Double) -> (Double -> a) -> g -> (a, g)
+--randomIvalDouble (l,h) fromDouble rng
+--  | l > h     = randomIvalDouble (h,l) fromDouble rng
+--  | otherwise =
+--       case (randomIvalInteger (toInteger (minBound::Int32), toInteger (maxBound::Int32)) rng) of
+--         (x, rng') ->
+--            let
+--             scaled_x =
+--                fromDouble (0.5*l + 0.5*h) +                   -- previously (l+h)/2, overflowed
+--                fromDouble ((0.5*h - 0.5*l) / (0.5 * realToFrac int32Count)) *  -- avoid overflow
+--                fromIntegral (x::Int32)
+--            in
+--            (scaled_x, rng')
+
+
+-- The global random number generator
+
+{- $globalrng #globalrng#
+
+There is a single, implicit, global random number generator of type
+'StdGen', held in some global variable maintained by the 'IO' monad. It is
+initialised automatically in some system-dependent fashion, for example, by
+using the time of day, or Linux's kernel random number generator. To get
+deterministic behaviour, use 'setStdGen'.
+-}
+{-
+-- |Sets the global random number generator.
+setStdGen :: StdGen -> IO ()
+setStdGen sgen = writeIORef theStdGen sgen
+
+-- |Gets the global random number generator.
+getStdGen :: IO StdGen
+getStdGen  = readIORef theStdGen
+
+theStdGen :: IORef StdGen
+theStdGen  = unsafePerformIO $ do
+   rng <- mkStdRNG 0
+   newIORef rng
+
+-- |Applies 'split' to the current global random generator,
+-- updates it with one of the results, and returns the other.
+newStdGen :: IO StdGen
+newStdGen = atomicModifyIORef' theStdGen split
+
+{- |Uses the supplied function to get a value from the current global
+random generator, and updates the global generator with the new generator
+returned by the function. For example, @rollDice@ gets a random integer
+between 1 and 6:
+
+>  rollDice :: IO Int
+>  rollDice = getStdRandom (randomR (1,6))
+
+-}
+
+getStdRandom :: (StdGen -> (a,StdGen)) -> IO a
+getStdRandom f = atomicModifyIORef' theStdGen (swap . f)
+  where swap (v,g) = (g,v)
+-}
similarity index 99%
rename from src/System/Random/PCG/Internal.hs
rename to src/System/Random/PCG32/Internal.hs
index e828840..aae6ce0 100644 (file)
@@ -6,7 +6,7 @@
 -- Standard PCG32 Random Number Generator with chosen streams, written in
 -- pure haskell. See <http://www.pcg-random.org> for details.
 --
-module System.Random.PCG.Internal
+module System.Random.PCG32.Internal
   ( -- * PCG 32
     PCG32 (..)
   , seed
index 14eb41b..ef04cc4 100644 (file)
@@ -1,11 +1,13 @@
 {-# LANGUAGE ScopedTypeVariables, BangPatterns, UnboxedTuples, MagicHash, GADTs #-}
-{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveFunctor, DeriveDataTypeable #-}
 
 
 module System.Random.SplitMix.Internal(
   nextSeedSplitMix
   ,splitGeneratorSplitMix
   ,nextWord64SplitMix
+  ,splitGeneratorSplitMix#
+  ,nextWord64SplitMix#
   ,SplitMix64(..)
   ,Random(..)
   ,sampleWord64Random
@@ -17,6 +19,7 @@ import qualified  Data.Bits  as DB
 import Data.Bits (xor,(.|.))
 import Data.Word(Word64)
 import Data.Functor.Identity
+import Data.Data(Data(),Typeable())
 
 {-# SPECIALIZE popCount :: Word64 -> Word64 #-}
 {-# SPECIALIZE popCount :: Int -> Word64 #-}
@@ -73,6 +76,7 @@ type SplitMix64 = (# Word64# , Word64# #)
 
 data SplitMix64 = SplitMix64 { sm64seed :: {-# UNPACK #-} !Word64
                               ,sm64Gamma :: {-# UNPACK #-} !Word64 }
+   deriving (Eq,Read,Show,Data,Typeable)
 
 
 advanceSplitMix :: SplitMix64 -> SplitMix64
@@ -96,7 +100,7 @@ instance  Functor Random where
 instance Applicative Random where
   pure = \ x ->  Random# $  \ s  -> (#  x , s  #)
   (<*>)  = \ (Random# frmb) (Random# rma) ->  Random# $ \ s ->
-                    let (# fseed, maseed #) = splitGeneratorSplitMix s
+                    let (# fseed, maseed #) = splitGeneratorSplitMix# s
                         (# f , _boringSeed #) = frmb fseed
                         (# a , newSeed #) = rma  maseed
                         in (#  f a , newSeed  #)
@@ -105,12 +109,12 @@ instance Monad Random where
   (>>=) =
     \(Random# ma) f ->
       Random# $ \ s ->
-        let (# splitSeed , nextSeed #) = splitGeneratorSplitMix s
+        let (# splitSeed , nextSeed #) = splitGeneratorSplitMix# s
             (# a, _boringSeed #) = ma splitSeed
             in  unRandom# (f a) nextSeed
 
 sampleWord64Random :: Random Word64
-sampleWord64Random = Random# nextWord64SplitMix
+sampleWord64Random = Random# nextWord64SplitMix#
 
 newtype RandomT m a = RandomT# { unRandomT# :: (SplitMix64 ->  m (a , SplitMix64) ) }
 
@@ -122,7 +126,7 @@ instance Functor m => Functor (RandomT m) where
 instance Applicative m => Applicative (RandomT m) where
   pure = \ x ->  RandomT# $  \ s  ->  pure  ( x , s  )
   (<*>)  = \ (RandomT# frmb) (RandomT# rma) ->  RandomT# $ \ s ->
-                    let (# !fseed, !maseed #) = splitGeneratorSplitMix s
+                    let (# !fseed, !maseed #) = splitGeneratorSplitMix# s
                         mfOldSeed= frmb fseed
                         mArgNewSeed = rma  maseed
                         in (fmap (\(f,_s)-> \(x,newSeed)-> (f x, newSeed) ) mfOldSeed)
@@ -131,7 +135,7 @@ instance Applicative m => Applicative (RandomT m) where
 instance Monad m => Monad (RandomT m) where
 
   (>>=) = \ (RandomT#  ma) f -> RandomT# $  \ s ->
-      let (# fseed, nextSeed #) = splitGeneratorSplitMix s
+      let (# fseed, nextSeed #) = splitGeneratorSplitMix# s
        in
           do
             (a,_boring) <- ma fseed
@@ -139,25 +143,36 @@ instance Monad m => Monad (RandomT m) where
 
 sampleWord64RandomT :: Applicative m => RandomT m Word64
 sampleWord64RandomT = RandomT#  $ \ s ->
-                        let (# !w, !ngen #) = nextWord64SplitMix s
+                        let (# !w, !ngen #) = nextWord64SplitMix# s
                            in  pure (w, ngen)
 
 --instance PrimMonad m => PrimMonad (RandomT m) where
 --  primitive = \ m ->
 --  {-# INLINE #-}
 
-nextWord64SplitMix :: SplitMix64 -> (# Word64 , SplitMix64 #)
-nextWord64SplitMix gen = mixedRes `seq` (# mixedRes , newgen #)
+nextWord64SplitMix# :: SplitMix64 -> (# Word64 , SplitMix64 #)
+nextWord64SplitMix# gen = mixedRes `seq` (# mixedRes , newgen #)
   where
     mixedRes = mix64 premixres
     (#  premixres , newgen  #) = nextSeedSplitMix  gen
 
-splitGeneratorSplitMix :: SplitMix64 -> (# SplitMix64 , SplitMix64 #)
-splitGeneratorSplitMix gen = splitGen `seq`( nextNextGen `seq` (# splitGen , nextNextGen #))
+{-# INLINE nextWord64SplitMix #-}
+nextWord64SplitMix :: SplitMix64 -> ( Word64 , SplitMix64 )
+nextWord64SplitMix gen = (mixedRes, newgen)
   where
-    (# splitSeed , nextGen  #) = nextWord64SplitMix gen
+    (# mixedRes,newgen #) = nextWord64SplitMix# gen
+
+
+splitGeneratorSplitMix# :: SplitMix64 -> (# SplitMix64 , SplitMix64 #)
+splitGeneratorSplitMix# gen = splitGen `seq`( nextNextGen `seq` (# splitGen , nextNextGen #))
+  where
+    (# splitSeed , nextGen  #) = nextWord64SplitMix# gen
     (# splitPreMixGamma , nextNextGen #) = nextSeedSplitMix nextGen
     !splitGenGamma = mixGamma splitPreMixGamma
     !splitGen = SplitMix64 splitSeed splitGenGamma
 
-
+{-# INLINE splitGeneratorSplitMix #-}
+splitGeneratorSplitMix :: SplitMix64 -> (SplitMix64 , SplitMix64)
+splitGeneratorSplitMix gen =  (splitGen, nextNextGen)
+    where
+      (# splitGen, nextNextGen #) = splitGeneratorSplitMix# gen
diff --git a/src/System/Random/SplitMix/Internal/Splitting.hs b/src/System/Random/SplitMix/Internal/Splitting.hs
new file mode 100644 (file)
index 0000000..be6b566
--- /dev/null
@@ -0,0 +1,2 @@
+module System.Random.SplitMix.Internal.Splitting where
+