packages feed

gloss-examples-1.12.0.0: picture/Visibility/Geometry/Randomish.hs

{-# LANGUAGE BangPatterns #-}

module Geometry.Randomish
        ( randomishPoints
        , randomishInts
        , randomishDoubles)
where
import Data.Word
import qualified Data.Vector.Generic            as G
import qualified Data.Vector.Unboxed.Mutable    as MV
import qualified Data.Vector.Unboxed            as V

-- | Some uniformly distributed points
randomishPoints
        :: Int                  -- ^ seed
        -> Int                  -- ^ number of points
        -> Float                -- ^ minimum coordinate
        -> Float                -- ^ maximum coordinate
        -> V.Vector (Float, Float)

randomishPoints seed' n pointMin pointMax
 = let  pts      = randomishFloats (n*2) pointMin pointMax seed'
        xs       = G.slice 0 n pts
        ys       = G.slice n n pts
   in   V.zip xs ys


-- | 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.
        -> V.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.
        -> V.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


-- | Generate some randomish doubles with terrible statistical properties.
--   This is good enough for test data, but not much else.
randomishFloats
        :: Int                  -- Length of vector
        -> Float                -- Minimum value in output
        -> Float                -- Maximum value in output
        -> Int                  -- Random seed.
        -> V.Vector Float       -- Vector of randomish doubles.

randomishFloats !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