-- (c) 1999-2005 by Martin Erwig [see file COPYRIGHT] -- | Static and Dynamic Inductive Graphs module Data.Graph.Inductive.Graph ( -- * General Type Defintions -- ** Node and Edge Types Node,LNode,UNode, Edge,LEdge,UEdge, -- ** Types Supporting Inductive Graph View Adj,Context,MContext,Decomp,GDecomp,UContext,UDecomp, Path,LPath(..),UPath, -- * Graph Type Classes -- | We define two graph classes: -- -- Graph: static, decomposable graphs. -- Static means that a graph itself cannot be changed -- -- DynGraph: dynamic, extensible graphs. -- Dynamic graphs inherit all operations from static graphs -- but also offer operations to extend and change graphs. -- -- Each class contains in addition to its essential operations those -- derived operations that might be overwritten by a more efficient -- implementation in an instance definition. -- -- Note that labNodes is essentially needed because the default definition -- for matchAny is based on it: we need some node from the graph to define -- matchAny in terms of match. Alternatively, we could have made matchAny -- essential and have labNodes defined in terms of ufold and matchAny. -- However, in general, labNodes seems to be (at least) as easy to define -- as matchAny. We have chosen labNodes instead of the function nodes since -- nodes can be easily derived from labNodes, but not vice versa. Graph(..), DynGraph(..), -- * Operations -- ** Graph Folds and Maps ufold,gmap,nmap,emap, -- ** Graph Projection nodes,edges,newNodes,gelem, -- ** Graph Construction and Destruction insNode,insEdge,delNode,delEdge,delLEdge, insNodes,insEdges,delNodes,delEdges, buildGr,mkUGraph, -- ** Graph Inspection context,lab,neighbors, suc,pre,lsuc,lpre, out,inn,outdeg,indeg,deg, equal, -- ** Context Inspection node',lab',labNode',neighbors', suc',pre',lpre',lsuc', out',inn',outdeg',indeg',deg', -- * Pretty-printing prettify, prettyPrint ) where import Data.List (sortBy) {- Signatures: -- basic operations empty :: Graph gr => gr a b isEmpty :: Graph gr => gr a b -> Bool match :: Graph gr => Node -> gr a b -> Decomp gr a b mkGraph :: Graph gr => [LNode a] -> [LEdge b] -> gr a b (&) :: DynGraph gr => Context a b -> gr a b -> gr a b -- graph folds and maps ufold :: Graph gr => ((Context a b) -> c -> c) -> c -> gr a b -> c gmap :: Graph gr => (Context a b -> Context c d) -> gr a b -> gr c d nmap :: Graph gr => (a -> c) -> gr a b -> gr c b emap :: Graph gr => (b -> c) -> gr a b -> gr a c -- graph projection matchAny :: Graph gr => gr a b -> GDecomp g a b nodes :: Graph gr => gr a b -> [Node] edges :: Graph gr => gr a b -> [Edge] labNodes :: Graph gr => gr a b -> [LNode a] labEdges :: Graph gr => gr a b -> [LEdge b] newNodes :: Graph gr => Int -> gr a b -> [Node] noNodes :: Graph gr => gr a b -> Int nodeRange :: Graph gr => gr a b -> (Node,Node) gelem :: Graph gr => Node -> gr a b -> Bool -- graph construction & destruction insNode :: DynGraph gr => LNode a -> gr a b -> gr a b insEdge :: DynGraph gr => LEdge b -> gr a b -> gr a b delNode :: Graph gr => Node -> gr a b -> gr a b delEdge :: DynGraph gr => Edge -> gr a b -> gr a b delLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b insNodes :: DynGraph gr => [LNode a] -> gr a b -> gr a b insEdges :: DynGraph gr => [LEdge b] -> gr a b -> gr a b delNodes :: Graph gr => [Node] -> gr a b -> gr a b delEdges :: DynGraph gr => [Edge] -> gr a b -> gr a b buildGr :: DynGraph gr => [Context a b] -> gr a b mkUGraph :: DynGraph gr => [Node] -> [Edge] -> gr () () -- graph inspection context :: Graph gr => gr a b -> Node -> Context a b lab :: Graph gr => gr a b -> Node -> Maybe a neighbors :: Graph gr => gr a b -> Node -> [Node] suc :: Graph gr => gr a b -> Node -> [Node] pre :: Graph gr => gr a b -> Node -> [Node] lsuc :: Graph gr => gr a b -> Node -> [(Node,b)] lpre :: Graph gr => gr a b -> Node -> [(Node,b)] out :: Graph gr => gr a b -> Node -> [LEdge b] inn :: Graph gr => gr a b -> Node -> [LEdge b] outdeg :: Graph gr => gr a b -> Node -> Int indeg :: Graph gr => gr a b -> Node -> Int deg :: Graph gr => gr a b -> Node -> Int -- context inspection node' :: Context a b -> Node lab' :: Context a b -> a labNode' :: Context a b -> LNode a neighbors' :: Context a b -> [Node] suc' :: Context a b -> [Node] pre' :: Context a b -> [Node] lpre' :: Context a b -> [(Node,b)] lsuc' :: Context a b -> [(Node,b)] out' :: Context a b -> [LEdge b] inn' :: Context a b -> [LEdge b] outdeg' :: Context a b -> Int indeg' :: Context a b -> Int deg' :: Context a b -> Int -} -- | Unlabeled node type Node = Int -- | Labeled node type LNode a = (Node,a) -- | Quasi-unlabeled node type UNode = LNode () -- | Unlabeled edge type Edge = (Node,Node) -- | Labeled edge type LEdge b = (Node,Node,b) -- | Quasi-unlabeled edge type UEdge = LEdge () -- | Unlabeled path type Path = [Node] -- | Labeled path newtype LPath a = LP [LNode a] instance Show a => Show (LPath a) where show (LP xs) = show xs -- | Quasi-unlabeled path type UPath = [UNode] -- | Labeled links to or from a 'Node'. type Adj b = [(b,Node)] -- | Links to the 'Node', the 'Node' itself, a label, links from the 'Node'. type Context a b = (Adj b,Node,a,Adj b) -- Context a b "=" Context' a b "+" Node type MContext a b = Maybe (Context a b) -- | 'Graph' decomposition - the context removed from a 'Graph', and the rest -- of the 'Graph'. type Decomp g a b = (MContext a b,g a b) -- | The same as 'Decomp', only more sure of itself. type GDecomp g a b = (Context a b,g a b) -- | Unlabeled context. type UContext = ([Node],Node,[Node]) -- | Unlabeled decomposition. type UDecomp g = (Maybe UContext,g) -- | Minimum implementation: 'empty', 'isEmpty', 'match', 'mkGraph', 'labNodes' class Graph gr where -- essential operations -- | An empty 'Graph'. empty :: gr a b -- | True if the given 'Graph' is empty. isEmpty :: gr a b -> Bool -- | Decompose a 'Graph' into the 'MContext' found for the given node and the -- remaining 'Graph'. match :: Node -> gr a b -> Decomp gr a b -- | Create a 'Graph' from the list of 'LNode's and 'LEdge's. mkGraph :: [LNode a] -> [LEdge b] -> gr a b -- | A list of all 'LNode's in the 'Graph'. labNodes :: gr a b -> [LNode a] -- derived operations -- | Decompose a graph into the 'Context' for an arbitrarily-chosen 'Node' -- and the remaining 'Graph'. matchAny :: gr a b -> GDecomp gr a b -- | The number of 'Node's in a 'Graph'. noNodes :: gr a b -> Int -- | The minimum and maximum 'Node' in a 'Graph'. nodeRange :: gr a b -> (Node,Node) -- | A list of all 'LEdge's in the 'Graph'. labEdges :: gr a b -> [LEdge b] -- default implementation of derived operations matchAny g = case labNodes g of [] -> error "Match Exception, Empty Graph" (v,_):_ -> (c,g') where (Just c,g') = match v g noNodes = length . labNodes nodeRange g = (minimum vs,maximum vs) where vs = map fst (labNodes g) labEdges = ufold (\(_,v,_,s)->((map (\(l,w)->(v,w,l)) s)++)) [] class Graph gr => DynGraph gr where -- | Merge the 'Context' into the 'DynGraph'. (&) :: Context a b -> gr a b -> gr a b -- | Fold a function over the graph. ufold :: Graph gr => ((Context a b) -> c -> c) -> c -> gr a b -> c ufold f u g | isEmpty g = u | otherwise = f c (ufold f u g') where (c,g') = matchAny g -- | Map a function over the graph. gmap :: DynGraph gr => (Context a b -> Context c d) -> gr a b -> gr c d gmap f = ufold (\c->(f c&)) empty -- | Map a function over the 'Node' labels in a graph. nmap :: DynGraph gr => (a -> c) -> gr a b -> gr c b nmap f = gmap (\(p,v,l,s)->(p,v,f l,s)) -- | Map a function over the 'Edge' labels in a graph. emap :: DynGraph gr => (b -> c) -> gr a b -> gr a c emap f = gmap (\(p,v,l,s)->(map1 f p,v,l,map1 f s)) where map1 g = map (\(l,v)->(g l,v)) -- | List all 'Node's in the 'Graph'. nodes :: Graph gr => gr a b -> [Node] nodes = map fst . labNodes -- | List all 'Edge's in the 'Graph'. edges :: Graph gr => gr a b -> [Edge] edges = map (\(v,w,_)->(v,w)) . labEdges -- | List N available 'Node's, i.e. 'Node's that are not used in the 'Graph'. newNodes :: Graph gr => Int -> gr a b -> [Node] newNodes i g = [n+1..n+i] where (_,n) = nodeRange g -- | 'True' if the 'Node' is present in the 'Graph'. gelem :: Graph gr => Node -> gr a b -> Bool gelem v g = case match v g of {(Just _,_) -> True; _ -> False} -- | Insert a 'LNode' into the 'Graph'. insNode :: DynGraph gr => LNode a -> gr a b -> gr a b insNode (v,l) = (([],v,l,[])&) {-# NOINLINE [0] insNode #-} -- | Insert a 'LEdge' into the 'Graph'. insEdge :: DynGraph gr => LEdge b -> gr a b -> gr a b insEdge (v,w,l) g = (pr,v,la,(l,w):su) & g' where (Just (pr,_,la,su),g') = match v g -- | Remove a 'Node' from the 'Graph'. delNode :: Graph gr => Node -> gr a b -> gr a b delNode v = delNodes [v] -- | Remove an 'Edge' from the 'Graph'. delEdge :: DynGraph gr => Edge -> gr a b -> gr a b delEdge (v,w) g = case match v g of (Nothing,_) -> g (Just (p,v',l,s),g') -> (p,v',l,filter ((/=w).snd) s) & g' -- | Remove an 'LEdge' from the 'Graph'. delLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b delLEdge (v,w,b) g = case match v g of (Nothing,_) -> g (Just (p,v',l,s),g') -> (p,v',l,filter (\(x,n) -> x /= b || n /= w) s) & g' -- | Insert multiple 'LNode's into the 'Graph'. insNodes :: DynGraph gr => [LNode a] -> gr a b -> gr a b insNodes vs g = foldr insNode g vs -- | Insert multiple 'LEdge's into the 'Graph'. insEdges :: DynGraph gr => [LEdge b] -> gr a b -> gr a b insEdges es g = foldr insEdge g es -- | Remove multiple 'Node's from the 'Graph'. delNodes :: Graph gr => [Node] -> gr a b -> gr a b delNodes [] g = g delNodes (v:vs) g = delNodes vs (snd (match v g)) -- | Remove multiple 'Edge's from the 'Graph'. delEdges :: DynGraph gr => [Edge] -> gr a b -> gr a b delEdges es g = foldr delEdge g es -- | Build a 'Graph' from a list of 'Context's. buildGr :: DynGraph gr => [Context a b] -> gr a b buildGr = foldr (&) empty -- mkGraph :: DynGraph gr => [LNode a] -> [LEdge b] -> gr a b -- mkGraph vs es = (insEdges es . insNodes vs) empty -- | Build a quasi-unlabeled 'Graph'. mkUGraph :: Graph gr => [Node] -> [Edge] -> gr () () mkUGraph vs es = mkGraph (labUNodes vs) (labUEdges es) where labUEdges = map (\(v,w)->(v,w,())) labUNodes = map (\v->(v,())) -- | Find the context for the given 'Node'. Causes an error if the 'Node' is -- not present in the 'Graph'. context :: Graph gr => gr a b -> Node -> Context a b context g v = case match v g of (Nothing,_) -> error ("Match Exception, Node: "++show v) (Just c,_) -> c -- | Find the label for a 'Node'. lab :: Graph gr => gr a b -> Node -> Maybe a lab g v = fst (match v g) >>= return.lab' -- | Find the neighbors for a 'Node'. neighbors :: Graph gr => gr a b -> Node -> [Node] neighbors = (\(p,_,_,s) -> map snd (p++s)) .: context -- | Find all 'Node's that have a link from the given 'Node'. suc :: Graph gr => gr a b -> Node -> [Node] suc = map snd .: context4l -- | Find all 'Node's that link to to the given 'Node'. pre :: Graph gr => gr a b -> Node -> [Node] pre = map snd .: context1l -- | Find all 'Node's that are linked from the given 'Node' and the label of -- each link. lsuc :: Graph gr => gr a b -> Node -> [(Node,b)] lsuc = map flip2 .: context4l -- | Find all 'Node's that link to the given 'Node' and the label of each link. lpre :: Graph gr => gr a b -> Node -> [(Node,b)] lpre = map flip2 .: context1l -- | Find all outward-bound 'LEdge's for the given 'Node'. out :: Graph gr => gr a b -> Node -> [LEdge b] out g v = map (\(l,w)->(v,w,l)) (context4l g v) -- | Find all inward-bound 'LEdge's for the given 'Node'. inn :: Graph gr => gr a b -> Node -> [LEdge b] inn g v = map (\(l,w)->(w,v,l)) (context1l g v) -- | The outward-bound degree of the 'Node'. outdeg :: Graph gr => gr a b -> Node -> Int outdeg = length .: context4l -- | The inward-bound degree of the 'Node'. indeg :: Graph gr => gr a b -> Node -> Int indeg = length .: context1l -- | The degree of the 'Node'. deg :: Graph gr => gr a b -> Node -> Int deg = (\(p,_,_,s) -> length p+length s) .: context -- | The 'Node' in a 'Context'. node' :: Context a b -> Node node' (_,v,_,_) = v -- | The label in a 'Context'. lab' :: Context a b -> a lab' (_,_,l,_) = l -- | The 'LNode' from a 'Context'. labNode' :: Context a b -> LNode a labNode' (_,v,l,_) = (v,l) -- | All 'Node's linked to or from in a 'Context'. neighbors' :: Context a b -> [Node] neighbors' (p,_,_,s) = map snd p++map snd s -- | All 'Node's linked to in a 'Context'. suc' :: Context a b -> [Node] suc' = map snd . context4l' -- | All 'Node's linked from in a 'Context'. pre' :: Context a b -> [Node] pre' = map snd . context1l' -- | All 'Node's linked from in a 'Context', and the label of the links. lsuc' :: Context a b -> [(Node,b)] lsuc' = map flip2 . context4l' -- | All 'Node's linked from in a 'Context', and the label of the links. lpre' :: Context a b -> [(Node,b)] lpre' = map flip2 . context1l' -- | All outward-directed 'LEdge's in a 'Context'. out' :: Context a b -> [LEdge b] out' c@(_,v,_,_) = map (\(l,w)->(v,w,l)) (context4l' c) -- | All inward-directed 'LEdge's in a 'Context'. inn' :: Context a b -> [LEdge b] inn' c@(_,v,_,_) = map (\(l,w)->(w,v,l)) (context1l' c) -- | The outward degree of a 'Context'. outdeg' :: Context a b -> Int outdeg' = length . context4l' -- | The inward degree of a 'Context'. indeg' :: Context a b -> Int indeg' = length . context1l' -- | The degree of a 'Context'. deg' :: Context a b -> Int deg' (p,_,_,s) = length p+length s -- graph equality -- nodeComp :: Eq b => LNode b -> LNode b -> Ordering nodeComp n@(v,_) n'@(w,_) | n == n' = EQ | v<w = LT | otherwise = GT slabNodes :: (Eq a,Graph gr) => gr a b -> [LNode a] slabNodes = sortBy nodeComp . labNodes edgeComp :: Eq b => LEdge b -> LEdge b -> Ordering edgeComp e@(v,w,_) e'@(x,y,_) | e == e' = EQ | v<x || (v==x && w<y) = LT | otherwise = GT slabEdges :: (Eq b,Graph gr) => gr a b -> [LEdge b] slabEdges = sortBy edgeComp . labEdges -- instance (Eq a,Eq b,Graph gr) => Eq (gr a b) where -- g == g' = slabNodes g == slabNodes g' && slabEdges g == slabEdges g' equal :: (Eq a,Eq b,Graph gr) => gr a b -> gr a b -> Bool equal g g' = slabNodes g == slabNodes g' && slabEdges g == slabEdges g' ---------------------------------------------------------------------- -- UTILITIES ---------------------------------------------------------------------- -- auxiliary functions used in the implementation of the -- derived class members -- (.:) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d) -- f .: g = \x y->f (g x y) -- f .: g = (f .) . g -- (.:) f = ((f .) .) -- (.:) = (.) (.) (.) (.:) = (.) . (.) flip2 :: (a,b) -> (b,a) flip2 (x,y) = (y,x) -- projecting on context elements -- context1l :: Graph gr => gr a b -> Node -> Adj b context1l = context1l' .: context context4l :: Graph gr => gr a b -> Node -> Adj b context4l = context4l' .: context context1l' :: Context a b -> Adj b context1l' (p,v,_,s) = p++filter ((==v).snd) s context4l' :: Context a b -> Adj b context4l' (p,v,_,s) = s++filter ((==v).snd) p ---------------------------------------------------------------------- -- PRETTY PRINTING ---------------------------------------------------------------------- -- ufold :: Graph gr => (Context a b -> c -> c) -> c -> gr a b -> c -- | Pretty-print the graph. Note that this loses a lot of -- information, such as edge inverses, etc. prettify :: (DynGraph gr, Show a, Show b) => gr a b -> String prettify g = ufold showsContext id g "" where showsContext (_,n,l,s) sg = shows n . (':':) . shows l . showString "->" . shows s . ('\n':) . sg -- | Pretty-print the graph to stdout. prettyPrint :: (DynGraph gr, Show a, Show b) => gr a b -> IO () prettyPrint = putStr . prettify