content: 
  -- It corresponds to model TimeOut described in document 
  -- Advanced Features of the SimPy Language
  -- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/AdvancedSimPy.pdf]. 
  -- SimPy is available on [http://simpy.sourceforge.net/].
  --   
  -- The model description is as follows.
  --
  -- Introductory example to illustrate the modeling of "competing
  -- events" such as timeouts, especially using the cancelProcess function. A
  -- network node sends a message but also sets a timeout period; if the
  -- node times out, it assumes the message it had sent was lost, and it
  -- will send again. The time to get an acknowledgement for a message is
  -- exponentially distributed with mean 1.0, and the timeout period is
  -- 0.5. Immediately after receiving an acknowledgement, the node sends
  -- out a new message.
  --
  -- We find the proportion of messages which timeout. The output should
  -- be about 0.61.
  
  import Control.Monad
  import Control.Monad.Trans
  
  import Simulation.Aivika
  
  ackRate = 1.0 / 1.0  -- reciprocal of the acknowledge mean time
  toPeriod = 0.5       -- timeout period
  
  specs = Specs { spcStartTime = 0.0,
                  spcStopTime = 10000.0,
                  spcDT = 1.0,
                  spcMethod = RungeKutta4,
                  spcGeneratorType = SimpleGenerator }
       
  model :: Simulation Double
  model =
    do -- number of messages sent
       nMsgs <- newRef 0
       
       -- number of timeouts which have occured
       nTimeOuts <- newRef 0
       
       -- reactivatedCode will 1 if timeout occurred, 
       -- 2 ACK if received
       reactivatedCode <- newRef 0
       
       nodePid <- newProcessId
       
       let node :: Process ()
           node =
             do liftEvent $ modifyRef nMsgs $ (+) 1
                -- create process IDs
                timeoutPid <- liftSimulation newProcessId
                ackPid <- liftSimulation newProcessId
                -- set up the timeout
                liftEvent $ runProcessUsingId timeoutPid (timeout ackPid)
                -- set up the message send/ACK
                liftEvent $ runProcessUsingId ackPid (acknowledge timeoutPid)
                passivateProcess
                liftEvent $
                  do code <- readRef reactivatedCode
                     when (code == 1) $
                       modifyRef nTimeOuts $ (+) 1
                     writeRef reactivatedCode 0
                node
                
           timeout :: ProcessId -> Process ()
           timeout ackPid =
             do holdProcess toPeriod
                liftEvent $
                  do writeRef reactivatedCode 1
                     reactivateProcess nodePid
                     cancelProcessWithId ackPid
           
           acknowledge :: ProcessId -> Process ()
           acknowledge timeoutPid =
             do ackTime <-
                  liftParameter $
                  randomExponential (1 / ackRate)
                holdProcess ackTime
                liftEvent $
                  do writeRef reactivatedCode 2
                     reactivateProcess nodePid
                     cancelProcessWithId timeoutPid
  
       runProcessInStartTimeUsingId
         nodePid node
       
       runEventInStopTime $
         do x <- readRef nTimeOuts
            y <- readRef nMsgs
            return $ x / y
    
  main = 
    do putStr "The percentage of timeout was "
       runSimulation model specs >>= print
  