-----------------------------------------------------------------------------
-- |
-- Module  :  ForSyDe.Shallow.MoC.SADF
-- Copyright   :  (c) Ricardo Bonna, KTH/ICT/ES, ForSyDe-Group
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  ricardobonna@gmail.com
-- Stability   :  experimental
-- Portability :  portable
--
-- Experimental lib. Further test needed
--
-----------------------------------------------------------------------------

module ForSyDe.Shallow.MoC.SADF (
  -- * Sequential Process Constructors
  -- | Sequential process constructors are used for processes that
  -- have a state. One of the input parameters is the initial state.
  delaySADF,
  -- * Kernels
  -- | Based on the process constructors in the SADF-MoC, the
  -- SADF-library provides SADF-kernels with single or multiple inputs
  kernel11SADF, kernel12SADF, kernel13SADF, kernel14SADF, kernel15SADF,
  kernel21SADF, kernel22SADF, kernel23SADF, kernel24SADF, kernel25SADF,
  kernel31SADF, kernel32SADF, kernel33SADF, kernel34SADF, kernel35SADF,
  kernel41SADF, kernel42SADF, kernel43SADF, kernel44SADF, kernel45SADF,
  kernel51SADF, kernel52SADF, kernel53SADF, kernel54SADF, kernel55SADF,
  -- * Detectors
  -- | Based on the process constructors in the SADF-MoC, the
  -- SADF-library provides SADF-detectors with single or multiple inputs
  detector11SADF, detector12SADF, detector13SADF, detector14SADF, detector15SADF,
  detector21SADF, detector22SADF, detector23SADF, detector24SADF, detector25SADF,
  detector31SADF, detector32SADF, detector33SADF, detector34SADF, detector35SADF,
  detector41SADF, detector42SADF, detector43SADF, detector44SADF, detector45SADF,
  detector51SADF, detector52SADF, detector53SADF, detector54SADF, detector55SADF
  ) where

import ForSyDe.Shallow.Core


-------------------------------------
--             --
-- SEQUENTIAL PROCESS CONSTRUCTORS --
--             --
-------------------------------------

-- | The process constructor 'delaynSADF' delays the signal n event
--   cycles by introducing n initial values at the beginning of the
--   output signal.
delaySADF :: [a] -> Signal a -> Signal a
delaySADF initial_tokens xs = signal initial_tokens +-+ xs


------------------------------------------------------------------------
--
-- SADF KERNELS
--
------------------------------------------------------------------------

-- > Kernels with one output

-- | The process constructor 'kernel11SADF' constructs an SADF kernel with
-- one data input and one data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel11SADF :: Signal (Int, Int, [a] -> [b]) -- ^ Control signal
             -> Signal a                      -- ^ Input
             -> Signal b                      -- ^ Output
kernel11SADF = mapSADF

-- | The process constructor 'kernel21SADF' constructs an SADF kernel with
-- two data input and one data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel21SADF :: Signal ((Int, Int), Int, [a] -> [b] -> [c])
             -> Signal a -> Signal b
             -> Signal c
kernel21SADF = zipWithSADF

-- | The process constructor 'kernel31SADF' constructs an SADF kernel with
-- three data input and one data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel31SADF :: Signal ((Int, Int, Int), Int, [a] -> [b] -> [c] -> [d])
             -> Signal a -> Signal b -> Signal c
             -> Signal d
kernel31SADF = zipWith3SADF

-- | The process constructor 'kernel41SADF' constructs an SADF kernel with
-- four data input and one data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel41SADF :: Signal ((Int, Int, Int, Int), Int, [a] -> [b] -> [c] -> [d] -> [e])
             -> Signal a -> Signal b -> Signal c -> Signal d
             -> Signal e
kernel41SADF = zipWith4SADF

-- | The process constructor 'kernel51SADF' constructs an SADF kernel with
-- five data input and one data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel51SADF :: Signal ((Int, Int, Int, Int, Int), Int, [a] -> [b] -> [c] -> [d] -> [e] -> [f])
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
             -> Signal f
kernel51SADF = zipWith5SADF


-- > Kernels with two outputs

-- | The process constructor 'kernel12SADF' constructs an SADF kernel with
-- one data input and two data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel12SADF :: Signal (Int, (Int, Int), [a] -> ([b], [c]))
             -> Signal a
             -> (Signal b, Signal c)
kernel12SADF ct xs = unzipSADF (get_prodToken ct) $ mapSADF (inpOut1n ct) xs

-- | The process constructor 'kernel22SADF' constructs an SADF kernel with
-- two data input and two data output signals. The scenario (token rates and
-- function) is determined by the control signal.
--
-- >>> let scen1 = ((1,1), (1,1), \[a] [b] -> ([2*a], [2*b]))
-- >>> let scen2 = ((2,2), (1,1), \[a,b] [c,d] -> ([a+b], [c+d]))
-- >>> let scen3 = ((1,2), (2,1), \[a] [b,c] -> ([b,c], [a]))
-- >>> let sc = signal [scen1, scen2, scen3]
-- >>> kernel22SADF sc (signal [1..20]) (signal [21 .. 40])
-- ({2,5,24,25},{42,45,4}) 
kernel22SADF :: Signal ((Int, Int), (Int, Int), [a] -> [b] -> ([c], [d]))
             -> Signal a -> Signal b
             -> (Signal c, Signal d)
kernel22SADF ct xs ys = unzipSADF (get_prodToken ct) $ zipWithSADF (inpOut2n ct) xs ys

-- | The process constructor 'kernel32SADF' constructs an SADF kernel with
-- three data input and two data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel32SADF :: Signal ((Int, Int, Int), (Int, Int), [a] -> [b] -> [c] -> ([d], [e]))
             -> Signal a -> Signal b -> Signal c
             -> (Signal d, Signal e)
kernel32SADF ct as bs cs
  = unzipSADF (get_prodToken ct) $ zipWith3SADF (inpOut3n ct) as bs cs

-- | The process constructor 'kernel42SADF' constructs an SADF kernel with
-- four data input and two data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel42SADF :: Signal ((Int, Int, Int, Int), (Int, Int), [a] -> [b] -> [c] -> [d] -> ([e], [f]))
             -> Signal a -> Signal b -> Signal c -> Signal d
             -> (Signal e, Signal f)
