packages feed

keiki-0.2.0.0: test/Keiki/Fixtures/ComposeStateful.hs

-- | Stateful fixtures for EP-74's sequential-composition regressions.
-- Keep these transducers independent of hspec so later property suites can
-- compare 'compose' with an explicit sequential reference.
module Keiki.Fixtures.ComposeStateful
  ( SourceCmd (..),
    MidVal (..),
    OutVal (..),
    StageOut (..),
    PairCmd (..),
    Mid2 (..),
    WrongOut (..),
    M2SourceCmd (..),
    CounterRegs,
    SinkRegs,
    PhaseRegs,
    CounterVertex (..),
    SinkVertex (..),
    PhaseVertex (..),
    PairVertex (..),
    M2SourceVertex (..),
    WrongVertex (..),
    counterSource,
    lastValueSink,
    pairSource,
    twoPhaseSink,
    m2aSource,
    wrongOrderSink,
    readSourceCount,
    readSinkLast,
    readPhase,
  )
where

import Data.Proxy (Proxy (..))
import Keiki.Core
import Keiki.Generics (Append)

data SourceCmd = Tick
  deriving stock (Eq, Show)

data MidVal = MidVal Int
  deriving stock (Eq, Show)

data OutVal = OutVal Int
  deriving stock (Eq, Show)

data StageOut = Stage1 Int | Stage2 Int
  deriving stock (Eq, Show)

data PairCmd = Go
  deriving stock (Eq, Show)

data M2SourceCmd = ProduceA
  deriving stock (Eq, Show)

data Mid2 = M2A Int | M2B Int
  deriving stock (Eq, Show)

data WrongOut = SawA Int | SawB Int
  deriving stock (Eq, Show)

type CounterRegs = '[ '("srcCount", Int)]

type SinkRegs = '[ '("sinkLast", Int)]

type PhaseRegs = '[ '("phase", Int)]

data CounterVertex = CounterVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

data SinkVertex = SinkVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

data PairVertex = PairVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

data PhaseVertex = PhaseVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

data M2SourceVertex = M2SourceVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

data WrongVertex = WrongVertex
  deriving stock (Eq, Ord, Show, Enum, Bounded)

inCtorTick :: InCtor SourceCmd '[]
inCtorTick =
  InCtor
    { icName = "Tick",
      icMatch = \case Tick -> Just RNil,
      icBuild = \RNil -> Tick
    }

inCtorGo :: InCtor PairCmd '[]
inCtorGo =
  InCtor
    { icName = "Go",
      icMatch = \case Go -> Just RNil,
      icBuild = \RNil -> Go
    }

inCtorProduceA :: InCtor M2SourceCmd '[]
inCtorProduceA =
  InCtor
    { icName = "ProduceA",
      icMatch = \case ProduceA -> Just RNil,
      icBuild = \RNil -> ProduceA
    }

inCtorMidVal :: InCtor MidVal '[ '("v", Int)]
inCtorMidVal =
  InCtor
    { icName = "MidVal",
      icMatch = \case MidVal v -> Just (RCons (Proxy @"v") v RNil),
      icBuild = \(RCons _ v RNil) -> MidVal v
    }

inCtorM2A :: InCtor Mid2 '[ '("a", Int)]
inCtorM2A =
  InCtor
    { icName = "M2A",
      icMatch = \case
        M2A a -> Just (RCons (Proxy @"a") a RNil)
        M2B _ -> Nothing,
      icBuild = \(RCons _ a RNil) -> M2A a
    }

inCtorM2B :: InCtor Mid2 '[ '("b", Int)]
inCtorM2B =
  InCtor
    { icName = "M2B",
      icMatch = \case
        M2A _ -> Nothing
        M2B b -> Just (RCons (Proxy @"b") b RNil),
      icBuild = \(RCons _ b RNil) -> M2B b
    }

