(**************************************************************************)
(*                                                                        *)
(*  Ocamlgraph: a generic graph library for OCaml                         *)
(*  Copyright (C) 2004-2010                                               *)
(*  Sylvain Conchon, Jean-Christophe Filliatre and Julien Signoles        *)
(*                                                                        *)
(*  This software is free software; you can redistribute it and/or        *)
(*  modify it under the terms of the GNU Library General Public           *)
(*  License version 2.1, with the special exception on linking            *)
(*  described in file LICENSE.                                            *)
(*                                                                        *)
(*  This software is distributed in the hope that it will be useful,      *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *)
(*                                                                        *)
(**************************************************************************)

(** Interface with {i GraphViz}

    This module provides a basic interface with dot and neato,
    two programs of the GraphViz toolbox.
    These tools are available at the following URLs:
      http://www.graphviz.org/
      http://www.research.att.com/sw/tools/graphviz/ *)

open Format
open Pervasives (* for compatibility with ocaml 3.12.0+dev17
		   (incoming ocaml3.12) *)

(***************************************************************************)
(** {2 Common stuff} *)

(** Because the neato and dot engines present a lot of common points -
    in particular in the graph description language, large parts of
    the code is shared.  First, the [!CommonAttributes] module defines
    attributes of graphs, nodes and edges that are understood by the
    two engines.  Second, given a module (of type [!ENGINE])
    describing an engine the [!MakeEngine] functor provides suitable
    interface function for it. *)

(*-------------------------------------------------------------------------*)
(** {3 Common attributes} *)

type color = int

type color_with_transparency = int32

let color_to_color_with_transparency color =
  Int32.add (Int32.shift_left (Int32.of_int color) 8) 0xFFl

let fprint_color ppf color =
  fprintf ppf "\"#%06X\"" color

let fprint_color_with_transparency ppf color =
  fprintf ppf "\"#%08lX\"" color

let fprint_string ppf s = fprintf ppf "\"%s\"" s
(*  let s' = String.escaped s in
  if s' = s && s <> ""
  then fprintf ppf "%s" s
  else fprintf ppf "\"%s\"" s'*)

let fprint_string_user ppf s =
(*  let s = String.escaped s in*)
    fprintf ppf "\"%s\"" s

type arrow_style =
  [ `None | `Normal | `Inv | `Dot | `Odot | `Invdot | `Invodot ]

let fprint_arrow_style ppf = function
    `None -> fprintf ppf "none"
  | `Normal -> fprintf ppf "normal"
  | `Inv -> fprintf ppf "inv"
  | `Dot -> fprintf ppf "dot"
  | `Odot -> fprintf ppf "odot"
  | `Invdot -> fprintf ppf "invdot"
  | `Invodot -> fprintf ppf "invodot"

let fprint_dir ppf = function
    `TopToBottom -> fprintf ppf "TB"
  | `LeftToRight -> fprintf ppf "LR"

(** The [ATTRIBUTES] module type defines the interface for the engines. *)
module type ATTRIBUTES = sig

  type graph  (** Attributes of graphs. *)

  type vertex (** Attributes of vertices. *)

  type edge   (** Attributes of edges. *)

  (** Attributes of (optional) boxes around vertices. *)
  type subgraph = {
    sg_name : string;            (** Box name. *)
    sg_attributes : vertex list; (** Box attributes. *)
  }

end

(** The [CommonAttributes] module defines attributes for graphs, nodes and
    edges that are available in the two engines, dot and neato. *)
module CommonAttributes = struct

  (** Attributes of graphs. *)
  type graph =
    [ `Center of bool
        (** Centers the drawing on the page.  Default value is [false]. *)
    | `Fontcolor of color
        (** Sets the font color.  Default value is [black]. *)
    | `Fontname of string
        (** Sets the font family name.  Default value is ["Times-Roman"]. *)
    | `Fontsize of int
        (** Sets the type size (in points).  Default value is [14]. *)
    | `Label of string
        (** Caption for graph drawing. *)
    | `Orientation of [ `Portrait | `Landscape ]
        (** Sets the page orientation.  Default value is [`Portrait]. *)
    | `Page of float * float
        (** Sets the PostScript pagination unit, e.g [8.5, 11.0]. *)
    | `Pagedir of [ `TopToBottom | `LeftToRight ]
        (** Traversal order of pages.  Default value is [`TopToBottom]. *)
    | `Size of float * float
        (** Sets the bounding box of drawing (in inches). *)
    | `OrderingOut
        (** Constrains  order of out-edges in a subgraph according to
          their file sequence *)
    ]

  (** Attributes of nodes. *)
  type vertex =
    [ `Color of color
        (** Sets the color of the border of the node. Default value is [black]
         *)
    | `ColorWithTransparency of color_with_transparency
        (** Sets the color of the border of the vertex with a transparency
            component. Default value is fully opaque [black] *)
    | `Fontcolor of color
        (** Sets the label font color.  Default value is [black]. *)
    | `Fontname of string
        (** Sets the label font family name.  Default value is
            ["Times-Roman"]. *)
    | `Fontsize of int
        (** Sets the label type size (in points).  Default value is [14]. *)
    | `Height of float
        (** Sets the minimum height.  Default value is [0.5]. *)
    | `Label of string
        (** Sets the label printed in the node. The string may include escaped
            newlines [\n], [\l], or [\r] for center, left, and right justified
	    lines.
            Record labels may contain recursive box lists delimited by { | }.
	*)
    | `Orientation of float
        (** Node rotation angle, in degrees.  Default value is [0.0]. *)
    | `Peripheries of int
        (** Sets  the  number  of periphery lines drawn around the polygon. *)
    | `Regular of bool
        (** If [true], then the polygon is made regular, i.e. symmetric about
	    the x and y axis, otherwise  the polygon   takes   on   the  aspect
	    ratio of the label.  Default value is [false]. *)
    | `Shape of
        [`Ellipse | `Box | `Circle | `Doublecircle | `Diamond
        | `Plaintext | `Record | `Polygon of int * float]
        (** Sets the shape of the node.  Default value is [`Ellipse].
            [`Polygon (i, f)] draws a polygon with [n] sides and a skewing
            of [f]. *)
    | `Style of [ `Filled | `Solid | `Dashed | `Dotted | `Bold | `Invis ]
        (** Sets the layout style of the node.  Several styles may be combined
            simultaneously. *)
    | `Width of float
        (** Sets the minimum width.  Default value is [0.75]. *)
    ]

  (** Attributes of edges. *)
  type edge =
    [ `Color of color
        (** Sets the edge stroke color.  Default value is [black]. *)
    | `ColorWithTransparency of color_with_transparency
        (** Sets the color of the border of the vertex with a transparency
            component. Default value is fully opaque [black] *)
    | `Decorate of bool
        (** If [true], draws a line connecting labels with their edges. *)
    | `Dir of [ `Forward | `Back | `Both | `None ]
        (** Sets arrow direction.  Default value is [`Forward]. *)
    | `Fontcolor of color
        (** Sets the label font color.  Default value is [black]. *)
    | `Fontname of string
        (** Sets the label font family name.  Default value is
	    ["Times-Roman"]. *)
    | `Fontsize of int
        (** Sets the label type size (in points).  Default value is [14]. *)
    | `Label of string
        (** Sets the label to be attached to the edge.  The string may include
	    escaped newlines [\n], [\l], or [\r] for centered, left, or right
	    justified lines. *)
    | `Labelfontcolor of color
        (** Sets the font color for head and tail labels.  Default value is
            [black]. *)
    | `Labelfontname of string
        (** Sets the font family name for head and tail labels.  Default
            value is ["Times-Roman"]. *)
    | `Labelfontsize of int
        (** Sets the font size for head and tail labels (in points).
            Default value is [14]. *)
    | `Style of [ `Solid | `Dashed | `Dotted | `Bold | `Invis ]
        (** Sets the layout style of the edge.  Several styles may be combined
            simultaneously. *)
    ]

  (** Pretty-print. *)

  let fprint_orientation ppf = function
      `Portrait -> fprintf ppf "portrait"
    | `Landscape -> fprintf ppf "landscape"

  let fprint_graph ppf = function
      `Center b -> fprintf ppf "center=%i" (if b then 1 else 0)
    | `Fontcolor a -> fprintf ppf "fontcolor=%a" fprint_color a
    | `Fontname s -> fprintf ppf "fontname=%a" fprint_string s
    | `Fontsize i -> fprintf ppf "fontsize=%i" i
    | `Label s -> fprintf ppf "label=%a" fprint_string_user s
    | `Orientation a -> fprintf ppf "orientation=%a" fprint_orientation a
    | `Page (x, y) -> fprintf ppf "page=\"%f,%f\"" x y
    | `Pagedir a -> fprintf ppf "pagedir=%a" fprint_dir a
    | `Size (x, y) -> fprintf ppf "size=\"%f,%f\"" x y
    | `OrderingOut -> fprintf ppf "ordering=out"

  let fprint_shape ppf = function
    | `Ellipse -> fprintf ppf "ellipse"
    | `Box -> fprintf ppf "box"
    | `Circle -> fprintf ppf "circle"
    | `Doublecircle -> fprintf ppf "doublecircle"
    | `Diamond -> fprintf ppf "diamond"
    | `Plaintext -> fprintf ppf "plaintext"
    | `Record -> fprintf ppf "record"
    | `Polygon (i, f) -> fprintf ppf "polygon, sides=%i, skew=%f" i f

  let fprint_node_style ppf = function
      `Filled -> fprintf ppf "filled"
    | `Solid -> fprintf ppf "solid"
    | `Dashed -> fprintf ppf "dashed"
    | `Dotted -> fprintf ppf "dotted"
    | `Bold -> fprintf ppf "bold"
    | `Invis -> fprintf ppf "invis"

  let fprint_vertex ppf = function
    | `Color a -> fprintf ppf "color=%a" fprint_color a
    | `ColorWithTransparency a ->
        fprintf ppf "color=%a" fprint_color_with_transparency a
    | `Fontcolor a -> fprintf ppf "fontcolor=%a" fprint_color a
    | `Fontname s -> fprintf ppf "fontname=%a"  fprint_string s
    | `Fontsize i -> fprintf ppf "fontsize=%i" i
    | `Height f -> fprintf ppf "height=%f" f
    | `Label s -> fprintf ppf "label=%a" fprint_string_user s
    | `Orientation f -> fprintf ppf "orientation=%f" f
    | `Peripheries i -> fprintf ppf "peripheries=%i" i
    | `Regular b -> fprintf ppf "regular=%b" b
    | `Shape a -> fprintf ppf "shape=%a" fprint_shape a
    | `Style a -> fprintf ppf "style=%a" fprint_node_style a
    | `Width f -> fprintf ppf "width=%f" f

  let fprint_edge_style =
    fprint_node_style

  let fprint_arrow_direction ppf = function
      `Forward -> fprintf ppf "forward"
    | `Back -> fprintf ppf "back"
    | `Both -> fprintf ppf "both"
    | `None -> fprintf ppf "none"

  let fprint_edge ppf = function
    | `Color a -> fprintf ppf "color=%a" fprint_color a
    | `ColorWithTransparency a ->
        fprintf ppf "color=%a" fprint_color_with_transparency a
    | `Decorate b -> fprintf ppf "decorate=%b" b
    | `Dir a -> fprintf ppf "dir=%a" fprint_arrow_direction a
    | `Fontcolor a -> fprintf ppf "fontcolor=%a" fprint_color a
    | `Fontname s -> fprintf ppf "fontname=%a" fprint_string s
    | `Fontsize i -> fprintf ppf "fontsize=%i" i
    | `Label s -> fprintf ppf "label=%a" fprint_string_user s
    | `Labelfontcolor a -> fprintf ppf "labelfontcolor=%a" fprint_color a
    | `Labelfontname s -> fprintf ppf "labelfontname=\"%s\"" s
	(* (String.escaped s) *)
    | `Labelfontsize i -> fprintf ppf "labelfontsize=%i" i
    | `Style a -> fprintf ppf "style=%a" fprint_edge_style a

end


(*-------------------------------------------------------------------------*)
(** {3 The [MakeEngine] functor} *)

(** An engine is described by a module of the following signature. *)
module type ENGINE = sig

  module Attributes : sig
    include ATTRIBUTES
    val fprint_graph:formatter -> graph -> unit
    val fprint_vertex: formatter -> vertex -> unit
    val fprint_edge: formatter -> edge -> unit
  end

  (** The litteral name of the engine. *)
  val name: string

  (** The keyword for graphs ("digraph" for dot, "graph" for neato) *)
  val opening: string

  (** The litteral for edge arrows ("->" for dot, "--" for neato) *)
  val edge_arrow: string

end

module type GRAPH = sig

end

module MakeEngine
  (EN: ENGINE)
  (X : sig
     type t
     module V : sig type t end
     module E : sig type t val src : t -> V.t val dst : t -> V.t end

     val iter_vertex : (V.t -> unit) -> t -> unit
     val iter_edges_e : (E.t -> unit) -> t -> unit

     val graph_attributes: t -> EN.Attributes.graph list

     val default_vertex_attributes: t -> EN.Attributes.vertex list
     val vertex_name : V.t -> string
     val vertex_attributes: V.t -> EN.Attributes.vertex list

     val default_edge_attributes: t -> EN.Attributes.edge list
     val edge_attributes: E.t -> EN.Attributes.edge list
     val get_subgraph : V.t -> EN.Attributes.subgraph option
   end) =
struct

  let command = ref EN.name
  let set_command cmd =
    command := cmd

  exception Error of string

  let handle_error f arg =
    try
      f arg
    with
	Error msg ->
	  Printf.eprintf "%s: %s failure\n   %s\n"
	  Sys.argv.(0) EN.name msg;
	  flush stderr;
	  exit 2

    (** [fprint_graph_attributes ppf list] pretty prints a list of
        graph attributes on the formatter [ppf].  Attributes are separated
        by a ";". *)
    let fprint_graph_attributes ppf list =
       List.iter (function att ->
	 fprintf ppf "%a;@ " EN.Attributes.fprint_graph att
       ) list

   (** [fprint_graph_attribute printer ppf list] pretty prints a list of
       attributes on the formatter [ppf], using the printer [printer] for
       each attribute.  The list appears between brackets and attributes
       are speparated by ",".  If the list is empty, nothing is printed. *)
    let fprint_attributes fprint_attribute ppf = function
	[] -> ()
      | hd :: tl ->
	  let rec fprint_attributes_rec ppf = function
	      [] -> ()
	    | hd' :: tl' ->
		fprintf ppf ",@ %a%a"
		  fprint_attribute hd'
		  fprint_attributes_rec tl'
	  in
	  fprintf ppf " [@[<hov>%a%a@]]"
	    fprint_attribute hd
	    fprint_attributes_rec tl

    (** [fprint_graph_attributes ppf list] pretty prints a list of
        node attributes using the format of [fprint_attributes]. *)
    let fprint_node_attributes ppf list =
      fprint_attributes EN.Attributes.fprint_vertex ppf list

    (** [fprint_graph_attributes ppf list] pretty prints a list of
        edge attributes using the format of [fprint_attributes]. *)
    let fprint_edge_attributes ppf list =
      fprint_attributes EN.Attributes.fprint_edge ppf list

    (** [fprint_graph ppf graph] pretty prints the graph [graph] in
        the CGL language on the formatter [ppf]. *)
    let fprint_graph ppf graph =
      let subgraphs = Hashtbl.create 7 in

      (* Printing nodes. *)

      let print_nodes ppf =

	let default_node_attributes = X.default_vertex_attributes graph in
	if default_node_attributes  <> [] then
	  fprintf ppf "node%a;@ "
	    fprint_node_attributes default_node_attributes;

	X.iter_vertex
          (function node ->
             begin match X.get_subgraph node with
             | None -> ()
             | Some sg ->
                 try
                   let (sg,nodes) =
		     Hashtbl.find subgraphs sg.EN.Attributes.sg_name
		   in
                   Hashtbl.replace subgraphs
		     sg.EN.Attributes.sg_name (sg,node::nodes)
                 with Not_found ->
                   Hashtbl.add subgraphs sg.EN.Attributes.sg_name (sg,[node])
             end;
	     fprintf ppf "%s%a;@ "
	       (X.vertex_name node)
	       fprint_node_attributes (X.vertex_attributes node))
          graph

      in

      (* Printing subgraphs *)

      let print_subgraphs ppf =

        Hashtbl.iter
          (fun name (sg,nodes) ->
             fprintf ppf "@[<v 2>subgraph cluster_%s { %t%t };@]@\n"
               name

               (fun ppf ->
                  (List.iter
                     (fun n ->
			fprintf ppf "%a;@\n" EN.Attributes.fprint_vertex n)
                     sg.EN.Attributes.sg_attributes))

               (fun ppf ->
                  (List.iter
		     (fun n -> fprintf ppf "%s;" (X.vertex_name n))
                     nodes))
          )
          subgraphs

      in

      (* Printing edges *)

      let print_edges ppf =

	let default_edge_attributes = X.default_edge_attributes graph in
	if default_edge_attributes <> [] then
	  fprintf ppf "edge%a;@ "
	    fprint_edge_attributes default_edge_attributes;

	X.iter_edges_e (function edge ->
	                  fprintf ppf "%s %s %s%a;@ "
	                    (X.vertex_name (X.E.src edge))
	                    EN.edge_arrow
	                    (X.vertex_name (X.E.dst edge))
	                    fprint_edge_attributes (X.edge_attributes edge)
                       ) graph

      in

      fprintf ppf "@[<v>%s G {@ @[<v 2>  %a"
	EN.opening
	fprint_graph_attributes (X.graph_attributes graph);
      fprintf ppf "%t@ " print_nodes;
      fprintf ppf "%t@ " print_subgraphs;
      fprintf ppf "%t@ " print_edges;
      fprintf ppf "@]}@]"

    (** [output_graph oc graph] pretty prints the graph [graph] in the dot
	language on the channel [oc]. *)
    let output_graph oc graph =
      let ppf = formatter_of_out_channel oc in
      fprint_graph ppf graph;
      pp_print_flush ppf ()

  end

(***************************************************************************)
(** {2 Interface with the dot engine} *)

(** The [DotAttributes] module defines attributes for graphs, nodes and edges
    that are available in the dot engine. *)
module DotAttributes = struct

  (** Attributes of graphs.  They include all common graph attributes and
      several specific ones.  All attributes described in the "dot User's
      Manual, February 4, 2002" are handled, excepted: clusterank, color,
      compound, labeljust, labelloc, ordering, rank, remincross, rotate,
      searchsize and style.
   *)
  type graph =
    [ CommonAttributes.graph
    | `Bgcolor of color
        (** Sets the background color and the inital fill color. *)
    | `BgcolorWithTransparency of color_with_transparency
        (** Sets the background color and the inital fill color with
            a transparency component. *)
    | `Comment of string
        (** Comment string. *)
    | `Concentrate of bool
        (** If [true], enables edge concentrators.  Default value is [false]. *)
    | `Fontpath of string
        (** List of directories for fonts. *)
    | `Layers of string list
        (** List of layers. *)
    | `Margin of float
        (** Sets the page margin (included in the page size).  Default value is
            [0.5]. *)
    | `Mclimit of float
        (** Scale factor for mincross iterations.  Default value is [1.0]. *)
    | `Nodesep of float
        (** Sets the minimum separation between nodes, in inches.  Default
            value is [0.25]. *)
    | `Nslimit of int
        (** If set of [f], bounds network simplex iterations by [f *
            <number of nodes>] when ranking nodes. *)
    | `Nslimit1 of int
        (** If set of [f], bounds network simplex iterations by [f *
            <number of nodes>] when setting x-coordinates. *)
    | `Ranksep of float
        (** Sets the minimum separation between ranks. *)
    | `Quantum of float
        (** If not [0.0], node label dimensions will be rounded to integral
	    multiples of it.  Default value is [0.0]. *)
    | `Rankdir of [ `TopToBottom | `LeftToRight ]
        (** Direction of rank ordering.  Default value is [`TopToBottom]. *)
    | `Ratio of [ `Float of float | `Fill | `Compress| `Auto ]
        (** Sets the aspect ratio. *)
    | `Samplepoints of int
        (** Number of points used to represent ellipses and circles on output.
	    Default value is [8]. *)
    | `Url of string
        (** URL associated with graph (format-dependent). *)
    ]

  (** Attributes of nodes.  They include all common node attributes and
      several specific ones.  All attributes described in the "dot User's
      Manual, February 4, 2002" are handled, excepted: bottomlabel, group,
      shapefile and toplabel.
   *)
  type vertex =
    [ CommonAttributes.vertex
    | `Comment of string
        (** Comment string. *)
    | `Distortion of float
        (* TEMPORARY *)
    | `Fillcolor of color
        (** Sets the fill color (used when `Style filled).  Default value
            is [lightgrey]. *)
    | `FillcolorWithTransparency of color_with_transparency
        (** Sets the fill color (used when `Style filled) with a transparency
            component.  Default value is fully opaque [lightgrey]. *)
    | `Fixedsize of bool
        (** If [true], forces the given dimensions to be the actual ones.
            Default value is [false]. *)
    | `Layer of string
        (** Overlay. *)
    | `Url of string
        (** The  default  url  for  image  map  files; in PostScript files,
            the base URL for all relative URLs, as recognized by Acrobat
	    Distiller 3.0 and up. *)
    | `Z of float
        (** z coordinate for VRML output. *)
    ]

  (** Attributes of edges.  They include all common edge attributes and
      several specific ones.  All attributes described in the "dot User's
      Manual, February 4, 2002" are handled, excepted: lhead and ltail.
   *)
  type edge =
    [ CommonAttributes.edge
    | `Arrowhead of arrow_style
        (** Sets the style of the head arrow.  Default value is [`Normal]. *)
    | `Arrowsize of float
        (** Sets the scaling factor of arrowheads.  Default value is [1.0]. *)
    | `Arrowtail of arrow_style
        (** Sets the style of the tail arrow.  Default value is [`Normal]. *)
    | `Comment of string
        (** Comment string. *)
    | `Constraint of bool
        (** If [false], causes an edge to be ignored for rank assignment.
            Default value is [true]. *)
    | `Headlabel of string
        (** Sets the label attached to the head arrow. *)
    | `Headport of [ `N | `NE | `E | `SE | `S | `SW | `W | `NW ]
        (* TEMPORARY *)
    | `Headurl of string
        (** Url attached to head label if output format is ismap. *)
    | `Labelangle of float
        (** Angle in degrees which head or tail label is rotated off edge.
            Default value is [-25.0]. *)
    | `Labeldistance of float
        (** Scaling factor for distance of head or tail label from node.
            Default value is [1.0]. *)
    | `Labelfloat of bool
        (** If [true], lessen constraints on edge label placement.
            Default value is [false]. *)
    | `Layer of string
        (** Overlay. *)
    | `Minlen of int
        (** Minimum rank distance between head an tail.  Default value is [1]. *)
    | `Samehead of string
        (** Tag for head node; edge heads with the same tag are merged onto the
	    same port. *)
    | `Sametail of string
        (** Tag for tail node; edge tails with the same tag are merged onto the
	    same port. *)
    | `Taillabel of string
        (** Sets the label attached to the tail arrow. *)
    | `Tailport of [ `N | `NE | `E | `SE | `S | `SW | `W | `NW ]
        (* TEMPORARY *)
    | `Tailurl of string
        (** Url attached to tail label if output format is ismap. *)
    | `Weight of int
        (** Sets the integer cost of stretching the edge.  Default value is
            [1]. *)
    ]

    type subgraph = {
      sg_name : string;
      sg_attributes : vertex list;
    }

    (** {4 Pretty-print of attributes} *)

    let rec fprint_string_list ppf = function
      [] -> ()
      | [hd] -> fprintf ppf "%s" hd
      | hd :: tl -> fprintf ppf "%s,%a" hd fprint_string_list tl

    let fprint_ratio ppf = function
	`Float f -> fprintf ppf "%f" f
      | `Fill -> fprintf ppf "fill"
      | `Compress -> fprintf ppf "compress"
      | `Auto -> fprintf ppf "auto"

    let fprint_graph ppf = function
	#CommonAttributes.graph as att -> CommonAttributes.fprint_graph ppf att
      | `Bgcolor a -> fprintf ppf "bgcolor=%a" fprint_color a
      | `BgcolorWithTransparency a ->
          fprintf ppf "bcolor=%a" fprint_color_with_transparency a
      | `Comment s -> fprintf ppf "comment=%a" fprint_string s
      | `Concentrate b -> fprintf ppf "concentrate=%b" b
      | `Fontpath s -> fprintf ppf "fontpath=%a" fprint_string s
      | `Layers s -> fprintf ppf "layers=%a" fprint_string_list s
      | `Margin f -> fprintf ppf "margin=%f" f
      | `Mclimit f -> fprintf ppf "mclimit=%f" f
      | `Nodesep f -> fprintf ppf "nodesep=%f" f
      | `Nslimit i -> fprintf ppf "nslimit=%i" i
      | `Nslimit1 i -> fprintf ppf "nslimit1=%i" i
      | `Ranksep f -> fprintf ppf "ranksep=%f" f
      | `Quantum f -> fprintf ppf "quantum=%f" f
      | `Rankdir a -> fprintf ppf "rankdir=%a" fprint_dir a
      | `Ratio a -> fprintf ppf "ratio=%a" fprint_ratio a
      | `Samplepoints i -> fprintf ppf "samplepoints=%i" i
      | `Url s -> fprintf ppf "URL=\"%s\"" s (*(String.escaped s)*)

    let fprint_vertex ppf = function
	#CommonAttributes.vertex as att ->
	  CommonAttributes.fprint_vertex ppf att
      | `Comment s -> fprintf ppf "comment=%a" fprint_string s
      | `Distortion f -> fprintf ppf "distortion=%f" f
      | `Fillcolor a -> fprintf ppf "fillcolor=%a" fprint_color a
      | `FillcolorWithTransparency a ->
          fprintf ppf "fillcolor=%a" fprint_color_with_transparency a
      | `Fixedsize b -> fprintf ppf "fixedsize=%b" b
      | `Layer s -> fprintf ppf "layer=%a" fprint_string s
      | `Url s -> fprintf ppf "URL=\"%s\"" s (*(String.escaped s)*)
      | `Z f -> fprintf ppf "z=%f" f

    let fprint_port ppf = function
	`N -> fprintf ppf "n"
      | `NE -> fprintf ppf "ne"
      | `E -> fprintf ppf "e"
      | `SE -> fprintf ppf "se"
      | `S -> fprintf ppf "s"
      | `SW -> fprintf ppf "sw"
      | `W -> fprintf ppf "w"
      | `NW -> fprintf ppf "nw"

    let fprint_edge ppf = function
	#CommonAttributes.edge as att -> CommonAttributes.fprint_edge ppf att
      | `Arrowhead a -> fprintf ppf "arrowhead=%a" fprint_arrow_style a
      | `Arrowsize f -> fprintf ppf "arrowsize=%f" f
      | `Arrowtail a -> fprintf ppf "arrowtail=%a" fprint_arrow_style a
      | `Comment s -> fprintf ppf "comment=%a" fprint_string s
      | `Constraint b -> fprintf ppf "constraint=%b" b
      | `Headlabel s -> fprintf ppf "headlabel=%a" fprint_string s
      | `Headport a -> fprintf ppf "headport=%a" fprint_port a
      | `Headurl s -> fprintf ppf "headURL=%a" fprint_string s
      | `Labelangle f -> fprintf ppf "labelangle=%f" f
      | `Labeldistance f -> fprintf ppf "labeldistance=%f" f
      | `Labelfloat b -> fprintf ppf "labelfloat=%b" b
      | `Layer s -> fprintf ppf "layer=%a" fprint_string s
      | `Minlen i -> fprintf ppf "minlen=%i" i
      | `Samehead s -> fprintf ppf "samehead=%a" fprint_string s
      | `Sametail s -> fprintf ppf "sametail=%a" fprint_string s
      | `Taillabel s -> fprintf ppf "taillabel=%a" fprint_string s
      | `Tailport a -> fprintf ppf "tailport=%a" fprint_port a
      | `Tailurl s -> fprintf ppf "tailURL=%a" fprint_string s
      | `Weight i -> fprintf ppf "weight=%i" i

end

(** Graph modules with dot attributes *)
module type GraphWithDotAttrs = sig
  include Sig.G

 (** Graph, vertex and edge attributes. *)
  val graph_attributes: t -> DotAttributes.graph list

  (** Vertex attributes *)
  val default_vertex_attributes: t -> DotAttributes.vertex list
  val vertex_name : V.t -> string
  val vertex_attributes: V.t -> DotAttributes.vertex list

  (** Edge attributes *)
  val default_edge_attributes: t -> DotAttributes.edge list
  val edge_attributes: E.t -> DotAttributes.edge list

  val get_subgraph : V.t -> DotAttributes.subgraph option
    (** The box (if exists) which the vertex belongs to. Boxes with same
	   names are not distinguished and so they should have the same
	   attributes. *)
end

module Dot =
  MakeEngine (struct
		module Attributes = DotAttributes
		let name = "dot"
		let opening = "digraph"
		let edge_arrow = "->"
	      end)

(***************************************************************************)
(** {2 Interface with the neato engine} *)

(** The [NeatoAttributes] module defines attributes for graphs, nodes and edges
    that are available in the neato engine. *)
module NeatoAttributes = struct

  (** Attributes of graphs.  They include all common graph attributes and
      several specific ones.  All attributes described in the "Neato User's
      manual, April 10, 2002" are handled. *)
  type graph =
    [ CommonAttributes.graph
    | `Margin of float * float
        (** Sets the page margin (included in the page size).  Default value is
            [0.5, 0.5]. *)
    | `Start of int
        (** Seed for random number generator. *)
    | `Overlap of bool
	(** Default value is [true]. *)
    | `Spline of bool
	(** [true] makes edge splines if nodes don't overlap.
	    Default value is [false]. *)
    | `Sep of float
	(** Edge spline separation factor from nodes.  Default value
	    is [0.0]. *)
    ]

  (** Attributes of nodes.  They include all common node attributes and
      several specific ones.  All attributes described in the "Neato User's
      manual, April 10, 2002" are handled. *)
  type vertex =
    [ CommonAttributes.vertex
    | `Pos of float * float
        (** Initial coordinates of the node. *)
    ]

  (** Attributes of edges.  They include all common edge attributes and
      several specific ones.  All attributes described in the "Neato User's
      manual, April 10, 2002" are handled. *)
  type edge =
    [ CommonAttributes.edge
    | `Id of string
        (** Optional value to distinguish multiple edges. *)
    | `Len of float
        (** Preferred length of edge.  Default value is [1.0]. *)
    | `Weight of float
        (** Strength of edge spring.  Default value is [1.0]. *)
    ]

    type subgraph = {
      sg_name : string;
      sg_attributes : vertex list;
    }

  (** {4 Pretty-print of attributes} *)

    let fprint_graph ppf = function
	#CommonAttributes.graph as att -> CommonAttributes.fprint_graph ppf att
      | `Margin (f1, f2) -> fprintf ppf "margin=\"%f,%f\"" f1 f2
      | `Start i -> fprintf ppf "start=%i" i
      | `Overlap b -> fprintf ppf "overlap=%b" b
      | `Spline b -> fprintf ppf "spline=%b" b
      | `Sep f -> fprintf ppf "sep=%f" f

    let fprint_vertex ppf = function
	#CommonAttributes.vertex as att ->
	  CommonAttributes.fprint_vertex ppf att
      | `Pos (f1, f2) -> fprintf ppf "pos=\"%f,%f\"" f1 f2

    let fprint_edge ppf = function
	#CommonAttributes.edge as att -> CommonAttributes.fprint_edge ppf att
      | `Id s -> fprintf ppf "id=%a" fprint_string s
      | `Len f -> fprintf ppf "len=%f" f
      | `Weight f -> fprintf ppf "weight=%f" f

end

module Neato =
  MakeEngine (struct
		module Attributes = NeatoAttributes
		let name = "neato"
		let opening = "graph"
		let edge_arrow = "--"
	      end)

(*
Local Variables:
compile-command: "make -C .."
End:
*)
