{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

-- |
-- Module     : Simulation.Aivika.IO.Dynamics.Memo
-- Copyright  : Copyright (c) 2009-2017, David Sorokin <david.sorokin@gmail.com>
-- License    : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- Stability  : experimental
-- Tested with: GHC 8.0.1
--
-- The 'IO' monad is an instance of the 'MonadMemo' type class.
--

module Simulation.Aivika.IO.Dynamics.Memo () where

import Control.Monad
import Control.Monad.Trans

import Data.Array.IO.Safe
import Data.Array.MArray.Safe
import Data.IORef

import Simulation.Aivika.Trans.Internal.Specs
import Simulation.Aivika.Trans.Internal.Parameter
import Simulation.Aivika.Trans.Internal.Simulation
import Simulation.Aivika.Trans.Internal.Dynamics
import Simulation.Aivika.Trans.Dynamics.Memo
import Simulation.Aivika.Trans.Dynamics.Extra
import Simulation.Aivika.Trans.Array

-- | The 'IO' monad is an instance of the 'MonadMemo' type class.
instance MonadMemo IO where
-- instance (Monad m, MonadIO m, MonadTemplate m) => MonadMemo m where

  {-# SPECIALISE instance MonadMemo IO #-}

  {-# INLINE memoDynamics #-}
  memoDynamics :: forall e. Dynamics IO e -> Simulation IO (Dynamics IO e)
memoDynamics (Dynamics Point IO -> IO e
m) = 
    forall (m :: * -> *) a. (Run m -> m a) -> Simulation m a
Simulation forall a b. (a -> b) -> a -> b
$ \Run IO
r ->
    do let sc :: Specs IO
sc  = forall (m :: * -> *). Run m -> Specs m
runSpecs Run IO
r
           (Int
phl, Int
phu) = forall (m :: * -> *). Specs m -> (Int, Int)
integPhaseBnds Specs IO
sc
           (Int
nl, Int
nu)   = forall (m :: * -> *). Specs m -> (Int, Int)
integIterationBnds Specs IO
sc
       IOArray (Int, Int) e
arr   <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall i e. Ix i => (i, i) -> IO (IOArray i e)
newIOArray_ ((Int
phl, Int
nl), (Int
phu, Int
nu))
       IORef Int
nref  <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (IORef a)
newIORef Int
0
       IORef Int
phref <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (IORef a)
newIORef Int
0
       let r :: Point IO -> IO e
r Point IO
p = 
             do let n :: Int
n  = forall (m :: * -> *). Point m -> Int
pointIteration Point IO
p
                    ph :: Int
ph = forall (m :: * -> *). Point m -> Int
pointPhase Point IO
p
                    loop :: Int -> Int -> IO e
loop Int
n' Int
ph' = 
                      if (Int
n' forall a. Ord a => a -> a -> Bool
> Int
n) Bool -> Bool -> Bool
|| ((Int
n' forall a. Eq a => a -> a -> Bool
== Int
n) Bool -> Bool -> Bool
&& (Int
ph' forall a. Ord a => a -> a -> Bool
> Int
ph)) 
                      then 
                        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray IOArray (Int, Int) e
arr (Int
ph, Int
n)
                      else 
                        let p' :: Point IO
p' = Point IO
p { pointIteration :: Int
pointIteration = Int
n', pointPhase :: Int
pointPhase = Int
ph',
                                     pointTime :: Double
pointTime = forall (m :: * -> *). Specs m -> Int -> Int -> Double
basicTime Specs IO
sc Int
n' Int
ph' }
                        in do e
a <- Point IO -> IO e
m Point IO
p'
                              e
a seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> e -> m ()
writeArray IOArray (Int, Int) e
arr (Int
ph', Int
n') e
a
                              if Int
ph' forall a. Ord a => a -> a -> Bool
>= Int
phu 
                                then do forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef Int
phref Int
0
                                        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef Int
nref (Int
n' forall a. Num a => a -> a -> a
+ Int
1)
                                        Int -> Int -> IO e
loop (Int
n' forall a. Num a => a -> a -> a
+ Int
1) Int
0
                                else do forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef Int
phref (Int
ph' forall a. Num a => a -> a -> a
+ Int
1)
                                        Int -> Int -> IO e
loop Int
n' (Int
ph' forall a. Num a => a -> a -> a
+ Int
1)
                Int
n'  <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Int
nref
                Int
ph' <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Int
phref
                Int -> Int -> IO e
loop Int
n' Int
ph'
       forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Dynamics m a -> Dynamics m a
interpolateDynamics forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. (Point m -> m a) -> Dynamics m a
Dynamics Point IO -> IO e
r

  {-# INLINE memo0Dynamics #-}
  memo0Dynamics :: forall e. Dynamics IO e -> Simulation IO (Dynamics IO e)
memo0Dynamics (Dynamics Point IO -> IO e
m) = 
    forall (m :: * -> *) a. (Run m -> m a) -> Simulation m a
Simulation forall a b. (a -> b) -> a -> b
$ \Run IO
r ->
    do let sc :: Specs IO
sc = forall (m :: * -> *). Run m -> Specs m
runSpecs Run IO
r
           bnds :: (Int, Int)
bnds = forall (m :: * -> *). Specs m -> (Int, Int)
integIterationBnds Specs IO
sc
       IOArray Int e
arr  <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall i e. Ix i => (i, i) -> IO (IOArray i e)
newIOArray_ (Int, Int)
bnds
       IORef Int
nref <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (IORef a)
newIORef Int
0
       let r :: Point IO -> IO e
r Point IO
p =
             do let sc :: Specs IO
sc = forall (m :: * -> *). Point m -> Specs m
pointSpecs Point IO
p
                    n :: Int
n  = forall (m :: * -> *). Point m -> Int
pointIteration Point IO
p
                    loop :: Int -> IO e
loop Int
n' = 
                      if Int
n' forall a. Ord a => a -> a -> Bool
> Int
n
                      then 
                        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray IOArray Int e
arr Int
n
                      else 
                        let p' :: Point IO
p' = Point IO
p { pointIteration :: Int
pointIteration = Int
n', pointPhase :: Int
pointPhase = Int
0,
                                     pointTime :: Double
pointTime = forall (m :: * -> *). Specs m -> Int -> Int -> Double
basicTime Specs IO
sc Int
n' Int
0 }
                        in do e
a <- Point IO -> IO e
m Point IO
p'
                              e
a seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> e -> m ()
writeArray IOArray Int e
arr Int
n' e
a
                              forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef Int
nref (Int
n' forall a. Num a => a -> a -> a
+ Int
1)
                              Int -> IO e
loop (Int
n' forall a. Num a => a -> a -> a
+ Int
1)
                Int
n' <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Int
nref
                Int -> IO e
loop Int
n'
       forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Dynamics m a -> Dynamics m a
discreteDynamics forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. (Point m -> m a) -> Dynamics m a
Dynamics Point IO -> IO e
r

  {-# INLINE iterateDynamics #-}
  iterateDynamics :: Dynamics IO () -> Simulation IO (Dynamics IO ())
iterateDynamics (Dynamics Point IO -> IO ()
m) = 
    forall (m :: * -> *) a. (Run m -> m a) -> Simulation m a
Simulation forall a b. (a -> b) -> a -> b
$ \Run IO
r ->
    do let sc :: Specs IO
sc = forall (m :: * -> *). Run m -> Specs m
runSpecs Run IO
r
       IORef Int
nref <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. a -> IO (IORef a)
newIORef Int
0
       let r :: Point IO -> IO ()
r Point IO
p =
             do let sc :: Specs IO
sc = forall (m :: * -> *). Point m -> Specs m
pointSpecs Point IO
p
                    n :: Int
n  = forall (m :: * -> *). Point m -> Int
pointIteration Point IO
p
                    loop :: Int -> IO ()
loop Int
n' = 
                      forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
n' forall a. Ord a => a -> a -> Bool
> Int
n) forall a b. (a -> b) -> a -> b
$
                      let p' :: Point IO
p' = Point IO
p { pointIteration :: Int
pointIteration = Int
n', pointPhase :: Int
pointPhase = Int
0,
                                   pointTime :: Double
pointTime = forall (m :: * -> *). Specs m -> Int -> Int -> Double
basicTime Specs IO
sc Int
n' Int
0 }
                      in do ()
a <- Point IO -> IO ()
m Point IO
p'
                            ()
a seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> a -> IO ()
writeIORef IORef Int
nref (Int
n' forall a. Num a => a -> a -> a
+ Int
1)
                            Int -> IO ()
loop (Int
n' forall a. Num a => a -> a -> a
+ Int
1)
                Int
n' <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef Int
nref
                Int -> IO ()
loop Int
n'
       forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Dynamics m a -> Dynamics m a
discreteDynamics forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. (Point m -> m a) -> Dynamics m a
Dynamics Point IO -> IO ()
r