wireMidVal :: WireCtor MidVal (Int, ())
wireMidVal =
  WireCtor
    { wcName = "MidVal",
      wcMatch = \case MidVal v -> Just (v, ()),
      wcBuild = \(v, ()) -> MidVal v
    }

wireM2A :: WireCtor Mid2 (Int, ())
wireM2A =
  WireCtor
    { wcName = "M2A",
      wcMatch = \case
        M2A a -> Just (a, ())
        M2B _ -> Nothing,
      wcBuild = \(a, ()) -> M2A a
    }

wireOutVal :: WireCtor OutVal (Int, ())
wireOutVal =
  WireCtor
    { wcName = "OutVal",
      wcMatch = \case OutVal v -> Just (v, ()),
      wcBuild = \(v, ()) -> OutVal v
    }

wireStage1 :: WireCtor StageOut (Int, ())
wireStage1 =
  WireCtor
    { wcName = "Stage1",
      wcMatch = \case
        Stage1 v -> Just (v, ())
        Stage2 _ -> Nothing,
      wcBuild = \(v, ()) -> Stage1 v
    }

wireStage2 :: WireCtor StageOut (Int, ())
wireStage2 =
  WireCtor
    { wcName = "Stage2",
      wcMatch = \case
        Stage1 _ -> Nothing
        Stage2 v -> Just (v, ()),
      wcBuild = \(v, ()) -> Stage2 v
    }

wireSawA :: WireCtor WrongOut (Int, ())
wireSawA =
  WireCtor
    { wcName = "SawA",
      wcMatch = \case
        SawA v -> Just (v, ())
        SawB _ -> Nothing,
      wcBuild = \(v, ()) -> SawA v
    }

wireSawB :: WireCtor WrongOut (Int, ())
wireSawB =
  WireCtor
    { wcName = "SawB",
      wcMatch = \case
        SawA _ -> Nothing
        SawB v -> Just (v, ()),
      wcBuild = \(v, ()) -> SawB v
    }

