Random.hs

------------------------------------------------------------------------------
-- Standard Library: Random numbers
--
-- Suitable for use with Hugs 98
--
-- The code in this file draws heavily from several different sources,
-- including the implementations in previous Hugs and GHC implementations.
-- Much of this was done by Sigbjorn Finne.  If there are mistakes here,
-- blame me.  The random number generation itself is based on a published
-- article by L'Ecuyer that was transliterated into Haskell by Lennart
-- Augustsson.  See the comments below for further details.
------------------------------------------------------------------------------

module Random(
	RandomGen(next, split),
	StdGen, mkStdGen,
	Random( random,   randomR,
		randoms,  randomRs,
		randomIO, randomRIO ),
	getStdRandom, getStdGen, setStdGen, newStdGen
  ) where

import IOExts  ( IORef, newIORef, writeIORef, readIORef,
		 unsafePerformIO
	       )
import Numeric ( showInt, showSigned, readDec )
import Char(ord,chr,isSpace)

-- The RandomGen class: ------------------------------------------------------

class RandomGen g where
   next  :: g -> (Int, g)
   split :: g -> (g, g)


-- An efficient and portable combined random number generator: ---------------
--
-- The June 1988 (v31 #6) issue of the Communications of the ACM has an
-- article by Pierre L'Ecuyer called, "Efficient and Portable Combined
-- Random Number Generators".  Here is the Portable Combined Generator of
-- L'Ecuyer for 32-bit computers.  It has a period of roughly 2.30584e18.
-- 
-- Transliterator: Lennart Augustsson
-- sof 1/99 - code brought (kicking and screaming) into the new Random
-- world..
------------------------------------------------------------------------------

data StdGen = StdGen Int Int

mkStdGen	      :: Int -> StdGen
mkStdGen seed          = StdGen (s1+1) (s2+1)
                         where s       = abs seed
			       (q, s1) = s `divMod` 2147483562
			       s2      = q `mod` 2147483398

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)

stdNext               :: StdGen -> (Int, StdGen)
stdNext (StdGen s1 s2) = (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
                        -- no statistical foundation for this!
                        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)

-- A standard instance of RandomGen: -----------------------------------------

instance RandomGen StdGen where
  next  = stdNext
  split = stdSplit

instance Show StdGen where
  showsPrec p (StdGen s1 s2)
    = showSigned showInt p s1 .  showChar ' ' . showSigned showInt 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)


-- The Random class: ---------------------------------------------------------

class Random a where
  -- Minimal complete definition: random and randomR
  random          :: RandomGen g => g -> (a, g)
  randomR         :: RandomGen g => (a,a) -> g -> (a,g)
  
  randoms         :: RandomGen g => g -> [a]

  randomRs        :: RandomGen g => (a,a) -> g -> [a]

  randomIO        :: IO a

  randomRIO       :: (a,a) -> IO a

  randomRIO range  = getStdRandom (randomR range)
  randomRs ival g  = x : randomRs ival g' where (x,g') = randomR ival g
  randomIO	   = getStdRandom random
  randoms  g       = x : randoms g' where (x,g') = random g

instance Random Int where
  random g        = randomR (minBound,maxBound) g
  randomR (a,b) g = randomIvalInteger (toInteger a, toInteger b) g

instance Random Char where
  random g	  = randomR (minBound,maxBound) g
  randomR (a,b) g = 
      case (randomIvalInteger (toInteger (ord a), toInteger (ord b)) g) of
        (x,g) -> (chr x, g)

instance Random Bool where
  random g	  = randomR (minBound,maxBound) g
  randomR (a,b) g = 
      case (randomIvalInteger (toInteger (bool2Int a), toInteger (bool2Int b)) g) of
        (x, g) -> (int2Bool x, g)
       where
         bool2Int False = 0
         bool2Int True  = 1

	 int2Bool 0	= False
	 int2Bool _	= True
 
instance Random Integer where
  random g	 = randomR (toInteger (minBound::Int),
                            toInteger (maxBound::Int)) g
  randomR ival g = randomIvalInteger ival g

instance Random Double where
  random g       = randomR (0::Double,1) g
  randomR ival g = randomIvalDouble ival id g
  
-- hah, so you thought you were saving cycles by using Float?
instance Random Float where
  random g        = randomIvalDouble (0::Double,1) realToFrac g
  randomR (a,b) g = randomIvalDouble (realToFrac a, realToFrac b) realToFrac g


-- Auxiliary functions: ------------------------------------------------------

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')
   where
     k = h - l + 1
     b = 2147483561
     n = iLogBase b k

     f 0 acc g = (acc, g)
     f n acc g = let (x,g') = next g
		 in f (n-1) (fromIntegral x + acc * b) g'

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::Int), toInteger (maxBound::Int)) rng) of
         (x, rng') -> 
	    let
	     scaled_x = 
		fromDouble ((l+h)/2) + 
                fromDouble ((h-l) / realToFrac intRange) *
		fromIntegral (x::Int)
	    in
	    (scaled_x, rng')

intRange :: Integer
intRange  = toInteger (maxBound::Int) - toInteger (minBound::Int)

iLogBase :: Integer -> Integer -> Integer
iLogBase b i = if i < b then 1 else 1 + iLogBase b (i `div` b)


-- The global standard random number generator: ------------------------------

foreign import getRandomSeed :: IO Integer

global_rng    :: IORef StdGen
global_rng     = unsafePerformIO (do seed <- getRandomSeed
                                     newIORef (mkStdGen (fromIntegral seed)))

setStdGen     :: StdGen -> IO ()
setStdGen sgen = writeIORef global_rng sgen

getStdGen     :: IO StdGen
getStdGen      = readIORef global_rng

newStdGen     :: IO StdGen
newStdGen      = do rng <- getStdGen
                    let (a,b) = split rng
                    setStdGen a
                    return b

getStdRandom  :: (StdGen -> (a,StdGen)) -> IO a
getStdRandom f = do rng	<- getStdGen
                    let (v, new_rng) = f rng
                    setStdGen new_rng
                    return v


------------------------------------------------------------------------------

Plain-text version of Random.hs | Valid HTML?