module Data.Random.List where

import Data.Random.RVar
import Data.Random.Distribution.Uniform

import qualified System.Random.Shuffle as SRS
import Control.Monad

-- | A random variable returning an arbitrary element of the given list.
-- Every element has equal probability of being chosen.  Because it is a
-- pure 'RVar' it has no memory - that is, it \"draws with replacement.\"
randomElement :: [a] -> RVar a
randomElement = randomElementT


randomElementT :: [a] -> RVarT m a
randomElementT [] = error "randomElementT: empty list!"
randomElementT xs = do
    n <- uniformT 0 (length xs - 1)
    return (xs !! n)

-- | A random variable that returns the given list in an arbitrary shuffled
-- order.  Every ordering of the list has equal probability.
shuffle :: [a] -> RVar [a]
shuffle = shuffleT

shuffleT :: [a] -> RVarT m [a]
shuffleT [] = return []
shuffleT xs = do
    is <- zipWithM (\_ i -> uniformT 0 i) (tail xs) [1..]

    return (SRS.shuffle xs (reverse is))

-- | A random variable that shuffles a list of a known length (or a list
-- prefix of the specified length). Useful for shuffling large lists when
-- the length is known in advance.  Avoids needing to traverse the list to
-- discover its length.  Each ordering has equal probability.
shuffleN :: Int -> [a] -> RVar [a]
shuffleN = shuffleNT

shuffleNT :: Int -> [a] -> RVarT m [a]
shuffleNT n xs = shuffleNofMT n n xs

-- | A random variable that selects N arbitrary elements of a list of known length M.
shuffleNofM :: Int -> Int -> [a] -> RVar [a]
shuffleNofM = shuffleNofMT

shuffleNofMT :: Int -> Int -> [a] -> RVarT m [a]
shuffleNofMT 0 _ _ = return []
shuffleNofMT n m xs
    | n > m     = error "shuffleNofMT: n > m"
    | n >= 0    = do
        is <- sequence [uniformT 0 i | i <- take n [m-1, m-2 ..1]]
        return (take n $ SRS.shuffle (take m xs) is)
shuffleNofMT _ _ _ = error "shuffleNofMT: negative length specified"
