packages feed

comfort-graph-0.0.5: test/Test/Data/Graph/Alternative.hs

module Test.Data.Graph.Alternative where

import qualified Data.Graph.Comfort as Graph
import Data.Graph.Comfort (Graph)

import qualified Control.Monad.Trans.State as MS
import Control.Monad (when)
import Control.Applicative (liftA2)

import qualified Data.NonEmpty.Set as NonEmptySet
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.Tree as Tree
import qualified Data.Traversable as Trav
import qualified Data.Foldable as Fold
import Data.Map (Map)
import Data.Set (Set)
import Data.Tree (Tree, Forest)
import Data.Maybe (catMaybes)
import Data.Tuple.HT (mapFst, mapSnd)



depthFirstSearch ::
   (Graph.Edge edge, Ord node) =>
   Graph edge node edgeLabel nodeLabel -> Forest node
depthFirstSearch gr =
   let go = do
         unvisited <- MS.get
         case Set.toAscList unvisited of
            n:_ -> liftA2 (:) (depthFirstSearchFrom gr n) go
            [] -> pure []
   in MS.evalState go $ Graph.nodeSet gr

depthFirstSearchFrom ::
   (Graph.Edge edge, Ord node) =>
   Graph edge node edgeLabel nodeLabel ->
   node -> MS.State (Set node) (Tree node)
depthFirstSearchFrom gr n = do
   MS.modify $ Set.delete n
   fmap (Tree.Node n . catMaybes) $
      Trav.for (Graph.successors gr n) $ \suc -> do
         unvisited <- MS.gets $ Set.member suc
         if unvisited
            then fmap Just $ depthFirstSearchFrom gr suc
            else pure Nothing



transposeMap :: (Ord k, Ord a) => Map k a -> Map a (NonEmptySet.T k)
transposeMap =
   Map.foldl (Map.unionWith NonEmptySet.union) Map.empty .
   Map.mapWithKey
      (\n root -> Map.singleton root $ NonEmptySet.singleton n)

{-
Direct imperative implementation of
<https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm>
-}
stronglyConnectedComponents ::
   (Ord node) =>
   Graph Graph.DirEdge node edgeLabel nodeLabel -> [NonEmptySet.T node]
stronglyConnectedComponents gr =
   let visit u = do
         unvisited <- MS.gets snd
         when (Set.member u unvisited) $ do
            MS.modify (mapSnd $ Set.delete u)
            mapM_ visit $ Graph.successors gr u
            MS.modify (mapFst (u :))
       (queue, []) =
         mapSnd Set.toList $
         MS.execState
            (mapM_ visit $ Graph.nodes gr)
            ([], Graph.nodeSet gr)
       assign root u = do
         assignments <- MS.get
         when (Map.notMember u assignments) $ do
            MS.put $ Map.insert u root assignments
            mapM_ (assign root) $ Graph.predecessors gr u
   in Map.elems $ transposeMap $
      MS.execState (Fold.mapM_ (\n -> assign n n) queue) Map.empty