counterSource :: SymTransducer (HsPred CounterRegs SourceCmd) CounterRegs CounterVertex SourceCmd MidVal
counterSource =
  SymTransducer
    { edgesOut = \CounterVertex ->
        [ Edge
            { guard = matchInCtor inCtorTick,
              update =
                USet
                  (#srcCount :: IndexN "srcCount" CounterRegs Int)
                  (proj (#srcCount :: Index CounterRegs Int) .+ lit 1),
              output =
                [ pack
                    inCtorTick
                    wireMidVal
                    (proj (#srcCount :: Index CounterRegs Int) *: oNil)
                ],
              target = CounterVertex
            }
        ],
      initial = CounterVertex,
      initialRegs = RCons (Proxy @"srcCount") 0 RNil,
      isFinal = const True
    }

lastValueSink :: SymTransducer (HsPred SinkRegs MidVal) SinkRegs SinkVertex MidVal OutVal
lastValueSink =
  SymTransducer
    { edgesOut = \SinkVertex ->
        [ Edge
            { guard = matchInCtor inCtorMidVal,
              update =
                USet
                  (#sinkLast :: IndexN "sinkLast" SinkRegs Int)
                  (inpCtor inCtorMidVal (#v :: Index '[ '("v", Int)] Int)),
              output =
                [ pack
                    inCtorMidVal
                    wireOutVal
                    (inpCtor inCtorMidVal (#v :: Index '[ '("v", Int)] Int) *: oNil)
                ],
              target = SinkVertex
            }
        ],
      initial = SinkVertex,
      initialRegs = RCons (Proxy @"sinkLast") (-1) RNil,
      isFinal = const True
    }

pairSource :: SymTransducer (HsPred '[] PairCmd) '[] PairVertex PairCmd MidVal
pairSource =
  SymTransducer
    { edgesOut = \PairVertex ->
        [ Edge
            { guard = matchInCtor inCtorGo,
              update = UKeep,
              output =
                [ pack inCtorGo wireMidVal (lit 10 *: oNil),
                  pack inCtorGo wireMidVal (lit 20 *: oNil)
                ],
              target = PairVertex
            }
        ],
      initial = PairVertex,
      initialRegs = RNil,
      isFinal = const True
    }

twoPhaseSink :: SymTransducer (HsPred PhaseRegs MidVal) PhaseRegs PhaseVertex MidVal StageOut
twoPhaseSink =
  SymTransducer
    { edgesOut = \PhaseVertex ->
        [ Edge
            { guard =
                matchInCtor inCtorMidVal
                  .&& (proj (#phase :: Index PhaseRegs Int) .== lit 0),
              update = USet (#phase :: IndexN "phase" PhaseRegs Int) (lit 1),
              output =
                [ pack
                    inCtorMidVal
                    wireStage1
                    (inpCtor inCtorMidVal (#v :: Index '[ '("v", Int)] Int) *: oNil)
                ],
              target = PhaseVertex
            },
          Edge
            { guard =
                matchInCtor inCtorMidVal
                  .&& (proj (#phase :: Index PhaseRegs Int) .== lit 1),
              update = USet (#phase :: IndexN "phase" PhaseRegs Int) (lit 2),
              output =
                [ pack
                    inCtorMidVal
                    wireStage2
                    (inpCtor inCtorMidVal (#v :: Index '[ '("v", Int)] Int) *: oNil)
                ],
              target = PhaseVertex
            }
        ],
      initial = PhaseVertex,
      initialRegs = RCons (Proxy @"phase") 0 RNil,
      isFinal = const True
    }

m2aSource :: SymTransducer (HsPred '[] M2SourceCmd) '[] M2SourceVertex M2SourceCmd Mid2
m2aSource =
  SymTransducer
    { edgesOut = \M2SourceVertex ->
        [ Edge
            { guard = matchInCtor inCtorProduceA,
              update = UKeep,
              output = [pack inCtorProduceA wireM2A (lit 5 *: oNil)],
              target = M2SourceVertex
            }
        ],
      initial = M2SourceVertex,
      initialRegs = RNil,
      isFinal = const True
    }

wrongOrderSink :: SymTransducer (HsPred '[] Mid2) '[] WrongVertex Mid2 WrongOut
wrongOrderSink =
  SymTransducer
    { edgesOut = \WrongVertex ->
        [ Edge
            { guard =
                matchInCtor inCtorM2A
                  .&& (inpCtor inCtorM2A (#a :: Index '[ '("a", Int)] Int) .== lit 5),
              update = UKeep,
              output =
                [ pack
                    inCtorM2A
                    wireSawA
                    (inpCtor inCtorM2A (#a :: Index '[ '("a", Int)] Int) *: oNil)
                ],
              target = WrongVertex
            },
          Edge
            { guard =
                (inpCtor inCtorM2B (#b :: Index '[ '("b", Int)] Int) .== lit 5)
                  .&& matchInCtor inCtorM2B,
              update = UKeep,
              output =
                [ pack
                    inCtorM2B
                    wireSawB
                    (inpCtor inCtorM2B (#b :: Index '[ '("b", Int)] Int) *: oNil)
                ],
              target = WrongVertex
            }
        ],
      initial = WrongVertex,
      initialRegs = RNil,
      isFinal = const True
    }

readSourceCount :: RegFile (Append CounterRegs SinkRegs) -> Int
readSourceCount regs = regs ! (#srcCount :: Index (Append CounterRegs SinkRegs) Int)

readSinkLast :: RegFile (Append CounterRegs SinkRegs) -> Int
readSinkLast regs = regs ! (#sinkLast :: Index (Append CounterRegs SinkRegs) Int)

readPhase :: RegFile (Append '[] PhaseRegs) -> Int
readPhase regs = regs ! (#phase :: Index (Append '[] PhaseRegs) Int)