(* This file is free software, part of containers. See file "license" for more details. *) (** {1 Lazy Tree Structure} This structure can be used to represent trees and directed graphs (as infinite trees) in a lazy fashion. It is a structural type. *) type 'a iter = ('a -> unit) -> unit type 'a gen = unit -> 'a option type 'a printer = Format.formatter -> 'a -> unit type +'a t = unit -> [ `Nil | `Node of 'a * 'a t list ] let empty () = `Nil let is_empty t = match t () with | `Nil -> true | `Node _ -> false let singleton x () = `Node (x, []) let node x l () = `Node (x, l) let node1 x t () = `Node (x, [ t ]) let node2 x t1 t2 () = `Node (x, [ t1; t2 ]) let rec fold f acc t = match t () with | `Nil -> acc | `Node (x, l) -> let acc = f acc x in List.fold_left (fold f) acc l let rec iter f t = match t () with | `Nil -> () | `Node (x, l) -> f x; List.iter (iter f) l let size t = fold (fun n _ -> n + 1) 0 t let height t = let rec aux t k = match t () with | `Nil -> k 0 | `Node (_, l) -> aux_l 0 l k and aux_l acc l k = match l with | [] -> k acc | t' :: l' -> aux t' (fun n -> aux_l (max acc n) l' k) in aux t (fun x -> x) let rec map f t () = match t () with | `Nil -> `Nil | `Node (x, l) -> `Node (f x, List.map (map f) l) let ( >|= ) t f = map f t let rec cut_depth n t () = match t () with | `Nil -> `Nil | `Node _ when n = 0 -> `Nil | `Node (x, l) -> `Node (x, List.map (cut_depth (n - 1)) l) (** {2 Graph Traversals} *) (** Abstract Set structure *) class type ['a] pset = object method add : 'a -> 'a pset method mem : 'a -> bool end let set_of_cmp (type elt) ~cmp () = let module S = Set.Make (struct type t = elt let compare = cmp end) in object val s = S.empty method add x = {} method mem x = S.mem x s end let _nil () = Seq.Nil let _cons x l = Seq.Cons (x, l) let dfs ~pset t = let rec dfs pset stack () = match stack with | [] -> Seq.Nil | `Explore t :: stack' -> (match t () with | `Nil -> dfs pset stack' () | `Node (x, _) when pset#mem x -> dfs pset stack' () (* loop *) | `Node (x, l) -> let pset' = pset#add x in let stack' = List.rev_append (List.rev_map (fun x -> `Explore x) l) (`Exit x :: stack') in _cons (`Enter x) (dfs pset' stack')) | `Exit x :: stack' -> _cons (`Exit x) (dfs pset stack') in dfs pset [ `Explore t ] (** Functional queues for BFS *) module FQ = struct type 'a t = { hd: 'a list; tl: 'a list } exception Empty (* invariant: if hd=[], then tl=[] *) let _make hd tl = match hd with | [] -> { hd = List.rev tl; tl = [] } | _ :: _ -> { hd; tl } let empty = _make [] [] let list_is_empty = function | [] -> true | _ :: _ -> false let is_empty q = list_is_empty q.hd let push q x = _make q.hd (x :: q.tl) let pop_exn q = match q.hd with | [] -> assert (list_is_empty q.tl); raise Empty | x :: hd' -> let q' = _make hd' q.tl in x, q' end let bfs ~pset t = let rec bfs pset q () = if FQ.is_empty q then Seq.Nil else ( let t, q' = FQ.pop_exn q in match t () with | `Nil -> bfs pset q' () | `Node (x, _) when pset#mem x -> bfs pset q' () (* loop *) | `Node (x, l) -> let q' = List.fold_left FQ.push q' l in let pset' = pset#add x in _cons x (bfs pset' q') ) in bfs pset (FQ.push FQ.empty t) let rec force t : [ `Nil | `Node of 'a * 'b list ] as 'b = match t () with | `Nil -> `Nil | `Node (x, l) -> `Node (x, List.map force l) let find ~pset f t = let rec _find_kl f l = match l () with | Seq.Nil -> None | Seq.Cons (x, l') -> (match f x with | None -> _find_kl f l' | Some _ as res -> res) in _find_kl f (bfs ~pset t) (** {2 Pretty-printing} *) let pp pp_x fmt t = (* at depth [lvl] *) let rec pp fmt t = match t with | `Nil -> () | `Node (x, children) -> let children = filter children in (match children with | [] -> pp_x fmt x | _ :: _ -> Format.fprintf fmt "@[(@[%a@]%a)@]" pp_x x pp_children children) and filter l = let l = List.fold_left (fun acc c -> match c () with | `Nil -> acc | `Node _ as sub -> sub :: acc) [] l in List.rev l and pp_children fmt children = (* remove empty children *) List.iter (fun c -> Format.fprintf fmt "@,"; pp fmt c) children in pp fmt (t ()); () (** {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 | `Id of string | `Other of string * string ] (** Dot attributes for nodes *) type graph = string * attribute list t list (** A dot graph is a name, plus a list of trees labelled with attributes *) let mk_id format = let buf = Buffer.create 64 in Printf.kbprintf (fun _ -> `Id (Buffer.contents buf)) buf format let mk_label format = let buf = Buffer.create 64 in Printf.kbprintf (fun _ -> `Label (Buffer.contents buf)) buf format let make ~name l = name, l let singleton ~name t = name, [ t ] (* find and remove the `Id attribute, if any *) let rec _find_id acc l = match l with | [] -> raise Not_found | `Id n :: l' -> n, List.rev_append acc l' | x :: l' -> _find_id (x :: acc) l' let _pp_attr fmt attr = match attr with | `Color c -> Format.fprintf fmt "color=%s" c | `Shape s -> Format.fprintf fmt "shape=%s" s | `Weight w -> Format.fprintf fmt "weight=%d" w | `Style s -> Format.fprintf fmt "style=%s" s | `Label l -> Format.fprintf fmt "label=\"%s\"" l | `Other (name, value) -> Format.fprintf fmt "%s=\"%s\"" name value | `Id _ -> () (* should not be here *) let rec _pp_attrs fmt l = match l with | [] -> () | [ x ] -> _pp_attr fmt x | x :: l' -> _pp_attr fmt x; Format.pp_print_char fmt ','; _pp_attrs fmt l' let pp out (name, l) = (* nodes already printed *) let tbl = Hashtbl.create 32 in (* fresh name generator *) let new_name = let n = ref 0 in fun () -> let s = Printf.sprintf "node_%d" !n in incr n; s in (* the name for some node is either defined, either a fresh random name *) let get_name x = try _find_id [] x with Not_found -> new_name (), x in (* recursive printing (bfs) *) let rec aux q = if FQ.is_empty q then () else ( let (parent, x), q' = FQ.pop_exn q in let q' = pp_node q' ?parent x in aux q' ) and pp_node q ?parent t = match t () with | `Nil -> q | `Node (x, l) -> let name, attrs = get_name x in (match parent with | None -> () | Some n -> Format.fprintf out " %s -> %s;@," n name); if not (Hashtbl.mem tbl name) then ( Hashtbl.add tbl name (); Format.fprintf out "@[%s [%a];@]@," name _pp_attrs attrs; List.fold_left (fun q y -> FQ.push q (Some name, y)) q l ) else q in let q = List.fold_left (fun q y -> FQ.push q (None, y)) FQ.empty l in (* preamble *) Format.fprintf out "@[digraph \"%s\" {@," name; aux q; Format.fprintf out "}@]@."; () let pp_single name out t = pp out (singleton ~name t) let print_to_file filename g = let oc = open_out filename in let fmt = Format.formatter_of_out_channel oc in try pp fmt g; Format.pp_print_flush fmt (); close_out oc with e -> close_out oc; raise e let to_file ?(name = "graph") filename trees = let g = make ~name trees in print_to_file filename g end