{-|
Copyright  :  (C) 2013-2016, University of Twente,
                  2017     , Google Inc.
                  2019     , Myrtle Software Ltd
License    :  BSD2 (see the file LICENSE)
Maintainer :  Christiaan Baaij <christiaan.baaij@gmail.com>
-}

{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

#if __GLASGOW_HASKELL__ < 806
{-# LANGUAGE TypeInType #-}
#endif

{-# LANGUAGE Safe #-}

{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}

module Clash.Signal.Delayed
  ( -- * Delay-annotated synchronous signals
    DSignal
  , delayed
  , delayedI
  , delayN
  , delayI
  , delayedFold
  , feedback
    -- * Signal \<-\> DSignal conversion
  , fromSignal
  , toSignal
    -- * List \<-\> DSignal conversion (not synthesizable)
  , dfromList
    -- ** lazy versions
  , dfromList_lazy
    -- * Experimental
  , unsafeFromSignal
  , antiDelay
  )
where

import           GHC.TypeLits
  (KnownNat, type (^), type (+), type (*))

import Clash.Signal.Delayed.Internal
  (DSignal(..), dfromList, dfromList_lazy, fromSignal, toSignal,
   unsafeFromSignal, antiDelay, feedback)
import qualified Clash.Explicit.Signal.Delayed as E
import           Clash.Sized.Vector            (Vec)
import           Clash.Signal
  (HiddenClock, HiddenClockResetEnable, HiddenEnable, hideClock,
   hideClockResetEnable, hideEnable)

import           Clash.Promoted.Nat            (SNat (..))
import           Clash.XException              (NFDataX)

{- $setup
>>> :set -XDataKinds -XTypeOperators -XTypeApplications -XFlexibleContexts
>>> import Clash.Prelude
>>> let delay3 = delayed (-1 :> -1 :> -1 :> Nil)
>>> let delay2 = delayedI :: HiddenClockResetEnable dom  => Int -> DSignal dom n Int -> DSignal dom (n + 2) Int
>>> let delayN2 = delayN d2
>>> let delayI2 = delayI :: HiddenClockResetEnable dom  => Int -> DSignal dom n Int -> DSignal dom (n + 2) Int
>>> let countingSignals = Clash.Prelude.repeat (dfromList [0..]) :: Vec 4 (DSignal dom 0 Int)
-}

-- | Delay a 'DSignal' for @d@ periods.
--
-- @
-- delay3
--   :: HiddenClockResetEnable dom
--   => 'DSignal' dom n Int
--   -> 'DSignal' dom (n + 3) Int
-- delay3 = 'delayed' (-1 ':>' -1 ':>' -1 ':>' 'Nil')
-- @
--
-- >>> sampleN @System 7 (toSignal (delay3 (dfromList [0..])))
-- [-1,-1,-1,-1,1,2,3]
delayed
  :: ( KnownNat d
     , HiddenClockResetEnable dom
     , NFDataX a
     )
  => Vec d a
  -> DSignal dom n a
  -> DSignal dom (n + d) a
delayed :: Vec d a -> DSignal dom n a -> DSignal dom (n + d) a
delayed = (KnownDomain dom =>
 Clock dom
 -> Reset dom
 -> Enable dom
 -> Vec d a
 -> DSignal dom n a
 -> DSignal dom (n + d) a)
-> Vec d a -> DSignal dom n a -> DSignal dom (n + d) a
forall (dom :: Symbol) r.
HiddenClockResetEnable dom =>
(KnownDomain dom => Clock dom -> Reset dom -> Enable dom -> r) -> r
hideClockResetEnable KnownDomain dom =>
Clock dom
-> Reset dom
-> Enable dom
-> Vec d a
-> DSignal dom n a
-> DSignal dom (n + d) a
forall (dom :: Symbol) a (n :: Nat) (d :: Nat).
(KnownDomain dom, KnownNat d, NFDataX a) =>
Clock dom
-> Reset dom
-> Enable dom
-> Vec d a
-> DSignal dom n a
-> DSignal dom (n + d) a
E.delayed

-- | Delay a 'DSignal' for @d@ periods, where @d@ is derived from the context.
--
-- @
-- delay2
--   :: HiddenClockResetEnable dom
--   => Int
--   -> 'DSignal' dom n Int
--   -> 'DSignal' dom (n + 2) Int
-- delay2 = 'delayedI'
-- @
--
-- >>> sampleN @System 7 (toSignal (delay2 (-1) (dfromList [0..])))
-- [-1,-1,-1,1,2,3,4]
--
-- Or @d@ can be specified using type application:
--
-- >>> :t delayedI @3
-- delayedI @3
--   :: (...
--       ...
--       ...
--       ...) =>
--      a -> DSignal dom n a -> DSignal dom (n + 3) a
delayedI
  :: ( KnownNat d
     , NFDataX a
     , HiddenClockResetEnable dom  )
  => a
  -- ^ Initial value
  -> DSignal dom n a
  -> DSignal dom (n + d) a
delayedI :: a -> DSignal dom n a -> DSignal dom (n + d) a
delayedI = (KnownDomain dom =>
 Clock dom
 -> Reset dom
 -> Enable dom
 -> a
 -> DSignal dom n a
 -> DSignal dom (n + d) a)
-> a -> DSignal dom n a -> DSignal dom (n + d) a
forall (dom :: Symbol) r.
HiddenClockResetEnable dom =>
(KnownDomain dom => Clock dom -> Reset dom -> Enable dom -> r) -> r
hideClockResetEnable KnownDomain dom =>
Clock dom
-> Reset dom
-> Enable dom
-> a
-> DSignal dom n a
-> DSignal dom (n + d) a
forall (d :: Nat) (dom :: Symbol) a (n :: Nat).
(KnownNat d, KnownDomain dom, NFDataX a) =>
Clock dom
-> Reset dom
-> Enable dom
-> a
-> DSignal dom n a
-> DSignal dom (n + d) a
E.delayedI

-- | Delay a 'DSignal' for @d@ cycles, the value at time 0..d-1 is /a/.
--
-- @
-- delayN2
--   :: HiddenClockResetEnable dom
--   => Int
--   -> 'DSignal' dom n Int
--   -> 'DSignal' dom (n + 2) Int
-- delayN2 = 'delayN' d2
-- @
--
-- >>> printX $ sampleN @System 6 (toSignal (delayN2 (-1) (dfromList [1..])))
-- [-1,-1,1,2,3,4]
delayN
  :: forall dom  a d n
   . ( HiddenClock dom
     , HiddenEnable dom
     , NFDataX a )
  => SNat d
  -> a
  -- ^ Initial value
  -> DSignal dom n a
  -> DSignal dom (n+d) a
delayN :: SNat d -> a -> DSignal dom n a -> DSignal dom (n + d) a
delayN d :: SNat d
d dflt :: a
dflt = (Clock dom -> DSignal dom n a -> DSignal dom (n + d) a)
-> DSignal dom n a -> DSignal dom (n + d) a
forall (dom :: Symbol) r. HiddenClock dom => (Clock dom -> r) -> r
hideClock ((Enable dom
 -> Clock dom -> DSignal dom n a -> DSignal dom (n + d) a)
-> Clock dom -> DSignal dom n a -> DSignal dom (n + d) a
forall (dom :: Symbol) r.
HiddenEnable dom =>
(Enable dom -> r) -> r
hideEnable (SNat d
-> a
-> Enable dom
-> Clock dom
-> DSignal dom n a
-> DSignal dom (n + d) a
forall (dom :: Symbol) a (d :: Nat) (n :: Nat).
(KnownDomain dom, NFDataX a) =>
SNat d
-> a
-> Enable dom
-> Clock dom
-> DSignal dom n a
-> DSignal dom (n + d) a
E.delayN SNat d
d a
dflt))

-- | Delay a 'DSignal' for @d@ cycles, where @d@ is derived from the context.
-- The value at time 0..d-1 is a default value.
--
-- @
-- delayI2
--   :: HiddenClockResetEnable dom
--   => Int
--   -> 'DSignal' dom n Int
--   -> 'DSignal' dom (n + 2) Int
-- delayI2 = 'delayI'
-- @
--
-- >>> sampleN @System 6 (toSignal (delayI2 (-1) (dfromList [1..])))
-- [-1,-1,1,2,3,4]
--
-- You can also use type application to do the same:
-- >>> sampleN @System 6 (toSignal (delayI @2 (-1) (dfromList [1..])))
-- [-1,-1,1,2,3,4]
delayI
  :: forall d n a dom
   . ( HiddenClockResetEnable dom
     , NFDataX a
     , KnownNat d )
  => a
  -- ^ Initial value
  -> DSignal dom n a
  -> DSignal dom (n+d) a
delayI :: a -> DSignal dom n a -> DSignal dom (n + d) a
delayI dflt :: a
dflt = SNat d -> a -> DSignal dom n a -> DSignal dom (n + d) a
forall (dom :: Symbol) a (d :: Nat) (n :: Nat).
(HiddenClock dom, HiddenEnable dom, NFDataX a) =>
SNat d -> a -> DSignal dom n a -> DSignal dom (n + d) a
delayN (SNat d
forall (n :: Nat). KnownNat n => SNat n
SNat :: SNat d) a
dflt

-- | Tree fold over a 'Vec' of 'DSignal's with a combinatorial function,
-- and delaying @delay@ cycles after each application.
-- Values at times 0..(delay*k)-1 are set to a default.
--
-- @
-- countingSignals :: Vec 4 (DSignal dom 0 Int)
-- countingSignals = repeat (dfromList [0..])
-- @
--
-- >>> printX $ sampleN @System 6 (toSignal (delayedFold d1 (-1) (+) countingSignals))
-- [-1,-2,0,4,8,12]
--
-- >>> printX $ sampleN @System 8 (toSignal (delayedFold d2 (-1) (*) countingSignals))
-- [-1,-1,1,1,0,1,16,81]
delayedFold
  :: forall dom  n delay k a
   . ( HiddenClock dom
     , HiddenEnable dom
     , NFDataX a
     , KnownNat delay
     , KnownNat k )
  => SNat delay
  -- ^ Delay applied after each step
  -> a
  -- ^ Initial value
  -> (a -> a -> a)
  -- ^ Fold operation to apply
  -> Vec (2^k) (DSignal dom n a)
  -- ^ Vector input of size 2^k
  -> DSignal dom (n + (delay * k)) a
  -- ^ Output Signal delayed by (delay * k)
delayedFold :: SNat delay
-> a
-> (a -> a -> a)
-> Vec (2 ^ k) (DSignal dom n a)
-> DSignal dom (n + (delay * k)) a
delayedFold d :: SNat delay
d dflt :: a
dflt f :: a -> a -> a
f = (Clock dom
 -> Vec (2 ^ k) (DSignal dom n a)
 -> DSignal dom (n + (delay * k)) a)
-> Vec (2 ^ k) (DSignal dom n a) -> DSignal dom (n + (delay * k)) a
forall (dom :: Symbol) r. HiddenClock dom => (Clock dom -> r) -> r
hideClock ((Enable dom
 -> Clock dom
 -> Vec (2 ^ k) (DSignal dom n a)
 -> DSignal dom (n + (delay * k)) a)
-> Clock dom
-> Vec (2 ^ k) (DSignal dom n a)
-> DSignal dom (n + (delay * k)) a
forall (dom :: Symbol) r.
HiddenEnable dom =>
(Enable dom -> r) -> r
hideEnable (SNat delay
-> a
-> (a -> a -> a)
-> Enable dom
-> Clock dom
-> Vec (2 ^ k) (DSignal dom n a)
-> DSignal dom (n + (delay * k)) a
forall (dom :: Symbol) (n :: Nat) (delay :: Nat) (k :: Nat) a.
(NFDataX a, KnownDomain dom, KnownNat delay, KnownNat k) =>
SNat delay
-> a
-> (a -> a -> a)
-> Enable dom
-> Clock dom
-> Vec (2 ^ k) (DSignal dom n a)
-> DSignal dom (n + (delay * k)) a
E.delayedFold SNat delay
d a
dflt a -> a -> a
f))