    {-# LANGUAGE ScopedTypeVariables #-}
    -- | Simulate state machines using streams.
    module Copilot.Library.StateMachines where

    import Copilot.Language (Stream, Typed, constant, ifThenElse, (&&), (++), (==))
    import Prelude          hiding ((&&), (++), (==))

    -- | A definition of a state machine where some elements are defined
    -- as streams.
    --
    -- A state machine is defined by an initial state, a final state, a no
    -- transition stream (true when tere is no input coming in), a list of
    -- transitions, and a bad state.
    type StateMachine a = (a, a, Stream Bool, [(a, Stream Bool, a)], a)

    -- | Produce a stream that, at any given time, contains the current state of
    -- the state machine.
    stateMachine :: forall a . (Eq a, Typed a) => StateMachine a -> Stream a
    stateMachine :: forall a. (Eq a, Typed a) => StateMachine a -> Stream a
    stateMachine (a
    initial, a
    final, Stream Bool
    noInputData, [(a, Stream Bool, a)]
    transitions, a
    bad) = Stream a
    state
      where
        state :: Stream a
    state         = [(a, Stream Bool, a)] -> Stream a
    ifThenElses [(a, Stream Bool, a)]
    transitions
        previousState :: Stream a
    previousState = [a
    initial] [a] -> Stream a -> Stream a
    forall a. Typed a => [a] -> Stream a -> Stream a
    ++ Stream a
    state

        ifThenElses :: [(a, Stream Bool, a)] -> Stream a
        ifThenElses :: [(a, Stream Bool, a)] -> Stream a
    ifThenElses [] =
          Stream Bool -> Stream a -> Stream a -> Stream a
    forall a.
    Typed a =>
    Stream Bool -> Stream a -> Stream a -> Stream a
    ifThenElse (Stream a
    previousState Stream a -> Stream a -> Stream Bool
    forall a. (Eq a, Typed a) => Stream a -> Stream a -> Stream Bool
    == a -> Stream a
    forall a. Typed a => a -> Stream a
    constant a
    final Stream Bool -> Stream Bool -> Stream Bool
    && Stream Bool
    noInputData)
            (a -> Stream a
    forall a. Typed a => a -> Stream a
    constant a
    final)
            (a -> Stream a
    forall a. Typed a => a -> Stream a
    constant a
    bad)

        ifThenElses ((a
    s1, Stream Bool
    i, a
    s2):[(a, Stream Bool, a)]
    ss) =
          Stream Bool -> Stream a -> Stream a -> Stream a
    forall a.
    Typed a =>
    Stream Bool -> Stream a -> Stream a -> Stream a
    ifThenElse
            (Stream a
    previousState Stream a -> Stream a -> Stream Bool
    forall a. (Eq a, Typed a) => Stream a -> Stream a -> Stream Bool
    == a -> Stream a
    forall a. Typed a => a -> Stream a
    constant a
    s1 Stream Bool -> Stream Bool -> Stream Bool
    && Stream Bool
    i)
            (a -> Stream a
    forall a. Typed a => a -> Stream a
    constant a
    s2)
            ([(a, Stream Bool, a)] -> Stream a
    ifThenElses [(a, Stream Bool, a)]
    ss)

    -- | Produce a stream that, at any given time, contains the current state of
    -- the state machine as the numeric representation of an enum.
    stateMachineEnum :: (Eq b, Typed b, Num b, Enum a)
                     => StateMachine a
                     -> Stream b
    stateMachineEnum :: forall b a.
    (Eq b, Typed b, Num b, Enum a) =>
    StateMachine a -> Stream b
    stateMachineEnum (a
    initial, a
    final, Stream Bool
    noInputData, [(a, Stream Bool, a)]
    transitions, a
    bad) =
        StateMachine b -> Stream b
    forall a. (Eq a, Typed a) => StateMachine a -> Stream a
    stateMachine (a -> b
    fe a
    initial, a -> b
    fe a
    final, Stream Bool
    noInputData, [(b, Stream Bool, b)]
    transitionsE, a -> b
    fe a
    bad)
      where
        transitionsE :: [(b, Stream Bool, b)]
    transitionsE = ((a, Stream Bool, a) -> (b, Stream Bool, b))
    -> [(a, Stream Bool, a)] -> [(b, Stream Bool, b)]
    forall a b. (a -> b) -> [a] -> [b]
    map (\(a
    s1, Stream Bool
    t, a
    s2) -> (a -> b
    fe a
    s1, Stream Bool
    t, a -> b
    fe a
    s2)) [(a, Stream Bool, a)]
    transitions
        fe :: a -> b
    fe           = Int -> b
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int -> b) -> (a -> Int) -> a -> b
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . a -> Int
    forall a. Enum a => a -> Int
    fromEnum