-- |
-- Module: Stacks
-- Description: Stream for a stack machine
-- Copyright: (c) 2011 National Institute of Aerospace / Galois, Inc.
--
-- This is a stream for a stack machine.
--
-- The stack is created from a specified depth, a specified start value, and
-- three input streams:
--
-- * a pop signal which pops off the stack when true,
-- * a push signal which pushes the value from the push stream onto the stack when true,
-- * a push stream.
-- The resultant stream is the top value of the stack.
--
-- In 'stack' the push signal takes priority over the pop signal in the event
-- that both are true in the same tick. This priority is reversed in 'stack''.
--
-- Here is an example sequence with one stack of each type, both depth 3 and
-- starting value 0:
--
-- @
-- popSignal:   pushSignal:  pushValue:   stack:       stack':
-- false        true         100          0            0
-- false        true         101          100          100
-- true         true         102          101          101
-- true         false        103          100          102
-- true         false        104          0            101
-- true         false        105          0            100
-- true         false        106          0            0
-- @
--
-- Note the difference at the 4th tick after /popSignal/ and /pushSignal/ were
-- both true.  Note also that one cannot pop the start value off the stack -
-- that is, the stack is never empty.

{-# LANGUAGE NoImplicitPrelude #-}

module Copilot.Library.Stacks
  ( stack, stack' ) where

import Copilot.Language

-- | Stack stream in which the pop signal has precedence over the push signal
-- in case both are true in the same tick
stack :: (Integral a, Typed b) =>
         a              -- ^ Depth
         -> b           -- ^ Start value
         -> Stream Bool -- ^ Pop signal
         -> Stream Bool -- ^ Push signal
         -> Stream b    -- ^ Push stream
         -> Stream b    -- ^ Stack top
stack depth startValue
  popSignal pushSignal pushValue =
  let depth'      = fromIntegral depth
      startValue' = constant startValue
      stackValue pushValue' popValue' =
        let stackValue'  = [ startValue ]
                           ++ mux popSignal
                                  popValue'
                                  ( mux pushSignal
                                        pushValue'
                                        stackValue' )
        in  stackValue'
      toStack l =
        let toStack' _    []           = startValue'
            toStack' prev ( sv : svs ) =
              let current = sv prev ( toStack' current svs )
              in  current
        in toStack' pushValue l

   in toStack $ replicate depth' stackValue

-- | Stack stream in which the push signal has precedence over the pop signal
-- in case both are true in the same tick
stack' :: (Integral a, Typed b) =>
         a              -- ^ Depth
         -> b           -- ^ Start value
         -> Stream Bool -- ^ Pop signal
         -> Stream Bool -- ^ Push signal
         -> Stream b    -- ^ Push stream
         -> Stream b    -- ^ Stack top
stack' depth startValue
  popSignal pushSignal pushValue =
  let depth'      = fromIntegral depth
      startValue' = constant startValue
      stackValue pushValue' popValue' =
        let stackValue'  = [ startValue ]
                           ++ mux pushSignal
                                  pushValue'
                                  ( mux popSignal
                                        popValue'
                                        stackValue' )
        in  stackValue'
      toStack l =
        let toStack' _    []           = startValue'
            toStack' prev ( sv : svs ) =
              let current = sv prev ( toStack' current svs )
              in  current
        in toStack' pushValue l

   in toStack $ replicate depth' stackValue
