201 lines
6.2 KiB
OCaml
201 lines
6.2 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Pierre Chambart, OCamlPro *)
|
|
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
|
(* *)
|
|
(* Copyright 2013--2016 OCamlPro SAS *)
|
|
(* Copyright 2014--2016 Jane Street Group LLC *)
|
|
(* *)
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
module Int = Numbers.Int
|
|
|
|
module Kosaraju : sig
|
|
type component_graph =
|
|
{ sorted_connected_components : int list array;
|
|
component_edges : int list array;
|
|
}
|
|
|
|
val component_graph : int list array -> component_graph
|
|
end = struct
|
|
let transpose graph =
|
|
let size = Array.length graph in
|
|
let transposed = Array.make size [] in
|
|
let add src dst = transposed.(src) <- dst :: transposed.(src) in
|
|
Array.iteri (fun src dsts -> List.iter (fun dst -> add dst src) dsts)
|
|
graph;
|
|
transposed
|
|
|
|
let depth_first_order (graph : int list array) : int array =
|
|
let size = Array.length graph in
|
|
let marked = Array.make size false in
|
|
let stack = Array.make size ~-1 in
|
|
let pos = ref 0 in
|
|
let push i =
|
|
stack.(!pos) <- i;
|
|
incr pos
|
|
in
|
|
let rec aux node =
|
|
if not marked.(node)
|
|
then begin
|
|
marked.(node) <- true;
|
|
List.iter aux graph.(node);
|
|
push node
|
|
end
|
|
in
|
|
for i = 0 to size - 1 do
|
|
aux i
|
|
done;
|
|
stack
|
|
|
|
let mark order graph =
|
|
let size = Array.length graph in
|
|
let graph = transpose graph in
|
|
let marked = Array.make size false in
|
|
let id = Array.make size ~-1 in
|
|
let count = ref 0 in
|
|
let rec aux node =
|
|
if not marked.(node)
|
|
then begin
|
|
marked.(node) <- true;
|
|
id.(node) <- !count;
|
|
List.iter aux graph.(node)
|
|
end
|
|
in
|
|
for i = size - 1 downto 0 do
|
|
let node = order.(i) in
|
|
if not marked.(node)
|
|
then begin
|
|
aux order.(i);
|
|
incr count
|
|
end
|
|
done;
|
|
id, !count
|
|
|
|
let kosaraju graph =
|
|
let dfo = depth_first_order graph in
|
|
let components, ncomponents = mark dfo graph in
|
|
ncomponents, components
|
|
|
|
type component_graph =
|
|
{ sorted_connected_components : int list array;
|
|
component_edges : int list array;
|
|
}
|
|
|
|
let component_graph graph =
|
|
let ncomponents, components = kosaraju graph in
|
|
let id_scc = Array.make ncomponents [] in
|
|
let component_graph = Array.make ncomponents Int.Set.empty in
|
|
let add_component_dep node set =
|
|
let node_deps = graph.(node) in
|
|
List.fold_left (fun set dep -> Int.Set.add components.(dep) set)
|
|
set node_deps
|
|
in
|
|
Array.iteri (fun node component ->
|
|
id_scc.(component) <- node :: id_scc.(component);
|
|
component_graph.(component) <-
|
|
add_component_dep node (component_graph.(component)))
|
|
components;
|
|
{ sorted_connected_components = id_scc;
|
|
component_edges = Array.map Int.Set.elements component_graph;
|
|
}
|
|
end
|
|
|
|
module type S = sig
|
|
module Id : Identifiable.S
|
|
|
|
type directed_graph = Id.Set.t Id.Map.t
|
|
|
|
type component =
|
|
| Has_loop of Id.t list
|
|
| No_loop of Id.t
|
|
|
|
val connected_components_sorted_from_roots_to_leaf
|
|
: directed_graph
|
|
-> component array
|
|
|
|
val component_graph : directed_graph -> (component * int list) array
|
|
end
|
|
|
|
module Make (Id : Identifiable.S) = struct
|
|
type directed_graph = Id.Set.t Id.Map.t
|
|
|
|
type component =
|
|
| Has_loop of Id.t list
|
|
| No_loop of Id.t
|
|
|
|
(* Ensure that the dependency graph does not have external dependencies. *)
|
|
(* Note: this function is currently not used. *)
|
|
let _check dependencies =
|
|
Id.Map.iter (fun id set ->
|
|
Id.Set.iter (fun v ->
|
|
if not (Id.Map.mem v dependencies)
|
|
then
|
|
Misc.fatal_errorf "Strongly_connected_components.check: the \
|
|
graph has external dependencies (%a -> %a)"
|
|
Id.print id Id.print v)
|
|
set)
|
|
dependencies
|
|
|
|
type numbering = {
|
|
back : int Id.Map.t;
|
|
forth : Id.t array;
|
|
}
|
|
|
|
let number graph =
|
|
let size = Id.Map.cardinal graph in
|
|
let bindings = Id.Map.bindings graph in
|
|
let a = Array.of_list bindings in
|
|
let forth = Array.map fst a in
|
|
let back =
|
|
let back = ref Id.Map.empty in
|
|
for i = 0 to size - 1 do
|
|
back := Id.Map.add forth.(i) i !back;
|
|
done;
|
|
!back
|
|
in
|
|
let integer_graph =
|
|
Array.init size (fun i ->
|
|
let _, dests = a.(i) in
|
|
Id.Set.fold (fun dest acc ->
|
|
let v =
|
|
try Id.Map.find dest back
|
|
with Not_found ->
|
|
Misc.fatal_errorf
|
|
"Strongly_connected_components: missing dependency %a"
|
|
Id.print dest
|
|
in
|
|
v :: acc)
|
|
dests [])
|
|
in
|
|
{ back; forth }, integer_graph
|
|
|
|
let component_graph graph =
|
|
let numbering, integer_graph = number graph in
|
|
let { Kosaraju. sorted_connected_components;
|
|
component_edges } =
|
|
Kosaraju.component_graph integer_graph
|
|
in
|
|
Array.mapi (fun component nodes ->
|
|
match nodes with
|
|
| [] -> assert false
|
|
| [node] ->
|
|
(if List.mem node integer_graph.(node)
|
|
then Has_loop [numbering.forth.(node)]
|
|
else No_loop numbering.forth.(node)),
|
|
component_edges.(component)
|
|
| _::_ ->
|
|
(Has_loop (List.map (fun node -> numbering.forth.(node)) nodes)),
|
|
component_edges.(component))
|
|
sorted_connected_components
|
|
|
|
let connected_components_sorted_from_roots_to_leaf graph =
|
|
Array.map fst (component_graph graph)
|
|
end
|