kernel42SADF ct as bs cs ds
  = unzipSADF (get_prodToken ct) $ zipWith4SADF (inpOut4n ct) as bs cs ds

-- | The process constructor 'kernel52SADF' constructs an SADF kernel with
-- five data input and two data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel52SADF :: Signal ((Int, Int, Int, Int, Int), (Int, Int), [a]
             -> [b] -> [c] -> [d] -> [e] -> ([f], [g]))
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
             -> (Signal f, Signal g)
kernel52SADF ct as bs cs ds es
  = unzipSADF (get_prodToken ct) $ zipWith5SADF (inpOut5n ct) as bs cs ds es


-- > Kernels with three outputs

-- | The process constructor 'kernel13SADF' constructs an SADF kernel with
-- one data input and three data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel13SADF :: Signal (Int, (Int, Int, Int), [a] -> ([b], [c], [d]))
             -> Signal a
             -> (Signal b, Signal c, Signal d)
kernel13SADF ct xs = unzip3SADF (get_prodToken ct) $ mapSADF (inpOut1n ct) xs

-- | The process constructor 'kernel23SADF' constructs an SADF kernel with
-- two data input and three data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel23SADF :: Signal ((Int, Int), (Int, Int, Int), [a] -> [b] -> ([c], [d], [e]))
             -> Signal a -> Signal b
             -> (Signal c, Signal d, Signal e)
kernel23SADF ct xs ys = unzip3SADF (get_prodToken ct) $ zipWithSADF (inpOut2n ct) xs ys

-- | The process constructor 'kernel33SADF' constructs an SADF kernel with
-- three data input and three data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel33SADF :: Signal ((Int, Int, Int), (Int, Int, Int), [a] -> [b] -> [c] -> ([d], [e], [f]))
             -> Signal a -> Signal b -> Signal c
             -> (Signal d, Signal e, Signal f)
kernel33SADF ct as bs cs
  = unzip3SADF (get_prodToken ct) $ zipWith3SADF (inpOut3n ct) as bs cs

