-- |
-- Module      : FRP.Yampa.Scan
-- Copyright   : (c) Ivan Perez, 2014-2023
--               (c) George Giorgidze, 2007-2012
--               (c) Henrik Nilsson, 2005-2006
--               (c) Antony Courtney and Henrik Nilsson, Yale University, 2003-2004
-- License     : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  : ivan.perez@keera.co.uk
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
--
-- Simple, stateful signal processing.
--
-- Scanning implements elementary, step-based accumulating over signal functions
-- by means of an auxiliary function applied to each input and to an
-- accumulator. For comparison with other FRP libraries and with stream
-- processing abstractions, think of fold.
module FRP.BearRiver.Scan
    ( sscan
    , sscanPrim
    )
  where

-- Internal imports (dunai)
import Data.MonadicStreamFunction.InternalCore (MSF (..))

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

-- * Simple, stateful signal processing

-- | Applies a function point-wise, using the last output as next input. This
-- creates a well-formed loop based on a pure, auxiliary function.
sscan :: Monad m => (b -> a -> b) -> b -> SF m a b
sscan :: forall (m :: * -> *) b a. Monad m => (b -> a -> b) -> b -> SF m a b
sscan b -> a -> b
f b
bInit = (b -> a -> Maybe (b, b)) -> b -> b -> SF m a b
forall (m :: * -> *) c a b.
Monad m =>
(c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
sscanPrim b -> a -> Maybe (b, b)
f' b
bInit b
bInit
  where
    f' :: b -> a -> Maybe (b, b)
f' b
b a
a = (b, b) -> Maybe (b, b)
forall a. a -> Maybe a
Just (b
b', b
b')
      where
        b' :: b
b' = b -> a -> b
f b
b a
a

-- | Generic version of 'sscan', in which the auxiliary function produces an
-- internal accumulator and an "held" output.
--
-- Applies a function point-wise, using the last known 'Just' output to form the
-- output, and next input accumulator. If the output is 'Nothing', the last
-- known accumulators are used. This creates a well-formed loop based on a pure,
-- auxiliary function.
sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
sscanPrim :: forall (m :: * -> *) c a b.
Monad m =>
(c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
sscanPrim c -> a -> Maybe (c, b)
f c
cInit b
bInit = (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
  let o :: Maybe (c, b)
o = c -> a -> Maybe (c, b)
f c
cInit a
a
  case Maybe (c, b)
o of
    Maybe (c, b)
Nothing       -> (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
bInit, (c -> a -> Maybe (c, b)) -> c -> b -> MSF (ReaderT DTime m) a b
forall (m :: * -> *) c a b.
Monad m =>
(c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
sscanPrim c -> a -> Maybe (c, b)
f c
cInit b
bInit)
    Just (c
c', 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',    (c -> a -> Maybe (c, b)) -> c -> b -> MSF (ReaderT DTime m) a b
forall (m :: * -> *) c a b.
Monad m =>
(c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
sscanPrim c -> a -> Maybe (c, b)
f c
c' b
b')