(** {2 Iter Helpers} *) type 'a iter = ('a -> unit) -> unit (** A sequence of items of type ['a], possibly infinite @since 2.8 *) type 'a iter_once = 'a iter (** Iter that should be used only once @since 2.8 *) exception Iter_once let ( |> ) x f = f x module Iter = struct type 'a t = 'a iter let return x k = k x let ( >>= ) a f k = a (fun x -> f x k) let map f a k = a (fun x -> k (f x)) let filter_map f a k = a (fun x -> match f x with | None -> () | Some y -> k y) let iter f a = a f let fold f acc a = let acc = ref acc in a (fun x -> acc := f !acc x); !acc let to_list seq = fold (fun acc x -> x :: acc) [] seq |> List.rev exception Exit_ let exists_ f seq = try seq (fun x -> if f x then raise Exit_); false with Exit_ -> true end (** {2 Interfaces for graphs} *) type ('v, 'e) t = 'v -> ('e * 'v) iter (** Directed graph with vertices of type ['v] and edges labeled with [e'] *) type ('v, 'e) graph = ('v, 'e) t let make (f : 'v -> ('e * 'v) iter) : ('v, 'e) t = f type 'v tag_set = { get_tag: 'v -> bool; set_tag: 'v -> unit; (** Set tag for the given element *) } (** Mutable bitset for values of type ['v] *) type ('k, 'a) table = { mem: 'k -> bool; find: 'k -> 'a; (** @raise Not_found *) add: 'k -> 'a -> unit; (** Erases previous binding *) } (** Mutable table with keys ['k] and values ['a] *) type 'a set = ('a, unit) table (** Mutable set *) let mk_table (type k) ~eq ?(hash = Hashtbl.hash) size = let module H = Hashtbl.Make (struct type t = k let equal = eq let hash = hash end) in let tbl = H.create size in { mem = (fun k -> H.mem tbl k); find = (fun k -> H.find tbl k); add = (fun k v -> H.replace tbl k v); } let mk_map (type k) ~cmp () = let module M = Map.Make (struct type t = k let compare = cmp end) in let tbl = ref M.empty in { mem = (fun k -> M.mem k !tbl); find = (fun k -> M.find k !tbl); add = (fun k v -> tbl := M.add k v !tbl); } (** {2 Bags} *) type 'a bag = { push: 'a -> unit; is_empty: unit -> bool; pop: unit -> 'a; (** raises some exception is empty *) } let mk_queue () = let q = Queue.create () in { push = (fun x -> Queue.push x q); is_empty = (fun () -> Queue.is_empty q); pop = (fun () -> Queue.pop q); } let mk_stack () = let s = Stack.create () in { push = (fun x -> Stack.push x s); is_empty = (fun () -> Stack.is_empty s); pop = (fun () -> Stack.pop s); } (** Implementation from http://en.wikipedia.org/wiki/Skew_heap *) module Heap = struct type 'a t = E | N of 'a * 'a t * 'a t let is_empty = function | E -> true | N _ -> false let rec union ~leq t1 t2 = match t1, t2 with | E, _ -> t2 | _, E -> t1 | N (x1, l1, r1), N (x2, l2, r2) -> if leq x1 x2 then N (x1, union ~leq t2 r1, l1) else N (x2, union ~leq t1 r2, l2) let insert ~leq h x = union ~leq (N (x, E, E)) h let pop ~leq h = match h with | E -> raise Not_found | N (x, l, r) -> x, union ~leq l r end let mk_heap ~leq = let t = ref Heap.E in { push = (fun x -> t := Heap.insert ~leq !t x); is_empty = (fun () -> Heap.is_empty !t); pop = (fun () -> let x, h = Heap.pop ~leq !t in t := h; x); } (** {2 Traversals} *) module Traverse = struct type ('v, 'e) path = ('v * 'e * 'v) list let generic_tag ~tags ~bag ~graph iter = let first = ref true in fun k -> (* ensure linearity *) if !first then first := false else raise Iter_once; Iter.iter bag.push iter; while not (bag.is_empty ()) do let x = bag.pop () in if not (tags.get_tag x) then ( k x; tags.set_tag x; Iter.iter (fun (_, dest) -> bag.push dest) (graph x) ) done let generic ~tbl ~bag ~graph iter = let tags = { get_tag = tbl.mem; set_tag = (fun v -> tbl.add v ()) } in generic_tag ~tags ~bag ~graph iter let bfs ~tbl ~graph iter = generic ~tbl ~bag:(mk_queue ()) ~graph iter let bfs_tag ~tags ~graph iter = generic_tag ~tags ~bag:(mk_queue ()) ~graph iter let dijkstra_tag ?(dist = fun _ -> 1) ~tags ~graph iter = let tags' = { get_tag = (fun (v, _, _) -> tags.get_tag v); set_tag = (fun (v, _, _) -> tags.set_tag v); } and iter' = Iter.map (fun v -> v, 0, []) iter and graph' (v, d, p) = graph v |> Iter.map (fun (e, v') -> e, (v', d + dist e, (v, e, v') :: p)) in let bag = mk_heap ~leq:(fun (_, d1, _) (_, d2, _) -> d1 <= d2) in generic_tag ~tags:tags' ~bag ~graph:graph' iter' let dijkstra ~tbl ?dist ~graph iter = let tags = { get_tag = tbl.mem; set_tag = (fun v -> tbl.add v ()) } in dijkstra_tag ~tags ?dist ~graph iter let dfs ~tbl ~graph iter = generic ~tbl ~bag:(mk_stack ()) ~graph iter let dfs_tag ~tags ~graph iter = generic_tag ~tags ~bag:(mk_stack ()) ~graph iter module Event = struct type edge_kind = [ `Forward | `Back | `Cross ] type ('v, 'e) t = [ `Enter of 'v * int * ('v, 'e) path (* unique index in traversal, path from start *) | `Exit of 'v | `Edge of 'v * 'e * 'v * edge_kind ] (** A traversal is a iteruence of such events *) let get_vertex = function | `Enter (v, _, _) -> Some (v, `Enter) | `Exit v -> Some (v, `Exit) | `Edge _ -> None let get_enter = function | `Enter (v, _, _) -> Some v | `Exit _ | `Edge _ -> None let get_exit = function | `Exit v -> Some v | `Enter _ | `Edge _ -> None let get_edge = function | `Edge (v1, e, v2, _) -> Some (v1, e, v2) | `Enter _ | `Exit _ -> None let get_edge_kind = function | `Edge (v, e, v', k) -> Some (v, e, v', k) | `Enter _ | `Exit _ -> None (* is [v] the origin of some edge in [path]? *) let rec list_mem_ ~eq ~graph v path = match path with | [] -> false | (v1, _, _) :: path' -> eq v v1 || list_mem_ ~eq ~graph v path' let dfs_tag ~eq ~tags ~graph iter = let first = ref true in fun k -> if !first then first := false else raise Iter_once; let bag = mk_stack () in let n = ref 0 in Iter.iter (fun v -> (* start DFS from this vertex *) bag.push (`Enter (v, [])); while not (bag.is_empty ()) do match bag.pop () with | `Enter (v, path) -> if not (tags.get_tag v) then ( let num = !n in incr n; tags.set_tag v; k (`Enter (v, num, path)); bag.push (`Exit v); Iter.iter (fun (e, v') -> bag.push (`Edge (v, e, v', (v, e, v') :: path))) (graph v) ) | `Exit x -> k (`Exit x) | `Edge (v, e, v', path) -> let edge_kind = if tags.get_tag v' then if list_mem_ ~eq ~graph v' path then `Back else `Cross else ( bag.push (`Enter (v', path)); `Forward ) in k (`Edge (v, e, v', edge_kind)) done) iter let dfs ~tbl ~eq ~graph iter = let tags = { set_tag = (fun v -> tbl.add v ()); get_tag = tbl.mem } in dfs_tag ~eq ~tags ~graph iter end end (** {2 Cycles} *) let is_dag ~tbl ~eq ~graph vs = Traverse.Event.dfs ~tbl ~eq ~graph vs |> Iter.exists_ (function | `Edge (_, _, _, `Back) -> true | _ -> false) (** {2 Topological Sort} *) exception Has_cycle let topo_sort_tag ~eq ?(rev = false) ~tags ~graph iter = (* use DFS *) let l = Traverse.Event.dfs_tag ~eq ~tags ~graph iter |> Iter.filter_map (function | `Exit v -> Some v | `Edge (_, _, _, `Back) -> raise Has_cycle | `Enter _ | `Edge _ -> None) |> Iter.fold (fun acc x -> x :: acc) [] in if rev then List.rev l else l let topo_sort ~eq ?rev ~tbl ~graph iter = let tags = { get_tag = tbl.mem; set_tag = (fun v -> tbl.add v ()) } in topo_sort_tag ~eq ?rev ~tags ~graph iter (** {2 Lazy Spanning Tree} *) module Lazy_tree = struct type ('v, 'e) t = { vertex: 'v; children: ('e * ('v, 'e) t) list Lazy.t } let make_ vertex children = { vertex; children } let rec map_v f { vertex = v; children = l } = let l' = lazy (List.map (fun (e, child) -> e, map_v f child) (Lazy.force l)) in make_ (f v) l' let rec fold_v f acc { vertex = v; children = l } = let acc = f acc v in List.fold_left (fun acc (_, t') -> fold_v f acc t') acc (Lazy.force l) end let spanning_tree_tag ~tags ~graph v = let rec mk_node v = let children = lazy (Iter.fold (fun acc (e, v') -> if tags.get_tag v' then acc else ( tags.set_tag v'; (e, mk_node v') :: acc )) [] (graph v)) in Lazy_tree.make_ v children in mk_node v let spanning_tree ~tbl ~graph v = let tags = { get_tag = tbl.mem; set_tag = (fun v -> tbl.add v ()) } in spanning_tree_tag ~tags ~graph v (** {2 Strongly Connected Components} *) module SCC = struct type 'v state = { mutable min_id: int; (* min ID of the vertex' scc *) id: int; (* ID of the vertex *) mutable on_stack: bool; vertex: 'v; } let mk_cell v n = { min_id = n; id = n; on_stack = false; vertex = v } (* pop elements of [stack] until we reach node with given [id] *) let rec pop_down_to ~id acc stack = assert (not (Stack.is_empty stack)); let cell = Stack.pop stack in cell.on_stack <- false; if cell.id = id then ( assert (cell.id = cell.min_id); cell.vertex :: acc (* return SCC *) ) else pop_down_to ~id (cell.vertex :: acc) stack let explore ~tbl ~graph iter = let first = ref true in fun k -> if !first then first := false else raise Iter_once; (* stack of nodes being explored, for the DFS *) let to_explore = Stack.create () in (* stack for Tarjan's algorithm itself *) let stack = Stack.create () in (* unique ID *) let n = ref 0 in (* exploration *) Iter.iter (fun v -> Stack.push (`Enter v) to_explore; while not (Stack.is_empty to_explore) do match Stack.pop to_explore with | `Enter v -> if not (tbl.mem v) then ( (* remember unique ID for [v] *) let id = !n in incr n; let cell = mk_cell v id in cell.on_stack <- true; tbl.add v cell; Stack.push cell stack; Stack.push (`Exit (v, cell)) to_explore; (* explore children *) Iter.iter (fun (_, v') -> Stack.push (`Enter v') to_explore) (graph v) ) | `Exit (v, cell) -> (* update [min_id] *) assert cell.on_stack; Iter.iter (fun (_, dest) -> (* must not fail, [dest] already explored *) let dest_cell = tbl.find dest in (* same SCC? yes if [dest] points to [cell.v] *) if dest_cell.on_stack then cell.min_id <- min cell.min_id dest_cell.min_id) (graph v); (* pop from stack if SCC found *) if cell.id = cell.min_id then ( let scc = pop_down_to ~id:cell.id [] stack in k scc ) done) iter; assert (Stack.is_empty stack); () end type 'v scc_state = 'v SCC.state let scc ~tbl ~graph iter = SCC.explore ~tbl ~graph iter (** {2 Pretty printing in the DOT (graphviz) format} *) module Dot = struct type attribute = [ `Color of string | `Shape of string | `Weight of int | `Style of string | `Label of string | `Other of string * string ] (** Dot attribute *) let pp_list pp_x out l = Format.pp_print_string out "["; List.iteri (fun i x -> if i > 0 then Format.fprintf out ",@;"; pp_x out x) l; Format.pp_print_string out "]" type vertex_state = { mutable explored: bool; id: int } (** Print an enum of Full.traverse_event *) let pp_all ~tbl ~eq ?(attrs_v = fun _ -> []) ?(attrs_e = fun _ -> []) ?(name = "graph") ~graph out iter = (* print an attribute *) let pp_attr out attr = match attr with | `Color c -> Format.fprintf out "color=%s" c | `Shape s -> Format.fprintf out "shape=%s" s | `Weight w -> Format.fprintf out "weight=%d" w | `Style s -> Format.fprintf out "style=%s" s | `Label l -> Format.fprintf out "label=\"%s\"" l | `Other (name, value) -> Format.fprintf out "%s=\"%s\"" name value (* map from vertices to integers *) and get_node = let count = ref 0 in fun v -> try tbl.find v with Not_found -> let node = { id = !count; explored = false } in incr count; tbl.add v node; node and vertex_explored v = try (tbl.find v).explored with Not_found -> false in let set_explored v = (get_node v).explored <- true and get_id v = (get_node v).id in (* the unique name of a vertex *) let pp_vertex out v = Format.fprintf out "vertex_%d" (get_id v) in (* print preamble *) Format.fprintf out "@[digraph \"%s\" {@;" name; (* traverse *) let tags = { get_tag = vertex_explored; set_tag = set_explored (* allocate new ID *); } in let events = Traverse.Event.dfs_tag ~eq ~tags ~graph iter in Iter.iter (function | `Enter (v, _n, _path) -> let attrs = attrs_v v in Format.fprintf out "@[%a %a;@]@," pp_vertex v (pp_list pp_attr) attrs | `Exit _ -> () | `Edge (v1, e, v2, _) -> let attrs = attrs_e e in Format.fprintf out "@[%a -> %a %a;@]@," pp_vertex v1 pp_vertex v2 (pp_list pp_attr) attrs) events; (* close *) Format.fprintf out "}@]@;@?"; () let pp ~tbl ~eq ?attrs_v ?attrs_e ?name ~graph fmt v = pp_all ~tbl ~eq ?attrs_v ?attrs_e ?name ~graph fmt (Iter.return v) let with_out filename f = let oc = open_out filename in try let fmt = Format.formatter_of_out_channel oc in let x = f fmt in Format.pp_print_flush fmt (); close_out oc; x with e -> close_out oc; raise e end (** {2 Mutable Graph} *) type ('v, 'e) mut_graph = { graph: ('v, 'e) t; add_edge: 'v -> 'e -> 'v -> unit; remove: 'v -> unit; } let mk_mut_tbl (type k) ~eq ?(hash = Hashtbl.hash) size = let module Tbl = Hashtbl.Make (struct type t = k let hash = hash let equal = eq end) in let tbl = Tbl.create size in { graph = (fun v yield -> try List.iter yield (Tbl.find tbl v) with Not_found -> ()); add_edge = (fun v1 e v2 -> let l = try Tbl.find tbl v1 with Not_found -> [] in Tbl.replace tbl v1 ((e, v2) :: l)); remove = (fun v -> Tbl.remove tbl v); } (** {2 Immutable Graph} *) module type MAP = sig type vertex type 'a t val as_graph : 'a t -> (vertex, 'a) graph (** Graph view of the map. *) val empty : 'a t val add_edge : vertex -> 'a -> vertex -> 'a t -> 'a t val remove_edge : vertex -> vertex -> 'a t -> 'a t val add : vertex -> 'a t -> 'a t (** Add a vertex, possibly with no outgoing edge. *) val remove : vertex -> 'a t -> 'a t (** Remove the vertex and all its outgoing edges. Edges that point to the vertex are {b NOT} removed, they must be manually removed with {!remove_edge}. *) val union : 'a t -> 'a t -> 'a t val vertices : _ t -> vertex iter val vertices_l : _ t -> vertex list val of_list : (vertex * 'a * vertex) list -> 'a t val add_list : (vertex * 'a * vertex) list -> 'a t -> 'a t val to_list : 'a t -> (vertex * 'a * vertex) list val of_iter : (vertex * 'a * vertex) iter -> 'a t (** @since 2.8 *) val add_iter : (vertex * 'a * vertex) iter -> 'a t -> 'a t (** @since 2.8 *) val to_iter : 'a t -> (vertex * 'a * vertex) iter (** @since 2.8 *) end module Map (O : Map.OrderedType) : MAP with type vertex = O.t = struct module M = Map.Make (O) type vertex = O.t type 'a t = 'a M.t M.t (* vertex -> set of (vertex * label) *) let as_graph m v yield = try let sub = M.find v m in M.iter (fun v' e -> yield (e, v')) sub with Not_found -> () let empty = M.empty let add_edge v1 e v2 m = let sub = try M.find v1 m with Not_found -> M.empty in M.add v1 (M.add v2 e sub) m let remove_edge v1 v2 m = try let map = M.remove v2 (M.find v1 m) in if M.is_empty map then M.remove v1 m else M.add v1 map m with Not_found -> m let add v m = if M.mem v m then m else M.add v M.empty m let remove v m = M.remove v m let union m1 m2 = M.merge (fun _ s1 s2 -> match s1, s2 with | Some s, None | None, Some s -> Some s | None, None -> assert false | Some s1, Some s2 -> let s = M.merge (fun _ e1 e2 -> match e1, e2 with | Some _, _ -> e1 | None, _ -> e2) s1 s2 in Some s) m1 m2 let vertices m yield = M.iter (fun v _ -> yield v) m let vertices_l m = M.fold (fun v _ acc -> v :: acc) m [] let add_list l m = List.fold_left (fun m (v1, e, v2) -> add_edge v1 e v2 m) m l let of_list l = add_list l empty let to_list m = M.fold (fun v map acc -> M.fold (fun v' e acc -> (v, e, v') :: acc) map acc) m [] let add_iter iter m = Iter.fold (fun m (v1, e, v2) -> add_edge v1 e v2 m) m iter let of_iter iter = add_iter iter empty let to_iter m k = M.iter (fun v map -> M.iter (fun v' e -> k (v, e, v')) map) m end (** {2 Misc} *) let of_list ~eq l v yield = List.iter (fun (a, b) -> if eq a v then yield ((), b)) l let of_fun f v yield = let l = f v in List.iter (fun v' -> yield ((), v')) l let of_hashtbl tbl v yield = try List.iter (fun b -> yield ((), b)) (Hashtbl.find tbl v) with Not_found -> () let divisors_graph i = (* divisors of [i] that are [>= j] *) let rec divisors j i yield = if j < i then ( if i mod j = 0 then yield ((), j); divisors (j + 1) i yield ) in divisors 1 i