-- Copyright (c) 2011, Colin Hill

-- | A simple implementation of a pure linear congruential psuedo-random number generator.
--
-- Example of use:
--
-- @
--main = do
--    let seed = 1
--    let (r, seed') = randomInt seed
--    putStrLn (\"Random number 1: \" ++ show r)
--    let (r', seed'') = randomInt seed'
--    putStrLn (\"Random number 2: \" ++ show r')
--    putStrLn (\"Random int list: \" ++ show (randomInts 10 seed))
--    putStrLn (\"Shuffled list: \" ++ show (shuffle [1..10] seed))
-- @
module Numeric.Noise.Random (
    randomInt,
    randomInts,
    shuffle
) where

import Numeric.Noise

import Data.Bits

-- | Returns a random 'Int' and the next seed given a seed.
randomInt :: Seed -> (Int, Seed)
randomInt seed = randomInt' seed 0 16

-- | Helper for 'randomInt'.
randomInt' :: Int -> Seed -> Int -> (Int, Seed)
randomInt' r seed 0 = (r, seed)
randomInt' r seed n = randomInt' r' seed' (n - 1)
    where seed' = 3039177861 * seed + 1
          r'    = shiftL r 2 + shiftR seed' 30

-- | Returns a random sequence of 'Int's given a seed and the number of 'Int's to generate.
randomInts :: Seed -> Int -> [Int]
randomInts _    0 = []
randomInts seed n = r : randomInts seed' (n - 1)
    where (r, seed') = randomInt seed

-- | Returns a shuffled list containing the same elements as the given list given a seed.
shuffle :: [a] -> Seed -> [a]
shuffle xs seed = fst (shuffle' xs [] seed)

-- | Helper function for 'shuffle'.
shuffle' :: [a] -> [a] -> Seed -> ([a], Seed)
shuffle' [] acc seed = (acc, seed)
shuffle' xs acc seed = shuffle' (h ++ ys) (y:acc) seed'
    where (seed', r) = randomInt seed
          (h, y:ys)  = splitAt (r `mod` length xs) xs