content: -- |
  -- 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
  