signals-0.0.0.1: Backend/Compiler/Cycles.hs
module Backend.Compiler.Cycles (
cycles
)
where
import Frontend.SignalObsv (TSignal(..), Node, edges)
import Control.Monad.State
import Data.Reify (Graph(..), Unique, reifyGraph)
import Data.Map (Map, (!))
import qualified Data.Map as M
import Prelude hiding (pred, cycle)
--------------------------------------------------------------------------------
-- * Cycles
--------------------------------------------------------------------------------
-- | A node can have three different states during cycle checking
-- * Visited, no cycles detected in node or children
-- * Visiting, node is being checked for cycles
-- * Unvisited, node has not yet been checked for cycles
data Status = Visited | Visiting | Unvisited deriving Eq
-- | A node's predecessor
type Predecessor = Unique
--------------------------------------------------------------------------------
-- | Updates the status for a node
mark :: Unique -> Status -> State (Map Unique (Status, p, n)) ()
mark u s = modify $ flip M.adjust u $ \(_, p, n) -> (s, p, n)
-- | Updates the predecessor for a node
pred :: Unique -> Predecessor -> State (Map Unique (s, Predecessor, n)) ()
pred u p = modify $ flip M.adjust u $ \(s, _, n) -> (s, p, n)
-- | Gets the status of a node
status :: Unique -> State (Map Unique (Status, p, n)) Status
status u = get >>= return . (\(s, _, _) -> s) . (! u)
-- | Gets the predecessor of a node
predecessor :: Unique -> State (Map Unique (s, Predecessor, n)) Predecessor
predecessor u = get >>= return . (\(_, p, _) -> p) . (! u)
-- | Gets the adjacent nodes of a node
adjacent :: Unique -> State (Map Unique (s, p, Node e)) [Unique]
adjacent u = get >>= return . (\(_, _, n) -> edges' n) . (! u)
where
-- simply ignore delay edges, this will make the algorithm fail only when
-- bad cycles are detected
edges' (TDelay {}) = []
edges' x = edges x
--------------------------------------------------------------------------------
-- | ...
cycle :: Unique -> State (Map Unique (Status, Predecessor, Node e)) Bool
cycle u =
do mark u Visiting
ns <- adjacent u
bs <- forM ns $ \n ->
do p <- predecessor n
s <- status n
case s of
Unvisited -> pred n u >> cycle n
Visiting | p /= u -> return False
_ -> return True
mark u Visited
return $ and bs
--------------------------------------------------------------------------------
-- | Checks if there are cycles in the given graph, returns true if there are
cycles :: Unique -> [(Unique, Node e)] -> Bool
cycles root nodes = go root init
where
init = M.fromList $ map (fmap ((,,) Unvisited 0)) nodes
go u s =
let (b, m) = runState (cycle u) s
n = M.filter (\(s, _, _) -> s == Unvisited) m
in case M.null n of
True -> not b
False -> go (fst $ M.findMin n) m