module M = import "data/map.ml" module S = import "data/set.ml" open import "prelude.ml" type t 'a <- M.t 'a (S.t 'a) let sccs (graph : t 'a) = let rec dfs (node : 'a) (path : M.t 'a int) (sccs : M.t 'a 'a) = let shallower old candidate = match M.lookup old path, M.lookup candidate path with | _, None -> old | None, _ -> candidate | Some a, Some b -> if b < a then candidate else old let children = match M.lookup node graph with | Some t -> t | None -> error "Node not in graph?" let go (folded, shallowest) child = match M.lookup child path with | Some _ -> (folded, shallower shallowest child) | _ -> let scc = dfs child (M.insert node (length path) path) folded let sfc = match M.lookup child scc with | Some x -> x | None -> error "no child in scc?" (scc, shallower shallowest sfc) let (new, shallowest) = S.members children |> foldl go (sccs, node) M.insert node shallowest new let go sccs next = match M.lookup next sccs with | Some _ -> sccs | _ -> dfs next M.empty sccs graph |> M.keys |> foldl go M.empty let toposort (graph : t 'a) : list 'a = let nodes = M.keys graph let l = ref [] let temp = ref S.empty let perm = ref S.empty let rec visit n = if n `S.member` !perm then () else if n `S.member` !temp then error "not a dag" else let o_temp = !temp temp := S.insert n o_temp match M.lookup n graph with | None -> () | Some xs -> iter visit (S.members xs) temp := o_temp perm := S.insert n !perm l := n :: !l iter visit nodes reverse !l let dot_of_graph (graph : t 'a) = let mk node = S.foldr (fun edge r -> show node ^ " -> " ^ show edge ^ "\n" ^ r) "\n" "strict digraph {" ^ M.foldr_with_key (fun node edges r -> mk node edges ^ r) "" graph ^ "}" let groups_of_sccs (graph : t 'a) = let sccs = sccs graph let edges_of n = match M.lookup n graph with | None -> error "not in graph" | Some v -> v let components = sccs |> M.assocs |> map (fun (k, s) -> M.singleton s (S.singleton k)) |> foldl (M.union_by (fun _ -> S.union)) M.empty let atd nodes = S.foldr (fun n -> S.union (edges_of n)) S.empty nodes `S.difference` nodes let comp_deps = components |> M.assocs |> map (fun (node, edges) -> (node, atd edges)) |> M.from_list let ordering = toposort comp_deps [ x | with k <- ordering, with Some x <- [M.lookup k components] ]