-- |
-- Module     : Simulation.Aivika.Stream.Random
-- Copyright  : Copyright (c) 2009-2013, David Sorokin <david.sorokin@gmail.com>
-- License    : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- Stability  : experimental
-- Tested with: GHC 7.6.3
--
-- This module defines random streams of events, which are useful
-- for describing the input of the model.
--

module Simulation.Aivika.Stream.Random
       (-- * Stream of Random Events
        randomStream,
        randomUniformStream,
        randomNormalStream,
        randomExponentialStream,
        randomErlangStream,
        randomPoissonStream,
        randomBinomialStream) where

import System.Random

import Control.Monad
import Control.Monad.Trans

import Simulation.Aivika.Parameter
import Simulation.Aivika.Parameter.Random
import Simulation.Aivika.Simulation
import Simulation.Aivika.Dynamics
import Simulation.Aivika.Event
import Simulation.Aivika.Process
import Simulation.Aivika.Processor
import Simulation.Aivika.Stream
import Simulation.Aivika.Statistics
import Simulation.Aivika.Ref
import Simulation.Aivika.Arrival

-- | Return a sream of random events that arrive with the specified delay.
randomStream :: Parameter Double -> Stream Arrival
randomStream delay = Cons z0 where
  z0 =
    do t0 <- liftDynamics time
       loop t0
  loop t0 =
    do t1 <- liftDynamics time
       when (t1 /= t0) $
         error $
         "The time of requesting for a new random event is different from " ++
         "the time when the previous event has arrived. Probably, your model " ++
         "contains a logical error. The random events should be requested permanently. " ++
         "At least, they can be lost, for example, when trying to enqueue them, but " ++
         "the random stream itself must always work: randomStream."
       delay <- liftParameter delay
       holdProcess delay
       t2 <- liftDynamics time
       let arrival = Arrival { arrivalTime  = t2,
                               arrivalDelay = delay }
       return (arrival, Cons $ loop t2)

-- | Create a new stream with delays distributed uniformly.
randomUniformStream :: Double
                       -- ^ the minimum delay
                       -> Double
                       -- ^ the maximum delay
                       -> Stream Arrival
                       -- ^ the stream of random events
randomUniformStream min max =
  randomStream $ randomUniform min max

-- | Create a new stream with delays distributed normally.
randomNormalStream :: Double
                      -- ^ the mean delay
                      -> Double
                      -- ^ the delay deviation
                      -> Stream Arrival
                      -- ^ the stream of random events
randomNormalStream mu nu =
  randomStream $ randomNormal mu nu
         
-- | Return a new stream with delays distibuted exponentially with the specified mean
-- (the reciprocal of the rate).
randomExponentialStream :: Double
                           -- ^ the mean delay (the reciprocal of the rate)
                           -> Stream Arrival
                           -- ^ the stream of random events
randomExponentialStream mu =
  randomStream $ randomExponential mu
         
-- | Return a new stream with delays having the Erlang distribution with the specified
-- scale (the reciprocal of the rate) and shape parameters.
randomErlangStream :: Double
                      -- ^ the scale (the reciprocal of the rate)
                      -> Int
                      -- ^ the shape
                      -> Stream Arrival
                      -- ^ the stream of random events
randomErlangStream beta m =
  randomStream $ randomErlang beta m

-- | Return a new stream with delays having the Poisson distribution with
-- the specified mean.
randomPoissonStream :: Double
                       -- ^ the mean delay
                       -> Stream Arrival
                       -- ^ the stream of random events
randomPoissonStream mu =
  randomStream $ fmap fromIntegral $ randomPoisson mu

-- | Return a new stream with delays having the binomial distribution with the specified
-- probability and trials.
randomBinomialStream :: Double
                        -- ^ the probability
                        -> Int
                        -- ^ the number of trials
                        -> Stream Arrival
                        -- ^ the stream of random events
randomBinomialStream prob trials =
  randomStream $ fmap fromIntegral $ randomBinomial prob trials