module Funcons.Operations.Graphs where
import Funcons.Operations.Internal
import Funcons.Operations.Booleans (tobool)
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List ((\\))
library :: (HasValues t, Ord t) => Library t
library = libFromList [
("is-cyclic", UnaryExpr is_cyclic)
, ("topological-sort", UnaryExpr topological_sort)
]
is_cyclic_ :: (Ord t, HasValues t) => [OpExpr t] -> OpExpr t
is_cyclic_ = unaryOp is_cyclic
is_cyclic :: (Ord t, HasValues t) => OpExpr t -> OpExpr t
is_cyclic = vUnaryOp "is-cyclic" op
where op mm | Just m <- toGraph mm = Normal $ inject $ tobool (cyclic m)
op _ = SortErr "is-cyclic not applied to a graph"
topological_sort_ :: (Ord t, HasValues t) => [OpExpr t] -> OpExpr t
topological_sort_ = unaryOp topological_sort
topological_sort :: (Ord t, HasValues t) => OpExpr t -> OpExpr t
topological_sort = vUnaryOp "topological-sort" op
where op mm | Just m <- toGraph mm = Normal $ inject $ multi $ map inject $ fst (schedule m)
op _ = SortErr "topological-sort not applied to a graph"
toGraph :: (Ord t) => Values t -> Maybe (Graph (Values t))
toGraph (Map m) = M.foldrWithKey combine (Just M.empty) m
where combine k [Set s] mm = fmap (M.insert k s) mm
combine _ _ _ = Nothing
toGraph _ = Nothing
type Graph e = M.Map e (S.Set e)
entries :: Eq e => Graph e -> [e]
entries m = M.keys m \\ withIncoming
where withIncoming = concatMap S.toList (M.elems m)
delete :: Ord e => e -> Graph e -> Graph e
delete n m = M.map (S.delete n) $ M.delete n m
schedule :: (Ord e) => Graph e -> ([e], Graph e)
schedule gr = schedule' gr (entries gr) []
where schedule' gr [] uset = (uset, gr)
schedule' gr (e:es) uset = schedule' gr' (entries gr') uset'
where uset' = uset ++ [e]
gr' = delete e gr
cyclic :: (Ord e) => Graph e -> Bool
cyclic gr = not (is_empty (snd (schedule gr)))
is_empty gr = M.null gr