module Scc (sccDepend,Depend(..),isRec) where
import List
import qualified Data.Set as Set
import qualified Data.Map as Map
addSet :: Ord a => Set.Set a -> a -> Set.Set a
addSet as a = if a `Set.member` as then as else Set.insert a as
-- scc :: (Eq a) => (a->[a]) -> (a->[a]) -> [a] -> [Set a]
scc :: (Ord a) => (a->[a]) -> (a->[a]) -> [a] -> [Set.Set a]
scc ins outs = span . depth
where depth = snd . depthSearch outs (Set.empty,[])
span = snd . spanSearch ins (Set.empty,[])
--depthSearch :: (Eq a) => (a->[a]) -> (Set a,[a]) -> [a] -> (Set a,[a])
depthSearch :: (Ord a) => (a->[a]) -> (Set.Set a,[a]) -> [a] -> (Set.Set a,[a])
depthSearch = foldl . dsearch
where dsearch rel (visited,seq) v | v `Set.member` visited = (visited,seq)
| otherwise = (visited',v:seq')
where (visited',seq') = depthSearch rel
(addSet visited v,seq)
(rel v)
--spanSearch :: (Eq a) => (a->[a]) -> (Set a,[Set a]) -> [a] -> (Set a,[Set a])
spanSearch :: (Ord a) => (a->[a]) -> (Set.Set a,[Set.Set a]) -> [a] -> (Set.Set a,[Set.Set a])
spanSearch = foldl . search
where search rel (visited,setseq) v | v `Set.member` visited = (visited,setseq)
| otherwise = (visited',Set.fromList (v:seq):setseq)
where (visited',seq) = depthSearch rel
(addSet visited v,[])
(rel v)
sccAssoc :: Ord a => Map.Map a [a] -> a -> [a]
sccAssoc at d =
case Map.lookup d at of
Nothing -> [d]
Just ds -> ds
mkout :: Ord k => [(k, [k])] -> k -> [k]
mkout ds = sccAssoc (foldr ( \ (k,vs) at -> Map.insert k vs at ) Map.empty ds)
mkin :: Ord k => [(k, [k])] -> k -> [k]
mkin ds = sccAssoc (foldr ( \ (k,vs) at ->
let ks = [k]
in foldr ( \ v at -> Map.insertWith comb v ks at) at vs) Map.empty ds)
where comb [v] vs = v:vs
data Depend a = NoRec a
| Rec [a]
isRec :: Depend a -> Bool
isRec (NoRec _) = False
isRec (Rec _) = True
instance (Show a) => Show (Depend a) where
showsPrec d (NoRec x) = ("NoRec "++).showsPrec d x
showsPrec d (Rec xs) = ("Rec "++).showsPrec d xs
sccDepend :: (Ord a) => [(a, [a])] -> [Depend a]
sccDepend dep = fix' (map Set.toList (scc (mkin dep) out (map fst dep)))
where
out = mkout dep
fix' [] = []
fix' ([x]:r) = (if x `elem` out x then Rec [x] else NoRec x) : fix' r
fix' (xs:r) = Rec xs : fix' r
|