-- |
-- Module:     FRP.NetWire.Calculus
-- Copyright:  (c) 2011 Ertugrul Soeylemez
-- License:    BSD3
-- Maintainer: Ertugrul Soeylemez <es@ertes.de>
--
-- Calculus functions.

module FRP.NetWire.Calculus
    ( -- * Calculus over time
      derivative,
      derivativeFrom,
      integral
    )
    where

import Control.DeepSeq
import Data.VectorSpace
import FRP.NetWire.Wire


-- | Differentiate over time.
--
-- Inhibits at first instant.

derivative :: (Monad m, NFData v, VectorSpace v, Scalar v ~ Double) => Wire m v v
derivative =
    mkGen $ \_ y2 ->
        return (Left (inhibitEx "Derivative at first instant"),
                derivativeFrom y2)


-- | Differentiate over time.  The argument is the value before the
-- first instant.
--
-- Never inhibits.  Feedback by delay.

derivativeFrom ::
    forall m v. (Monad m, NFData v, VectorSpace v, Scalar v ~ Double) =>
    v -> Wire m v v
derivativeFrom y1 = derivativeFrom' zeroV y1
    where
    derivativeFrom' :: v -> v -> Wire m v v
    derivativeFrom' dy' y1 =
        mkGen $ \(wsDTime -> dt) y2 -> do
            let dy = (y2 ^-^ y1) ^/ dt
            dy' `deepseq` return (Right dy, derivativeFrom' dy y2)


-- | Integrate over time.  The argument is the integration constant.
--
-- Never inhibits.  Feedback by delay.

integral :: (Monad m, NFData v, VectorSpace v, Scalar v ~ Double) => v -> Wire m v v
integral x1 =
    mkGen $ \ws dx -> do
        let dt = wsDTime ws
            x2 = x1 ^+^ dt *^ dx
        x1 `deepseq` return (Right x2, integral x2)