------------------------------------------------------------------------------ --- Library for pseudo-random number generation in Curry. ---

--- This library provides operations for generating pseudo-random --- number sequences. --- For any given seed, the sequences generated by the operations --- in this module should be identical to the sequnces --- generated by the java.util.Random package. ---

--- The algorithm is a linear congruential pseudo-random number generator --- described in Donald E. Knuth, The --- Art of Computer Programming, Volume 2: Seminumerical --- Algorithms, section 3.2.1. --- --- @author Sergio Antoy (with extensions by Michael Hanus) --- @version December 2004 ------------------------------------------------------------------------------ module Random(nextInt, nextIntRange, nextBoolean, getRandomSeed) where import Time import System(getCPUTime) ------------------------------------------------------------------ -- Private Operations ------------------------------------------------------------------ -- a few constants multiplier = 25214903917 addend = 11 powermask = 48 mask = 281474976710656 -- 2^powermask intsize = 32 intspan = 4294967296 -- 2^intsize intlimit = 2147483648 -- 2^(intsize-1) -- the basic sequence of random values sequence seed = next : sequence next where next = nextseed seed -- auxiliary private operations nextseed seed = (seed * multiplier + addend) `mod` mask xor x y = if (x==0) && (y==0) then 0 else lastBit + 2 * restBits where lastBit = if (x `mod` 2) == (y `mod` 2) then 0 else 1 restBits = xor (x `div` 2) (y `div` 2) power base exp = binary 1 base exp where binary x b e = if (e == 0) then x else binary (x * if (e `mod` 2 == 1) then b else 1) (b * b) (e `div` 2) nextIntBits seed bits = map adjust list where init = (xor seed multiplier) `mod` mask list = sequence init shift = power 2 (powermask - bits) adjust x = if arg > intlimit then arg - intspan else arg where arg = (x `div` shift) `mod` intspan ------------------------------------------------------------------ -- Public Operations ------------------------------------------------------------------ --- Returns a sequence of pseudorandom, uniformly distributed 32-bits --- integer values. All 232 --- possible integer values are produced with --- (approximately) equal probability. --- --- @param seed - The seed of the random sequence. nextInt :: Int -> [Int] nextInt seed = nextIntBits seed intsize --- Returns a pseudorandom, uniformly distributed sequence of values --- between 0 (inclusive) and the specified value (exclusive). --- Each value is a 32-bits positive integer. --- All n possible values are produced with (approximately) equal --- probability. --- @param seed - The seed of the random sequence. --- @param n - The bound on the random number to be returned. --- Must be positive. nextIntRange :: Int -> Int -> [Int] nextIntRange seed n | n>0 = if power_of_2 n then map adjust_a seq else map adjust_b (filter adjust_c seq) where seq = nextIntBits seed (intsize - 1) adjust_a x = (n * x) `div` intlimit adjust_b x = x `mod` n adjust_c x = x - (x `mod` n) + (n - 1) >= 0 power_of_2 k = k == 2 || k > 2 && k `mod` 2 == 0 && power_of_2 (k `div` 2) --- Returns a pseudorandom, uniformly distributed sequence of --- boolean values. The --- values True and False are produced with --- (approximately) equal probability. --- @param seed - The seed of the random sequence. nextBoolean :: Int -> [Bool] nextBoolean seed = map (/= 0) (nextIntBits seed 1) --- Returns a time-dependent integer number as a seed for really random numbers. --- Should only be used as a seed for pseudorandom number sequence --- and not as a random number since the precision is limited to milliseconds getRandomSeed :: IO Int getRandomSeed = getClockTime >>= \time -> getCPUTime >>= \msecs -> let (CalendarTime y mo d h m s _) = toUTCTime time in return ((y+mo+d+h+m*s*msecs) `mod` mask) {- Simple tests and examples testInt = take 20 (nextInt 0) testIntRange = take 120 (nextIntRange 0 6) testBoolean = take 20 (nextBoolean 0) reallyRandom = do seed <- getRandomSeed putStrLn (show (take 20 (nextIntRange seed 100))) -}