#if __GLASGOW_HASKELL__ >= 701 {-# LANGUAGE Trustworthy #-} #endif #include "MachDeps.h" module System.Random ( #ifdef ENABLE_SPLITTABLEGEN RandomGen(next, genRange) , SplittableGen(split) #else RandomGen(next, genRange, split) #endif , StdGen , mkStdGen , getStdRandom , getStdGen , setStdGen , newStdGen , Random ( random, randomR, randoms, randomRs, randomIO, randomRIO ) ) where import Prelude import Data.Bits import Data.Int import Data.Word import Foreign.C.Types #ifdef __NHC__ import CPUTime ( getCPUTime ) import Foreign.Ptr ( Ptr, nullPtr ) import Foreign.C ( CTime, CUInt ) #else import System.CPUTime ( getCPUTime ) import Data.Time ( getCurrentTime, UTCTime(..) ) import Data.Ratio ( numerator, denominator ) #endif 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 {-# 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 #ifdef __NHC__ foreign import ccall "time.h time" readtime :: Ptr CTime -> IO CTime getTime :: IO (Integer, Integer) getTime = do CTime t <- readtime nullPtr; return (toInteger t, 0) #else getTime :: IO (Integer, Integer) getTime = do utc <- getCurrentTime let daytime = toRational $ utctDayTime utc return $ quotRem (numerator daytime) (denominator daytime) #endif #ifdef ENABLE_SPLITTABLEGEN #else #endif class RandomGen g where next :: g -> (Int, g) genRange :: g -> (Int,Int) genRange _ = (minBound, maxBound) #ifdef ENABLE_SPLITTABLEGEN class SplittableGen g where #endif split :: g -> (g, g) data StdGen = StdGen !Int32 !Int32 instance RandomGen StdGen where next = stdNext genRange _ = stdRange #ifdef ENABLE_SPLITTABLEGEN instance SplittableGen StdGen where #endif 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] where try_read r = do (s1, r1) <- readDec (dropWhile isSpace r) (s2, r2) <- readDec (dropWhile isSpace r1) return (StdGen s1 s2, r2) 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) mkStdGen :: Int -> StdGen mkStdGen s = mkStdGen32 $ fromIntegral s mkStdGen32 :: Int32 -> StdGen mkStdGen32 sMaybeNegative = StdGen (s1+1) (s2+1) where s = sMaybeNegative .&. maxBound (q, s1) = s `divMod` 2147483562 s2 = q `mod` 2147483398 createStdGen :: Integer -> StdGen createStdGen s = mkStdGen32 $ fromIntegral s class Random a where randomR :: RandomGen g => (a,a) -> g -> (a,g) random :: RandomGen g => g -> (a, g) {-# INLINE randomRs #-} randomRs :: RandomGen g => (a,a) -> g -> [a] randomRs ival g = build (\cons _nil -> buildRandoms cons (randomR ival) g) {-# INLINE randoms #-} randoms :: RandomGen g => g -> [a] randoms g = build (\cons _nil -> buildRandoms cons random g) randomRIO :: (a,a) -> IO a randomRIO range = getStdRandom (randomR range) randomIO :: IO a randomIO = getStdRandom random {-# INLINE buildRandoms #-} buildRandoms :: RandomGen g => (a -> as -> as) -> (g -> (a,g)) -> g -> as buildRandoms cons rand = go where 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 #ifndef __NHC__ instance Random Word where randomR = randomIvalIntegral; random = randomBounded #endif 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') instance Random Double where randomR = randomRFloating random rng = case random rng of (x,rng') -> ((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 = case random rng of (x,rng') -> ((fromIntegral (mask24 .&. (x::Int32)) :: Float) / fromIntegral twoto24, rng') where mask24 = twoto24 - 1 twoto24 = (2::Int32) ^ (24::Int32) instance Random CFloat where randomR = randomRFloating random rng = case random rng of (x,rng') -> (realToFrac (x::Float), rng') instance Random CDouble where randomR = randomRFloating random = randomFrac mkStdRNG :: Integer -> IO StdGen mkStdRNG o = do ct <- getCPUTime (sec, psec) <- getTime return (createStdGen (sec * 12345 + psec + ct + o)) randomBounded :: (RandomGen g, Random a, Bounded a) => g -> (a, g) randomBounded = randomR (minBound, maxBound) 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 q = 1000 k = h - l + 1 magtgt = k * q 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)) 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) + fromDouble ((0.5*h - 0.5*l) / (0.5 * realToFrac int32Count)) * fromIntegral (x::Int32) in (scaled_x, rng') int32Count :: Integer int32Count = toInteger (maxBound::Int32) - toInteger (minBound::Int32) + 1 stdRange :: (Int,Int) stdRange = (1, 2147483562) stdNext :: StdGen -> (Int, StdGen) stdNext (StdGen s1 s2) = (fromIntegral z', StdGen s1'' s2'') where z' = if z < 1 then z + 2147483562 else z z = s1'' - s2'' k = s1 `quot` 53668 s1' = 40014 * (s1 - k * 53668) - k * 12211 s1'' = if s1' < 0 then s1' + 2147483563 else s1' k' = s2 `quot` 52774 s2' = 40692 * (s2 - k' * 52774) - k' * 3791 s2'' = if s2' < 0 then s2' + 2147483399 else s2' stdSplit :: StdGen -> (StdGen, StdGen) stdSplit std@(StdGen s1 s2) = (left, right) where left = StdGen new_s1 t2 right = StdGen t1 new_s2 new_s1 | s1 == 2147483562 = 1 | otherwise = s1 + 1 new_s2 | s2 == 1 = 2147483398 | otherwise = s2 - 1 StdGen t1 t2 = snd (next std) setStdGen :: StdGen -> IO () setStdGen sgen = writeIORef theStdGen sgen getStdGen :: IO StdGen getStdGen = readIORef theStdGen theStdGen :: IORef StdGen theStdGen = unsafePerformIO $ do rng <- mkStdRNG 0 newIORef rng newStdGen :: IO StdGen newStdGen = atomicModifyIORef' theStdGen split getStdRandom :: (StdGen -> (a,StdGen)) -> IO a getStdRandom f = atomicModifyIORef' theStdGen (swap . f) where swap (v,g) = (g,v)