{-# LANGUAGE Arrows #-}
-- |
-- Copyright  : (c) Ivan Perez, 2019-2023
--              (c) Ivan Perez and Manuel Baerenz, 2016-2018
-- License    : BSD3
-- Maintainer : ivan.perez@keera.co.uk
--
-- Implementation of integrals and derivatives using Monadic Stream Processing
-- library.
module FRP.BearRiver.Integration
    (
      -- * Integration
      integral

      -- * Differentiation
    , derivative
    , iterFrom
    )
  where

-- External imports
import Control.Arrow    (returnA)
import Data.VectorSpace (VectorSpace, zeroVector, (*^), (^+^), (^-^), (^/))

-- Internal imports (dunai)
import Control.Monad.Trans.MSF                 (ask)
import Data.MonadicStreamFunction              (accumulateWith, constM, iPre)
import Data.MonadicStreamFunction.InternalCore (MSF (MSF))

-- Internal imports
import FRP.BearRiver.InternalCore (DTime, SF)

-- * Integration and differentiation

-- | Integration using the rectangle rule.
integral :: (Monad m, Fractional s, VectorSpace a s) => SF m a a
integral :: forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
SF m a a
integral = a -> SF m a a
forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
a -> SF m a a
integralFrom a
forall v a. VectorSpace v a => v
zeroVector

-- | Integrate using an auxiliary function that takes the current and the last
-- input, the time between those samples, and the last output, and returns a
-- new output.
integralFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a
integralFrom :: forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
a -> SF m a a
integralFrom a
a0 = proc a
a -> do
  DTime
dt <- ClockInfo m DTime -> MSF (ClockInfo m) () DTime
forall (m :: * -> *) b a. Monad m => m b -> MSF m a b
constM ClockInfo m DTime
forall (m :: * -> *) r. Monad m => ReaderT r m r
ask        -< ()
  (a -> a -> a) -> a -> MSF (ClockInfo m) a a
forall (m :: * -> *) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith a -> a -> a
forall v a. VectorSpace v a => v -> v -> v
(^+^) a
a0 -< DTime -> s
forall a b. (Real a, Fractional b) => a -> b
realToFrac DTime
dt s -> a -> a
forall v a. VectorSpace v a => a -> v -> v
*^ a
a

-- | A very crude version of a derivative. It simply divides the value
-- difference by the time difference. Use at your own risk.
derivative :: (Monad m, Fractional s, VectorSpace a s) => SF m a a
derivative :: forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
SF m a a
derivative = a -> SF m a a
forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
a -> SF m a a
derivativeFrom a
forall v a. VectorSpace v a => v
zeroVector

-- | A very crude version of a derivative. It simply divides the value
-- difference by the time difference. Use at your own risk.
--
-- Starts from a given value for the input signal at time zero.
derivativeFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a
derivativeFrom :: forall (m :: * -> *) s a.
(Monad m, Fractional s, VectorSpace a s) =>
a -> SF m a a
derivativeFrom a
a0 = proc a
a -> do
  DTime
dt   <- ClockInfo m DTime -> MSF (ClockInfo m) () DTime
forall (m :: * -> *) b a. Monad m => m b -> MSF m a b
constM ClockInfo m DTime
forall (m :: * -> *) r. Monad m => ReaderT r m r
ask  -< ()
  a
aOld <- a -> MSF (ClockInfo m) a a
forall (m :: * -> *) a. Monad m => a -> MSF m a a
iPre a
a0     -< a
a
  MSF (ClockInfo m) a a
forall (a :: * -> * -> *) b. Arrow a => a b b
returnA             -< (a
a a -> a -> a
forall v a. VectorSpace v a => v -> v -> v
^-^ a
aOld) a -> s -> a
forall v a. VectorSpace v a => v -> a -> v
^/ DTime -> s
forall a b. (Real a, Fractional b) => a -> b
realToFrac DTime
dt

-- | Integrate using an auxiliary function that takes the current and the last
-- input, the time between those samples, and the last output, and returns a
-- new output.

-- NOTE: BUG in this function, it needs two a's but we can only provide one
iterFrom :: Monad m => (a -> a -> DTime -> b -> b) -> b -> SF m a b
iterFrom :: forall (m :: * -> *) a b.
Monad m =>
(a -> a -> DTime -> b -> b) -> b -> SF m a b
iterFrom a -> a -> DTime -> b -> b
f b
b = (a -> ClockInfo m (b, MSF (ReaderT DTime m) a b))
-> MSF (ReaderT DTime m) a b
forall (m :: * -> *) a b. (a -> m (b, MSF m a b)) -> MSF m a b
MSF ((a -> ClockInfo m (b, MSF (ReaderT DTime m) a b))
 -> MSF (ReaderT DTime m) a b)
-> (a -> ClockInfo m (b, MSF (ReaderT DTime m) a b))
-> MSF (ReaderT DTime m) a b
forall a b. (a -> b) -> a -> b
$ \a
a -> do
  DTime
dt <- ReaderT DTime m DTime
forall (m :: * -> *) r. Monad m => ReaderT r m r
ask
  let b' :: b
b' = a -> a -> DTime -> b -> b
f a
a a
a DTime
dt b
b
  (b, MSF (ReaderT DTime m) a b)
-> ClockInfo m (b, MSF (ReaderT DTime m) a b)
forall a. a -> ReaderT DTime m a
forall (m :: * -> *) a. Monad m => a -> m a
return (b
b, (a -> a -> DTime -> b -> b) -> b -> MSF (ReaderT DTime m) a b
forall (m :: * -> *) a b.
Monad m =>
(a -> a -> DTime -> b -> b) -> b -> SF m a b
iterFrom a -> a -> DTime -> b -> b
f b
b')