From 9d4e63d467746b36431faad34b038beb6ac0165c Mon Sep 17 00:00:00 2001 From: rlacroix Date: Fri, 18 Nov 2022 14:25:41 +0100 Subject: [PATCH] First Commit --- Makefile | 20 +++++++++ README.md | 21 +++++++++ _tags | 3 ++ graphs/graph1 | 24 +++++++++++ graphs/graph1.svg | 106 ++++++++++++++++++++++++++++++++++++++++++++++ graphs/graph1.txt | 21 +++++++++ graphs/graph2.txt | 37 ++++++++++++++++ src/ftest.ml | 35 +++++++++++++++ src/gfile.ml | 101 +++++++++++++++++++++++++++++++++++++++++++ src/gfile.mli | 18 ++++++++ src/graph.ml | 49 +++++++++++++++++++++ src/graph.mli | 63 +++++++++++++++++++++++++++ 12 files changed, 498 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 _tags create mode 100644 graphs/graph1 create mode 100644 graphs/graph1.svg create mode 100644 graphs/graph1.txt create mode 100644 graphs/graph2.txt create mode 100644 src/ftest.ml create mode 100644 src/gfile.ml create mode 100644 src/gfile.mli create mode 100644 src/graph.ml create mode 100644 src/graph.mli diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..876857b --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ + +build: + @echo "\n==== COMPILING ====\n" + ocamlbuild ftest.native + +format: + ocp-indent --inplace src/* + +edit: + code . -n + +demo: build + @echo "\n==== EXECUTING ====\n" + ./ftest.native graphs/graph1 1 2 outfile + @echo "\n==== RESULT ==== (content of outfile) \n" + @cat outfile + +clean: + -rm -rf _build/ + -rm ftest.native diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ee1f32 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +Base project for Ocaml project on Ford-Fulkerson. This project contains some simple configuration files to facilitate editing Ocaml in VSCode. + +To use, you should install the *OCaml* extension in VSCode. Other extensions might work as well but make sure there is only one installed. +Then open VSCode in the root directory of this repository (command line: `code path/to/ocaml-maxflow-project`). + +Features : + - full compilation as VSCode build task (Ctrl+Shift+b) + - highlights of compilation errors as you type + - code completion + - automatic indentation on file save + + +A makefile provides some useful commands: + - `make build` to compile. This creates an ftest.native executable + - `make demo` to run the `ftest` program with some arguments + - `make format` to indent the entire project + - `make edit` to open the project in VSCode + - `make clean` to remove build artifacts + +In case of trouble with the VSCode extension (e.g. the project does not build, there are strange mistakes), a common workaround is to (1) close vscode, (2) `make clean`, (3) `make build` and (4) reopen vscode (`make edit`). + diff --git a/_tags b/_tags new file mode 100644 index 0000000..e8bfe6d --- /dev/null +++ b/_tags @@ -0,0 +1,3 @@ +: include + + diff --git a/graphs/graph1 b/graphs/graph1 new file mode 100644 index 0000000..54b8523 --- /dev/null +++ b/graphs/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/graphs/graph1.svg b/graphs/graph1.svg new file mode 100644 index 0000000..0e114ab --- /dev/null +++ b/graphs/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 + + + diff --git a/graphs/graph1.txt b/graphs/graph1.txt new file mode 100644 index 0000000..e1cb393 --- /dev/null +++ b/graphs/graph1.txt @@ -0,0 +1,21 @@ +% This is a graph. + +n 20 300 0 +n 200 300 1 +n 200 200 2 +n 200 400 3 +n 380 300 4 +n 380 200 5 + +e 0 2 0 8 +e 0 3 1 10 +e 0 1 2 7 +e 2 4 3 12 +e 3 4 4 5 +e 3 2 5 2 +e 3 1 6 11 +e 1 4 7 1 +e 1 5 8 21 +e 4 5 9 14 + +% End of graph diff --git a/graphs/graph2.txt b/graphs/graph2.txt new file mode 100644 index 0000000..b772ff0 --- /dev/null +++ b/graphs/graph2.txt @@ -0,0 +1,37 @@ +% This is a graph. + +n 20 300 0 +n 200 300 1 +n 200 200 2 +n 200 400 3 +n 380 300 4 +n 380 200 5 +n 380 400 6 +n 380 100 7 +n 380 500 8 +n 560 300 9 +n 560 200 10 +n 560 400 11 +n 560 100 12 + +e 0 3 0 2 +e 0 2 1 2 +e 0 1 2 4 +e 1 4 3 2 +e 1 6 4 2 +e 1 5 5 2 +e 4 10 6 2 +e 2 5 7 2 +e 10 12 8 6 +e 3 6 9 2 +e 3 8 10 2 +e 6 9 11 2 +e 8 11 12 2 +e 11 9 13 2 +e 9 10 14 4 +e 5 10 15 1 +e 5 7 16 1 +e 7 12 17 2 +e 7 10 18 1 + +% End of graph diff --git a/src/ftest.ml b/src/ftest.ml new file mode 100644 index 0000000..80489df --- /dev/null +++ b/src/ftest.ml @@ -0,0 +1,35 @@ +open Gfile + +let () = + + (* Check the number of command-line arguments *) + if Array.length Sys.argv <> 5 then + begin + Printf.printf + "\n ✻ Usage: %s infile source sink outfile\n\n%s%!" Sys.argv.(0) + (" 🟄 infile : input file containing a graph\n" ^ + " 🟄 source : identifier of the source vertex (used by the ford-fulkerson algorithm)\n" ^ + " 🟄 sink : identifier of the sink vertex (ditto)\n" ^ + " 🟄 outfile : output file in which the result should be written.\n\n") ; + 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/src/gfile.ml b/src/gfile.ml new file mode 100644 index 0000000..9599cc3 --- /dev/null +++ b/src/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/src/gfile.mli b/src/gfile.mli new file mode 100644 index 0000000..465e693 --- /dev/null +++ b/src/gfile.mli @@ -0,0 +1,18 @@ +(* 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://algorithms.discrete.ma.tum.de/graph-algorithms/flow-ford-fulkerson/index_en.html +*) diff --git a/src/graph.ml b/src/graph.ml new file mode 100644 index 0000000..2bef130 --- /dev/null +++ b/src/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/src/graph.mli b/src/graph.mli new file mode 100644 index 0000000..dd9c265 --- /dev/null +++ b/src/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 + +