-- | The process constructor 'kernel43SADF' constructs an SADF kernel with
-- four data input and three data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel43SADF :: Signal ((Int, Int, Int, Int), (Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> ([e], [f], [g]))
             -> Signal a -> Signal b -> Signal c -> Signal d
             -> (Signal e, Signal f, Signal g)
kernel43SADF ct as bs cs ds
  = unzip3SADF (get_prodToken ct) $ zipWith4SADF (inpOut4n ct) as bs cs ds

-- | The process constructor 'kernel53SADF' constructs an SADF kernel with
-- five data input and three data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel53SADF :: Signal ((Int, Int, Int, Int, Int), (Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> [e] -> ([f], [g], [h]))
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
             -> (Signal f, Signal g, Signal h)
kernel53SADF ct as bs cs ds es
  = unzip3SADF (get_prodToken ct) $ zipWith5SADF (inpOut5n ct) as bs cs ds es


-- > Kernels with four outputs

-- | The process constructor 'kernel14SADF' constructs an SADF kernel with
-- one data input and four data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel14SADF :: Signal (Int, (Int, Int, Int, Int), [a] -> ([b], [c], [d], [e]))
             -> Signal a
             -> (Signal b, Signal c, Signal d, Signal e)
kernel14SADF ct xs = unzip4SADF (get_prodToken ct) $ mapSADF (inpOut1n ct) xs

-- | The process constructor 'kernel24SADF' constructs an SADF kernel with
-- two data input and four data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel24SADF :: Signal ((Int, Int), (Int, Int, Int, Int), [a] -> [b] -> ([c], [d], [e], [f]))
             -> Signal a -> Signal b
             -> (Signal c, Signal d, Signal e, Signal f)
kernel24SADF ct xs ys = unzip4SADF (get_prodToken ct) $ zipWithSADF (inpOut2n ct) xs ys

-- | The process constructor 'kernel34SADF' constructs an SADF kernel with
-- three data input and four data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel34SADF :: Signal ((Int, Int, Int), (Int, Int, Int, Int),
             [a] -> [b] -> [c] -> ([d], [e], [f], [g]))
             -> Signal a -> Signal b -> Signal c
             -> (Signal d, Signal e, Signal f, Signal g)
kernel34SADF ct as bs cs
  = unzip4SADF (get_prodToken ct) $ zipWith3SADF (inpOut3n ct) as bs cs

-- | The process constructor 'kernel44SADF' constructs an SADF kernel with
-- four data input and four data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel44SADF :: Signal ((Int, Int, Int, Int), (Int, Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> ([e], [f], [g], [h]))
             -> Signal a -> Signal b -> Signal c -> Signal d
             -> (Signal e, Signal f, Signal g, Signal h)
kernel44SADF ct as bs cs ds
  = unzip4SADF (get_prodToken ct) $ zipWith4SADF (inpOut4n ct) as bs cs ds

-- | The process constructor 'kernel54SADF' constructs an SADF kernel with
-- five data input and four data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel54SADF :: Signal ((Int, Int, Int, Int, Int), (Int, Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> [e] -> ([f], [g], [h], [i]))
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
             -> (Signal f, Signal g, Signal h, Signal i)
kernel54SADF ct as bs cs ds es
  = unzip4SADF (get_prodToken ct) $ zipWith5SADF (inpOut5n ct) as bs cs ds es


-- > Kernels with five outputs

-- | The process constructor 'kernel15SADF' constructs an SADF kernel with
-- one data input and five data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel15SADF :: Signal (Int, (Int, Int, Int, Int, Int), [a] -> ([b], [c], [d], [e], [f]))
             -> Signal a
             -> (Signal b, Signal c, Signal d, Signal e, Signal f)
kernel15SADF ct xs = unzip5SADF (get_prodToken ct) $ mapSADF (inpOut1n ct) xs

-- | The process constructor 'kernel25SADF' constructs an SADF kernel with
-- two data input and five data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel25SADF :: Signal ((Int, Int), (Int, Int, Int, Int, Int), [a] -> [b] -> ([c], [d], [e], [f], [g]))
             -> Signal a -> Signal b
             -> (Signal c, Signal d, Signal e, Signal f, Signal g)
kernel25SADF ct xs ys = unzip5SADF (get_prodToken ct) $ zipWithSADF (inpOut2n ct) xs ys

-- | The process constructor 'kernel35SADF' constructs an SADF kernel with
-- three data input and five data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel35SADF :: Signal ((Int, Int, Int), (Int, Int, Int, Int, Int),
             [a] -> [b] -> [c] -> ([d], [e], [f], [g], [h]))
             -> Signal a -> Signal b -> Signal c
             -> (Signal d, Signal e, Signal f, Signal g, Signal h)
kernel35SADF ct as bs cs
  = unzip5SADF (get_prodToken ct) $ zipWith3SADF (inpOut3n ct) as bs cs

-- | The process constructor 'kernel45SADF' constructs an SADF kernel with
-- four data input and five data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel45SADF :: Signal ((Int, Int, Int, Int), (Int, Int, Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> ([e], [f], [g], [h], [i]))
             -> Signal a -> Signal b -> Signal c -> Signal d
             -> (Signal e, Signal f, Signal g, Signal h, Signal i)
kernel45SADF ct as bs cs ds
  = unzip5SADF (get_prodToken ct) $ zipWith4SADF (inpOut4n ct) as bs cs ds

-- | The process constructor 'kernel55SADF' constructs an SADF kernel with
-- five data input and five data output signals. The scenario (token rates and
-- function) is determined by the control signal.
kernel55SADF :: Signal ((Int, Int, Int, Int, Int), (Int, Int, Int, Int, Int),
             [a] -> [b] -> [c] -> [d] -> [e] -> ([f], [g], [h], [i], [j]))
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
             -> (Signal f, Signal g, Signal h, Signal i, Signal j)
kernel55SADF ct as bs cs ds es
  = unzip5SADF (get_prodToken ct) $ zipWith5SADF (inpOut5n ct) as bs cs ds es


------------------------------------------------------------------------
--
-- SADF DETECTORS
--
------------------------------------------------------------------------

-- > Detectors with one output

-- | The process constructor 'detector11SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with
-- a single data input and a single control output signals.
detector11SADF :: Int                 -- ^ consumption rates (@c@)
               -> (s -> [a] -> s)     -- ^ next state function (@f@)
               -> (s -> (Int, [y]))   -- ^ scenario selection (@g@)
               -> s                   -- ^ initial state (@s0@)
               -> Signal a            -- ^ Input
               -> Signal y            -- ^ Output
detector11SADF c f g s0 as = outputFSM g next_state
  where next_state = nextStateFSM c f current_state as
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector21SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with two data input and a
-- single control output signals.
detector21SADF :: (Int, Int)
               -> (s -> [a] -> [b] -> s)
               -> (s -> (Int, [y]))
               -> s
               -> Signal a -> Signal b
               -> Signal y
detector21SADF c f g s0 as bs = outputFSM g next_state
  where next_state = nextStateFSM2 c f current_state as bs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector31SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with three data input and a
-- single control output signals.
detector31SADF :: (Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> s)
               -> (s -> (Int, [y]))
               -> s
               -> Signal a -> Signal b -> Signal c
               -> Signal y
detector31SADF c f g s0 as bs cs = outputFSM g next_state
  where next_state = nextStateFSM3 c f current_state as bs cs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector41SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with four data input and a
-- single control output signals.
detector41SADF :: (Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> s)
               -> (s -> (Int, [y]))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d
               -> Signal y
detector41SADF c f g s0 as bs cs ds = outputFSM g next_state
  where next_state = nextStateFSM4 c f current_state as bs cs ds
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector51SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with five data input and a
-- single control output signals.
detector51SADF :: (Int, Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
               -> (s -> (Int, [y]))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
               -> Signal y
detector51SADF c f g s0 as bs cs ds es = outputFSM g next_state
  where next_state = nextStateFSM5 c f current_state as bs cs ds es
        current_state = delaySADF [s0] next_state


-- > Detectors with two output

-- | The process constructor 'detector12SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with a single data input and two
-- control output signals.
detector12SADF :: Int
               -> (s -> [a] -> s)
               -> (s -> ((Int, Int), ([y1], [y2])))
               -> s
               -> Signal a
               -> (Signal y1, Signal y2)
detector12SADF c f g s0 as = outputFSM2 g next_state
  where next_state = nextStateFSM c f current_state as
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector22SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with two data input and two
-- control output signals.
detector22SADF :: (Int, Int)
               -> (s -> [a] -> [b] -> s)
               -> (s -> ((Int, Int), ([y1], [y2])))
               -> s
               -> Signal a -> Signal b
               -> (Signal y1, Signal y2)
detector22SADF c f g s0 as bs = outputFSM2 g next_state
  where next_state = nextStateFSM2 c f current_state as bs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector32SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with three data input and two
-- control output signals.
detector32SADF :: (Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> s)
               -> (s -> ((Int, Int), ([y1], [y2])))
               -> s
               -> Signal a -> Signal b -> Signal c
               -> (Signal y1, Signal y2)
detector32SADF c f g s0 as bs cs = outputFSM2 g next_state
  where next_state = nextStateFSM3 c f current_state as bs cs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector42SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with four data input and two
-- control output signals.
detector42SADF :: (Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> s)
               -> (s -> ((Int, Int), ([y1], [y2])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d
               -> (Signal y1, Signal y2)
detector42SADF c f g s0 as bs cs ds = outputFSM2 g next_state
  where next_state = nextStateFSM4 c f current_state as bs cs ds
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector52SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with five data input and two
-- control output signals.
detector52SADF :: (Int, Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
               -> (s -> ((Int, Int), ([y1], [y2])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
               -> (Signal y1, Signal y2)
detector52SADF c f g s0 as bs cs ds es = outputFSM2 g next_state
  where next_state = nextStateFSM5 c f current_state as bs cs ds es
        current_state = delaySADF [s0] next_state


-- > Detectors with three output

-- | The process constructor 'detector13SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with a single data input and three
-- control output signals.
detector13SADF :: Int
               -> (s -> [a] -> s)
               -> (s -> ((Int, Int, Int), ([y1], [y2], [y3])))
               -> s
               -> Signal a
               -> (Signal y1, Signal y2, Signal y3)
detector13SADF c f g s0 as = outputFSM3 g next_state
  where next_state = nextStateFSM c f current_state as
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector23SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with two data input and three
-- control output signals.
detector23SADF :: (Int, Int)
               -> (s -> [a] -> [b] -> s)
               -> (s -> ((Int, Int, Int), ([y1], [y2], [y3])))
               -> s
               -> Signal a -> Signal b
               -> (Signal y1, Signal y2, Signal y3)
detector23SADF c f g s0 as bs = outputFSM3 g next_state
  where next_state = nextStateFSM2 c f current_state as bs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector33SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with three data input and three
-- control output signals.
detector33SADF :: (Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> s)
               -> (s -> ((Int, Int, Int), ([y1], [y2], [y3])))
               -> s
               -> Signal a -> Signal b -> Signal c
               -> (Signal y1, Signal y2, Signal y3)
detector33SADF c f g s0 as bs cs = outputFSM3 g next_state
  where next_state = nextStateFSM3 c f current_state as bs cs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector43SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with four data input and three
-- control output signals.
detector43SADF :: (Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> s)
               -> (s -> ((Int, Int, Int), ([y1], [y2], [y3])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d
               -> (Signal y1, Signal y2, Signal y3)
detector43SADF c f g s0 as bs cs ds = outputFSM3 g next_state
  where next_state = nextStateFSM4 c f current_state as bs cs ds
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector53SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with five data input and three
-- control output signals.
detector53SADF :: (Int, Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
               -> (s -> ((Int, Int, Int), ([y1], [y2], [y3])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
               -> (Signal y1, Signal y2, Signal y3)
detector53SADF c f g s0 as bs cs ds es = outputFSM3 g next_state
  where next_state = nextStateFSM5 c f current_state as bs cs ds es
        current_state = delaySADF [s0] next_state


-- > Detectors with four output

-- | The process constructor 'detector14SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with a single data input and four
-- control output signals.
detector14SADF :: Int
               -> (s -> [a] -> s)
               -> (s -> ((Int, Int, Int, Int), ([y1], [y2], [y3], [y4])))
               -> s
               -> Signal a
               -> (Signal y1, Signal y2, Signal y3, Signal y4)
detector14SADF c f g s0 as = outputFSM4 g next_state
  where next_state = nextStateFSM c f current_state as
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector24SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with two data input and four
-- control output signals.
detector24SADF :: (Int, Int)
               -> (s -> [a] -> [b] -> s)
               -> (s -> ((Int, Int, Int, Int), ([y1], [y2], [y3], [y4])))
               -> s
               -> Signal a -> Signal b
               -> (Signal y1, Signal y2, Signal y3, Signal y4)
detector24SADF c f g s0 as bs = outputFSM4 g next_state
  where next_state = nextStateFSM2 c f current_state as bs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector34SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with three data input and four
-- control output signals.
detector34SADF :: (Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> s)
               -> (s -> ((Int, Int, Int, Int), ([y1], [y2], [y3], [y4])))
               -> s
               -> Signal a -> Signal b -> Signal c
               -> (Signal y1, Signal y2, Signal y3, Signal y4)
detector34SADF c f g s0 as bs cs = outputFSM4 g next_state
  where next_state = nextStateFSM3 c f current_state as bs cs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector44SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with four data input and four
-- control output signals.
detector44SADF :: (Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> s)
               -> (s -> ((Int, Int, Int, Int), ([y1], [y2], [y3], [y4])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d
               -> (Signal y1, Signal y2, Signal y3, Signal y4)
detector44SADF c f g s0 as bs cs ds = outputFSM4 g next_state
  where next_state = nextStateFSM4 c f current_state as bs cs ds
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector54SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with five data input and four
-- control output signals.
detector54SADF :: (Int, Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
               -> (s -> ((Int, Int, Int, Int), ([y1], [y2], [y3], [y4])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
               -> (Signal y1, Signal y2, Signal y3, Signal y4)
detector54SADF c f g s0 as bs cs ds es = outputFSM4 g next_state
  where next_state = nextStateFSM5 c f current_state as bs cs ds es
        current_state = delaySADF [s0] next_state


-- > Detectors with five output

-- | The process constructor 'detector15SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with a single data input and five
-- control output signals.
detector15SADF :: Int
               -> (s -> [a] -> s)
               -> (s -> ((Int, Int, Int, Int, Int), ([y1], [y2], [y3], [y4], [y5])))
               -> s
               -> Signal a
               -> (Signal y1, Signal y2, Signal y3, Signal y4, Signal y5)
detector15SADF c f g s0 as = outputFSM5 g next_state
  where next_state = nextStateFSM c f current_state as
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector25SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with two data input and five
-- control output signals.
detector25SADF :: (Int, Int)
               -> (s -> [a] -> [b] -> s)
               -> (s -> ((Int, Int, Int, Int, Int), ([y1], [y2], [y3], [y4], [y5])))
               -> s
               -> Signal a -> Signal b
               -> (Signal y1, Signal y2, Signal y3, Signal y4, Signal y5)
detector25SADF c f g s0 as bs = outputFSM5 g next_state
  where next_state = nextStateFSM2 c f current_state as bs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector35SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with three data input and five
-- control output signals.
detector35SADF :: (Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> s)
               -> (s -> ((Int, Int, Int, Int, Int), ([y1], [y2], [y3], [y4], [y5])))
               -> s
               -> Signal a -> Signal b -> Signal c
               -> (Signal y1, Signal y2, Signal y3, Signal y4, Signal y5)
detector35SADF c f g s0 as bs cs = outputFSM5 g next_state
  where next_state = nextStateFSM3 c f current_state as bs cs
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector45SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with four data input and five
-- control output signals.
detector45SADF :: (Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> s)
               -> (s -> ((Int, Int, Int, Int, Int), ([y1], [y2], [y3], [y4], [y5])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d
               -> (Signal y1, Signal y2, Signal y3, Signal y4, Signal y5)
detector45SADF c f g s0 as bs cs ds = outputFSM5 g next_state
  where next_state = nextStateFSM4 c f current_state as bs cs ds
        current_state = delaySADF [s0] next_state

-- | The process constructor 'detector55SADF' takes the consumption token rate
-- (@c@), the state transition function (@f@), the scenario selection (@g@) and
-- the initial state (@s0@), and constructs an SADF detector with five data input and five
-- control output signals.
detector55SADF :: (Int, Int, Int, Int, Int)
               -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
               -> (s -> ((Int, Int, Int, Int, Int), ([y1], [y2], [y3], [y4], [y5])))
               -> s
               -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
               -> (Signal y1, Signal y2, Signal y3, Signal y4, Signal y5)
detector55SADF c f g s0 as bs cs ds es = outputFSM5 g next_state
  where next_state = nextStateFSM5 c f current_state as bs cs ds es
        current_state = delaySADF [s0] next_state


------------------------------------------------------------------------
-- COMBINATIONAL PROCESS CONSTRUCTORS (not exported)
------------------------------------------------------------------------

-- | The process constructor 'mapSADF' takes a signal of scenarios
-- (tuples with the consumed and produced tokens as well as a function operating
-- on lists), and results in an SADF-process that takes an input signal and results
-- in an output signal
mapSADF :: Signal (Int, Int, [a] -> [b]) -> Signal a -> Signal b
mapSADF NullS _ = NullS
mapSADF ct xs
  | c < 0 = error "mapSADF: Number of consumed tokens must be a non-negative integer"
  | not $ sufficient_tokens c xs  = NullS
  | otherwise = if length produced_tokens == p then
                  signal produced_tokens +-+ mapSADF (tailS ct) (dropS c xs)
                else
                  error "mapSADF: Function does not produce correct number of tokens"
  where (c, p, f) = headS ct
        consumed_tokens = fromSignal $ takeS c xs
        produced_tokens = f consumed_tokens


-- | The process constructor 'zipWithSADF' takes a signal of scenarios
-- (tuples with the consumed and produced tokens as well as a function operating
-- on lists), and results in an SADF-process that takes two input signals and
-- results in an output signal
zipWithSADF :: Signal ((Int, Int), Int, [a] -> [b] -> [c])
            -> Signal a -> Signal b -> Signal c
zipWithSADF NullS _ _ = NullS
zipWithSADF ct as bs
  | c1 < 0 || c2 < 0  = error "zipWithSADF: Number of consumed tokens must be a non-negative integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs) = NullS
  | otherwise = if length produced_tokens == p then
                  signal produced_tokens +-+ zipWithSADF (tailS ct) (dropS c1 as) (dropS c2 bs)
                else
                  error "zipWithSADF: Function does not produce correct number of tokens"
  where ((c1,c2), p, f) = headS ct
        consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        produced_tokens = f consumed_tokens_as consumed_tokens_bs


-- | The process constructor 'zipWith3SADF' takes a signal of scenarios
-- (tuples with the consumed and produced tokens as well as a function operating
-- on lists), and results in an SADF-process that takes three input signals and
-- results in an output signal
zipWith3SADF :: Signal ((Int, Int, Int), Int, [a] -> [b] -> [c] -> [d])
             -> Signal a -> Signal b -> Signal c -> Signal d
zipWith3SADF NullS _ _ _ = NullS
zipWith3SADF ct as bs cs
  | c1 < 0 || c2 < 0 || c3 < 0
    = error "zipWith3SADF: Number of consumed tokens must be a non-negative integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs) = NullS
  | otherwise = if length produced_tokens == p then
                  signal produced_tokens +-+ zipWith3SADF (tailS ct) (dropS c1 as)
                                                        (dropS c2 bs) (dropS c3 cs)
                else
                  error "zipWith3SADF: Function does not produce correct number of tokens"
  where ((c1, c2, c3), p, f) = headS ct
        consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        produced_tokens = f consumed_tokens_as consumed_tokens_bs consumed_tokens_cs


-- | The process constructor 'zipWith4SADF' takes a signal of scenarios
-- (tuples with the consumed and produced tokens as well as a function operating
-- on lists), and results in an SADF-process that takes four input signals and
-- results in an output signal
zipWith4SADF :: Signal ((Int, Int, Int, Int), Int, [a] -> [b] -> [c] -> [d] -> [e])
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e
zipWith4SADF NullS _ _ _ _ = NullS
zipWith4SADF ct as bs cs ds
  | c1 < 0 || c2 < 0 || c3 < 0 || c4 < 0
    = error "zipWith4SADF: Number of consumed tokens must be a non-negative integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs)
    || (not $ sufficient_tokens c4 ds) = NullS
  | otherwise = if length produced_tokens == p then
                  signal produced_tokens +-+ zipWith4SADF (tailS ct) (dropS c1 as)
                                              (dropS c2 bs) (dropS c3 cs) (dropS c4 ds)
                else
                  error "zipWith4SADF: Function does not produce correct number of tokens"
  where ((c1, c2, c3, c4), p, f) = headS ct
        consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        consumed_tokens_ds = fromSignal $ takeS c4 ds
        produced_tokens = f consumed_tokens_as consumed_tokens_bs
                            consumed_tokens_cs consumed_tokens_ds


-- | The process constructor 'zipWith5SADF' takes a signal of scenarios
-- (tuples with the consumed and produced tokens as well as a function operating
-- on lists), and results in an SADF-process that takes five input signals and
-- results in an output signal
zipWith5SADF :: Signal ((Int, Int, Int, Int, Int), Int, [a] -> [b] -> [c] -> [d] -> [e] -> [f])
             -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e -> Signal f
zipWith5SADF NullS _ _ _ _ _ = NullS
zipWith5SADF ct as bs cs ds es
  | c1 < 0 || c2 < 0 || c3 < 0 || c4 < 0 || c5 < 0
    = error "zipWith5SADF: Number of consumed tokens must be a non-negative integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs)
    || (not $ sufficient_tokens c4 ds)
    || (not $ sufficient_tokens c5 es) = NullS
  | otherwise = if length produced_tokens == p then
                  signal produced_tokens +-+ zipWith5SADF (tailS ct) (dropS c1 as)
                                              (dropS c2 bs) (dropS c3 cs) (dropS c4 ds) (dropS c5 es)
                else
                  error "zipWith5SADF: Function does not produce correct number of tokens"
  where ((c1, c2, c3, c4, c5), p, f) = headS ct
        consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        consumed_tokens_ds = fromSignal $ takeS c4 ds
        consumed_tokens_es = fromSignal $ takeS c5 es
        produced_tokens = f consumed_tokens_as consumed_tokens_bs
                            consumed_tokens_cs consumed_tokens_ds consumed_tokens_es


------------------------------------------------------------------------
-- unzipSADF Processes (not exported)
------------------------------------------------------------------------

unzipSADF :: [(Int, Int)] -> Signal ([a], [b]) -> (Signal a, Signal b)
unzipSADF [] _ = (NullS, NullS)
unzipSADF _ NullS = (NullS, NullS)
unzipSADF ((p1, p2) : ps) ((s1, s2) :- ss)
  | length s1 /= p1 || length s2 /= p2 = error "unzipSADF: Process does not produce correct number of tokens"
  | otherwise = (signal s1 +-+ sr1, signal s2 +-+ sr2)
  where (sr1, sr2) = unzipSADF ps ss


unzip3SADF :: [(Int, Int, Int)] -> Signal ([a], [b], [c])
           -> (Signal a, Signal b, Signal c)
unzip3SADF [] _ = (NullS, NullS, NullS)
unzip3SADF _ NullS = (NullS, NullS, NullS)
unzip3SADF ((p1, p2, p3) : ps) ((s1, s2, s3) :- ss)
  | length s1 /= p1 || length s2 /= p2
    || length s3 /= p3 = error "unzip3SADF: Process does not produce correct number of tokens"
  | otherwise = (signal s1 +-+ sr1, signal s2 +-+ sr2, signal s3 +-+ sr3)
  where (sr1, sr2, sr3) = unzip3SADF ps ss


unzip4SADF :: [(Int, Int, Int, Int)] -> Signal ([a], [b], [c], [d])
           -> (Signal a, Signal b, Signal c, Signal d)
unzip4SADF [] _ = (NullS, NullS, NullS, NullS)
unzip4SADF _ NullS = (NullS, NullS, NullS, NullS)
unzip4SADF ((p1, p2, p3, p4) : ps) ((s1, s2, s3, s4) :- ss)
  | length s1 /= p1 || length s2 /= p2
    || length s3 /= p3 || length s4 /= p4 = error "unzip4SADF: Process does not produce correct number of tokens"
  | otherwise = (signal s1 +-+ sr1, signal s2 +-+ sr2, signal s3 +-+ sr3, signal s4 +-+ sr4)
  where (sr1, sr2, sr3, sr4) = unzip4SADF ps ss


unzip5SADF :: [(Int, Int, Int, Int, Int)] -> Signal ([a], [b], [c], [d], [e])
           -> (Signal a, Signal b, Signal c, Signal d, Signal e)
unzip5SADF [] _ = (NullS, NullS, NullS, NullS, NullS)
unzip5SADF _ NullS = (NullS, NullS, NullS, NullS, NullS)
unzip5SADF ((p1, p2, p3, p4, p5) : ps) ((s1, s2, s3, s4, s5) :- ss)
  | length s1 /= p1 || length s2 /= p2
    || length s3 /= p3 || length s4 /= p4
    || length s5 /= p5 = error "unzip5SADF: Process does not produce correct number of tokens"
  | otherwise = (signal s1 +-+ sr1, signal s2 +-+ sr2, signal s3 +-+ sr3,
                 signal s4 +-+ sr4, signal s5 +-+ sr5)
  where (sr1, sr2, sr3, sr4, sr5) = unzip5SADF ps ss

------------------------------------------------------------------------
--
-- Helper functions (not exported!)
--
------------------------------------------------------------------------

sufficient_tokens :: (Num a, Eq a, Ord a) => a -> Signal t -> Bool
sufficient_tokens 0 _     = True
sufficient_tokens _ NullS = False
sufficient_tokens n (_:-xs)
 = if n < 0 then
     error "sufficient_tokens: n must not be negative"
   else
     sufficient_tokens (n-1) xs


get_prodToken :: Signal (a,b,c) -> [b]
get_prodToken NullS = []
get_prodToken ((_, x, _):-xs) = x : get_prodToken xs


inpOut1n :: Signal (it, ot, [a] -> y) -> Signal (it, Int, [a] -> [y])
inpOut1n NullS = NullS
inpOut1n ((it, _, f):-xs) = (it, 1, \a -> [f a]) :- inpOut1n xs

inpOut2n :: Signal (it, ot, [a] -> [b] -> y) -> Signal (it, Int, [a] -> [b] -> [y])
inpOut2n NullS = NullS
inpOut2n ((it, _, f):-xs) = (it, 1, \a b -> [f a b]) :- inpOut2n xs

inpOut3n :: Signal (it, ot, [a] -> [b] -> [c] -> y)
         -> Signal (it, Int, [a] -> [b] -> [c] -> [y])
inpOut3n NullS = NullS
inpOut3n ((it, _, f):-xs) = (it, 1, \a b c -> [f a b c]) :- inpOut3n xs

inpOut4n :: Signal (it, ot, [a] -> [b] -> [c] -> [d] -> y)
         -> Signal (it, Int, [a] -> [b] -> [c] -> [d] -> [y])
inpOut4n NullS = NullS
inpOut4n ((it, _, f):-xs) = (it, 1, \a b c d -> [f a b c d]) :- inpOut4n xs

inpOut5n :: Signal (it, ot, [a] -> [b] -> [c] -> [d] -> [e] -> y)
         -> Signal (it, Int, [a] -> [b] -> [c] -> [d] -> [e] -> [y])
inpOut5n NullS = NullS
inpOut5n ((it, _, f):-xs) = (it, 1, \a b c d e -> [f a b c d e]) :- inpOut5n xs

---------------------------------------------------------
-- Helper functios to the detector's FSM  (not exported)
---------------------------------------------------------

nextStateFSM :: Int -> (s -> [a] -> s)
             -> Signal s -> Signal a -> Signal s
nextStateFSM _ _ NullS _ = NullS
nextStateFSM _ _ _ NullS = NullS
nextStateFSM c f ss as
  | c <= 0 = error "nextStateFSM: Number of consumed tokens must be positive integer"
  | not $ sufficient_tokens c as = NullS
  | otherwise = signal [next_state] +-+ nextStateFSM c f (tailS ss) (dropS c as)
  where consumed_tokens_as = fromSignal $ takeS c as
        current_state = headS ss
        next_state = f current_state consumed_tokens_as


nextStateFSM2 :: (Int, Int) -> (s -> [a] -> [b] -> s)
              -> Signal s -> Signal a -> Signal b -> Signal s
nextStateFSM2 _ _ NullS _ _ = NullS
nextStateFSM2 _ _ _ NullS _ = NullS
nextStateFSM2 _ _ _ _ NullS = NullS
nextStateFSM2 (c1, c2) f ss as bs
  | c1 <= 0 || c2 <= 0 = error "nextStateFSM2: Number of consumed tokens must be positive integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs) = NullS
  | otherwise = signal [next_state] +-+ nextStateFSM2 (c1, c2) f (tailS ss) (dropS c1 as) (dropS c2 bs)
  where consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        current_state = headS ss
        next_state = f current_state consumed_tokens_as consumed_tokens_bs


nextStateFSM3 :: (Int, Int, Int) -> (s -> [a] -> [b] -> [c] -> s)
              -> Signal s -> Signal a -> Signal b -> Signal c -> Signal s
nextStateFSM3 _ _ NullS _ _ _ = NullS
nextStateFSM3 _ _ _ NullS _ _ = NullS
nextStateFSM3 _ _ _ _ NullS _ = NullS
nextStateFSM3 _ _ _ _ _ NullS = NullS
nextStateFSM3 (c1, c2, c3) f ss as bs cs
  | c1 <= 0 || c2 <= 0 || c3 <= 0
    = error "nextStateFSM3: Number of consumed tokens must be positive integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs) = NullS
  | otherwise = signal [next_state] +-+ nextStateFSM3 (c1, c2, c3) f (tailS ss)
                                        (dropS c1 as) (dropS c2 bs) (dropS c3 cs)
  where consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        current_state = headS ss
        next_state = f current_state consumed_tokens_as
                       consumed_tokens_bs consumed_tokens_cs


nextStateFSM4 :: (Int, Int, Int, Int) -> (s -> [a] -> [b] -> [c] -> [d] -> s)
              -> Signal s -> Signal a -> Signal b -> Signal c -> Signal d -> Signal s
nextStateFSM4 _ _ NullS _ _ _ _ = NullS
nextStateFSM4 _ _ _ NullS _ _ _ = NullS
nextStateFSM4 _ _ _ _ NullS _ _ = NullS
nextStateFSM4 _ _ _ _ _ NullS _ = NullS
nextStateFSM4 _ _ _ _ _ _ NullS = NullS
nextStateFSM4 (c1, c2, c3, c4) f ss as bs cs ds
  | c1 <= 0 || c2 <= 0 || c3 <= 0 || c4 <= 0
    = error "nextStateFSM4: Number of consumed tokens must be positive integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs)
    || (not $ sufficient_tokens c4 ds) = NullS
  | otherwise = signal [next_state] +-+ nextStateFSM4 (c1, c2, c3, c4) f (tailS ss)
                                        (dropS c1 as) (dropS c2 bs) (dropS c3 cs) (dropS c4 ds)
  where consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        consumed_tokens_ds = fromSignal $ takeS c4 ds
        current_state = headS ss
        next_state = f current_state consumed_tokens_as
                       consumed_tokens_bs consumed_tokens_cs consumed_tokens_ds


nextStateFSM5 :: (Int, Int, Int, Int, Int) -> (s -> [a] -> [b] -> [c] -> [d] -> [e] -> s)
              -> Signal s -> Signal a -> Signal b -> Signal c -> Signal d -> Signal e -> Signal s
nextStateFSM5 _ _ NullS _ _ _ _ _ = NullS
nextStateFSM5 _ _ _ NullS _ _ _ _ = NullS
nextStateFSM5 _ _ _ _ NullS _ _ _ = NullS
nextStateFSM5 _ _ _ _ _ NullS _ _ = NullS
nextStateFSM5 _ _ _ _ _ _ NullS _ = NullS
nextStateFSM5 _ _ _ _ _ _ _ NullS = NullS
nextStateFSM5 (c1, c2, c3, c4, c5) f ss as bs cs ds es
  | c1 <= 0 || c2 <= 0 || c3 <= 0 || c4 <= 0 || c5 <= 0
    = error "nextStateFSM4: Number of consumed tokens must be positive integer"
  | (not $ sufficient_tokens c1 as)
    || (not $ sufficient_tokens c2 bs)
    || (not $ sufficient_tokens c3 cs)
    || (not $ sufficient_tokens c4 ds)
    || (not $ sufficient_tokens c5 es) = NullS
  | otherwise = signal [next_state] +-+ nextStateFSM5 (c1, c2, c3, c4, c5) f (tailS ss)
                                        (dropS c1 as) (dropS c2 bs) (dropS c3 cs)
                                        (dropS c4 ds) (dropS c5 es)
  where consumed_tokens_as = fromSignal $ takeS c1 as
        consumed_tokens_bs = fromSignal $ takeS c2 bs
        consumed_tokens_cs = fromSignal $ takeS c3 cs
        consumed_tokens_ds = fromSignal $ takeS c4 ds
        consumed_tokens_es = fromSignal $ takeS c5 es
        current_state = headS ss
        next_state = f current_state consumed_tokens_as
                       consumed_tokens_bs consumed_tokens_cs
                       consumed_tokens_ds consumed_tokens_es


outputFSM :: (s -> (Int, [a])) -> Signal s -> Signal a
outputFSM _ NullS = NullS
outputFSM g (s:-ss)
  | length y1 /= p = error "outputFSM: Incorrect number of produced tokens."
  | otherwise = signal y1 +-+ outputFSM g ss
  where (p, y1) = g s


outputFSM2 :: (s -> ((Int, Int), ([a], [b]))) -> Signal s -> (Signal a, Signal b)
outputFSM2 _ NullS = (NullS, NullS)
outputFSM2 g (s:-ss)
  | length y1 /= p1 || length y2 /= p2 = error "outputFSM2: Incorrect number of produced tokens."
  | otherwise = (signal y1 +-+ yr1, signal y2 +-+ yr2)
  where ((p1, p2), (y1, y2)) = g s
        (yr1, yr2) = outputFSM2 g ss


outputFSM3 :: (s -> ((Int, Int, Int), ([a], [b], [c])))
           -> Signal s -> (Signal a, Signal b, Signal c)
outputFSM3 _ NullS = (NullS, NullS, NullS)
outputFSM3 g (s:-ss)
  | length y1 /= p1
    || length y2 /= p2
    || length y3 /= p3 = error "outputFSM3: Incorrect number of produced tokens."
  | otherwise = (signal y1 +-+ yr1, signal y2 +-+ yr2, signal y3 +-+ yr3)
  where ((p1, p2, p3), (y1, y2, y3)) = g s
        (yr1, yr2, yr3) = outputFSM3 g ss


outputFSM4 :: (s -> ((Int, Int, Int, Int), ([a], [b], [c], [d])))
           -> Signal s -> (Signal a, Signal b, Signal c, Signal d)
outputFSM4 _ NullS = (NullS, NullS, NullS, NullS)
outputFSM4 g (s:-ss)
  | length y1 /= p1
    || length y2 /= p2
    || length y3 /= p3
    || length y4 /= p4 = error "outputFSM4: Incorrect number of produced tokens."
  | otherwise = (signal y1 +-+ yr1, signal y2 +-+ yr2, signal y3 +-+ yr3, signal y4 +-+ yr4)
  where ((p1, p2, p3, p4), (y1, y2, y3, y4)) = g s
        (yr1, yr2, yr3, yr4) = outputFSM4 g ss


outputFSM5 :: (s -> ((Int, Int, Int, Int, Int), ([a], [b], [c], [d], [e])))
           -> Signal s -> (Signal a, Signal b, Signal c, Signal d, Signal e)
outputFSM5 _ NullS = (NullS, NullS, NullS, NullS, NullS)
outputFSM5 g (s:-ss)
  | length y1 /= p1
    || length y2 /= p2 || length y3 /= p3
    || length y4 /= p4 || length y5 /= p5 = error "outputFSM5: Incorrect number of produced tokens."
  | otherwise = (signal y1 +-+ yr1, signal y2 +-+ yr2, signal y3 +-+ yr3,
                 signal y4 +-+ yr4, signal y5 +-+ yr5)
  where ((p1, p2, p3, p4, p5),(y1, y2, y3, y4, y5)) = g s
        (yr1, yr2, yr3, yr4, yr5) = outputFSM5 g ss


------------------------------------------------------------------------
--
-- Test of Library (not exported)
--
------------------------------------------------------------------------

{-

---------------------------------------------------------
-- test1: kernel22SADF test
---------------------------------------------------------

test1 :: Signal ((Int, Int), (Int, Int), [a] -> [b] -> ([c], [d]))
      -> Signal a -> Signal b -> (Signal c, Signal d)
test1 = kernel22SADF

ct = signal [((1,1), (1,1), \[a] [b] -> ([2*a], [2*b])),
             ((2,2), (1,1), \[a,b] [c,d] -> ([a+b], [c+d])),
             ((1,2), (2,1), \[a] [b,c] -> ([b,c], [a]))]

x = signal [1..20]
y = signal [21 .. 40]

test1out = test1 ct x y

---------------------------------------------------------
-- test2: Anti Wind-up system
---------------------------------------------------------

-- State transition function for the detector
f :: (Num a, Ord a) => Int -> [a] -> [a] -> Int
f 1 [y] [v] = if (y > 100 && v > 0 || y < (-100) && v < 0) then 2 else 1
f 2 [y] [v] = if (y > 100 && v > 0 || y < (-100) && v < 0) then 2 else 1

-- Output function for the detector
g :: Num a => Int -> ((Int, Int), ([((Int, Int), Int, [a] -> [a] -> [a])], [(Int, Int, [a] -> [a])]))
g 1 = ((1,1), ([((1,1), 1, \[a] [b] -> [a+b])], [(1, 1, \[a] -> [a])]))
g 2 = ((1,1), ([((0,1), 1, \_ [b] -> [b])], [(1, 0, \[a] -> [])]))

-- Detector
detector :: (Num a, Ord a) => Signal a -> Signal a
         -> (Signal ((Int, Int), Int, [a] -> [a] -> [a]), Signal (Int, Int, [a] -> [a]))
detector = detector22SADF (1,1) f g 1

syst :: (Num a, Ord a) => Signal a -> Signal a
syst input = output
  where output = integrator c1 s1 s3
        s3 = delaySADF [0] output
        s1 = kernel11SADF c2 input
        (c1, c2) = detector s3 input
        integrator = kernel21SADF


---------------------------------------------------------
-- test3: Register Bank (3 registers)
---------------------------------------------------------

-- Scenarios list
scenarios :: Int -> ((Int, Int, Int, Int), (Int, Int, Int, Int),
             [a] -> [a] -> [a] -> [a] -> ([a], [a], [a], [a]))
scenarios 0 = ((0,0,0,0), (0,0,0,0), \_ _ _ _ -> ([], [], [], []))
scenarios 1 = ((0,1,0,0), (1,1,0,0), \_ [r1] _ _ -> ([r1], [r1], [], []))
scenarios 2 = ((0,0,1,0), (1,0,1,0), \_ _ [r2] _ -> ([r2], [], [r2], []))
scenarios 3 = ((0,0,0,1), (1,0,0,1), \_ _ _ [r3] -> ([r3], [], [], [r3]))
scenarios 4 = ((1,1,0,0), (0,1,0,0), \[r1] _ _ _ -> ([], [r1], [], []))
scenarios 5 = ((1,0,1,0), (0,0,1,0), \[r2] _ _ _ -> ([], [], [r2], []))
scenarios 6 = ((1,0,0,1), (0,0,0,1), \[r3] _ _ _ -> ([], [], [], [r3]))
scenarios _ = error "scenarios: outside the state list"

switchState :: Int -> [String] -> Int
switchState _ ["sc0"] = 0     -- No operation (kernel inactive)
switchState _ ["lr1"] = 1     -- Load r1
switchState _ ["lr2"] = 2     -- Load r2
switchState _ ["lr3"] = 3     -- Load r3
switchState _ ["sr1"] = 4     -- Store r1
switchState _ ["sr2"] = 5     -- Store r2
switchState _ ["sr3"] = 6     -- Store r3
switchState _ _ = error "switchState: Input not recognized"

regDetector = detector11SADF 1 switchState (\e -> (1, [scenarios e])) 0
regKernel = kernel44SADF

registerBank inputControl inputData = output
  where ct = regDetector inputControl
        (output, r1, r2, r3) = regKernel ct inputData r1' r2' r3'
        r1' = delaySADF [0] r1
        r2' = delaySADF [0] r2
        r3' = delaySADF [0] r3

cInput = signal ["lr1","lr2","lr3","sr1","sr2","sr3","sc0","lr1","lr2","lr3","lr1","lr2","lr3"]
dInput = signal [1..10]

regOutput = registerBank cInput dInput
-- Expected output {0,0,0,1,2,3,1,2,3}

-}