content: 
  {-# LANGUAGE RecursiveDo #-}
  
  -- Example: Port Operations
  --
  -- It is described in different sources [1, 2]. So, this is chapter 12 of [2] and section 6.13 of [1].
  --
  -- [1] A. Alan B. Pritsker, Simulation with Visual SLAM and AweSim, 2nd ed.
  -- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006
  -- 
  -- A port in Africa is used to load tankers with crude oil for overwater shipment.
  -- The port has facilities for loading as many as three tankers simultaneously.
  -- The  tankers, which arrive at the port every 11 +/- 7 hours, are of three different
  -- types. The relative frequency of the various types, and their loading time
  -- requirements, are as follows:
  -- 
  -- Type      Relative Frequency      Loading Time, Hours
  --   1              0.25                   18 +/- 2
  --   2              0.55                   24 +/- 3
  --   3              0.20                   36 +/- 4
  -- 
  -- There is one tug at the port. Tankers of all types require the services of this tug
  -- to move into a berth, and later to move out of a berth. When the tug is available,
  -- any berthing or de-berthing activity takes about one hour. Top priority is given to
  -- the berthing activity.
  -- 
  -- A shipper is considering bidding on a contract to transport oil from the port to
  -- the United Kingdom. He has determined that 5 tankers of a particular type would
  -- have to be committed to this task to meet contract specifications. These tankers
  -- would require 21 +/- 3 hours to load oil at the port. After loading and de-berthing,
  -- they would travel to the United Kingdom, offload the oil, and return to the port for
  -- reloading. Their round-trip travel time, including offloading, is estimated to be
  -- 240 +/- hours.
  -- 
  -- A complicated factor is that the port experiences storms. The time between
  -- the onset of storms is exponentially distributed with a mean of 48 hours and a 
  -- storm lasts 4 +/- 2 hours. No tug can start an operation until a storm is over.
  -- 
  -- Before the port authorities can commit themselves to accommodating the
  -- proposed 5 tankers, the effect of the additional port traffic on the in-port residence
  -- time of the current port users must be determined. It is desired to simulate the
  -- operation of the port for a one-year period (= 8640 hours) under the proposed new
  -- commitment to measure in-port residence time of the proposed additional tankers,
  -- as well as the three types of tankers which already use the port. All durations
  -- given as ranges are uniformly distributed.        
  
  import Prelude hiding (id, (.)) 
  
  import Control.Monad
  import Control.Monad.Trans
  import Control.Arrow
  import Control.Category (id, (.))
  
  import Data.Array
  
  import Simulation.Aivika
  import Simulation.Aivika.Queue
  import qualified Simulation.Aivika.Resource as R
  
  -- | The simulation specs.
  specs = Specs { spcStartTime = 0.0,
                  spcStopTime = 8760.0,
                  spcDT = 0.1,
                  spcMethod = RungeKutta4,
                  spcGeneratorType = SimpleGenerator }
  
  data Tunker =
    Tunker { tunkerLoadingTime :: Double,
             tunkerType :: Int }
  
  model :: Simulation Results
  model = mdo
    portTime' <- forM [1..4] $ \i ->
      newRef emptySamplingStats
    let portTime =
          array (1, 4) $ zip [1..] portTime'
    berth <-
      runEventInStartTime $
      R.newFCFSResource 3
    tug   <-
      runEventInStartTime $
      R.newFCFSResource 1
    let tunkers13 = randomUniformStream 4 18
        tunkers4  = takeStream 5 $
                    randomUniformStream 48 48
    runProcessInStartTime $
      flip consumeStream tunkers13 $ \x ->
      do p <- liftParameter $
              randomUniform 0 1
         let tp | p <= 0.25 = 1
                | p <= 0.25 + 0.55 = 2
                | otherwise = 3
         case tp of
           1 -> liftEvent arv1
           2 -> liftEvent arv2
           3 -> liftEvent arv3
    runProcessInStartTime $
      flip consumeStream tunkers4 $ \x ->
      liftEvent arv4
    let arv1 :: Event ()
        arv1 = do
          loadingTime <- liftParameter $
                         randomUniform 16 20
          let t = Tunker loadingTime 1
          runProcess (port t)
        arv2 :: Event ()
        arv2 = do
          loadingTime <- liftParameter $
                         randomUniform 21 27
          let t = Tunker loadingTime 2
          runProcess (port t)
        arv3 :: Event ()
        arv3 = do
          loadingTime <- liftParameter $
                         randomUniform 32 40
          let t = Tunker loadingTime 3
          runProcess (port t)
        arv4 :: Event ()
        arv4 = do
          loadingTime <- liftParameter $
                         randomUniform 18 24
          let t = Tunker loadingTime 4
          runProcess (port t)
    let port :: Tunker -> Process ()
        port t = do
          t0 <- liftDynamics time
          R.requestResource berth
          R.requestResource tug
          holdProcess 1
          R.releaseResource tug
          holdProcess (tunkerLoadingTime t)
          R.requestResource tug
          holdProcess 1
          R.releaseResource tug
          R.releaseResource berth
          t1 <- liftDynamics time
          let tp = tunkerType t 
          liftEvent $
            modifyRef (portTime ! tp) $
            addSamplingStats (t1 - t0)
          when (tp == 4) $
            liftEvent $
            runProcess $
            do randomUniformProcess_  216 264
               liftEvent arv4
        storm :: Process ()
        storm = do
          randomExponentialProcess_ 48
          R.decResourceCount tug 1
          randomUniformProcess_ 2 6
          liftEvent $
            R.incResourceCount tug 1
          storm
    runProcessInStartTime storm
    return $
      results
      [resultSource
       "portTime" "Port Time"
       portTime,
       --
       resultSource
       "berth" "Berth"
       berth,
       --
       resultSource
       "tug" "Tug"
       tug ]
  
  modelSummary :: Simulation Results
  modelSummary =
    fmap resultSummary model
  
  main =
    printSimulationResultsInStopTime
    printResultSourceInEnglish
    -- model specs
    modelSummary specs
  