diff --git a/ftest.ml b/ftest.ml new file mode 100644 index 0000000..c525a6b --- /dev/null +++ b/ftest.ml @@ -0,0 +1,30 @@ +open Gfile + +let () = + + (* Check the number of command-line arguments *) + if Array.length Sys.argv <> 5 then + begin + Printf.printf "\nUsage: %s infile source sink outfile\n\n%!" Sys.argv.(0) ; + exit 0 + end ; + + + (* Arguments are : infile(1) source-id(2) sink-id(3) outfile(4) *) + + let infile = Sys.argv.(1) + and outfile = Sys.argv.(4) + + (* These command-line arguments are not used for the moment. *) + and _source = int_of_string Sys.argv.(2) + and _sink = int_of_string Sys.argv.(3) + in + + (* Open file *) + let graph = from_file infile in + + (* Rewrite the graph that has been read. *) + let () = write_file outfile graph in + + () + diff --git a/gfile.ml b/gfile.ml new file mode 100644 index 0000000..ccea4c6 --- /dev/null +++ b/gfile.ml @@ -0,0 +1,101 @@ +open Graph +open Printf + +type path = string + +(* Format of text files: + % This is a comment + + % A node with its coordinates (which are not used). + n 88.8 209.7 + n 408.9 183.0 + + % The first node has id 0, the next is 1, and so on. + + % Edges: e source dest label + e 3 1 11 + e 0 2 8 + + *) + +let write_file path graph = + + (* Open a write-file. *) + let ff = open_out path in + + (* Write in this file. *) + fprintf ff "%% This is a graph.\n\n" ; + + (* Write all nodes (with fake coordinates) *) + n_iter_sorted graph (fun id -> fprintf ff "n %.1f 1.0\n" (float_of_int id)) ; + fprintf ff "\n" ; + + (* Write all arcs *) + e_iter graph (fun id1 id2 lbl -> fprintf ff "e %d %d %s\n" id1 id2 lbl) ; + + fprintf ff "\n%% End of graph\n" ; + + close_out ff ; + () + +(* Reads a line with a node. *) +let read_node id graph line = + try Scanf.sscanf line "n %f %f" (fun _ _ -> new_node graph id) + with e -> + Printf.printf "Cannot read node in line - %s:\n%s\n%!" (Printexc.to_string e) line ; + failwith "from_file" + +(* Ensure that the given node exists in the graph. If not, create it. + * (Necessary because the website we use to create online graphs does not generate correct files when some nodes have been deleted.) *) +let ensure graph id = if node_exists graph id then graph else new_node graph id + +(* Reads a line with an arc. *) +let read_arc graph line = + try Scanf.sscanf line "e %d %d %s" + (fun id1 id2 label -> new_arc (ensure (ensure graph id1) id2) id1 id2 label) + with e -> + Printf.printf "Cannot read arc in line - %s:\n%s\n%!" (Printexc.to_string e) line ; + failwith "from_file" + +(* Reads a comment or fail. *) +let read_comment graph line = + try Scanf.sscanf line " %%" graph + with _ -> + Printf.printf "Unknown line:\n%s\n%!" line ; + failwith "from_file" + +let from_file path = + + let infile = open_in path in + + (* Read all lines until end of file. + * n is the current node counter. *) + let rec loop n graph = + try + let line = input_line infile in + + (* Remove leading and trailing spaces. *) + let line = String.trim line in + + let (n2, graph2) = + (* Ignore empty lines *) + if line = "" then (n, graph) + + (* The first character of a line determines its content : n or e. *) + else match line.[0] with + | 'n' -> (n+1, read_node n graph line) + | 'e' -> (n, read_arc graph line) + + (* It should be a comment, otherwise we complain. *) + | _ -> (n, read_comment graph line) + in + loop n2 graph2 + + with End_of_file -> graph (* Done *) + in + + let final_graph = loop 0 empty_graph in + + close_in infile ; + final_graph + diff --git a/gfile.mli b/gfile.mli new file mode 100644 index 0000000..81a2c14 --- /dev/null +++ b/gfile.mli @@ -0,0 +1,19 @@ +(* Read a graph from a file, + * Write a graph to a file. *) + +open Graph + +type path = string + +(* Values are read as strings. *) +val from_file: path -> string graph + +(* Similarly, we write only a string graph. + * If necessary, use gmap (to be written by you) to prepare the input graph. *) +val write_file: path -> string graph -> unit + + + +(* The format of files is compatible with the files generated by: + https://www-m9.ma.tum.de/graph-algorithms/flow-ford-fulkerson/index_en.html +*) diff --git a/graph.ml b/graph.ml new file mode 100644 index 0000000..2bef130 --- /dev/null +++ b/graph.ml @@ -0,0 +1,49 @@ +type id = int + +type 'a out_arcs = (id * 'a) list + +(* A graph is just a list of pairs: a node & its outgoing arcs. *) +type 'a graph = (id * 'a out_arcs) list + +exception Graph_error of string + +let empty_graph = [] + +let node_exists gr id = List.mem_assoc id gr + +let out_arcs gr id = + try List.assoc id gr + with Not_found -> raise (Graph_error ("Node " ^ string_of_int id ^ " does not exist in this graph.")) + +let find_arc gr id1 id2 = + let out = out_arcs gr id1 in + try Some (List.assoc id2 out) + with Not_found -> None + +let new_node gr id = + if node_exists gr id then raise (Graph_error ("Node " ^ string_of_int id ^ " already exists in the graph.")) + else (id, []) :: gr + +let new_arc gr id1 id2 lbl = + + (* Existing out-arcs *) + let outa = out_arcs gr id1 in + + (* Update out-arcs. + * remove_assoc does not fail if id2 is not bound. *) + let outb = (id2, lbl) :: List.remove_assoc id2 outa in + + (* Replace out-arcs in the graph. *) + let gr2 = List.remove_assoc id1 gr in + (id1, outb) :: gr2 + +let n_iter gr f = List.iter (fun (id, _) -> f id) gr + +let n_iter_sorted gr f = n_iter (List.sort compare gr) f + +let n_fold gr f acu = List.fold_left (fun acu (id, _) -> f acu id) acu gr + +let e_iter gr f = List.iter (fun (id1, out) -> List.iter (fun (id2, x) -> f id1 id2 x) out) gr + +let e_fold gr f acu = List.fold_left (fun acu (id1, out) -> List.fold_left (fun acu (id2, x) -> f acu id1 id2 x) acu out) acu gr + diff --git a/graph.mli b/graph.mli new file mode 100644 index 0000000..dd9c265 --- /dev/null +++ b/graph.mli @@ -0,0 +1,63 @@ + +(* Type of a directed graph in which arcs have labels of type 'a. *) +type 'a graph + +(* Each node has a unique identifier (a number). *) +type id = int + +exception Graph_error of string + + +(************** CONSTRUCTORS **************) + +(* The empty graph. *) +val empty_graph: 'a graph + +(* Add a new node with the given identifier. + * @raise Graph_error if the id already exists. *) +val new_node: 'a graph -> id -> 'a graph + +(* new_arc gr id1 id2 lbl : adds an arc from node id1 to node id2 with label lbl + * Both nodes must already exist in the graph. + * If the arc already exists, its label is replaced by lbl. + * @raise Graph_error if node id1 or id2 does not exist in the graph. *) +val new_arc: 'a graph -> id -> id -> 'a -> 'a graph + + +(************** GETTERS *****************) + +(* node_exists gr id indicates if the node with identifier id exists in graph gr. *) +val node_exists: 'a graph -> id -> bool + +(* Type of lists of outgoing arcs of a node. + * An arc is represented by a pair of the destination identifier and the arc label. *) +type 'a out_arcs = (id * 'a) list + +(* Find the out_arcs of a node. + * @raise Graph_error if the id is unknown in the graph. *) +val out_arcs: 'a graph -> id -> 'a out_arcs + +(* find_arc gr id1 id2 finds an arc between id1 and id2 and returns its label. Returns None if the arc does not exist. +* @raise Graph_error if id1 is unknown. *) +val find_arc: 'a graph -> id -> id -> 'a option + + +(************** COMBINATORS, ITERATORS **************) + +(* Iterate on all nodes, in no special order. *) +val n_iter: 'a graph -> (id -> unit) -> unit + +(* Like n_iter, but the nodes are sorted. *) +val n_iter_sorted: 'a graph -> (id -> unit) -> unit + +(* Fold on all (unsorted) nodes. You must remember what List.fold_left does. *) +val n_fold: 'a graph -> ('b -> id -> 'b) -> 'b -> 'b + + +(* Iter on all arcs (edges) *) +val e_iter: 'a graph -> (id -> id -> 'a -> unit) -> unit + +(* Fold on all arcs (edges) *) +val e_fold: 'a graph -> ('b -> id -> id -> 'a -> 'b) -> 'b -> 'b + + diff --git a/graph1 b/graph1 new file mode 100644 index 0000000..54b8523 --- /dev/null +++ b/graph1 @@ -0,0 +1,24 @@ +%% Test graph #1 + +%% Nodes + +n 88 209 % This is node #0, with its coordinates (which are not used by the algorithms). +n 408 183 +n 269 491 +n 261 297 +n 401 394 +n 535 294 % This is node #5. + + +%% Edges + +e 3 1 11 % An edge from 3 to 1, labeled "11". +e 3 2 2 +e 1 5 21 +e 4 5 14 +e 1 4 1 +e 0 1 7 +e 0 3 10 +e 3 4 5 +e 2 4 12 +e 0 2 8 diff --git a/graph1.svg b/graph1.svg new file mode 100644 index 0000000..0e114ab --- /dev/null +++ b/graph1.svg @@ -0,0 +1,106 @@ + + +]> + + + + +finite_state_machine + + +0 + +0 + + +2 + +2 + + +0->2 + + +8 + + +3 + +3 + + +0->3 + + +10 + + +1 + +1 + + +0->1 + + +7 + + +4 + +4 + + +2->4 + + +12 + + +3->2 + + +2 + + +3->1 + + +11 + + +3->4 + + +5 + + +1->4 + + +1 + + +5 + +5 + + +1->5 + + +21 + + +4->5 + + +14 + + +