{-# LANGUAGE BangPatterns #-}
module Randomish
( randomishInts
, randomishDoubles)
where
import Data.Word
import Data.Vector.Unboxed (Vector)
import qualified Data.Vector.Unboxed.Mutable as MV
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Generic as G
-- | Use the "minimal standard" Lehmer generator to quickly generate some random
-- numbers with reasonable statistical properties. By "reasonable" we mean good
-- enough for games and test data, but not cryptography or anything where the
-- quality of the randomness really matters.
--
-- From "Random Number Generators: Good ones are hard to find"
-- Stephen K. Park and Keith W. Miller.
-- Communications of the ACM, Oct 1988, Volume 31, Number 10.
--
randomishInts
:: Int -- Length of vector.
-> Int -- Minumum value in output.
-> Int -- Maximum value in output.
-> Int -- Random seed.
-> Vector Int -- Vector of random numbers.
randomishInts !len !valMin' !valMax' !seed'
= let -- a magic number (don't change it)
multiplier :: Word64
multiplier = 16807
-- a merzenne prime (don't change it)
modulus :: Word64
modulus = 2^(31 :: Integer) - 1
-- if the seed is 0 all the numbers in the sequence are also 0.
seed
| seed' == 0 = 1
| otherwise = seed'
!valMin = fromIntegral valMin'
!valMax = fromIntegral valMax' + 1
!range = valMax - valMin
{-# INLINE f #-}
f x = multiplier * x `mod` modulus
in G.create
$ do
vec <- MV.new len
let go !ix !x
| ix == len = return ()
| otherwise
= do let x' = f x
MV.write vec ix $ fromIntegral $ (x `mod` range) + valMin
go (ix + 1) x'
go 0 (f $ f $ f $ fromIntegral seed)
return vec
-- | Generate some randomish doubles with terrible statistical properties.
-- This is good enough for test data, but not much else.
randomishDoubles
:: Int -- Length of vector
-> Double -- Minimum value in output
-> Double -- Maximum value in output
-> Int -- Random seed.
-> Vector Double -- Vector of randomish doubles.
randomishDoubles !len !valMin !valMax !seed
= let range = valMax - valMin
mx = 2^(30 :: Integer) - 1
mxf = fromIntegral mx
ints = randomishInts len 0 mx seed
in V.map (\n -> valMin + (fromIntegral n / mxf) * range) ints