packages feed

aivika 0.3 → 0.4

raw patch · 30 files changed

+1756/−149 lines, 30 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Simulation.Aivika.Dynamics: runDynamics :: Dynamics a -> Simulation [IO a]
- Simulation.Aivika.Dynamics: runDynamicsInFinal :: Dynamics a -> Simulation a
- Simulation.Aivika.Dynamics: runDynamicsInStart :: Dynamics a -> Simulation a
- Simulation.Aivika.Dynamics.Cont: runCont :: Cont a -> (a -> Dynamics ()) -> Dynamics ()
- Simulation.Aivika.Dynamics.EventQueue: enqueueCont :: EventQueue -> Double -> (() -> Dynamics ()) -> Dynamics ()
+ Simulation.Aivika.Dynamics: catchDynamics :: Dynamics a -> (IOException -> Dynamics a) -> Dynamics a
+ Simulation.Aivika.Dynamics: finallyDynamics :: Dynamics a -> Dynamics b -> Dynamics a
+ Simulation.Aivika.Dynamics: runDynamicsInIntegTimes :: Dynamics a -> Simulation [IO a]
+ Simulation.Aivika.Dynamics: runDynamicsInStartTime :: Dynamics a -> Simulation a
+ Simulation.Aivika.Dynamics: runDynamicsInStopTime :: Dynamics a -> Simulation a
+ Simulation.Aivika.Dynamics: runDynamicsInTime :: Double -> Dynamics a -> Simulation a
+ Simulation.Aivika.Dynamics: runDynamicsInTimes :: [Double] -> Dynamics a -> Simulation [IO a]
+ Simulation.Aivika.Dynamics: throwDynamics :: IOException -> Dynamics a
+ Simulation.Aivika.Dynamics.Agent: agentStateChanged :: Agent -> Signal (Maybe AgentState)
+ Simulation.Aivika.Dynamics.Agent: agentStateChanged_ :: Agent -> Signal ()
+ Simulation.Aivika.Dynamics.Buffer: bufferCount :: Buffer -> Dynamics Int
+ Simulation.Aivika.Dynamics.Buffer: bufferFull :: Buffer -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Buffer: bufferLostCount :: Buffer -> Dynamics Int
+ Simulation.Aivika.Dynamics.Buffer: bufferMaxCount :: Buffer -> Int
+ Simulation.Aivika.Dynamics.Buffer: bufferNull :: Buffer -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Buffer: bufferQueue :: Buffer -> EventQueue
+ Simulation.Aivika.Dynamics.Buffer: data Buffer
+ Simulation.Aivika.Dynamics.Buffer: dequeueBuffer :: Buffer -> Process ()
+ Simulation.Aivika.Dynamics.Buffer: enqueueBuffer :: Buffer -> Process ()
+ Simulation.Aivika.Dynamics.Buffer: enqueueBufferOrLost :: Buffer -> Dynamics ()
+ Simulation.Aivika.Dynamics.Buffer: newBuffer :: EventQueue -> Int -> Simulation Buffer
+ Simulation.Aivika.Dynamics.Buffer: tryDequeueBuffer :: Buffer -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Buffer: tryEnqueueBuffer :: Buffer -> Dynamics Bool
+ Simulation.Aivika.Dynamics.FIFO: data FIFO a
+ Simulation.Aivika.Dynamics.FIFO: dequeueFIFO :: FIFO a -> Process a
+ Simulation.Aivika.Dynamics.FIFO: enqueueFIFO :: FIFO a -> a -> Process ()
+ Simulation.Aivika.Dynamics.FIFO: enqueueFIFOOrLost :: FIFO a -> a -> Dynamics ()
+ Simulation.Aivika.Dynamics.FIFO: fifoCount :: FIFO a -> Dynamics Int
+ Simulation.Aivika.Dynamics.FIFO: fifoFull :: FIFO a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.FIFO: fifoLostCount :: FIFO a -> Dynamics Int
+ Simulation.Aivika.Dynamics.FIFO: fifoMaxCount :: FIFO a -> Int
+ Simulation.Aivika.Dynamics.FIFO: fifoNull :: FIFO a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.FIFO: fifoQueue :: FIFO a -> EventQueue
+ Simulation.Aivika.Dynamics.FIFO: newFIFO :: EventQueue -> Int -> Simulation (FIFO a)
+ Simulation.Aivika.Dynamics.FIFO: tryDequeueFIFO :: FIFO a -> Dynamics (Maybe a)
+ Simulation.Aivika.Dynamics.FIFO: tryEnqueueFIFO :: FIFO a -> a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.LIFO: data LIFO a
+ Simulation.Aivika.Dynamics.LIFO: dequeueLIFO :: LIFO a -> Process a
+ Simulation.Aivika.Dynamics.LIFO: enqueueLIFO :: LIFO a -> a -> Process ()
+ Simulation.Aivika.Dynamics.LIFO: enqueueLIFOOrLost :: LIFO a -> a -> Dynamics ()
+ Simulation.Aivika.Dynamics.LIFO: lifoCount :: LIFO a -> Dynamics Int
+ Simulation.Aivika.Dynamics.LIFO: lifoFull :: LIFO a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.LIFO: lifoLostCount :: LIFO a -> Dynamics Int
+ Simulation.Aivika.Dynamics.LIFO: lifoMaxCount :: LIFO a -> Int
+ Simulation.Aivika.Dynamics.LIFO: lifoNull :: LIFO a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.LIFO: lifoQueue :: LIFO a -> EventQueue
+ Simulation.Aivika.Dynamics.LIFO: newLIFO :: EventQueue -> Int -> Simulation (LIFO a)
+ Simulation.Aivika.Dynamics.LIFO: tryDequeueLIFO :: LIFO a -> Dynamics (Maybe a)
+ Simulation.Aivika.Dynamics.LIFO: tryEnqueueLIFO :: LIFO a -> a -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Process: cancelProcess :: ProcessID -> Dynamics ()
+ Simulation.Aivika.Dynamics.Process: catchProcess :: Process a -> (IOException -> Process a) -> Process a
+ Simulation.Aivika.Dynamics.Process: finallyProcess :: Process a -> Process b -> Process a
+ Simulation.Aivika.Dynamics.Process: interruptProcess :: ProcessID -> Dynamics ()
+ Simulation.Aivika.Dynamics.Process: newProcessIDWithCatch :: EventQueue -> Simulation ProcessID
+ Simulation.Aivika.Dynamics.Process: processCanceled :: ProcessID -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Process: processInterrupted :: ProcessID -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Process: runProcessNow :: Process () -> ProcessID -> Dynamics ()
+ Simulation.Aivika.Dynamics.Process: throwProcess :: IOException -> Process a
+ Simulation.Aivika.Dynamics.Ref: refChanged :: Ref a -> Signal a
+ Simulation.Aivika.Dynamics.Ref: refChanged_ :: Ref a -> Signal ()
+ Simulation.Aivika.Dynamics.Resource: newResourceWithCount :: EventQueue -> Int -> Int -> Simulation Resource
+ Simulation.Aivika.Dynamics.Resource: releaseResourceInDynamics :: Resource -> Dynamics ()
+ Simulation.Aivika.Dynamics.Resource: tryRequestResourceInDynamics :: Resource -> Dynamics Bool
+ Simulation.Aivika.Dynamics.Resource: usingResource :: Resource -> Process a -> Process a
+ Simulation.Aivika.Dynamics.Signal: apSignal :: Dynamics (a -> b) -> Signal a -> Signal b
+ Simulation.Aivika.Dynamics.Signal: awaitSignal :: Signal a -> Process a
+ Simulation.Aivika.Dynamics.Signal: composeSignal :: (a -> Dynamics b) -> Signal a -> Signal b
+ Simulation.Aivika.Dynamics.Signal: data Signal a
+ Simulation.Aivika.Dynamics.Signal: data SignalHistory a
+ Simulation.Aivika.Dynamics.Signal: data SignalSource a
+ Simulation.Aivika.Dynamics.Signal: filterSignal :: (a -> Bool) -> Signal a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: handleSignal :: Signal a -> (a -> Dynamics ()) -> Simulation (Simulation ())
+ Simulation.Aivika.Dynamics.Signal: handleSignal_ :: Signal a -> (a -> Dynamics ()) -> Simulation ()
+ Simulation.Aivika.Dynamics.Signal: mapSignal :: (a -> b) -> Signal a -> Signal b
+ Simulation.Aivika.Dynamics.Signal: merge2Signals :: Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: merge4Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: merge5Signals :: Signal a -> Signal a -> Signal a -> Signal a -> Signal a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: newSignalHistory :: Signal a -> Simulation (SignalHistory a)
+ Simulation.Aivika.Dynamics.Signal: newSignalSource :: EventQueue -> Simulation (SignalSource a)
+ Simulation.Aivika.Dynamics.Signal: newSignalSourceWithUpdate :: Dynamics () -> Simulation (SignalSource a)
+ Simulation.Aivika.Dynamics.Signal: publishSignal :: SignalSource a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: readSignalHistory :: SignalHistory a -> Dynamics (Array Int Double, Array Int a)
+ Simulation.Aivika.Dynamics.Signal: signalHistorySignal :: SignalHistory a -> Signal a
+ Simulation.Aivika.Dynamics.Signal: triggerSignal :: SignalSource a -> a -> Dynamics ()
+ Simulation.Aivika.Dynamics.Signal: updateSignal :: Signal a -> Dynamics ()
+ Simulation.Aivika.Dynamics.UVar: uvarChanged :: UVar a -> Signal a
+ Simulation.Aivika.Dynamics.UVar: uvarChanged_ :: UVar a -> Signal ()
+ Simulation.Aivika.Dynamics.Var: varChanged :: Var a -> Signal a
+ Simulation.Aivika.Dynamics.Var: varChanged_ :: Var a -> Signal ()
- Simulation.Aivika.Dynamics.Parameter: newIndexedParameter :: (Int -> IO a) -> IO (Dynamics a)
+ Simulation.Aivika.Dynamics.Parameter: newIndexedParameter :: (Int -> IO a) -> IO (Simulation a)
- Simulation.Aivika.Dynamics.Parameter: newParameter :: IO a -> IO (Dynamics a)
+ Simulation.Aivika.Dynamics.Parameter: newParameter :: IO a -> IO (Simulation a)
- Simulation.Aivika.Dynamics.Parameter: newTableParameter :: Array Int a -> IO (Dynamics a)
+ Simulation.Aivika.Dynamics.Parameter: newTableParameter :: Array Int a -> IO (Simulation a)

Files

