-- | Ear decomposition of a graph. module Data.Graph.Inductive.Query.Ear where import Data.Function import Data.Graph.Inductive.Basic import Data.Graph.Inductive.Example import Data.Graph.Inductive.Graph import Data.Graph.Inductive.Query import Data.Graph.Inductive.Tree import Data.List import Data.Tree import Data.Tuple -- | The 'ears' function takes a graph with no node or edge annotation and -- produces an ear decomposition. Each edge is annotated with a weight. Edges -- with the same weight are in the same ear. -- Maon, Schieber, Vishkin (1986) ears :: forall gr . DynGraph gr => gr () () -> gr () Int ears g | isConnected g = gWs | otherwise = error "called ears on disconnected graph" where -- (1.1) create spanning tree t :: Tree Node [t] = dff' g tps = treeToPaths t te = treeToEdges t -- (1.2) graph without spanning tree edges g' = mkUGraph (nodes g) ((edges g \\ te) \\ (map swap te)) `asTypeOf` g -- (2) gE is the graph of all edges not in the spanning tree. The edge -- weight is the distance between the tree root and the lowest common -- ancestor of the two nodes making up each edge gE :: gr () Int = mkGraph (labNodes g') (map (lca tps) $ labEdges g') -- (3.1) add back all tree edges (in both directions) gE' = mkGraph (labNodes gE) (labEdges gE ++ map (\(a,b) -> (a,b,0)) (te ++ map swap te)) -- (3.2) for each edge in the tree, find the shortest-path weight -- TODO on second thought, is this right? teWs = map (shortestPaths gE') te -- build a new graph, adding edge weights, where all edges with the same -- weight belong to the same ear gWs = mkGraph (labNodes g') (labEdges gE ++ teWs ++ map swap12 teWs) shortestPaths :: Gr () Int -> Edge -> LEdge Int shortestPaths g (u,v) = (u,v,spLength u v g') where g' = delEdge (u,v) $ delEdge (v,u) g -- | Lowest common ancestor calculation lca :: [[Node]] -> (LEdge ()) -> (LEdge Int) lca ps (u,v,()) | any null ps = error $ "null path: " ++ show ps | otherwise = (u,v,) . (subtract 1) . length . filter (uncurry (==)) $ zip u' v' where [u'] = filter ((==u) . last) ps [v'] = filter ((==v) . last) ps swap12 (a,b,c) = (b,a,c) sel3 (_,_,s) = s -- tree edges treeToEdges :: Tree Node -> [Edge] treeToEdges (Node _ []) = [] treeToEdges (Node k xs) = map ((k,) . rootLabel) xs ++ concatMap treeToEdges xs -- paths treeToPaths :: Tree Node -> [[Node]] treeToPaths (Node k []) = [[k]] treeToPaths (Node k xs) = [[k]] ++ [ (k:ys) | ys <- concatMap treeToPaths xs ] {- - Test graph - fig13 :: Gr () () fig13 = undir $ mkUGraph ns es where ns = [1..9] es = [ (1,2),(1,4) , (2,3),(2,5) , (3,4),(3,6) , (4,7) , (5,6),(5,8) , (6,7) , (7,9) , (8,9) ] -}