harmony-0.1.0.0: src/Data/DiGraph.hs
module Data.DiGraph( Graph ()
, empty
, create
, addNode
, addAllNodes
, addNeighbors
, addAllNeighbors
, contains
, deleteNode
, getNeighbors
, getNodes
, isEmpty
, hasCycle
, generateInverseDependencyList) where
import qualified Data.Map as M
import qualified Data.Set as S
data (Ord a) => Graph a = Graph { nodes :: S.Set a
, next :: M.Map a (S.Set a)
} deriving (Eq, Show)
empty :: Ord a => Graph a
empty = Graph { nodes = S.empty
, next = M.empty
}
create :: Ord a => [(a, [a])] -> Graph a
create pairs = addAllNeighbors pairs $ addAllNodes (map fst pairs) empty
addNode :: Ord a => a -> Graph a -> Graph a
addNode v graph = graph { nodes = v `S.insert` nodes graph }
deleteNode :: Ord a => a -> Graph a -> Graph a
deleteNode v graph = graph { nodes = v `S.delete` nodes graph
, next = deleteNodeFromNext v $ next graph}
where
deleteNodeFromNext v m = rmFromAllSets v $ M.delete v m
rmFromAllSets v = M.map (S.delete v)
addAllNodes :: Ord a => [a] -> Graph a -> Graph a
addAllNodes nodes graph = foldl (flip addNode) graph nodes
addNeighbors :: Ord a => a -> [a] -> Graph a -> Graph a
addNeighbors v neighboors graph | graph `contains` v = graph { nodes = nodes $ addAllNodes neighboors graph, next = M.insert v (S.fromList neighboors) $ next graph }
| otherwise = addNeighbors v neighboors $ addNode v graph
addAllNeighbors :: Ord a => [(a, [a])] -> Graph a -> Graph a
addAllNeighbors pairs graph = foldl (\g (v, next) -> addNeighbors v next g) graph pairs
contains :: Ord a => Graph a -> a -> Bool
contains graph v = v `S.member` nodes graph
getNeighbors :: Ord a => Graph a -> a -> Maybe [a]
getNeighbors graph v = do
ns <- M.lookup v $ next graph
let nList = S.toList ns
if null nList
then Nothing
else return nList
getNodes :: Ord a => Graph a -> [a]
getNodes = S.toList . nodes
isEmpty :: Ord a => Graph a -> Bool
isEmpty graph = S.null $ nodes graph
hasCycle :: Ord a => Graph a -> Bool
hasCycle graph | isEmpty graph = False
| otherwise = any (`go` S.empty) $ getNodes graph
where
go node visited | node `S.member` visited = True
| otherwise = case getNeighbors graph node of
Nothing -> False
Just next -> any (flip go $ node `S.insert` visited) next
dfs :: Ord a => Graph a -> Maybe a
dfs graph | isEmpty graph = Nothing
| otherwise = go (head $ getNodes graph) graph
where
go :: Ord a => a -> Graph a -> Maybe a
go node graph =
case getNeighbors graph node of
Nothing -> return node
Just [] -> error "getNeighbors returning \"Just []\" should never happen"
Just (node':_) -> go node' graph
generateInverseDependencyList :: Ord a => Graph a -> [a]
generateInverseDependencyList graph = go graph []
where
go graph accum =
case dfs graph of
Nothing -> reverse accum
Just n -> go (deleteNode n graph) (n:accum)