packages feed

gloss-examples-1.3.1.1: 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