{-# LANGUAGE RecursiveDo, RankNTypes #-}
module Simulation.Aivika.Internal.Parameter
(
Parameter(..),
ParameterLift(..),
invokeParameter,
runParameter,
runParameters,
catchParameter,
finallyParameter,
throwParameter,
simulationIndex,
simulationCount,
simulationSpecs,
starttime,
stoptime,
dt,
generatorParameter,
memoParameter,
tableParameter) where
import Control.Exception
import Control.Concurrent.MVar
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Fix
import qualified Control.Monad.Catch as MC
import Control.Applicative
import Data.IORef
import qualified Data.IntMap as M
import Data.Array
import Simulation.Aivika.Generator
import Simulation.Aivika.Internal.Specs
newtype Parameter a = Parameter (Run -> IO a)
instance Monad Parameter where
return = returnP
m >>= k = bindP m k
returnP :: a -> Parameter a
{-# INLINE returnP #-}
returnP a = Parameter (\r -> return a)
bindP :: Parameter a -> (a -> Parameter b) -> Parameter b
{-# INLINE bindP #-}
bindP (Parameter m) k =
Parameter $ \r ->
do a <- m r
let Parameter m' = k a
m' r
runParameter :: Parameter a -> Specs -> IO a
runParameter (Parameter m) sc =
do q <- newEventQueue sc
g <- newGenerator $ spcGeneratorType sc
m Run { runSpecs = sc,
runIndex = 1,
runCount = 1,
runEventQueue = q,
runGenerator = g }
runParameters :: Parameter a -> Specs -> Int -> [IO a]
runParameters (Parameter m) sc runs = map f [1 .. runs]
where f i = do q <- newEventQueue sc
g <- newGenerator $ spcGeneratorType sc
m Run { runSpecs = sc,
runIndex = i,
runCount = runs,
runEventQueue = q,
runGenerator = g }
simulationIndex :: Parameter Int
simulationIndex = Parameter $ return . runIndex
simulationCount :: Parameter Int
simulationCount = Parameter $ return . runCount
simulationSpecs :: Parameter Specs
simulationSpecs = Parameter $ return . runSpecs
generatorParameter :: Parameter Generator
generatorParameter = Parameter $ return . runGenerator
instance Functor Parameter where
fmap = liftMP
instance Applicative Parameter where
pure = return
(<*>) = ap
instance Eq (Parameter a) where
x == y = error "Can't compare parameters."
instance Show (Parameter a) where
showsPrec _ x = showString "<< Parameter >>"
liftMP :: (a -> b) -> Parameter a -> Parameter b
{-# INLINE liftMP #-}
liftMP f (Parameter x) =
Parameter $ \r -> do { a <- x r; return $ f a }
liftM2P :: (a -> b -> c) -> Parameter a -> Parameter b -> Parameter c
{-# INLINE liftM2P #-}
liftM2P f (Parameter x) (Parameter y) =
Parameter $ \r -> do { a <- x r; b <- y r; return $ f a b }
instance (Num a) => Num (Parameter a) where
x + y = liftM2P (+) x y
x - y = liftM2P (-) x y
x * y = liftM2P (*) x y
negate = liftMP negate
abs = liftMP abs
signum = liftMP signum
fromInteger i = return $ fromInteger i
instance (Fractional a) => Fractional (Parameter a) where
x / y = liftM2P (/) x y
recip = liftMP recip
fromRational t = return $ fromRational t
instance (Floating a) => Floating (Parameter a) where
pi = return pi
exp = liftMP exp
log = liftMP log
sqrt = liftMP sqrt
x ** y = liftM2P (**) x y
sin = liftMP sin
cos = liftMP cos
tan = liftMP tan
asin = liftMP asin
acos = liftMP acos
atan = liftMP atan
sinh = liftMP sinh
cosh = liftMP cosh
tanh = liftMP tanh
asinh = liftMP asinh
acosh = liftMP acosh
atanh = liftMP atanh
instance MonadIO Parameter where
liftIO m = Parameter $ const m
class ParameterLift m where
liftParameter :: Parameter a -> m a
instance ParameterLift Parameter where
liftParameter = id
catchParameter :: Exception e => Parameter a -> (e -> Parameter a) -> Parameter a
catchParameter (Parameter m) h =
Parameter $ \r ->
catch (m r) $ \e ->
let Parameter m' = h e in m' r
finallyParameter :: Parameter a -> Parameter b -> Parameter a
finallyParameter (Parameter m) (Parameter m') =
Parameter $ \r ->
finally (m r) (m' r)
throwParameter :: Exception e => e -> Parameter a
throwParameter = throw
maskParameter :: ((forall a. Parameter a -> Parameter a) -> Parameter b) -> Parameter b
maskParameter a =
Parameter $ \r ->
MC.mask $ \u ->
invokeParameter r (a $ q u)
where q u (Parameter b) = Parameter (u . b)
uninterruptibleMaskParameter :: ((forall a. Parameter a -> Parameter a) -> Parameter b) -> Parameter b
uninterruptibleMaskParameter a =
Parameter $ \r ->
MC.uninterruptibleMask $ \u ->
invokeParameter r (a $ q u)
where q u (Parameter b) = Parameter (u . b)
invokeParameter :: Run -> Parameter a -> IO a
{-# INLINE invokeParameter #-}
invokeParameter r (Parameter m) = m r
instance MonadFix Parameter where
mfix f =
Parameter $ \r ->
do { rec { a <- invokeParameter r (f a) }; return a }
instance MC.MonadThrow Parameter where
throwM = throwParameter
instance MC.MonadCatch Parameter where
catch = catchParameter
instance MC.MonadMask Parameter where
mask = maskParameter
uninterruptibleMask = uninterruptibleMaskParameter
memoParameter :: Parameter a -> IO (Parameter a)
memoParameter x =
do lock <- newMVar ()
dict <- newIORef M.empty
return $ Parameter $ \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 <- invokeParameter r x
writeIORef dict $ M.insert i v m
return v }
tableParameter :: Array Int a -> Parameter a
tableParameter t =
do i <- simulationIndex
return $ t ! (((i - i1) `mod` n) + i1)
where (i1, i2) = bounds t
n = i2 - i1 + 1
starttime :: Parameter Double
starttime =
Parameter $ return . spcStartTime . runSpecs
stoptime :: Parameter Double
stoptime =
Parameter $ return . spcStopTime . runSpecs
dt :: Parameter Double
dt =
Parameter $ return . spcDT . runSpecs