Skip to content
Snippets Groups Projects
graphGen_arg.ml 10.2 KiB
Newer Older
let () = Random.self_init ();

type action = string
type grid_arg = {
  mutable width: int;
  mutable height: int;
}

type qudg_arg = {
  mutable width: float;
  mutable height: float;
  mutable radius: float;
  mutable r1: float;
  mutable p: float;
}

type er_prob = float (*between 0 and 1*)
type ba_m = int (*positive*)

type t = {
  mutable outputFile: string;
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
  mutable dotUDG: string;
  mutable dotUDGrad: string;
  mutable action: action;

  mutable n : int;
  mutable grid : grid_arg;
  mutable er : er_prob;
  mutable ba : ba_m;
  mutable qudg : qudg_arg;
  mutable directed : bool;

  mutable _args : (string * Arg.spec * string) list;
  mutable _man : (string * (string list * action) list) list;

  mutable _others : string list;
  mutable _margin : int;
}

let usage_msg do_print_command tool =
  "gg is an experimental graph generator.\n"^
  (if do_print_command then 
    ("usage: " ^ tool ^ " <graph-kind> [<option>]*\n")
    ("usage: "^tool^" [option]*\n"))

let print_usage output do_print_command tool = 
  Printf.fprintf output "%s%s" (usage_msg do_print_command tool) (
    if (do_print_command) then 
      "use -h to see the available <graph-kind>.\n\n" 
    else "use -h to see available <option>.\n"
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
let (make_args : unit -> t) =
  fun () ->
  {
    outputFile = "";
    dotUDG = "";
    dotUDGrad = "";
    action = "void";

    n = -1;
    grid = {
      width = 0;
      height = 0;
    };
    er = 0.3;
    ba = 2;
    qudg = {
      width = 10.;
      height = 10.;
      radius = 3.;
      r1 = 2.;
      p = 0.5;
    };


    silent = false;
    directed = false;
    _args  = [];
    _man = [];

    _others  = [];
    _margin  = 18;

let first_line b = (
  try (
    let f = String.index b '\n' in
    String.sub b 0 f
  ) with Not_found -> b
)
let unexpected s = (
  prerr_string ("unexpected argument \""^s^"\"");
  prerr_newline ();
)


let printSpec args outChannel action (c, messageList) = (
  List.iter (fun (ml,action_type) ->
      let (m1, oth) = match ml with
       |  h::t -> (h,t)
       |  _ -> ("",[])
      in
      let t2 = String.make args._margin ' ' in
      let cl = 1 + String.length c in
      let t1 = if (cl < args._margin ) then
       String.make (args._margin - cl) ' '
      else
       "\n"^t2
      in
       Printf.fprintf outChannel "  %s%s%s" c t1 m1;
       List.iter (function x -> Printf.fprintf outChannel "\n%s%s" t2 x) oth ;
       Printf.fprintf outChannel "\n" ;
)

let help args tool = (
  Printf.printf "%s" (usage_msg (args.action = "void") tool);
  (
      Printf.printf "where <graph-kind> can be:\n";
      List.iter (printSpec args stdout "") [
        ("clique",[(["Generate a clique graph"],"")]);
        ("star",[(["Generate a star graph"],"")]);
        ("ring",[(["Generate a ring graph"],"")]);
        ("grid",[(["Generate a grid graph"],"")]);
        ("HC",[(["Generate a hyper-cube graph"],"")]);
        ("ER",[(["Generate a graph using the Erdos Renyi algo"],"")]);
        ("BA",[(["Generate a graph using the Barabasi–Albert algo"],"")]);
        ("tree",[(["Generate an acyclic graph (tree)"],"")]);
        ("UDG",[(["Generate a graph using the Unit Disc Graph algo"],"")]);
        ("QUDG",[(["Generate a graph using the Quasi UDG algo"],"")]);
    Printf.printf "<option> can be:\n";
    List.iter (printSpec args stdout "void") (List.rev args._man);
    if (args.action <> "void") then (
      Printf.printf "<option> specific to this <graph-kind>:\n";
      List.iter (printSpec args stdout args.action) (List.rev args._man)
    ) else (
      Printf.printf "Use '%s <graph-kind> -h' to see specific options." tool;
      Printf.printf "\n";
    )
)


let (mkopt : t -> string list -> ?arg:string -> Arg.spec ->
     (string list * action) list -> unit) =
  fun opt ol ?(arg="") se ml ->
    let add_option o = opt._args <- (o, se, "")::opt._args in
     List.iter add_option ol ;
     let col1 = (String.concat ", " ol)^arg in
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
let (mkoptab : string array -> t -> unit) =
    mkopt args ["--output";"-o"] ~arg:" <string>"
      (Arg.String (fun s -> args.outputFile <- s))
      [(["Redirect stdout into a file"],"void")];
    let msg = "Set the node number in the graph" in
    mkopt args  ["--nodes-number";"-n"] ~arg:" <int>"
      (Arg.Int (fun n -> match args.action with
      | "grid" | "HC" | "void" -> unexpected "-n"
      | _ -> args.n <- n ))
      [([msg],"clique");([msg],"star");([msg],"ring");
      ([msg],"ER");([msg],"BA");([msg],"tree");([msg],"UDG");([msg],"QUDG")];

    mkopt args  ["--dimension";"-d"] ~arg:" <int>"
      (Arg.Int (fun n -> match args.action with
      | "HC"-> args.n <- n
      | _ -> unexpected "-d"))
      [(["Set the hyper-cube dimension"],"HC")];

    mkopt args  ["--width";"-w"] ~arg:" <float>"
      (Arg.Float (fun w -> match args.action with
      | "grid" -> args.grid.width <- (int_of_float w)
      | "UDG" | "QUDG" ->  args.qudg.width <- w
      [(["Set the grid's width \n"],"grid");
      (["Set the UDG's terrain width";"10 by default.\n"],"UDG");
      (["Set the QUDG's terrain width";"10 by default.\n"],"QUDG")];
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
    mkopt args  ["--height";"-he"] ~arg:" <float>"
      (Arg.Float (fun h -> match args.action with
      | "grid" -> args.grid.height <- (int_of_float h)
      | "UDG" | "QUDG" ->  args.qudg.height <- h
      [(["Set the grid's height\n"],"grid");
      (["Set the UDG's terrain height";"10 by default.\n"],"UDG");
      (["Set the QUDG's terrain height";"10 by default.\n"],"QUDG")];
    mkopt args ["--edge-probability";"-p"]~arg:" <float>"
      (Arg.Float (fun p -> match args.action with
      | "ER" ->  args.er <- p
      | _ -> unexpected "-p"))
      [(["Set the edge appearing probability.";
        "Must be between 0 and 1, and is set to 0.3 by default\n"],"ER")];


    mkopt args ["--";"-m"]~arg:" <int>"
      (Arg.Int (fun m -> match args.action with
      | "BA" ->  args.ba <- m
      | _ -> unexpected "-m"))
      [(["Set the number of edge generated per additional node";
    mkopt args ["--radius";"-r"]~arg:" <float>"
      (Arg.Float (fun r -> match args.action with
      | "UDG" ->  args.qudg.radius <- r
      [(["Set the Unit Disc's radius around all nodes.";
         "3 by default.\n"],"UDG")];

    mkopt args ["--first-radius";"-r0"]~arg:" <float>"
      (Arg.Float (fun r -> match args.action with
      | "QUDG" ->  args.qudg.radius <- r
      | _ -> unexpected "-r0"))
      [(["Set the first radius around all nodes.";
         "3 by default.\n"],"QUDG")];

    mkopt args ["--second-radius";"-r1"]~arg:" <float>"
      (Arg.Float (fun r -> match args.action with
      | "QUDG" ->  args.qudg.r1 <- r
      | _ -> unexpected "-r1"))
      [(["Set the second radius around all nodes.";
         "2 by default.\n"],"QUDG")];
    mkopt args ["--probability"]~arg:" <float>"
      (Arg.Float (fun p -> args.qudg.p <- p))
      [(["Sets the probability of the nodes being neighbors when they are ";
           "inside the second";
         "radius, but not the first one.\n"
        ], "QUDG")];
    let msg = ["Create a DOT file to visualize the UDG plan.";
        "When it transformed into a PDF that takes the positioning tags into account"; 
        "(like 'neato' command from GraphViz), each node is visible at the coordinates";
        "where they were placed during execution.\n"] in
    mkopt args ["--dot-udg";"-du"]~arg:" <file>"
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
      (Arg.String (fun f -> match args.action with
                            | "UDG" | "QUDG" -> args.dotUDG <- f
                            | _ -> unexpected "-du"))
      [(msg,"UDG");(msg,"QUDG")];
    mkopt args ["--dot-udg-radius";"-dur"]~arg:" <file>"
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
      (Arg.String (fun f -> match args.action with
                            | "UDG" | "QUDG" -> args.dotUDGrad <- f
                            | _ -> unexpected "-dur"))
      [(["Create a DOT file to visualize the UDG plan.";
        "Same as the option '-du', but with the radius being also displayed.\n"],"UDG");
      (["Create a DOT file to visualize the UDG plan.";
        "Same as the option '-du', but with the two radiuses being ";
           "also displayed.\n"],"QUDG")];
      (Arg.Unit (fun () -> args.silent <- true))
      [(["be quiet"],"void")];
    mkopt args ["--directed";"-dir"]
      (Arg.Unit (fun () -> args.directed <- true))
      [(["Generate a directed graph."],"void")];
      (Arg.Unit (fun () -> help args ((argv.(0))^(if args.action = "void" then "" 
        else " "^args.action))))
      [(["Print this help\n"],"void")];
  )

(* all unrecognized options are accumulated *)
let (add_other : t -> string -> unit) =
Nathan Rébiscoul's avatar
Nathan Rébiscoul committed
  fun opt s ->
    opt._others <- s::opt._others

let current = ref 1;;

let parse argv = (
  let save_current = !current in
  let args = make_args () in
  mkoptab argv args;
  try (
    (if (Array.length argv) = 1 then (
        print_usage stdout true argv.(0);
        exit 1 (* no command or invalid command *)));
    let possible_actions =
      ["clique";"star";"ring";"grid";"HC";"ER";"BA";"tree";"UDG";"QUDG"]
    in
    (
      if (List.mem argv.(1) possible_actions) then args.action <- argv.(1)
      else
      if (List.mem argv.(1) ["-h";"--help";"-help"]) then help args (argv.(0)) else
        (Printf.fprintf stderr "*** Error when calling '%s %s': No such command\n\n"
           (argv.(0)) (argv.(1));
         (print_usage stderr true argv.(0));
         exit 1 (* no command or invalid command *))
    );

    Arg.parse_argv ~current:current argv args._args (add_other args)
      (usage_msg false (argv.(0)^argv.(1)));
    current := save_current;
    args._others <- List.rev args._others;
    args
  with
  | Arg.Bad msg ->
    Printf.fprintf stderr "*** Error when calling '%s': %s\n" (argv.(0))
      (first_line msg); 
    (print_usage stderr true argv.(0)); exit 3 (* bad argument *); 
  | Arg.Help _msg -> 
    help args argv.(0)
)