Simulation/Aivika/Dynamics.hs view
@@ -1,11 +1,11 @@  -- | -- Module     : Simulation.Aivika.Dynamics--- Copyright  : Copyright (c) 2009-2011, David Sorokin <david.sorokin@gmail.com>+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com> -- License    : BSD3 -- Maintainer : David Sorokin <david.sorokin@gmail.com> -- Stability  : experimental--- Tested with: GHC 7.0.3+-- Tested with: GHC 7.4.1 -- -- The module defines the 'Dynamics' monad representing an abstract dynamic  -- process, i.e. a time varying polymorphic function. @@ -13,8 +13,13 @@ module Simulation.Aivika.Dynamics         (Dynamics,         DynamicsLift(..),-        runDynamicsInStart,-        runDynamicsInFinal,-        runDynamics) where+        runDynamicsInStartTime,+        runDynamicsInStopTime,+        runDynamicsInIntegTimes,+        runDynamicsInTime,+        runDynamicsInTimes,+        catchDynamics,+        finallyDynamics,+        throwDynamics) where  import Simulation.Aivika.Dynamics.Internal.Dynamics
Simulation/Aivika/Dynamics/Agent.hs view
@@ -18,6 +18,8 @@         newSubstate,         agentQueue,         agentState,+        agentStateChanged,+        agentStateChanged_,         activateState,         initState,         stateAgent,@@ -33,6 +35,7 @@ import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Internal.Signal  -- -- Agent-based Modeling@@ -41,8 +44,11 @@ -- | Represents an agent. data Agent = Agent { agentQueue :: EventQueue,                      -- ^ Return the bound event queue.+                     agentTimeRef :: IORef Double,                      agentModeRef :: IORef AgentMode,-                     agentStateRef :: IORef (Maybe AgentState) }+                     agentStateRef :: IORef (Maybe AgentState), +                     agentStateChangedSource :: SignalSource (Maybe AgentState), +                     agentStateUpdatedSource :: SignalSource (Maybe AgentState) }  -- | Represents the agent state. data AgentState = AgentState { stateAgent :: Agent,@@ -111,6 +117,8 @@                activate st p                when (st == target) $                  writeIORef (agentModeRef agent) ProcessingMode+          unless (null path1 && null path2) $+            triggerAgentStateChanged p agent  -- | Add to the state a timeout handler that will be actuated  -- in the specified time period, while the state remains active.@@ -120,9 +128,11 @@   do let q = agentQueue (stateAgent st)          Dynamics m0 = queueRun q      m0 p    -- ensure that the agent state is actual+     checkTime p (stateAgent st) "addTimeout"      v <- readIORef (stateVersionRef st)      let m1 = Dynamics $ \p ->-           do v' <- readIORef (stateVersionRef st)+           do -- checkTime p (stateAgent st) "addTimeout"+              v' <- readIORef (stateVersionRef st)               when (v == v') $ action p          Dynamics m2 = enqueue q (pointTime p + dt) m1      m2 p@@ -136,9 +146,11 @@   do let q = agentQueue (stateAgent st)          Dynamics m0 = queueRun q      m0 p    -- ensure that the agent state is actual+     checkTime p (stateAgent st) "addTimer"      v <- readIORef (stateVersionRef st)      let m1 = Dynamics $ \p ->-           do v' <- readIORef (stateVersionRef st)+           do -- checkTime p (stateAgent st) "addTimer"+              v' <- readIORef (stateVersionRef st)               when (v == v') $ do { m2 p; action p }          Dynamics m2 =             Dynamics $ \p ->@@ -178,11 +190,19 @@ newAgent :: EventQueue -> Simulation Agent newAgent queue =   Simulation $ \r ->-  do modeRef    <- newIORef CreationMode+  do timeRef    <- newIORef $ spcStartTime $ runSpecs r+     modeRef    <- newIORef CreationMode      stateRef   <- newIORef Nothing+     let Simulation m1 = newSignalSourceUnsafe+         Simulation m2 = newSignalSourceWithUpdate $ queueRun queue+     stateChangedSource <- m1 r+     stateUpdatedSource <- m2 r      return Agent { agentQueue = queue,+                    agentTimeRef = timeRef,                     agentModeRef = modeRef,-                    agentStateRef = stateRef }+                    agentStateRef = stateRef, +                    agentStateChangedSource = stateChangedSource, +                    agentStateUpdatedSource = stateUpdatedSource }  -- | Return the selected downmost active state. agentState :: Agent -> Dynamics (Maybe AgentState)@@ -190,6 +210,7 @@   Dynamics $ \p ->    do let Dynamics m = queueRun $ agentQueue agent       m p    -- ensure that the agent state is actual+     checkTime p agent "agentState"      readIORef (agentStateRef agent)                     -- | Select the next downmost active state.       @@ -199,6 +220,7 @@   do let agent = stateAgent st          Dynamics m = queueRun $ agentQueue agent       m p    -- ensure that the agent state is actual+     checkTime p agent "activateState"      mode <- readIORef (agentModeRef agent)      case mode of        CreationMode ->@@ -213,6 +235,7 @@                 Dynamics m <- readIORef (stateActivateRef st)                 m p                 writeIORef (agentModeRef agent) ProcessingMode+                triggerAgentStateChanged p agent        InitialMode ->          error $           "Use the initState function during " ++@@ -234,6 +257,7 @@   do let agent = stateAgent st          Dynamics m = queueRun $ agentQueue agent       m p    -- ensure that the agent state is actual+     checkTime p agent "initState"      mode <- readIORef (agentModeRef agent)      case mode of        CreationMode ->@@ -263,3 +287,30 @@   Simulation $ \r ->   writeIORef (stateDeactivateRef st) action   +-- | Trigger the signal when the agent state changes.+triggerAgentStateChanged :: Point -> Agent -> IO ()+triggerAgentStateChanged p agent =+  do st <- readIORef (agentStateRef agent)+     let Dynamics m = triggerSignal (agentStateChangedSource agent) st+     m p++-- | Return a signal that notifies about every change of the state.+agentStateChanged :: Agent -> Signal (Maybe AgentState)+agentStateChanged agent = merge2Signals m1 m2+  where m1 = publishSignal (agentStateChangedSource agent)+        m2 = publishSignal (agentStateUpdatedSource agent)+        +-- | Return a signal that notifies about every change of the state.+agentStateChanged_ :: Agent -> Signal ()+agentStateChanged_ agent =+  mapSignal (const ()) $ agentStateChanged agent+        +-- | Check that we don't request for the past data.+checkTime :: Point -> Agent -> String -> IO ()+{-# INLINE checkTime #-}+checkTime p agent name =+  do t <- readIORef (agentTimeRef agent)+     when (pointTime p < t) $+       error $ "You cannot request for past data: " ++ name+     when (pointTime p > t) $+       writeIORef (agentTimeRef agent) (pointTime p)
+ Simulation/Aivika/Dynamics/Buffer.hs view
@@ -0,0 +1,148 @@++-- |+-- Module     : Simulation.Aivika.Dynamics.Buffer+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>+-- License    : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability  : experimental+-- Tested with: GHC 7.4.1+--+-- This module defines the limited queue similar to 'LIFO' and 'FIFO' but where +-- the items are not represented. We know only of their number in the buffer and +-- how many items were lost.+--+module Simulation.Aivika.Dynamics.Buffer+       (Buffer,+        bufferQueue,+        bufferNull,+        bufferFull,+        bufferMaxCount,+        bufferCount,+        bufferLostCount,+        newBuffer,+        dequeueBuffer,+        tryDequeueBuffer,+        enqueueBuffer,+        tryEnqueueBuffer,+        enqueueBufferOrLost) where++import Data.IORef+import Data.Array+import Data.Array.IO++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Dynamics+import Simulation.Aivika.Dynamics.Simulation+import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Dynamics.Resource++import Simulation.Aivika.Dynamics.LIFO+import Simulation.Aivika.Dynamics.FIFO++-- | Represents the limited queue similar to 'LIFO' and 'FIFO' but where the items are not repsented.+-- So, there is no order of items but their number is strictly limited.+data Buffer =+  Buffer { bufferQueue :: EventQueue,  -- ^ Return the event queue.+           bufferMaxCount :: Int,      -- ^ The maximum available number of items.+           bufferReadRes  :: Resource,+           bufferWriteRes :: Resource,+           bufferCountRef :: IORef Int,+           bufferLostCountRef :: IORef Int }+  +-- | Create a new queue with the specified maximum available number of items.  +newBuffer :: EventQueue -> Int -> Simulation Buffer  +newBuffer q count =+  do i <- liftIO $ newIORef 0+     l <- liftIO $ newIORef 0+     r <- newResourceWithCount q count 0+     w <- newResourceWithCount q count count+     return Buffer { bufferQueue = q,+                     bufferMaxCount = count,+                     bufferReadRes  = r,+                     bufferWriteRes = w,+                     bufferCountRef = i,+                     bufferLostCountRef = l }+  +-- | Test whether the queue is empty.+bufferNull :: Buffer -> Dynamics Bool+bufferNull q =+  do a <- bufferCount q+     return (a == 0)++-- | Test whether the queue is full.+bufferFull :: Buffer -> Dynamics Bool+bufferFull q =+  do a <- bufferCount q+     return (a == bufferMaxCount q)++-- | Return the queue size.+bufferCount :: Buffer -> Dynamics Int+bufferCount q =+  liftIO $ readIORef (bufferCountRef q)+  +-- | Return the number of lost items.+bufferLostCount :: Buffer -> Dynamics Int+bufferLostCount q =+  liftIO $ readIORef (bufferLostCountRef q)+  +-- | Dequeue suspending the process if the buffer is empty.+dequeueBuffer :: Buffer -> Process ()+dequeueBuffer q =+  do requestResource (bufferReadRes q)+     liftIO $ dequeueImpl q+     releaseResource (bufferWriteRes q)+  +-- | Try to dequeue immediately.  +tryDequeueBuffer :: Buffer -> Dynamics Bool+tryDequeueBuffer q =+  do x <- tryRequestResourceInDynamics (bufferReadRes q)+     if x +       then do liftIO $ dequeueImpl q+               releaseResourceInDynamics (bufferWriteRes q)+               return True+       else return False++-- | Enqueue the item suspending the process +-- if the buffer is full.  +enqueueBuffer :: Buffer -> Process ()+enqueueBuffer q =+  do requestResource (bufferWriteRes q)+     liftIO $ enqueueImpl q+     releaseResource (bufferReadRes q)+     +-- | Try to enqueue the item immediately.  +tryEnqueueBuffer :: Buffer -> Dynamics Bool+tryEnqueueBuffer q =+  do x <- tryRequestResourceInDynamics (bufferWriteRes q)+     if x +       then do liftIO $ enqueueImpl q+               releaseResourceInDynamics (bufferReadRes q)+               return True+       else return False++-- | Try to enqueue the item. If the buffer is full+-- then the item will be lost.+enqueueBufferOrLost :: Buffer -> Dynamics ()+enqueueBufferOrLost q =+  do x <- tryRequestResourceInDynamics (bufferWriteRes q)+     if x+       then do liftIO $ enqueueImpl q+               releaseResourceInDynamics (bufferReadRes q)+       else liftIO $ modifyIORef (bufferLostCountRef q) $ (+) 1++-- | An implementation method.+dequeueImpl :: Buffer -> IO ()+dequeueImpl q =+  do i <- readIORef (bufferCountRef q)+     let i' = i - 1+     i' `seq` writeIORef (bufferCountRef q) i'++-- | An implementation method.+enqueueImpl :: Buffer -> IO ()+enqueueImpl q =+  do i <- readIORef (bufferCountRef q)+     let i' = i + 1+     i' `seq` writeIORef (bufferCountRef q) i'
Simulation/Aivika/Dynamics/Cont.hs view
@@ -7,12 +7,12 @@ -- Stability  : experimental -- Tested with: GHC 7.0.3 ----- The 'Cont' monad is a variation of the standard Cont monad, where--- the result of applying the continuation is a dynamic process.+-- The 'Cont' monad is a variation of the standard Cont monad +-- and F# async workflow, where the result of applying +-- the continuation is a dynamic process. -- module Simulation.Aivika.Dynamics.Cont-       (Cont,-        runCont) where+       (Cont) where  import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.Internal.Cont
Simulation/Aivika/Dynamics/EventQueue.hs view
@@ -15,7 +15,6 @@ module Simulation.Aivika.Dynamics.EventQueue        (EventQueue,         newQueue,-        enqueueCont,         enqueue,         queueRun) where @@ -28,7 +27,7 @@  -- | The 'EventQueue' type represents the event queue. data EventQueue = EventQueue { -  queuePQ   :: PQ.PriorityQueue (() -> Dynamics ()),+  queuePQ   :: PQ.PriorityQueue (Dynamics ()),   queueRun  :: Dynamics (),   -- ^ Run the event queue processing its events   queueBusy :: IORef Bool,   queueTime :: IORef Double }@@ -48,13 +47,9 @@      return q               -- | Enqueue the event which must be actuated at the specified time.-enqueueCont :: EventQueue -> Double -> (() -> Dynamics ()) -> Dynamics ()-enqueueCont q t c = Dynamics r where-  r p = let pq = queuePQ q in PQ.enqueue pq t c-    --- | Enqueue the event which must be actuated at the specified time. enqueue :: EventQueue -> Double -> Dynamics () -> Dynamics ()-enqueue q t m = enqueueCont q t (const m) +enqueue q t c = Dynamics r where+  r p = let pq = queuePQ q in PQ.enqueue pq t c      -- | Run the event queue processing its events. runQueue :: EventQueue -> Dynamics ()@@ -82,7 +77,7 @@                      t0  = spcStartTime sc                      dt  = spcDT sc                      n2  = fromInteger $ toInteger $ floor ((t2 - t0) / dt)-                     Dynamics k = c2 ()+                     Dynamics k = c2                  k $ p { pointTime = t2,                          pointIteration = n2,                          pointPhase = -1 }
+ Simulation/Aivika/Dynamics/FIFO.hs view
@@ -0,0 +1,164 @@++-- |+-- Module     : Simulation.Aivika.Dynamics.FIFO+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>+-- License    : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability  : experimental+-- Tested with: GHC 7.4.1+--+-- This module defines the FIFO queue.+--+module Simulation.Aivika.Dynamics.FIFO+       (FIFO,+        fifoQueue,+        fifoNull,+        fifoFull,+        fifoMaxCount,+        fifoCount,+        fifoLostCount,+        newFIFO,+        dequeueFIFO,+        tryDequeueFIFO,+        enqueueFIFO,+        tryEnqueueFIFO,+        enqueueFIFOOrLost) where++import Data.IORef+import Data.Array+import Data.Array.IO++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Dynamics+import Simulation.Aivika.Dynamics.Simulation+import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Dynamics.Resource++-- | Represents the FIFO queue with rule: first input - first output.+data FIFO a =+  FIFO { fifoQueue :: EventQueue,  -- ^ Return the event queue.+         fifoMaxCount :: Int,      -- ^ The maximum available number of items.+         fifoReadRes  :: Resource,+         fifoWriteRes :: Resource,+         fifoCountRef :: IORef Int,+         fifoLostCountRef :: IORef Int,+         fifoStartRef :: IORef Int,+         fifoEndRef   :: IORef Int,+         fifoArray :: IOArray Int a }+  +-- | Create a new FIFO queue with the specified maximum available number of items.  +newFIFO :: EventQueue -> Int -> Simulation (FIFO a)  +newFIFO q count =+  do i <- liftIO $ newIORef 0+     l <- liftIO $ newIORef 0+     s <- liftIO $ newIORef 0+     e <- liftIO $ newIORef 0+     a <- liftIO $ newArray_ (0, count - 1)+     r <- newResourceWithCount q count 0+     w <- newResourceWithCount q count count+     return FIFO { fifoQueue = q,+                   fifoMaxCount = count,+                   fifoReadRes  = r,+                   fifoWriteRes = w,+                   fifoCountRef = i,+                   fifoLostCountRef = l,+                   fifoStartRef = s,+                   fifoEndRef   = e,+                   fifoArray = a }+  +-- | Test whether the FIFO queue is empty.+fifoNull :: FIFO a -> Dynamics Bool+fifoNull fifo =+  do a <- fifoCount fifo+     return (a == 0)++-- | Test whether the FIFO queue is full.+fifoFull :: FIFO a -> Dynamics Bool+fifoFull fifo =+  do a <- fifoCount fifo+     return (a == fifoMaxCount fifo)++-- | Return the queue size.+fifoCount :: FIFO a -> Dynamics Int+fifoCount fifo =+  liftIO $ readIORef (fifoCountRef fifo)+  +-- | Return the number of lost items.+fifoLostCount :: FIFO a -> Dynamics Int+fifoLostCount fifo =+  liftIO $ readIORef (fifoLostCountRef fifo)+  +-- | Dequeue from the FIFO queue suspending the process if+-- the queue is empty.+dequeueFIFO :: FIFO a -> Process a  +dequeueFIFO fifo =+  do requestResource (fifoReadRes fifo)+     a <- liftIO $ dequeueImpl fifo+     releaseResource (fifoWriteRes fifo)+     return a+  +-- | Try to dequeue from the FIFO queue immediately.  +tryDequeueFIFO :: FIFO a -> Dynamics (Maybe a)+tryDequeueFIFO fifo =+  do x <- tryRequestResourceInDynamics (fifoReadRes fifo)+     if x +       then do a <- liftIO $ dequeueImpl fifo+               releaseResourceInDynamics (fifoWriteRes fifo)+               return $ Just a+       else return Nothing++-- | Enqueue the item in the FIFO queue suspending the process+-- if the queue is full.  +enqueueFIFO :: FIFO a -> a -> Process ()+enqueueFIFO fifo a =+  do requestResource (fifoWriteRes fifo)+     liftIO $ enqueueImpl fifo a+     releaseResource (fifoReadRes fifo)+     +-- | Try to enqueue the item in the FIFO queue. Return 'False' in+-- the monad if the queue is full.+tryEnqueueFIFO :: FIFO a -> a -> Dynamics Bool+tryEnqueueFIFO fifo a =+  do x <- tryRequestResourceInDynamics (fifoWriteRes fifo)+     if x +       then do liftIO $ enqueueImpl fifo a+               releaseResourceInDynamics (fifoReadRes fifo)+               return True+       else return False++-- | Try to enqueue the item in the FIFO queue. If the queue is full+-- then the item will be lost.+enqueueFIFOOrLost :: FIFO a -> a -> Dynamics ()+enqueueFIFOOrLost fifo a =+  do x <- tryRequestResourceInDynamics (fifoWriteRes fifo)+     if x+       then do liftIO $ enqueueImpl fifo a+               releaseResourceInDynamics (fifoReadRes fifo)+       else liftIO $ modifyIORef (fifoLostCountRef fifo) $ (+) 1++-- | An implementation method.+dequeueImpl :: FIFO a -> IO a+dequeueImpl fifo =+  do i <- readIORef (fifoCountRef fifo)+     s <- readIORef (fifoStartRef fifo)+     let i' = i - 1+         s' = (s + 1) `mod` fifoMaxCount fifo+     a <- readArray (fifoArray fifo) s+     writeArray (fifoArray fifo) s undefined+     i' `seq` writeIORef (fifoCountRef fifo) i'+     s' `seq` writeIORef (fifoStartRef fifo) s'+     return a++-- | An implementation method.+enqueueImpl :: FIFO a -> a -> IO ()+enqueueImpl fifo a =+  do i <- readIORef (fifoCountRef fifo)+     e <- readIORef (fifoEndRef fifo)+     let i' = i + 1+         e' = (e + 1) `mod` fifoMaxCount fifo+     a `seq` writeArray (fifoArray fifo) e a+     i' `seq` writeIORef (fifoCountRef fifo) i'+     e' `seq` writeIORef (fifoEndRef fifo) e'
Simulation/Aivika/Dynamics/Internal/Cont.hs view
@@ -7,23 +7,48 @@ -- Stability  : experimental -- Tested with: GHC 7.0.3 ----- The 'Cont' monad is a variation of the standard Cont monad, where--- the result of applying the continuation is a dynamic process.+-- The 'Cont' monad is a variation of the standard Cont monad +-- and F# async workflow, where the result of applying +-- the continuation is a dynamic process. -- module Simulation.Aivika.Dynamics.Internal.Cont        (Cont(..),-        runCont) where+        ContParams,+        runCont,+        catchCont,+        finallyCont,+        throwCont,+        resumeContByParams,+        contParamsCanceled) where +import Data.IORef++import qualified Control.Exception as C+import Control.Exception (IOException, throw)+ import Control.Monad import Control.Monad.Trans  import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics --- | The 'Cont' type is similar to the standard Cont monad but only--- the continuation uses a dynamic process as a result.-newtype Cont a = Cont ((a -> Dynamics ()) -> Dynamics ())+-- | The 'Cont' type is similar to the standard Cont monad +-- and F# async workflow but only the continuations return+-- a dynamic process as a result.+newtype Cont a = Cont (ContParams a -> Dynamics ()) +-- | The continuation parameters.+data ContParams a = +  ContParams { contCont :: a -> Dynamics (), +               contAux  :: ContParamsAux }++-- | The auxiliary continuation parameters.+data ContParamsAux =+  ContParamsAux { contECont :: IOException -> Dynamics (),+                  contCCont :: () -> Dynamics (),+                  contCancelRef :: IORef Bool, +                  contCatchFlag :: Bool }+ instance Monad Cont where   return  = returnC   m >>= k = bindC m k@@ -40,46 +65,240 @@ instance MonadIO Cont where   liftIO = liftIOC  +invokeC :: Cont a -> ContParams a -> Dynamics ()+{-# INLINE invokeC #-}+invokeC (Cont m) = m++invokeD :: Point -> Dynamics a -> IO a+{-# INLINE invokeD #-}+invokeD p (Dynamics m) = m p++cancelD :: Point -> ContParams a -> IO ()+{-# NOINLINE cancelD #-}+cancelD p c =+  do writeIORef (contCancelRef . contAux $ c) False+     invokeD p $ (contCCont . contAux $ c) ()+ returnC :: a -> Cont a {-# INLINE returnC #-}-returnC a = Cont $ \c -> c a+returnC a = +  Cont $ \c ->+  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ contCont c a                           +-- bindC :: Cont a -> (a -> Cont b) -> Cont b+-- {-# INLINE bindC #-}+-- bindC m k = +--   Cont $ \c -> +--   if (contCatchFlag . contAux $ c) +--   then bindWithCatch m k c+--   else bindWithoutCatch m k c+   bindC :: Cont a -> (a -> Cont b) -> Cont b {-# INLINE bindC #-}-bindC (Cont m) k = Cont $ \c -> m (\a -> let Cont m' = k a in m' c)+bindC m k = +  Cont $ bindWithoutCatch m k  -- Another version is not tail recursive!+  +bindWithoutCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Dynamics ()+{-# INLINE bindWithoutCatch #-}+bindWithoutCatch (Cont m) k c = +  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ m $ +            let cont a = invokeC (k a) c+            in c { contCont = cont } --- | Run the 'Cont' computation.-runCont :: Cont a -> (a -> Dynamics ()) -> Dynamics ()-{-# INLINE runCont #-}-runCont (Cont m) = m+-- It is not tail recursive!+bindWithCatch :: Cont a -> (a -> Cont b) -> ContParams b -> Dynamics ()+{-# NOINLINE bindWithCatch #-}+bindWithCatch (Cont m) k c = +  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ m $ +            let cont a = catchDynamics +                         (invokeC (k a) c)+                         (contECont . contAux $ c)+            in c { contCont = cont } +-- Like "bindWithoutCatch (return a) k"+callWithoutCatch :: (a -> Cont b) -> a -> ContParams b -> Dynamics ()+callWithoutCatch k a c =+  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ invokeC (k a) c++-- Like "bindWithCatch (return a) k" but it is not tail recursive!+callWithCatch :: (a -> Cont b) -> a -> ContParams b -> Dynamics ()+callWithCatch k a c =+  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ catchDynamics +            (invokeC (k a) c)+            (contECont . contAux $ c)++-- | Exception handling within 'Cont' computations.+catchCont :: Cont a -> (IOException -> Cont a) -> Cont a+catchCont m h = +  Cont $ \c -> +  if contCatchFlag . contAux $ c+  then catchWithCatch m h c+  else error $+       "To catch exceptions, the process must be created " +++       "with help of newProcessIDWithCatch: catchCont."+  +catchWithCatch :: Cont a -> (IOException -> Cont a) -> ContParams a -> Dynamics ()+catchWithCatch (Cont m) h c =+  Dynamics $ \p -> +  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ m $+            -- let econt e = callWithCatch h e c   -- not tail recursive!+            let econt e = callWithoutCatch h e c+            in c { contAux = (contAux c) { contECont = econt } }+               +-- | A computation with finalization part.+finallyCont :: Cont a -> Cont b -> Cont a+finallyCont m m' = +  Cont $ \c -> +  if contCatchFlag . contAux $ c+  then finallyWithCatch m m' c+  else error $+       "To finalize computation, the process must be created " +++       "with help of newProcessIDWithCatch: finallyCont."+  +finallyWithCatch :: Cont a -> Cont b -> ContParams a -> Dynamics ()               +finallyWithCatch (Cont m) (Cont m') c =+  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z +       then cancelD p c+       else invokeD p $ m $+            let cont a   = +                  Dynamics $ \p ->+                  invokeD p $ m' $+                  let cont b = contCont c a+                  in c { contCont = cont }+                econt e  =+                  Dynamics $ \p ->+                  invokeD p $ m' $+                  let cont b = (contECont . contAux $ c) e+                  in c { contCont = cont }+                ccont () = +                  Dynamics $ \p ->+                  invokeD p $ m' $+                  let cont b  = (contCCont . contAux $ c) ()+                      econt e = (contCCont . contAux $ c) ()+                  in c { contCont = cont,+                         contAux  = (contAux c) { contECont = econt } }+            in c { contCont = cont,+                   contAux  = (contAux c) { contECont = econt,+                                            contCCont = ccont } }++-- | Throw the exception with the further exception handling.+-- By some reasons, the standard 'throw' function per se is not handled +-- properly within 'Cont' computations, altough it will be still handled +-- if it will be hidden under the 'liftIO' function. The problem arises +-- namely with the @throw@ function, not 'IO' computations.+throwCont :: IOException -> Cont a+throwCont e = liftIO $ throw e++-- | Run the 'Cont' computation with the specified cancelation token +-- and flag indicating whether to catch exceptions.+runCont :: Cont a -> +           (a -> Dynamics ()) ->+           (IOError -> Dynamics ()) ->+           (() -> Dynamics ()) ->+           IORef Bool -> +           Bool -> +           Dynamics ()+runCont (Cont m) cont econt ccont cancelToken catchFlag = +  m ContParams { contCont = cont,+                 contAux  = +                   ContParamsAux { contECont = econt,+                                   contCCont = ccont,+                                   contCancelRef = cancelToken, +                                   contCatchFlag = catchFlag } }+ -- | Lift the 'Simulation' computation. liftSC :: Simulation a -> Cont a-{-# INLINE liftSC #-} liftSC (Simulation m) =    Cont $ \c ->   Dynamics $ \p ->-  do a <- m $ pointRun p-     let Dynamics m' = c a-     m' p+  if contCatchFlag . contAux $ c+  then liftIOWithCatch (m $ pointRun p) p c+  else liftIOWithoutCatch (m $ pointRun p) p c       -- | Lift the 'Dynamics' computation. liftDC :: Dynamics a -> Cont a-{-# INLINE liftDC #-} liftDC (Dynamics m) =   Cont $ \c ->   Dynamics $ \p ->-  do a <- m p-     let Dynamics m' = c a-     m' p+  if contCatchFlag . contAux $ c+  then liftIOWithCatch (m p) p c+  else liftIOWithoutCatch (m p) p c       -- | Lift the IO computation. liftIOC :: IO a -> Cont a-{-# INLINE liftIOC #-} liftIOC m =   Cont $ \c ->   Dynamics $ \p ->-  do a <- m-     let Dynamics m' = c a-     m' p+  if contCatchFlag . contAux $ c+  then liftIOWithCatch m p c+  else liftIOWithoutCatch m p c   +liftIOWithoutCatch :: IO a -> Point -> ContParams a -> IO ()+{-# INLINE liftIOWithoutCatch #-}+liftIOWithoutCatch m p c =+  do z <- readIORef $ (contCancelRef . contAux) c+     if z+       then cancelD p c+       else do a <- m+               invokeD p $ contCont c a++liftIOWithCatch :: IO a -> Point -> ContParams a -> IO ()+{-# NOINLINE liftIOWithCatch #-}+liftIOWithCatch m p c =+  do z <- readIORef $ (contCancelRef . contAux) c+     if z+       then cancelD p c+       else do aref <- newIORef undefined+               eref <- newIORef Nothing+               C.catch (m >>= writeIORef aref) +                 (writeIORef eref . Just)+               e <- readIORef eref+               case e of+                 Nothing -> +                   do a <- readIORef aref+                      -- tail recursive+                      invokeD p $ contCont c a+                 Just e ->+                   -- tail recursive+                   invokeD p $ (contECont . contAux) c e++-- | Resume the computation by the specified parameters.+resumeContByParams :: ContParams a -> a -> Dynamics ()+{-# INLINE resumeContByParams #-}+resumeContByParams c a = +  Dynamics $ \p ->+  do z <- readIORef $ (contCancelRef . contAux) c+     if z+       then cancelD p c+       else invokeD p $ contCont c a++-- | Test whether the computation is canceled+contParamsCanceled :: ContParams a -> IO Bool+{-# INLINE contParamsCanceled #-}+contParamsCanceled c = +  readIORef $ (contCancelRef . contAux) c
Simulation/Aivika/Dynamics/Internal/Dynamics.hs view
@@ -1,11 +1,11 @@  -- | -- Module     : Simulation.Aivika.Dynamics.Internal.Dynamics--- Copyright  : Copyright (c) 2009-2011, David Sorokin <david.sorokin@gmail.com>+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com> -- License    : BSD3 -- Maintainer : David Sorokin <david.sorokin@gmail.com> -- Stability  : experimental--- Tested with: GHC 7.0.3+-- Tested with: GHC 7.4.1 -- -- The module defines the 'Dynamics' monad representing an abstract dynamic  -- process, i.e. a time varying polymorphic function. @@ -15,9 +15,15 @@         Dynamics(..),         DynamicsLift(..),         Point(..),-        runDynamicsInStart,-        runDynamicsInFinal,-        runDynamics,+        runDynamicsInStartTime,+        runDynamicsInStopTime,+        runDynamicsInIntegTimes,+        runDynamicsInTime,+        runDynamicsInTimes,+        -- * Error Handling+        catchDynamics,+        finallyDynamics,+        throwDynamics,         -- * Utilities         basicTime,         iterationBnds,@@ -27,6 +33,9 @@         phaseHiBnd,         phaseLoBnd) where +import qualified Control.Exception as C+import Control.Exception (IOException, throw, finally)+ import Control.Monad import Control.Monad.Trans @@ -133,8 +142,8 @@      m' p  -- | Run the dynamic process in the initial simulation point.-runDynamicsInStart :: Dynamics a -> Simulation a-runDynamicsInStart (Dynamics m) =+runDynamicsInStartTime :: Dynamics a -> Simulation a+runDynamicsInStartTime (Dynamics m) =   Simulation $ \r ->   do let sc = runSpecs r           n  = 0@@ -146,8 +155,8 @@                pointPhase = 0 }  -- | Run the dynamic process in the final simulation point.-runDynamicsInFinal :: Dynamics a -> Simulation a-runDynamicsInFinal (Dynamics m) =+runDynamicsInStopTime :: Dynamics a -> Simulation a+runDynamicsInStopTime (Dynamics m) =   Simulation $ \r ->   do let sc = runSpecs r           n  = iterationHiBnd sc@@ -159,8 +168,8 @@                pointPhase = 0 }  -- | Run the dynamic process in all integration time points-runDynamics :: Dynamics a -> Simulation [IO a]-runDynamics (Dynamics m) =+runDynamicsInIntegTimes :: Dynamics a -> Simulation [IO a]+runDynamicsInIntegTimes (Dynamics m) =   Simulation $ \r ->   do let sc = runSpecs r          (nl, nu) = iterationBnds sc@@ -171,6 +180,36 @@                            pointPhase = 0 }      return $ map (m . point) [nl .. nu] +-- | Run the dynamic process in the specified time point.+runDynamicsInTime :: Double -> Dynamics a -> Simulation a+runDynamicsInTime t (Dynamics m) =+  Simulation $ \r ->+  do let sc = runSpecs r+         t0 = spcStartTime sc+         dt = spcDT sc+         n  = fromInteger $ toInteger $ floor ((t - t0) / dt)+     m Point { pointSpecs = sc,+               pointRun = r,+               pointTime = t,+               pointIteration = n,+               pointPhase = -1 }++-- | Run the dynamic process in the specified time points.+runDynamicsInTimes :: [Double] -> Dynamics a -> Simulation [IO a]+runDynamicsInTimes ts (Dynamics m) =+  Simulation $ \r ->+  do let sc = runSpecs r+         t0 = spcStartTime sc+         dt = spcDT sc+         point t =+           let n = fromInteger $ toInteger $ floor ((t - t0) / dt)+           in Point { pointSpecs = sc,+                      pointRun = r,+                      pointTime = t,+                      pointIteration = n,+                      pointPhase = -1 }+     return $ map (m . point) ts+ instance Functor Dynamics where   fmap = liftMD @@ -239,3 +278,20 @@      -- | Lift the specified 'Dynamics' computation in another monad.   liftDynamics :: Dynamics a -> m a+  +-- | Exception handling within 'Dynamics' computations.+catchDynamics :: Dynamics a -> (IOException -> Dynamics a) -> Dynamics a+catchDynamics (Dynamics m) h =+  Dynamics $ \p -> +  C.catch (m p) $ \e ->+  let Dynamics m' = h e in m' p+                           +-- | A computation with finalization part like the 'finally' function.+finallyDynamics :: Dynamics a -> Dynamics b -> Dynamics a+finallyDynamics (Dynamics m) (Dynamics m') =+  Dynamics $ \p ->+  C.finally (m p) (m' p)++-- | Like the standard 'throw' function.+throwDynamics :: IOException -> Dynamics a+throwDynamics = throw
Simulation/Aivika/Dynamics/Internal/Process.hs view
@@ -22,15 +22,25 @@         Process(..),         processQueue,         newProcessID,+        newProcessIDWithCatch,         holdProcess,+        interruptProcess,+        processInterrupted,         passivateProcess,         processPassive,         reactivateProcess,         processID,-        runProcess) where+        cancelProcess,+        processCanceled,+        runProcess,+        runProcessNow,+        catchProcess,+        finallyProcess,+        throwProcess) where  import Data.Maybe import Data.IORef+import Control.Exception (IOException, throw) import Control.Monad import Control.Monad.Trans @@ -43,7 +53,13 @@ data ProcessID =    ProcessID { processQueue   :: EventQueue,  -- ^ Return the event queue.               processStarted :: IORef Bool,-              processCont    :: IORef (Maybe (() -> Dynamics ())) }+              processCatchFlag     :: Bool,+              processReactCont     :: IORef (Maybe (ContParams ())), +              processCancelRef     :: IORef Bool, +              processCancelToken   :: IORef Bool,+              processInterruptRef  :: IORef Bool, +              processInterruptCont :: IORef (Maybe (ContParams ())), +              processInterruptVersion :: IORef Int }  -- | Specifies a discontinuous process that can suspend at any time -- and then resume later.@@ -55,16 +71,51 @@   Process $ \pid ->   Cont $ \c ->   Dynamics $ \p ->-  do let Dynamics m = enqueueCont (processQueue pid) (pointTime p + dt) c+  do let x = processInterruptCont pid+     writeIORef x $ Just c+     writeIORef (processInterruptRef pid) False+     v <- readIORef (processInterruptVersion pid)+     let Dynamics m = +           enqueue (processQueue pid) (pointTime p + dt) $+           Dynamics $ \p ->+           do v' <- readIORef (processInterruptVersion pid)+              when (v == v') $ +                do writeIORef x Nothing+                   let Dynamics m = resumeContByParams c ()+                   m p      m p +-- | Interrupt a process with the specified ID if the process+-- was held by computation 'holdProcess'.+interruptProcess :: ProcessID -> Dynamics ()+interruptProcess pid =+  Dynamics $ \p ->+  do let x = processInterruptCont pid+     a <- readIORef x+     case a of+       Nothing -> return ()+       Just c ->+         do writeIORef x Nothing+            writeIORef (processInterruptRef pid) True+            modifyIORef (processInterruptVersion pid) $ (+) 1+            let Dynamics m = +                  enqueue (processQueue pid) (pointTime p) $ +                  resumeContByParams c ()+            m p+            +-- | Test whether the process with the specified ID was interrupted.+processInterrupted :: ProcessID -> Dynamics Bool+processInterrupted pid =+  Dynamics $ \p ->+  readIORef (processInterruptRef pid)+ -- | Passivate the process. passivateProcess :: Process () passivateProcess =   Process $ \pid ->   Cont $ \c ->   Dynamics $ \p ->-  do let x = processCont pid+  do let x = processReactCont pid      a <- readIORef x      case a of        Nothing -> writeIORef x $ Just c@@ -76,7 +127,7 @@   Dynamics $ \p ->   do let Dynamics m = queueRun $ processQueue pid      m p-     let x = processCont pid+     let x = processReactCont pid      a <- readIORef x      return $ isJust a @@ -86,44 +137,105 @@   Dynamics $ \p ->   do let Dynamics m = queueRun $ processQueue pid      m p-     let x = processCont pid+     let x = processReactCont pid      a <- readIORef x      case a of        Nothing ->           return ()        Just c ->          do writeIORef x Nothing-            let Dynamics m  = enqueueCont (processQueue pid) (pointTime p) c+            let Dynamics m  = enqueue (processQueue pid) (pointTime p) $ +                              resumeContByParams c ()             m p  -- | Start the process with the specified ID at the desired time. runProcess :: Process () -> ProcessID -> Double -> Dynamics () runProcess (Process p) pid t =-  runCont m return-    where m = do y <- liftIO $ readIORef (processStarted pid)+  runCont m cont econt ccont (processCancelToken pid) (processCatchFlag pid)+    where cont  = return+          econt = throw+          ccont = return+          m = do y <- liftIO $ readIORef (processStarted pid)                  if y                     then error $                         "A process with such ID " ++                         "has been started already: runProc"                    else liftIO $ writeIORef (processStarted pid) True-                 Cont $ \c -> enqueueCont (processQueue pid) t c+                 Cont $ \c -> enqueue (processQueue pid) t $ +                              resumeContByParams c ()                  p pid +-- | Start the process with the specified ID at the current simulation time.+runProcessNow :: Process () -> ProcessID -> Dynamics ()+runProcessNow process pid =+  Dynamics $ \p ->+  do let Dynamics m = runProcess process pid (pointTime p)+     m p+ -- | Return the current process ID. processID :: Process ProcessID processID = Process $ \pid -> return pid --- | Create a new process ID.+-- | Create a new process ID without exception handling. newProcessID :: EventQueue -> Simulation ProcessID newProcessID q =   do x <- liftIO $ newIORef Nothing      y <- liftIO $ newIORef False+     c <- liftIO $ newIORef False+     t <- liftIO $ newIORef False+     i <- liftIO $ newIORef False+     z <- liftIO $ newIORef Nothing+     v <- liftIO $ newIORef 0      return ProcessID { processQueue   = q,                         processStarted = y,-                        processCont    = x }+                        processCatchFlag     = False,+                        processReactCont     = x, +                        processCancelRef     = c, +                        processCancelToken   = t,+                        processInterruptRef  = i,+                        processInterruptCont = z, +                        processInterruptVersion = v } +-- | Create a new process ID with capabilities of catching +-- the IOError exceptions and finalizing the computation. +-- The corresponded process will be slower than that one+-- which identifier is created with help of 'newProcessID'.+newProcessIDWithCatch :: EventQueue -> Simulation ProcessID+newProcessIDWithCatch q =+  do x <- liftIO $ newIORef Nothing+     y <- liftIO $ newIORef False+     c <- liftIO $ newIORef False+     t <- liftIO $ newIORef False+     i <- liftIO $ newIORef False+     z <- liftIO $ newIORef Nothing+     v <- liftIO $ newIORef 0+     return ProcessID { processQueue   = q,+                        processStarted = y,+                        processCatchFlag     = True,+                        processReactCont     = x, +                        processCancelRef     = c, +                        processCancelToken   = t,+                        processInterruptRef  = i,+                        processInterruptCont = z, +                        processInterruptVersion = v }++-- | Cancel a process with the specified ID.+cancelProcess :: ProcessID -> Dynamics ()+cancelProcess pid =+  Dynamics $ \p ->+  do z <- readIORef (processCancelRef pid) +     unless z $+       do writeIORef (processCancelRef pid) True+          writeIORef (processCancelToken pid) True++-- | Test whether the process with the specified ID is canceled.+processCanceled :: ProcessID -> Dynamics Bool+processCanceled pid =+  Dynamics $ \p ->+  readIORef (processCancelRef pid)+ instance Eq ProcessID where-  x == y = processCont x == processCont y    -- for the references are unique+  x == y = processReactCont x == processReactCont y    -- for the references are unique  instance Monad Process where   return  = returnP@@ -143,7 +255,7 @@    returnP :: a -> Process a {-# INLINE returnP #-}-returnP a = Process (\pid -> return a)+returnP a = Process $ \pid -> return a  bindP :: Process a -> (a -> Process b) -> Process b {-# INLINE bindP #-}@@ -164,3 +276,25 @@ liftIOP :: IO a -> Process a {-# INLINE liftIOP #-} liftIOP m = Process $ \pid -> liftIO m++-- | Exception handling within 'Process' computations.+catchProcess :: Process a -> (IOException -> Process a) -> Process a+catchProcess (Process m) h =+  Process $ \pid ->+  catchCont (m pid) $ \e ->+  let Process m' = h e in m' pid+                           +-- | A computation with finalization part.+finallyProcess :: Process a -> Process b -> Process a+finallyProcess (Process m) (Process m') =+  Process $ \pid ->+  finallyCont (m pid) (m' pid)++-- | Throw the exception with the further exception handling.+-- By some reasons, the standard 'throw' function per se is not handled +-- properly within 'Process' computations, although it will be still +-- handled if it will be hidden under the 'liftIO' function. The problem +-- arises namely with the @throw@ function, not 'IO' computations.+throwProcess :: IOException -> Process a+throwProcess = liftIO . throw+
+ Simulation/Aivika/Dynamics/Internal/Signal.hs view
@@ -0,0 +1,287 @@++-- |+-- Module     : Simulation.Aivika.Dynamics.Internal.Signal+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>+-- License    : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability  : experimental+-- Tested with: GHC 7.4.1+--+-- This module defines the signal which we can subscribe handlers to. +-- These handlers can be disposed. The signal is triggered in the +-- current time point actuating the corresponded computations from +-- the handlers. +--++module Simulation.Aivika.Dynamics.Internal.Signal+       (Signal,+        SignalSource,+        newSignalSourceWithUpdate,+        newSignalSourceUnsafe,+        publishSignal,+        triggerSignal,+        handleSignal,+        handleSignal_,+        updateSignal,+        mapSignal,+        composeSignal,+        apSignal,+        filterSignal,+        merge2Signals,+        merge3Signals,+        merge4Signals,+        merge5Signals) where++import Data.IORef++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Dynamics.Internal.Dynamics+import Simulation.Aivika.Dynamics.Internal.Simulation++-- | The signal source that can publish its signal.+data SignalSource a =+  SignalSource { publishSignal :: Signal a,+                                  -- ^ Publish the signal.+                 triggerSignal :: a -> Dynamics ()+                                  -- ^ Trigger the signal actuating +                                  -- all its handlers at the current +                                  -- simulation time point.+               }+  +-- | The signal that can have disposable handlers.  +data Signal a =+  Signal { handleSignal :: (a -> Dynamics ()) -> +                           Simulation (Simulation ()),+           -- ^ Subscribe the handler to the specified +           -- signal and return a nested computation +           -- that, being applied, unsubscribes the +           -- handler from this signal.+           updateSignal :: Dynamics ()+           -- ^ Update the signal to its actual state.+         }+  +-- | The queue of signal handlers.+data SignalHandlerQueue a =+  SignalHandlerQueue { queueStart :: IORef (Maybe (SignalHandler a)),+                       queueEnd   :: IORef (Maybe (SignalHandler a)) }+  +-- | It contains the information about the disposable queue handler.+data SignalHandler a =+  SignalHandler { handlerComp :: a -> Dynamics (),+                  handlerPrev :: IORef (Maybe (SignalHandler a)),+                  handlerNext :: IORef (Maybe (SignalHandler a)) }++-- | Subscribe the handler to the specified signal.+-- To subscribe the disposable handlers, use function 'handleSignal'.+handleSignal_ :: Signal a -> (a -> Dynamics ()) -> Simulation ()+handleSignal_ signal h = +  do x <- handleSignal signal h+     return ()+     +-- | Create a new signal source with the specified update computation.+newSignalSourceWithUpdate :: Dynamics () -> Simulation (SignalSource a)+newSignalSourceWithUpdate update =+  Simulation $ \r ->+  do start <- newIORef Nothing+     end <- newIORef Nothing+     let queue  = SignalHandlerQueue { queueStart = start,+                                       queueEnd   = end }+         signal = Signal { handleSignal = handle, +                           updateSignal = update }+         source = SignalSource { publishSignal = signal, +                                 triggerSignal = trigger }+         handle h =+           Simulation $ \r ->+           do x <- enqueueSignalHandler queue h+              return $ liftIO $ dequeueSignalHandler queue x+         trigger a =+           Dynamics $ \p ->+           do let Dynamics m = update +              m p+              let h = queueStart queue+              triggerSignalHandlers h a p+     return source+     +-- | Create a new signal source that has no update computation.+newSignalSourceUnsafe :: Simulation (SignalSource a)+newSignalSourceUnsafe =+  Simulation $ \r ->+  do start <- newIORef Nothing+     end <- newIORef Nothing+     let queue  = SignalHandlerQueue { queueStart = start,+                                       queueEnd   = end }+         signal = Signal { handleSignal = handle, +                           updateSignal = update }+         source = SignalSource { publishSignal = signal, +                                 triggerSignal = trigger }+         handle h =+           Simulation $ \r ->+           do x <- enqueueSignalHandler queue h+              return $ liftIO $ dequeueSignalHandler queue x+         trigger a =+           Dynamics $ \p ->+           let h = queueStart queue+           in triggerSignalHandlers h a p+         update = return ()+     return source++-- | Trigger all next signal handlers.+triggerSignalHandlers :: IORef (Maybe (SignalHandler a)) -> a -> Point -> IO ()+{-# INLINE triggerSignalHandlers #-}+triggerSignalHandlers r a p =+  do x <- readIORef r+     case x of+       Nothing -> return ()+       Just h ->+         do let Dynamics m = handlerComp h a+            m p+            triggerSignalHandlers (handlerNext h) a p+            +-- | Enqueue the handler and return its representative in the queue.            +enqueueSignalHandler :: SignalHandlerQueue a -> (a -> Dynamics ()) -> IO (SignalHandler a)+enqueueSignalHandler q h = +  do tail <- readIORef (queueEnd q)+     case tail of+       Nothing ->+         do prev <- newIORef Nothing+            next <- newIORef Nothing+            let handler = SignalHandler { handlerComp = h,+                                          handlerPrev = prev,+                                          handlerNext = next }+            writeIORef (queueStart q) (Just handler)+            writeIORef (queueEnd q) (Just handler)+            return handler+       Just x ->+         do prev <- newIORef tail+            next <- newIORef Nothing+            let handler = SignalHandler { handlerComp = h,+                                          handlerPrev = prev,+                                          handlerNext = next }+            writeIORef (handlerNext x) (Just handler)+            writeIORef (queueEnd q) (Just handler)+            return handler++-- | Dequeue the handler representative.+dequeueSignalHandler :: SignalHandlerQueue a -> SignalHandler a -> IO ()+dequeueSignalHandler q h = +  do prev <- readIORef (handlerPrev h)+     case prev of+       Nothing ->+         do next <- readIORef (handlerNext h)+            case next of+              Nothing ->+                do writeIORef (queueStart q) Nothing+                   writeIORef (queueEnd q) Nothing+              Just y ->+                do writeIORef (handlerPrev y) Nothing+                   writeIORef (handlerNext h) Nothing+                   writeIORef (queueStart q) next+       Just x ->+         do next <- readIORef (handlerNext h)+            case next of+              Nothing ->+                do writeIORef (handlerPrev h) Nothing+                   writeIORef (handlerNext x) Nothing+                   writeIORef (queueEnd q) prev+              Just y ->+                do writeIORef (handlerPrev h) Nothing+                   writeIORef (handlerNext h) Nothing+                   writeIORef (handlerPrev y) prev+                   writeIORef (handlerNext x) next++-- | Map the signal according the specified function.+mapSignal :: (a -> b) -> Signal a -> Signal b+mapSignal f m =+  Signal { handleSignal = \h -> +            handleSignal m $ h . f, +           updateSignal = +             updateSignal m }++instance Functor Signal where+  fmap = mapSignal+  +-- | Filter only those signal values that satisfy to +-- the specified predicate.+filterSignal :: (a -> Bool) -> Signal a -> Signal a+filterSignal p m =+  Signal { handleSignal = \h ->+            handleSignal m $ \a ->+            when (p a) $ h a, +           updateSignal =+             updateSignal m }+  +-- | Merge two signals.+merge2Signals :: Signal a -> Signal a -> Signal a+merge2Signals m1 m2 =+  Signal { handleSignal = \h ->+            do x1 <- handleSignal m1 h+               x2 <- handleSignal m2 h+               return $ do { x1; x2 }, +           updateSignal =+             do updateSignal m1+                updateSignal m2 }++-- | Merge three signals.+merge3Signals :: Signal a -> Signal a -> Signal a -> Signal a+merge3Signals m1 m2 m3 =+  Signal { handleSignal = \h ->+            do x1 <- handleSignal m1 h+               x2 <- handleSignal m2 h+               x3 <- handleSignal m3 h+               return $ do { x1; x2; x3 },+           updateSignal =+             do updateSignal m1+                updateSignal m2 +                updateSignal m3 }++-- | Merge four signals.+merge4Signals :: Signal a -> Signal a -> Signal a -> +                 Signal a -> Signal a+merge4Signals m1 m2 m3 m4 =+  Signal { handleSignal = \h ->+            do x1 <- handleSignal m1 h+               x2 <- handleSignal m2 h+               x3 <- handleSignal m3 h+               x4 <- handleSignal m4 h+               return $ do { x1; x2; x3; x4 },+           updateSignal =+             do updateSignal m1+                updateSignal m2 +                updateSignal m3 +                updateSignal m4 }+           +-- | Merge five signals.+merge5Signals :: Signal a -> Signal a -> Signal a -> +                 Signal a -> Signal a -> Signal a+merge5Signals m1 m2 m3 m4 m5 =+  Signal { handleSignal = \h ->+            do x1 <- handleSignal m1 h+               x2 <- handleSignal m2 h+               x3 <- handleSignal m3 h+               x4 <- handleSignal m4 h+               x5 <- handleSignal m5 h+               return $ do { x1; x2; x3; x4; x5 },+           updateSignal =+             do updateSignal m1+                updateSignal m2 +                updateSignal m3 +                updateSignal m4+                updateSignal m5 }++-- | Compose the signal.+composeSignal :: (a -> Dynamics b) -> Signal a -> Signal b+composeSignal f m =+  Signal { handleSignal = \h ->+            handleSignal m (f >=> h),+           updateSignal = +             updateSignal m }+  +-- | Transform the signal.+apSignal :: Dynamics (a -> b) -> Signal a -> Signal b+apSignal f m =+  Signal { handleSignal = \h ->+            handleSignal m $ \a -> do { x <- f; h (x a) },+           updateSignal =+             updateSignal m }
+ Simulation/Aivika/Dynamics/LIFO.hs view
@@ -0,0 +1,152 @@++-- |+-- Module     : Simulation.Aivika.Dynamics.LIFO+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>+-- License    : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability  : experimental+-- Tested with: GHC 7.4.1+--+-- This module defines the LIFO queue.+--+module Simulation.Aivika.Dynamics.LIFO+       (LIFO,+        lifoQueue,+        lifoNull,+        lifoFull,+        lifoMaxCount,+        lifoCount,+        lifoLostCount,+        newLIFO,+        dequeueLIFO,+        tryDequeueLIFO,+        enqueueLIFO,+        tryEnqueueLIFO,+        enqueueLIFOOrLost) where++import Data.IORef+import Data.Array+import Data.Array.IO++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Dynamics+import Simulation.Aivika.Dynamics.Simulation+import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Process+import Simulation.Aivika.Dynamics.Resource++-- | Represents the LIFO queue with rule: last input - first output.+data LIFO a =+  LIFO { lifoQueue :: EventQueue,  -- ^ Return the event queue.+         lifoMaxCount :: Int,      -- ^ The maximum available number of items.+         lifoReadRes  :: Resource,+         lifoWriteRes :: Resource,+         lifoCountRef :: IORef Int,+         lifoLostCountRef :: IORef Int,+         lifoArray :: IOArray Int a }+  +-- | Create a new LIFO queue with the specified maximum available number of items.  +newLIFO :: EventQueue -> Int -> Simulation (LIFO a)  +newLIFO q count =+  do i <- liftIO $ newIORef 0+     l <- liftIO $ newIORef 0+     a <- liftIO $ newArray_ (0, count - 1)+     r <- newResourceWithCount q count 0+     w <- newResourceWithCount q count count+     return LIFO { lifoQueue = q,+                   lifoMaxCount = count,+                   lifoReadRes  = r,+                   lifoWriteRes = w,+                   lifoCountRef = i,+                   lifoLostCountRef = l,+                   lifoArray = a }+  +-- | Test whether the LIFO queue is empty.+lifoNull :: LIFO a -> Dynamics Bool+lifoNull lifo =+  do a <- lifoCount lifo+     return (a == 0)++-- | Test whether the LIFO queue is full.+lifoFull :: LIFO a -> Dynamics Bool+lifoFull lifo =+  do a <- lifoCount lifo+     return (a == lifoMaxCount lifo)++-- | Return the queue size.+lifoCount :: LIFO a -> Dynamics Int+lifoCount lifo =+  liftIO $ readIORef (lifoCountRef lifo)+  +-- | Return the number of lost items.+lifoLostCount :: LIFO a -> Dynamics Int+lifoLostCount lifo =+  liftIO $ readIORef (lifoLostCountRef lifo)+  +-- | Dequeue from the LIFO queue suspending the process if+-- the queue is empty.+dequeueLIFO :: LIFO a -> Process a  +dequeueLIFO lifo =+  do requestResource (lifoReadRes lifo)+     a <- liftIO $ dequeueImpl lifo+     releaseResource (lifoWriteRes lifo)+     return a+  +-- | Try to dequeue from the LIFO queue immediately.  +tryDequeueLIFO :: LIFO a -> Dynamics (Maybe a)+tryDequeueLIFO lifo =+  do x <- tryRequestResourceInDynamics (lifoReadRes lifo)+     if x +       then do a <- liftIO $ dequeueImpl lifo+               releaseResourceInDynamics (lifoWriteRes lifo)+               return $ Just a+       else return Nothing++-- | Enqueue the item in the LIFO queue suspending the process if+-- the queue is full.  +enqueueLIFO :: LIFO a -> a -> Process ()+enqueueLIFO lifo a =+  do requestResource (lifoWriteRes lifo)+     liftIO $ enqueueImpl lifo a+     releaseResource (lifoReadRes lifo)+     +-- | Try to enqueue the item in the LIFO queue. Return 'False' in+-- the monad if the queue is full.+tryEnqueueLIFO :: LIFO a -> a -> Dynamics Bool+tryEnqueueLIFO lifo a =+  do x <- tryRequestResourceInDynamics (lifoWriteRes lifo)+     if x +       then do liftIO $ enqueueImpl lifo a+               releaseResourceInDynamics (lifoReadRes lifo)+               return True+       else return False++-- | Try to enqueue the item in the LIFO queue. If the queue is full+-- then the item will be lost.+enqueueLIFOOrLost :: LIFO a -> a -> Dynamics ()+enqueueLIFOOrLost lifo a =+  do x <- tryRequestResourceInDynamics (lifoWriteRes lifo)+     if x+       then do liftIO $ enqueueImpl lifo a+               releaseResourceInDynamics (lifoReadRes lifo)+       else liftIO $ modifyIORef (lifoLostCountRef lifo) $ (+) 1++-- | An implementation method.+dequeueImpl :: LIFO a -> IO a+dequeueImpl lifo =+  do i <- readIORef (lifoCountRef lifo)+     let j = i - 1+     a <- j `seq` readArray (lifoArray lifo) j+     writeArray (lifoArray lifo) j undefined+     writeIORef (lifoCountRef lifo) j+     return a++-- | An implementation method.+enqueueImpl :: LIFO a -> a -> IO ()+enqueueImpl lifo a =+  do i <- readIORef (lifoCountRef lifo)+     let j = i + 1+     a `seq` writeArray (lifoArray lifo) i a+     j `seq` writeIORef (lifoCountRef lifo) j
Simulation/Aivika/Dynamics/Parameter.hs view
@@ -1,11 +1,11 @@  -- | -- Module     : Simulation.Aivika.Dynamics.Parameter--- Copyright  : Copyright (c) 2009-2011, David Sorokin <david.sorokin@gmail.com>+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com> -- License    : BSD3 -- Maintainer : David Sorokin <david.sorokin@gmail.com> -- Stability  : experimental--- Tested with: GHC 7.0.3+-- Tested with: GHC 7.4.1 -- -- This module defines the parameters of simulation experiments. --@@ -25,35 +25,35 @@  -- | Create a thread-safe parameter that returns always the same value during the simulation run,  -- where the value is recalculated for each new run.-newParameter :: IO a -> IO (Dynamics a)+newParameter :: IO a -> IO (Simulation a) newParameter a = newIndexedParameter $ \_ -> a  -- | Create a thread-safe parameter that returns always the same value during the simulation run, -- where the value is taken consequently from the specified table based on the number of the  -- current run starting from zero. After all values from the table are used, it takes the first  -- value of the table, then the second one and so on.-newTableParameter :: Array Int a -> IO (Dynamics a)+newTableParameter :: Array Int a -> IO (Simulation a) newTableParameter t = newIndexedParameter (\i -> return $ t ! (((i - i1) `mod` n) + i1))   where (i1, i2) = bounds t         n = i2 - i1 + 1  -- | Create a thread-safe parameter that returns always the same value during the simulation run,  -- where the value depends on the number of this run starting from zero.-newIndexedParameter :: (Int -> IO a) -> IO (Dynamics a)+newIndexedParameter :: (Int -> IO a) -> IO (Simulation a) newIndexedParameter f =    do lock <- newMVar ()      dict <- newIORef M.empty-     return $ Dynamics $ \p ->-       do let i = runIndex $ pointRun p+     return $ Simulation $ \r ->+       do let i = runIndex r           m <- readIORef dict           if M.member i m             then do let Just v = M.lookup i m                     return v             else withMVar lock $                   \() -> do { m <- readIORef dict;-                            if M.member i m-                            then do let Just v = M.lookup i m-                                    return v-                            else do v <- f i-                                    writeIORef dict $ M.insert i v m-                                    return v }+                             if M.member i m+                             then do let Just v = M.lookup i m+                                     return v+                             else do v <- f i+                                     writeIORef dict $ M.insert i v m+                                     return v }
Simulation/Aivika/Dynamics/Process.hs view
@@ -22,12 +22,21 @@         Process,         processQueue,         newProcessID,+        newProcessIDWithCatch,         holdProcess,+        interruptProcess,+        processInterrupted,         passivateProcess,         processPassive,         reactivateProcess,         processID,-        runProcess) where+        cancelProcess,+        processCanceled,+        runProcess,+        runProcessNow,+        catchProcess,+        finallyProcess,+        throwProcess) where  import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.Internal.Process
Simulation/Aivika/Dynamics/Ref.hs view
@@ -12,6 +12,8 @@ module Simulation.Aivika.Dynamics.Ref        (Ref,         refQueue,+        refChanged,+        refChanged_,         newRef,         readRef,         writeRef,@@ -24,6 +26,7 @@ import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Internal.Signal  -- | The 'Ref' type represents a mutable variable similar to the 'IORef' variable  -- but only bound to some event queue, which makes the variable coordinated @@ -31,35 +34,56 @@ data Ref a =    Ref { refQueue :: EventQueue,  -- ^ Return the bound event queue.         refRun   :: Dynamics (),-        refValue :: IORef a }+        refValue :: IORef a, +        refChangedSource :: SignalSource a, +        refUpdatedSource :: SignalSource a }  -- | Create a new reference bound to the specified event queue. newRef :: EventQueue -> a -> Simulation (Ref a) newRef q a =   do x <- liftIO $ newIORef a+     s <- newSignalSourceUnsafe+     u <- newSignalSourceWithUpdate (queueRun q)      return Ref { refQueue = q,                   refRun   = queueRun q,-                  refValue = x }+                  refValue = x, +                  refChangedSource = s, +                  refUpdatedSource = u }       -- | Read the value of a reference, forcing the bound event queue to raise  -- the events in case of need. readRef :: Ref a -> Dynamics a readRef r = Dynamics $ \p -> -  do let Dynamics m = refRun r-     m p+  do invokeDynamics p $ refRun r      readIORef (refValue r)  -- | Write a new value into the reference. writeRef :: Ref a -> a -> Dynamics () writeRef r a = Dynamics $ \p -> -  a `seq` writeIORef (refValue r) a+  do a `seq` writeIORef (refValue r) a+     invokeDynamics p $ triggerSignal (refChangedSource r) a  -- | Mutate the contents of the reference, forcing the bound event queue to -- raise all pending events in case of need. modifyRef :: Ref a -> (a -> a) -> Dynamics () modifyRef r f = Dynamics $ \p -> -  do let Dynamics m = refRun r-     m p+  do invokeDynamics p $ refRun r      a <- readIORef (refValue r)      let b = f a      b `seq` writeIORef (refValue r) b+     invokeDynamics p $ triggerSignal (refChangedSource r) b++-- | Return a signal that notifies about every change of the reference state.+refChanged :: Ref a -> Signal a+refChanged r = merge2Signals m1 m2+  where+    m1 = publishSignal (refChangedSource r)+    m2 = publishSignal (refUpdatedSource r)++-- | Return a signal that notifies about every change of the reference state.+refChanged_ :: Ref a -> Signal ()+refChanged_ r = mapSignal (const ()) $ refChanged r++invokeDynamics :: Point -> Dynamics a -> IO a+{-# INLINE invokeDynamics #-}+invokeDynamics p (Dynamics m) = m p
Simulation/Aivika/Dynamics/Resource.hs view
@@ -13,14 +13,19 @@ module Simulation.Aivika.Dynamics.Resource        (Resource,         newResource,+        newResourceWithCount,         resourceQueue,         resourceInitCount,         resourceCount,         requestResource,-        releaseResource) where+        tryRequestResourceInDynamics,+        releaseResource,+        releaseResourceInDynamics,+        usingResource) where  import Data.IORef import Control.Monad+import Control.Monad.Trans  import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics@@ -36,7 +41,7 @@              resourceInitCount :: Int,              -- ^ Return the initial count of the resource.              resourceCountRef  :: IORef Int, -             resourceWaitQueue :: Q.Queue (() -> Dynamics ())}+             resourceWaitQueue :: Q.Queue (ContParams ())}  instance Eq Resource where   x == y = resourceCountRef x == resourceCountRef y  -- unique references@@ -52,12 +57,33 @@                        resourceCountRef  = countRef,                        resourceWaitQueue = waitQueue } +-- | Create a new resource with the specified initial count.+-- The third argument specifies how the resource is consumed +-- at the beginning, i.e. it defines the current count, which must be +-- non-negative and less or equal to the initial count.+newResourceWithCount :: EventQueue -> Int -> Int -> Simulation Resource+newResourceWithCount q initCount count = do+  when (count < 0) $+    error $+    "The resource count cannot be negative: " +++    "newResourceWithCount."+  when (count > initCount) $+    error $+    "The resource count cannot be greater than " +++    "its initial value: newResourceWithCount."+  Simulation $ \r ->+    do countRef  <- newIORef count+       waitQueue <- Q.newQueue+       return Resource { resourceQueue     = q,+                         resourceInitCount = initCount,+                         resourceCountRef  = countRef,+                         resourceWaitQueue = waitQueue }+ -- | Return the current count of the resource. resourceCount :: Resource -> Dynamics Int resourceCount r =   Dynamics $ \p ->-  do let Dynamics m = queueRun (resourceQueue r)-     m p+  do invokeDynamics p $ queueRun (resourceQueue r)      readIORef (resourceCountRef r)  -- | Request for the resource decreasing its count in case of success,@@ -65,7 +91,7 @@ -- process releases the resource. requestResource :: Resource -> Process () requestResource r =-  Process $ \_ ->+  Process $ \pid ->   Cont $ \c ->   Dynamics $ \p ->   do a <- readIORef (resourceCountRef r)@@ -73,28 +99,73 @@        then Q.enqueue (resourceWaitQueue r) c        else do let a' = a - 1                a' `seq` writeIORef (resourceCountRef r) a'-               let Dynamics m = c ()-               m p+               invokeDynamics p $ resumeContByParams c ()  -- | Release the resource increasing its count and resuming one of the -- previously suspended processes as possible. releaseResource :: Resource -> Process ()-releaseResource r =+releaseResource r =    Process $ \_ ->   Cont $ \c ->   Dynamics $ \p ->+  do invokeDynamics p $ releaseResourceUnsafe r+     invokeDynamics p $ resumeContByParams c ()++-- | Release the resource increasing its count and resuming one of the+-- previously suspended processes as possible.+releaseResourceInDynamics :: Resource -> Dynamics ()+releaseResourceInDynamics r =+  Dynamics $ \p ->+  do invokeDynamics p $ queueRun (resourceQueue r)+     invokeDynamics p $ releaseResourceUnsafe r++releaseResourceUnsafe :: Resource -> Dynamics ()+{-# INLINE releaseResourceUnsafe #-}+releaseResourceUnsafe r =+  Dynamics $ \p ->   do a <- readIORef (resourceCountRef r)      let a' = a + 1      when (a' > resourceInitCount r) $        error $        "The resource count cannot be greater than " ++-       "its initial value: releaseResource."+       "its initial value: releaseResourceUnsafe."      f <- Q.queueNull (resourceWaitQueue r)      if f         then a' `seq` writeIORef (resourceCountRef r) a'-       else do c2 <- Q.queueFront (resourceWaitQueue r)+       else do c <- Q.queueFront (resourceWaitQueue r)                Q.dequeue (resourceWaitQueue r)-               let Dynamics m = enqueueCont (resourceQueue r) (pointTime p) c2-               m p-     let Dynamics m' = c ()-     m' p+               invokeDynamics p $ enqueue (resourceQueue r) (pointTime p) $+                 Dynamics $ \p ->+                 do z <- contParamsCanceled c+                    if z+                      then do invokeDynamics p $ releaseResourceUnsafe r+                              invokeDynamics p $ resumeContByParams c ()+                      else invokeDynamics p $ resumeContByParams c ()++-- | Try to request for the resource decreasing its count in case of success+-- and returning 'True' in the 'Dynamics' monad; otherwise, returning 'False'.+tryRequestResourceInDynamics :: Resource -> Dynamics Bool+tryRequestResourceInDynamics r =+  Dynamics $ \p ->+  do invokeDynamics p $ queueRun (resourceQueue r)+     a <- readIORef (resourceCountRef r)+     if a == 0 +       then return False+       else do let a' = a - 1+               a' `seq` writeIORef (resourceCountRef r) a'+               return True+               +-- | Acquire the resource, perform some action and safely release the resource               +-- in the end, even if the 'IOException' was raised within the action. +-- The process identifier must be created with support of exception +-- handling, i.e. with help of function 'newProcessIDWithCatch'. Unfortunately,+-- such processes are slower than those that are created with help of+-- other function 'newProcessID'.+usingResource :: Resource -> Process a -> Process a+usingResource r m =+  do requestResource r+     finallyProcess m $ releaseResource r++invokeDynamics :: Point -> Dynamics a -> IO a+{-# INLINE invokeDynamics #-}+invokeDynamics p (Dynamics m) = m p 
+ Simulation/Aivika/Dynamics/Signal.hs view
@@ -0,0 +1,108 @@++-- |+-- Module     : Simulation.Aivika.Dynamics.Signal+-- Copyright  : Copyright (c) 2009-2012, David Sorokin <david.sorokin@gmail.com>+-- License    : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability  : experimental+-- Tested with: GHC 7.4.1+--+-- This module defines the signal which we can subscribe handlers to. +-- These handlers can be disposed. The signal is triggered in the +-- current time point actuating the corresponded computations from +-- the handlers. +--+module Simulation.Aivika.Dynamics.Signal+       (Signal,+        SignalSource,+        newSignalSource,+        newSignalSourceWithUpdate,+        publishSignal,+        triggerSignal,+        handleSignal,+        handleSignal_,+        updateSignal,+        awaitSignal,+        mapSignal,+        composeSignal,+        apSignal,+        filterSignal,+        merge2Signals,+        merge3Signals,+        merge4Signals,+        merge5Signals,+        SignalHistory,+        signalHistorySignal,+        newSignalHistory,+        readSignalHistory) where++import Data.IORef+import Data.Array++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Internal.Signal+import Simulation.Aivika.Dynamics.Internal.Simulation+import Simulation.Aivika.Dynamics.Internal.Dynamics+import Simulation.Aivika.Dynamics.Internal.Cont+import Simulation.Aivika.Dynamics.Internal.Process++import qualified Simulation.Aivika.Vector as V+import qualified Simulation.Aivika.UVector as UV++-- | Create a new signal source when the state depends on the event queue.+newSignalSource :: EventQueue -> Simulation (SignalSource a)+newSignalSource queue = +  newSignalSourceWithUpdate $ queueRun queue++-- | Await the signal.+awaitSignal :: Signal a -> Process a+awaitSignal signal =+  Process $ \pid ->+  Cont $ \c ->+  Dynamics $ \p ->+  do r <- newIORef Nothing+     let Simulation m = +           handleSignal signal $ +           \a -> Dynamics $ +                 \p -> do x <- readIORef r+                          case x of+                            Nothing ->+                              error "The signal was lost: awaitSignal."+                            Just x ->+                              do let Simulation m = x+                                 m $ pointRun p+                                 let Dynamics m = resumeContByParams c a+                                 m p+     h <- m $ pointRun p+     writeIORef r $ Just h+          +-- | Represents the history of the signal values.+data SignalHistory a =+  SignalHistory { signalHistorySignal :: Signal a,  +                  -- ^ The signal for which the history is created.+                  signalHistoryTimes  :: UV.UVector Double,+                  signalHistoryValues :: V.Vector a }++-- | Create a history of the signal values.+newSignalHistory :: Signal a -> Simulation (SignalHistory a)+newSignalHistory signal =+  do ts <- liftIO UV.newVector+     xs <- liftIO V.newVector+     handleSignal_ signal $ \a ->+       Dynamics $ \p ->+       do liftIO $ UV.appendVector ts (pointTime p)+          liftIO $ V.appendVector xs a+     return SignalHistory { signalHistorySignal = signal,+                            signalHistoryTimes  = ts,+                            signalHistoryValues = xs }+       +-- | Read the history of signal values.+readSignalHistory :: SignalHistory a -> Dynamics (Array Int Double, Array Int a)+readSignalHistory history =+  do updateSignal $ signalHistorySignal history+     xs <- liftIO $ UV.freezeVector (signalHistoryTimes history)+     ys <- liftIO $ V.freezeVector (signalHistoryValues history)+     return (xs, ys)     
Simulation/Aivika/Dynamics/UVar.hs view
@@ -15,6 +15,8 @@ module Simulation.Aivika.Dynamics.UVar        (UVar,         uvarQueue,+        uvarChanged,+        uvarChanged_,         newUVar,         readUVar,         writeUVar,@@ -29,6 +31,7 @@ import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Internal.Signal  import qualified Simulation.Aivika.UVector as UV @@ -38,7 +41,9 @@   UVar { uvarQueue :: EventQueue, -- ^ Return the bound event queue.          uvarRun   :: Dynamics (),          uvarXS    :: UV.UVector Double, -         uvarYS    :: UV.UVector a}+         uvarYS    :: UV.UVector a,+         uvarChangedSource :: SignalSource a, +         uvarUpdatedSource :: SignalSource a }       -- | Create a new variable bound to the specified event queue. newUVar :: (MArray IOUArray a IO) => EventQueue -> a -> Simulation (UVar a)@@ -48,18 +53,21 @@      ys <- UV.newVector      UV.appendVector xs $ spcStartTime $ runSpecs r      UV.appendVector ys a+     s  <- invokeSimulation r newSignalSourceUnsafe+     u  <- invokeSimulation r $ newSignalSourceWithUpdate $ queueRun q      return UVar { uvarQueue = q,                    uvarRun   = queueRun q,                    uvarXS = xs,-                   uvarYS = ys }+                   uvarYS = ys, +                   uvarChangedSource = s, +                   uvarUpdatedSource = u }  -- | Read the value of a variable, forcing the bound event queue to raise  -- the events in case of need. readUVar :: (MArray IOUArray a IO) => UVar a -> Dynamics a readUVar v =   Dynamics $ \p ->-  do let Dynamics m = uvarRun v-     m p+  do invokeDynamics p $ uvarRun v      let xs = uvarXS v          ys = uvarYS v          t  = pointTime p@@ -80,6 +88,7 @@   do let xs = uvarXS v          ys = uvarYS v          t  = pointTime p+         s  = uvarChangedSource v      count <- UV.vectorCount xs      let i = count - 1      x <- UV.readVector xs i@@ -89,17 +98,18 @@             then UV.writeVector ys i $! a             else do UV.appendVector xs t                     UV.appendVector ys $! a+     invokeDynamics p $ triggerSignal s a  -- | Mutate the contents of the variable, forcing the bound event queue to -- raise all pending events in case of need. modifyUVar :: (MArray IOUArray a IO) => UVar a -> (a -> a) -> Dynamics () modifyUVar v f =   Dynamics $ \p ->-  do let Dynamics m = uvarRun v-     m p+  do invokeDynamics p $ uvarRun v      let xs = uvarXS v          ys = uvarYS v          t  = pointTime p+         s  = uvarChangedSource v      count <- UV.vectorCount xs      let i = count - 1      x <- UV.readVector xs i@@ -107,15 +117,21 @@        then error "Cannot update the past data: modifyUVar."        else if t == x             then do a <- UV.readVector ys i-                    UV.writeVector ys i $! f a+                    let b = f a+                    UV.writeVector ys i $! b+                    invokeDynamics p $ triggerSignal s b             else do i <- UV.vectorBinarySearch xs t                     if i >= 0                       then do a <- UV.readVector ys i+                              let b = f a                               UV.appendVector xs t-                              UV.appendVector ys $! f a+                              UV.appendVector ys $! b+                              invokeDynamics p $ triggerSignal s b                       else do a <- UV.readVector ys $ - (i + 1) - 1+                              let b = f a                               UV.appendVector xs t-                              UV.appendVector ys $! f a+                              UV.appendVector ys $! b+                              invokeDynamics p $ triggerSignal s b  -- | Freeze the variable and return in arrays the time points and corresponded  -- values when the variable had changed.@@ -123,8 +139,25 @@               UVar a -> Dynamics (Array Int Double, Array Int a) freezeUVar v =   Dynamics $ \p ->-  do let Dynamics m = uvarRun v-     m p+  do invokeDynamics p $ uvarRun v      xs <- UV.freezeVector (uvarXS v)      ys <- UV.freezeVector (uvarYS v)      return (xs, ys)+     +-- | Return a signal that notifies about every change of the variable state.+uvarChanged :: UVar a -> Signal a+uvarChanged v = merge2Signals m1 m2+  where m1 = publishSignal (uvarChangedSource v)+        m2 = publishSignal (uvarUpdatedSource v)++-- | Return a signal that notifies about every change of the variable state.+uvarChanged_ :: UVar a -> Signal ()+uvarChanged_ v = mapSignal (const ()) $ uvarChanged v          ++invokeDynamics :: Point -> Dynamics a -> IO a+{-# INLINE invokeDynamics #-}+invokeDynamics p (Dynamics m) = m p++invokeSimulation :: Run -> Simulation a -> IO a+{-# INLINE invokeSimulation #-}+invokeSimulation r (Simulation m) = m r
Simulation/Aivika/Dynamics/Var.hs view
@@ -13,6 +13,8 @@ module Simulation.Aivika.Dynamics.Var        (Var,         varQueue,+        varChanged,+        varChanged_,         newVar,         readVar,         writeVar,@@ -26,6 +28,7 @@ import Simulation.Aivika.Dynamics.Internal.Simulation import Simulation.Aivika.Dynamics.Internal.Dynamics import Simulation.Aivika.Dynamics.EventQueue+import Simulation.Aivika.Dynamics.Internal.Signal  import qualified Simulation.Aivika.Vector as V import qualified Simulation.Aivika.UVector as UV@@ -38,7 +41,9 @@   Var { varQueue :: EventQueue,  -- ^ Return the bound event queue.         varRun   :: Dynamics (),         varXS    :: UV.UVector Double, -        varYS    :: V.Vector a}+        varYS    :: V.Vector a,+        varChangedSource :: SignalSource a, +        varUpdatedSource :: SignalSource a }       -- | Create a new variable bound to the specified event queue. newVar :: EventQueue -> a -> Simulation (Var a)@@ -48,18 +53,21 @@      ys <- V.newVector      UV.appendVector xs $ spcStartTime $ runSpecs r      V.appendVector ys a+     s  <- invokeSimulation r newSignalSourceUnsafe+     u  <- invokeSimulation r $ newSignalSourceWithUpdate $ queueRun q      return Var { varQueue = q,                   varRun   = queueRun q,                   varXS = xs,-                  varYS = ys }+                  varYS = ys, +                  varChangedSource = s, +                  varUpdatedSource = u }  -- | Read the value of a variable, forcing the bound event queue to raise  -- the events in case of need. readVar :: Var a -> Dynamics a readVar v =   Dynamics $ \p ->-  do let Dynamics m = varRun v-     m p+  do invokeDynamics p $ varRun v      let xs = varXS v          ys = varYS v          t  = pointTime p@@ -80,6 +88,7 @@   do let xs = varXS v          ys = varYS v          t  = pointTime p+         s  = varChangedSource v      count <- UV.vectorCount xs      let i = count - 1      x <- UV.readVector xs i@@ -89,17 +98,18 @@             then V.writeVector ys i $! a             else do UV.appendVector xs t                     V.appendVector ys $! a+     invokeDynamics p $ triggerSignal s a  -- | Mutate the contents of the variable, forcing the bound event queue to -- raise all pending events in case of need. modifyVar :: Var a -> (a -> a) -> Dynamics () modifyVar v f =   Dynamics $ \p ->-  do let Dynamics m = varRun v-     m p+  do invokeDynamics p $ varRun v      let xs = varXS v          ys = varYS v          t  = pointTime p+         s  = varChangedSource v      count <- UV.vectorCount xs      let i = count - 1      x <- UV.readVector xs i@@ -107,23 +117,46 @@        then error "Cannot update the past data: modifyVar."        else if t == x             then do a <- V.readVector ys i-                    V.writeVector ys i $! f a+                    let b = f a+                    V.writeVector ys i $! b+                    invokeDynamics p $ triggerSignal s b             else do i <- UV.vectorBinarySearch xs t                     if i >= 0                       then do a <- V.readVector ys i+                              let b = f a                               UV.appendVector xs t-                              V.appendVector ys $! f a+                              V.appendVector ys $! b+                              invokeDynamics p $ triggerSignal s b                       else do a <- V.readVector ys $ - (i + 1) - 1+                              let b = f a                               UV.appendVector xs t-                              V.appendVector ys $! f a+                              V.appendVector ys $! b+                              invokeDynamics p $ triggerSignal s b  -- | Freeze the variable and return in arrays the time points and corresponded  -- values when the variable had changed. freezeVar :: Var a -> Dynamics (Array Int Double, Array Int a) freezeVar v =   Dynamics $ \p ->-  do let Dynamics m = varRun v-     m p+  do invokeDynamics p $ varRun v      xs <- UV.freezeVector (varXS v)      ys <- V.freezeVector (varYS v)      return (xs, ys)+     +-- | Return a signal that notifies about every change of the variable state.+varChanged :: Var a -> Signal a+varChanged v = merge2Signals m1 m2+  where m1 = publishSignal (varChangedSource v)+        m2 = publishSignal (varUpdatedSource v)++-- | Return a signal that notifies about every change of the variable state.+varChanged_ :: Var a -> Signal ()+varChanged_ v = mapSignal (const ()) $ varChanged v     ++invokeDynamics :: Point -> Dynamics a -> IO a+{-# INLINE invokeDynamics #-}+invokeDynamics p (Dynamics m) = m p++invokeSimulation :: Run -> Simulation a -> IO a+{-# INLINE invokeSimulation #-}+invokeSimulation r (Simulation m) = m r
Simulation/Aivika/UVector.hs view
@@ -20,6 +20,9 @@         readVector,          writeVector,          vectorBinarySearch,+        vectorInsert,+        vectorDeleteAt,+        vectorIndex,         freezeVector) where   import Data.Array@@ -127,4 +130,59 @@   do vector' <- copyVector vector      array   <- readIORef (vectorArrayRef vector')      freeze array+     +     +-- | Insert the element in the vector at the specified index.+vectorInsert :: (MArray IOUArray a IO) => UVector a -> Int -> a -> IO ()          +vectorInsert vector index item =+  do count <- readIORef (vectorCountRef vector)+     when (index < 0) $+       error $+       "Index cannot be " +++       "negative: vectorInsert."+     when (index > count) $+       error $+       "Index cannot be greater " +++       "than the count: vectorInsert."+     vectorEnsureCapacity vector (count + 1)+     array <- readIORef (vectorArrayRef vector)+     forM_ [count, count - 1 .. index + 1] $ \i ->+       do x <- readArray array (i - 1)+          writeArray array i x+     writeArray array index item+     writeIORef (vectorCountRef vector) (count + 1)+     +-- | Delete the element at the specified index.+vectorDeleteAt :: (MArray IOUArray a IO) => UVector a -> Int -> IO ()+vectorDeleteAt vector index =+  do count <- readIORef (vectorCountRef vector)+     when (index < 0) $+       error $+       "Index cannot be " +++       "negative: vectorDeleteAt."+     when (index >= count) $+       error $+       "Index must be less " +++       "than the count: vectorDeleteAt."+     array <- readIORef (vectorArrayRef vector)+     forM_ [index, index + 1 .. count - 2] $ \i ->+       do x <- readArray array (i + 1)+          writeArray array i x+     writeArray array (count - 1) undefined+     writeIORef (vectorCountRef vector) (count - 1)+     +-- | Return the index of the item or -1.     +vectorIndex :: (MArray IOUArray a IO, Eq a) => +               UVector a -> a -> IO Int+vectorIndex vector item =+  do count <- readIORef (vectorCountRef vector)+     array <- readIORef (vectorArrayRef vector)+     let loop index =+           if index >= count+           then return $ -1+           else do x <- readArray array index+                   if item == x+                     then return index+                     else loop $ index + 1+     loop 0      
Simulation/Aivika/Vector.hs view
@@ -18,6 +18,9 @@         readVector,          writeVector,         vectorBinarySearch,+        vectorInsert,+        vectorDeleteAt,+        vectorIndex,         freezeVector) where   import Data.Array@@ -125,3 +128,55 @@      array   <- readIORef (vectorArrayRef vector')      freeze array      +-- | Insert the element in the vector at the specified index.+vectorInsert :: Vector a -> Int -> a -> IO ()          +vectorInsert vector index item =+  do count <- readIORef (vectorCountRef vector)+     when (index < 0) $+       error $+       "Index cannot be " +++       "negative: vectorInsert."+     when (index > count) $+       error $+       "Index cannot be greater " +++       "than the count: vectorInsert."+     vectorEnsureCapacity vector (count + 1)+     array <- readIORef (vectorArrayRef vector)+     forM_ [count, count - 1 .. index + 1] $ \i ->+       do x <- readArray array (i - 1)+          writeArray array i x+     writeArray array index item+     writeIORef (vectorCountRef vector) (count + 1)+     +-- | Delete the element at the specified index.+vectorDeleteAt :: Vector a -> Int -> IO ()+vectorDeleteAt vector index =+  do count <- readIORef (vectorCountRef vector)+     when (index < 0) $+       error $+       "Index cannot be " +++       "negative: vectorDeleteAt."+     when (index >= count) $+       error $+       "Index must be less " +++       "than the count: vectorDeleteAt."+     array <- readIORef (vectorArrayRef vector)+     forM_ [index, index + 1 .. count - 2] $ \i ->+       do x <- readArray array (i + 1)+          writeArray array i x+     writeArray array (count - 1) undefined+     writeIORef (vectorCountRef vector) (count - 1)+     +-- | Return the index of the item or -1.     +vectorIndex :: Eq a => Vector a -> a -> IO Int+vectorIndex vector item =+  do count <- readIORef (vectorCountRef vector)+     array <- readIORef (vectorArrayRef vector)+     let loop index =+           if index >= count+           then return $ -1+           else do x <- readArray array index+                   if item == x+                     then return index+                     else loop $ index + 1+     loop 0
aivika.cabal view
@@ -1,5 +1,5 @@ name:            aivika-version:         0.3+version:         0.4 synopsis:        A multi-paradigm simulation library description:     Aivika is a small simulation library that covers many paradigms. @@ -27,8 +27,8 @@     .     The PDF documentation is available at      <https://github.com/dsorokin/aivika/blob/master/doc/aivika.pdf>.-    Please note that the documentation is obsolete and it corresponds to -    the previous version but it can still be helpful.+    Please note that the documentation is outdated and it corresponds to +    version 0.2 but it can still be helpful.     . category:        Simulation license:         BSD3@@ -39,7 +39,7 @@ homepage:        http://github.com/dsorokin/aivika cabal-version:   >= 1.2.0 build-type:      Simple-tested-with:     GHC == 7.0.4+tested-with:     GHC == 7.4.1  extra-source-files:  examples/BassDiffusion.hs                      examples/ChemicalReaction.hs@@ -69,6 +69,10 @@                      Simulation.Aivika.Dynamics.SystemDynamics                      Simulation.Aivika.Dynamics.UVar                      Simulation.Aivika.Dynamics.Var+                     Simulation.Aivika.Dynamics.FIFO+                     Simulation.Aivika.Dynamics.LIFO+                     Simulation.Aivika.Dynamics.Buffer+                     Simulation.Aivika.Dynamics.Signal                      Simulation.Aivika.Statistics                      Simulation.Aivika.PriorityQueue                      Simulation.Aivika.Queue@@ -81,6 +85,7 @@                      Simulation.Aivika.Dynamics.Internal.Memo                      Simulation.Aivika.Dynamics.Internal.Interpolate                      Simulation.Aivika.Dynamics.Internal.Fold+                     Simulation.Aivika.Dynamics.Internal.Signal                      Simulation.Aivika.Vector                      Simulation.Aivika.UVector                      
examples/BassDiffusion.hs view
@@ -95,9 +95,9 @@      adopters <- newRef q 0      ps <- createPersons q      definePersons ps potentialAdopters adopters-     runDynamicsInStart $+     runDynamicsInStartTime $        activatePersons ps-     runDynamics $+     runDynamicsInIntegTimes $        do i1 <- readRef potentialAdopters           i2 <- readRef adopters           return [i1, i2]
examples/ChemicalReaction.hs view
@@ -21,6 +21,6 @@      integDiff integA (- ka * a)      integDiff integB (ka * a - kb * b)      integDiff integC (kb * b)-     runDynamicsInFinal $ sequence [a, b, c]+     runDynamicsInStopTime $ sequence [a, b, c]  main = runSimulation model specs >>= print
examples/FishBank.hs view
@@ -53,6 +53,6 @@      integDiff shipsInteg shipBuildingRate      integDiff totalProfitInteg annualProfit      -- results ---     runDynamicsInFinal annualProfit+     runDynamicsInStopTime annualProfit  main = runSimulation model specs >>= print
examples/Furnace.hs view
@@ -344,19 +344,19 @@      furnace <- newFurnace queue      pid <- newProcessID queue      -     runDynamicsInStart $+     runDynamicsInStartTime $        initializeFurnace furnace            -- get the furnace iterator      iterator <- iterateFurnace furnace            -- accept input ingots-     runDynamicsInStart $+     runDynamicsInStartTime $        do t0 <- starttime           runProcess (processFurnace furnace) pid t0            -- run the model in the final time point-     runDynamicsInFinal $+     runDynamicsInStopTime $        do iterator   --  iterate in each time point                     -- the ingots
examples/MachRep1.hs view
@@ -59,12 +59,12 @@               holdProcess repairTime               machine          -     runDynamicsInStart $+     runDynamicsInStartTime $        do t0 <- starttime           runProcess machine pid1 t0           runProcess machine pid2 t0      -     runDynamicsInFinal $+     runDynamicsInStopTime $        do x <- readRef totalUpTime           y <- stoptime           return $ x / (2 * y)
examples/MachRep1EventDriven.hs view
@@ -63,14 +63,14 @@               let t = startUpTime + upTime               enqueue queue t $ machineBroken startUpTime      -     runDynamicsInStart $+     runDynamicsInStartTime $        do t0 <- starttime           -- start the first machine           enqueue queue t0 machineRepaired           -- start the second machine           enqueue queue t0 machineRepaired           -     runDynamicsInFinal $+     runDynamicsInStopTime $        do x <- readRef totalUpTime           y <- stoptime           return $ x / (2 * y)
examples/MachRep1TimeDriven.hs view
@@ -105,7 +105,7 @@      c1 <- iterateDynamics m1      c2 <- iterateDynamics m2      -     runDynamicsInFinal $+     runDynamicsInStopTime $        do c1    -- involve in the simulation           c2    -- involve in the simulation           x <- readRef totalUpTime
examples/MachRep2.hs view
@@ -83,12 +83,12 @@                              machine          -     runDynamicsInStart $+     runDynamicsInStartTime $        do t0 <- starttime           runProcess machine pid1 t0           runProcess machine pid2 t0           -     runDynamicsInFinal $+     runDynamicsInStopTime $        do x <- readRef totalUpTime           y <- stoptime           n <- readRef nRep
examples/MachRep3.hs view
@@ -79,12 +79,12 @@                              machine pid -     runDynamicsInStart $+     runDynamicsInStartTime $        do t0 <- starttime           runProcess (machine pid2) pid1 t0           runProcess (machine pid1) pid2 t0      -     runDynamicsInFinal $+     runDynamicsInStopTime $        do x <- readRef totalUpTime           y <- stoptime           return $ x / (2 * y)