random 1.0.1.1 → 1.0.1.3
raw patch · 5 files changed
+146/−32 lines, 5 filesdep +randomdep ~base
Dependencies added: random
Dependency ranges changed: base
Files
- System/Random.hs +56/−28
- random.cabal +34/−4
- tests/T7936.hs +14/−0
- tests/TestRandomIOs.hs +20/−0
- tests/TestRandomRs.hs +22/−0
System/Random.hs view
@@ -96,6 +96,15 @@ import Data.IORef 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+ -- The standard nhc98 implementation of Time.ClockTime does not match -- the extended one expected in this module, so we lash-up a quick -- replacement here.@@ -189,7 +198,7 @@ -} data StdGen - = StdGen Int32 Int32+ = StdGen !Int32 !Int32 instance RandomGen StdGen where next = stdNext@@ -235,6 +244,11 @@ 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@@ -247,8 +261,6 @@ createStdGen :: Integer -> StdGen createStdGen s = mkStdGen32 $ fromIntegral s --- FIXME: 1/2/3 below should be ** (vs@30082002) XXX- {- | With a source of random number supply in hand, the 'Random' class allows the programmer to extract random values of a variety of types.@@ -279,13 +291,15 @@ -- | 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 = x : randomRs ival g' where (x,g') = randomR ival g+ 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 = (\(x,g') -> x : randoms g') (random g)+ randoms g = build (\cons _nil -> buildRandoms cons random g) -- | A variant of 'randomR' that uses the global random number generator -- (see "System.Random#globalrng").@@ -297,7 +311,19 @@ 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@@ -421,25 +447,34 @@ 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 n 1 rng) of (v, rng') -> (fromInteger (l + v `mod` k), 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- -- ERROR: b here (2^31-87) represents a baked-in assumption about genRange:- b = 2147483561- n = iLogBase b k+ magtgt = k * q - -- Here we loop until we've generated enough randomness to cover the range:- f 0 acc g = (acc, g)- f n' acc g =- let- (x,g') = next g- in- -- We shift over the random bits generated thusfar (* b) and add in the new ones.- f (n' - 1) (fromIntegral x + acc * b) g'+ -- 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@@ -459,17 +494,10 @@ (scaled_x, rng') int32Count :: Integer-int32Count = toInteger (maxBound::Int32) - toInteger (minBound::Int32) + 1---- Perform an expensive logarithm on arbitrary-size integers by repeated division.--- --- (NOTE: This actually returns ceiling(log(i) base b) except with an--- incorrect result at iLogBase b b = 2.)-iLogBase :: Integer -> Integer -> Integer-iLogBase b i = if i < b then 1 else 1 + iLogBase b (i `div` b)+int32Count = toInteger (maxBound::Int32) - toInteger (minBound::Int32) + 1 -- GHC ticket #3982 stdRange :: (Int,Int)-stdRange = (0, 2147483562)+stdRange = (1, 2147483562) stdNext :: StdGen -> (Int, StdGen) -- Returns values in the range stdRange@@ -528,7 +556,7 @@ -- |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+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@@ -541,7 +569,7 @@ -} getStdRandom :: (StdGen -> (a,StdGen)) -> IO a-getStdRandom f = atomicModifyIORef theStdGen (swap . f)+getStdRandom f = atomicModifyIORef' theStdGen (swap . f) where swap (v,g) = (g,v) {- $references
random.cabal view
@@ -1,13 +1,15 @@ name: random-version: 1.0.1.1+version: 1.0.1.3 -- 1.0.1.0 -- bump for bug fixes, but no SplittableGen yet -- 1.0.1.1 -- bump for overflow bug fixes+-- 1.0.1.2 -- bump for ticket 8704, build fusion+-- 1.0.1.3 -- bump for various bug fixes license: BSD3 license-file: LICENSE maintainer: rrnewton@gmail.com-bug-reports: http://hackage.haskell.org/trac/ghc/newticket?component=libraries/random+bug-reports: https://github.com/haskell/random/issues synopsis: random number library category: System description:@@ -15,7 +17,9 @@ library, including the ability to split random number generators. build-type: Simple-Cabal-Version: >= 1.6+-- cabal-version 1.8 needed because "the field 'build-depends: random' refers+-- to a library which is defined within the same package"+cabal-version: >= 1.8 @@ -28,5 +32,31 @@ source-repository head type: git- location: http://darcs.haskell.org/packages/random.git/+ location: http://git.haskell.org/packages/random.git +-- To run the Test-Suite:+-- $ cabal configure --enable-tests+-- $ cabal test --show-details=always --test-options="+RTS -M1M -RTS"++Test-Suite T7936+ type: exitcode-stdio-1.0+ main-is: T7936.hs+ hs-source-dirs: tests+ build-depends: base >= 3 && < 5, random+ ghc-options: -rtsopts -O2++Test-Suite TestRandomRs+ type: exitcode-stdio-1.0+ main-is: TestRandomRs.hs+ hs-source-dirs: tests+ build-depends: base >= 3 && < 5, random+ ghc-options: -rtsopts -O2+ -- TODO. Why does the following not work?+ --test-options: +RTS -M1M -RTS++Test-Suite TestRandomIOs+ type: exitcode-stdio-1.0+ main-is: TestRandomIOs.hs+ hs-source-dirs: tests+ build-depends: base >= 3 && < 5, random+ ghc-options: -rtsopts -O2
+ tests/T7936.hs view
@@ -0,0 +1,14 @@+-- Test for ticket #7936:+-- https://ghc.haskell.org/trac/ghc/ticket/7936+--+-- Used to fail with:+--+-- $ cabal test T7936 --test-options="+RTS -M1M -RTS"+-- T7936: Heap exhausted;++module Main where++import System.Random (newStdGen)+import Control.Monad (replicateM_)++main = replicateM_ 100000 newStdGen
+ tests/TestRandomIOs.hs view
@@ -0,0 +1,20 @@+-- Test for ticket #4218 (TestRandomIOs):+-- https://ghc.haskell.org/trac/ghc/ticket/4218+--+-- Used to fail with:+--+-- $ cabal test TestRandomIOs --test-options="+RTS -M1M -RTS"+-- TestRandomIOs: Heap exhausted;++module Main where++import Control.Monad (replicateM)+import System.Random (randomIO)++-- Build a list of 5000 random ints in memory (IO Monad is strict), and print+-- the last one.+-- Should use less than 1Mb of heap space, or we are generating a list of+-- unevaluated thunks.+main = do+ rs <- replicateM 5000 randomIO :: IO [Int]+ print $ last rs
+ tests/TestRandomRs.hs view
@@ -0,0 +1,22 @@+-- Test for ticket #4218 (TestRandomRs):+-- https://ghc.haskell.org/trac/ghc/ticket/4218+--+-- Fixed together with ticket #8704+-- https://ghc.haskell.org/trac/ghc/ticket/8704+-- Commit 4695ffa366f659940369f05e419a4f2249c3a776+--+-- Used to fail with:+--+-- $ cabal test TestRandomRs --test-options="+RTS -M1M -RTS"+-- TestRandomRs: Heap exhausted;++module Main where++import Control.Monad (liftM, replicateM)+import System.Random (randomRs, getStdGen)++-- Return the five-thousandth random number:+-- Should run in constant space (< 1Mb heap).+main = do+ n <- (last . take 5000 . randomRs (0, 1000000)) `liftM` getStdGen+ print (n::Integer)