module Data.Graph import Prelude import Data.SortedMap import Data.SnocList -- https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm#The_algorithm_in_pseudocode -- Based on the wikipedia article, probably could use cleanup. Maybe switch to state monad. record TVertex k where constructor MkTV name : k out : List k index : Int lowLink : Int onStack : Bool record TState k where constructor MkTState lastIndex : Int stack : List k result : List (List k) graph : SortedMap k (TVertex k) strongConnect : ∀ k. {{Eq k}} {{Ord k}} → TState k → TVertex k → TState k strongConnect {k} st vtx = let index' = st.lastIndex + 1 vtx' = MkTV vtx.name vtx.out index' index' True stack' = vtx.name :: st.stack graph' = updateMap vtx'.name vtx' st.graph st' = MkTState index' stack' st.result graph' in checkRoot $ foldl doEdge st' vtx.out where -- There is a lot in here because everything is public at the moment -- Although we do reach for `k` and `vtx.name` a few times min : Int → Int → Int min a b = if a < b then a else b splitComp : List k → List k → (List k × List k) splitComp acc Nil = (acc, Nil) splitComp acc (x :: xs) = if compare x vtx.name == EQ then (x :: acc, xs) else splitComp (x :: acc) xs updateNode : TState k → k → (TVertex k → TVertex k) → TState k updateNode st@(MkTState lastIndex stack result graph) name f = case lookupMap' name graph of Just v => MkTState lastIndex stack result (updateMap name (f v) graph) Nothing => st updateLowLink : TState k → k → Int → TState k updateLowLink st nm v = updateNode st nm $ \ vt => MkTV vt.name vt.out vt.index (min vt.lowLink v) vt.onStack offStack : TState k → k → TState k offStack st name = updateNode st name $ \ vt => MkTV vt.name vt.out vt.index vt.lowLink False doEdge : TState k → k → TState k doEdge st k = let (Just w) = lookupMap' k st.graph | _ => st in if w.onStack then updateLowLink st vtx.name w.index else if w.index == 0 then let st' = strongConnect st w in let (Just w) = lookupMap' k st'.graph | _ => st' in updateLowLink st' vtx.name w.lowLink else st checkRoot : TState k → TState k checkRoot st = let (Just v) = lookupMap' vtx.name st.graph | _ => st in if v.lowLink == v.index then let (comp,stack) = splitComp Nil st.stack in let st = foldl offStack st comp in -- as a variation on tarjan, we only return singletons that point to themselves if length' comp > 1 || elem v.name v.out then MkTState st.lastIndex stack (comp :: st.result) st.graph else MkTState st.lastIndex stack st.result st.graph else st -- leave on stack -- A variant of tarjan that only returns singletons that call themselves -- I'd like to have {{Ord k}} => {{Eq k}} but need to tweak the solver a little -- to ignore ambiguity from indirect solutions tarjan : ∀ k. {{Eq k}} {{Ord k}} → List (k × List k) → List (List k) tarjan {k} nodes = let g = foldMap const emptyMap $ map mkVertex nodes in .result $ foldl checkVertex (MkTState 0 Nil Nil g) $ map fst nodes where mkVertex : k × List k → k × TVertex k mkVertex (n,out) = (n, MkTV n out 0 0 False) checkVertex : TState k → k → TState k checkVertex st k = let (Just vtx) = lookupMap' k st.graph | _ => st in if vtx.index > 0 then st else strongConnect st vtx