diff --git a/MaxFlow/.classpath b/MaxFlow/.classpath
new file mode 100644
index 0000000..539dba0
--- /dev/null
+++ b/MaxFlow/.classpath
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MaxFlow/.gitignore b/MaxFlow/.gitignore
new file mode 100644
index 0000000..ae3c172
--- /dev/null
+++ b/MaxFlow/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/MaxFlow/.project b/MaxFlow/.project
new file mode 100644
index 0000000..0cddd73
--- /dev/null
+++ b/MaxFlow/.project
@@ -0,0 +1,17 @@
+
+
+ MaxFlow
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/MaxFlow/src/lab/Edge.java b/MaxFlow/src/lab/Edge.java
new file mode 100644
index 0000000..6219fc6
--- /dev/null
+++ b/MaxFlow/src/lab/Edge.java
@@ -0,0 +1,79 @@
+/**
+ *
+ */
+package lab;
+
+/**
+ * Class representing an edge in a directed graph
+ */
+public class Edge {
+
+ protected final Vertex from;
+ protected final Vertex to;
+ private final int capacity;
+ private int flow;
+ private boolean bold = false;
+
+ /**
+ * Constructor
+ * @param from Source vertex of the edge
+ * @param to Destination vertex of the edge
+ * @param distance Length (km) of the edge
+ * @param speedLimit Speed with which the 'car' can travel along this edge
+ */
+ public Edge(Vertex from, Vertex to, int capacity) {
+ this.from = from;
+ this.to = to;
+ this.capacity = capacity;
+ this.flow = 0;
+ }
+
+ /**
+ * Get driving time for this edge
+ * @return Driving time (m) for this edge
+ */
+ public int flow() {
+ return this.flow;
+ }
+
+ /**
+ * Get length of this edge
+ * @return Length of the edge
+ */
+ public int capacity() {
+ return this.capacity;
+ }
+
+ public int freeCapacity() {
+ return this.capacity - this.flow;
+ }
+
+ public int backCapacity() {
+ return this.flow;
+ }
+
+ public void increaseFlow(int inc) {
+ this.flow += inc;
+ if(this.flow < 0 || this.flow > this.capacity)
+ throw new RuntimeException("Edge capacity exceeded: "+this.flow);
+ }
+
+ public void reset() {
+ this.flow = 0;
+ }
+
+ /**
+ * Embolden the edge if it's capacity was not fully utilized
+ */
+ public void emboldenIfFreeCapacity() {
+ this.bold = this.freeCapacity() > 0;
+ }
+
+ /**
+ * Get a dot code representation of this edge
+ * @return Dot code line representing the edge
+ */
+ public String toDotCode() {
+ return from.getName() + " -> " + to.getName() + " [label=\""+ this.capacity + "-" + this.flow + "\"]" + (this.bold ? "[style=bold];" : ";");
+ }
+}
diff --git a/MaxFlow/src/lab/InfiniteFlowException.java b/MaxFlow/src/lab/InfiniteFlowException.java
new file mode 100644
index 0000000..4879564
--- /dev/null
+++ b/MaxFlow/src/lab/InfiniteFlowException.java
@@ -0,0 +1,18 @@
+/**
+ *
+ */
+package lab;
+
+/**
+ * Exception signaling that a given graph has infinite maximum flow because at
+ * least one source and destination are equal
+ */
+public class InfiniteFlowException extends Exception {
+
+ public InfiniteFlowException(String string) {
+ super(string);
+ }
+
+ private static final long serialVersionUID = -2738022894329680602L;
+
+}
diff --git a/MaxFlow/src/lab/MaxFlow.java b/MaxFlow/src/lab/MaxFlow.java
index 4f2ed05..fd99f5b 100644
--- a/MaxFlow/src/lab/MaxFlow.java
+++ b/MaxFlow/src/lab/MaxFlow.java
@@ -1,6 +1,12 @@
package lab;
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
/**
* MaxFlow.java
*/
@@ -21,14 +27,57 @@ public class MaxFlow {
public static final int NO_PATH = -4;
public static final int SOURCES_SAME_AS_DESTINATIONS = Integer.MAX_VALUE;
+ private HashMap vertices;
+ private ArrayList edges;
+
/**
* The constructor, setting the name of the file to parse.
*
* @param filename the absolute or relative path and filename of the file
*/
public MaxFlow(final String filename) {
- //TODO Add you code here
+ this.vertices = new HashMap();
+ this.edges = new ArrayList();
+ try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
+ String line;
+
+ // Read all lines
+ while ((line = br.readLine()) != null) {
+ if(line.matches(".* -> .*;")) {
+ String[] info = line.replaceAll("\\s+", " ").replaceFirst(" -> ", ",").replaceFirst("\\[label=\"", ",").replaceFirst("\".*$", "").split(",");
+ info[0] = info[0].replaceFirst(" ", "");
+ info[1] = info[1].replaceFirst(" ", "");
+ Vertex origin;
+ Vertex destination;
+ if(vertices.containsKey(info[0]))
+ origin = vertices.get(info[0]);
+ else {
+ origin = new Vertex(info[0]);
+ this.vertices.put(info[0], origin);
+ }
+
+ if(vertices.containsKey(info[1]))
+ destination = vertices.get(info[1]);
+ else {
+ destination = new Vertex(info[1]);
+ this.vertices.put(info[1], destination);
+ }
+
+ Edge edge = new Edge(origin, destination, Integer.parseInt(info[2]));
+ // Add edge to source and destination vertex
+ origin.addOutgoing(edge);
+ destination.addIncoming(edge);
+ this.edges.add(edge);
+ //System.out.println("Parse edge "+info[0]+"->"+info[1]+" cap: "+info[2]);
+ }
+ }
+
+ } catch (FileNotFoundException e) {
+ System.out.println("File not found");
+ } catch (IOException e) {
+ System.out.println("IO Exception");
+ }
}
/**
@@ -45,8 +94,40 @@ public class MaxFlow {
* SOURCES_SAME_AS_DESTINATIONS if sources == destinations
*/
public final int findMaxFlow(final String[] sources, final String[] destinations) {
- //TODO Add you code here
- return 0; // dummy, replace
+ ArrayList sourceVertices = new ArrayList();
+ ArrayList destinationVertices = new ArrayList();
+
+ for(Edge edge : edges)
+ edge.reset();
+
+ for(String sourceKey : sources)
+ if(vertices.containsKey(sourceKey))
+ sourceVertices.add(vertices.get(sourceKey));
+ for(String destinationKey : destinations)
+ if(vertices.containsKey(destinationKey))
+ destinationVertices.add(vertices.get(destinationKey));
+
+ if(sourceVertices.isEmpty() && destinationVertices.isEmpty())
+ return MaxFlow.NO_SOURCE_DESTINATION_FOUND;
+ else if(sourceVertices.isEmpty())
+ return MaxFlow.NO_SOURCE_FOUND;
+ else if(destinationVertices.isEmpty())
+ return MaxFlow.NO_DESTINATION_FOUND;
+
+ for(Vertex source : sourceVertices) {
+ try {
+ while(findAugmentingPath(source, destinationVertices)) {}
+ }
+ catch(InfiniteFlowException e) {
+ return MaxFlow.SOURCES_SAME_AS_DESTINATIONS;
+ }
+ }
+
+ int res = 0;
+ for(Vertex source : sourceVertices)
+ res += source.getTotalFlow();
+
+ return res == 0 ? MaxFlow.NO_PATH : res;
}
/**
@@ -57,8 +138,122 @@ public class MaxFlow {
* @return a ArrayList of Strings as specified in the task in dot code
*/
public final ArrayList findResidualNetwork(final String[] sources, final String[] destinations) {
- //TODO Add you code here
- return null; // dummy, replace
+ ArrayList sourceVertices = new ArrayList();
+ ArrayList destinationVertices = new ArrayList();
+
+ for(Edge edge : edges)
+ edge.reset();
+
+ for(String sourceKey : sources)
+ if(vertices.containsKey(sourceKey))
+ sourceVertices.add(vertices.get(sourceKey));
+ for(String destinationKey : destinations)
+ if(vertices.containsKey(destinationKey))
+ destinationVertices.add(vertices.get(destinationKey));
+
+ if(!sourceVertices.isEmpty() && !destinationVertices.isEmpty()) {
+ for(Vertex source : sourceVertices) {
+ try {
+ while(findAugmentingPath(source, destinationVertices)) {}
+ }
+ catch(InfiniteFlowException e) {
+ break; // Doesn't reset capacities already used before exception came up
+ }
+ }
+ }
+
+ for(Edge edge : edges)
+ edge.emboldenIfFreeCapacity();
+
+ ArrayList res = new ArrayList();
+
+ res.add("digraph{");
+
+ for(String sourceName : sources)
+ res.add(sourceName+" [shape=doublecircle][style=bold];");
+ for(String destName : destinations)
+ res.add(destName+" [shape=circle][style=bold];");
+ for(Edge edge : this.edges)
+ res.add(edge.toDotCode());
+
+ res.add("}");
+
+ return res;
+ }
+
+ private boolean findAugmentingPath(Vertex source, ArrayList destinations) throws InfiniteFlowException{
+ HashMap pred = new HashMap();
+ ArrayList queue = new ArrayList();
+ ArrayList res = new ArrayList();
+
+ System.out.println("Looking for augmenting path from "+source.getName());
+
+ queue.add(source);
+ pred.put(source.getName(), null);
+
+ Vertex current;
+ while(!queue.isEmpty()) {
+ current = queue.remove(0);
+
+ //System.out.println("Visiting "+current.getName());
+
+ if(destinations.contains(current)) {
+ if(current == source)
+ throw new InfiniteFlowException("Source " + source.getName() + " is also destination");
+
+ System.out.println("Found augmenting path");
+
+ // Build path and return
+ String nextName = current.getName();
+ String currentName;
+ Edge backedge;
+ int maxCapacity = Integer.MAX_VALUE;
+ while(pred.get(nextName) != null) {
+ backedge = pred.get(nextName);
+ currentName = nextName;
+ //System.out.println("Walking backwards: "+backedge.from.getName()+" -> "+backedge.to.getName());
+ res.add(0, backedge);
+ nextName = (backedge.to.getName().equals(currentName)) ? backedge.from.getName() : backedge.to.getName();
+ maxCapacity = (backedge.to.getName().equals(currentName)) ? Math.min(maxCapacity, backedge.freeCapacity()) : Math.min(maxCapacity, backedge.backCapacity());
+ }
+ Vertex nextVertex = source;
+ System.out.println(maxCapacity);
+ for(Edge edge : res) {
+ if(edge.from == nextVertex) {
+ edge.increaseFlow(maxCapacity);
+ nextVertex = edge.to;
+ }
+ else if(edge.to == nextVertex) {
+ edge.increaseFlow(- maxCapacity);
+ nextVertex = edge.from;
+ }
+ else
+ throw new RuntimeException("Edge is neither from nor to source node");
+ }
+
+ return true;
+ }
+
+ for(Edge edge : current.getResidual()) {
+ if(edge.from == current && pred.containsKey(edge.to.getName()) || edge.to == current && pred.containsKey(edge.from.getName()))
+ continue;
+
+ if(edge.from == current && edge.freeCapacity() > 0) {
+ queue.add(edge.to);
+ pred.put(edge.to.getName(), edge);
+ System.out.println("Adding "+current.getName()+" -> "+edge.to.getName() + " cap: "+edge.freeCapacity());
+ }
+ else if(edge.to == current && edge.backCapacity() > 0) {
+ queue.add(edge.from);
+ pred.put(edge.from.getName(), edge);
+ System.out.println("Adding "+current.getName()+" -> "+edge.from.getName() + " cap: "+edge.backCapacity());
+ }
+ }
+ }
+
+ System.out.println("No augmenting path from "+source.getName());
+
+ return false;
}
}
\ No newline at end of file
diff --git a/MaxFlow/src/lab/Vertex.java b/MaxFlow/src/lab/Vertex.java
new file mode 100644
index 0000000..94fe88d
--- /dev/null
+++ b/MaxFlow/src/lab/Vertex.java
@@ -0,0 +1,81 @@
+package lab;
+
+import java.util.ArrayList;
+
+/**
+ * Class representing a vertex ('street crossing' in this context) in a directed graph
+ */
+public class Vertex {
+
+ private final String name;
+ private ArrayList incoming;
+ private ArrayList outgoing;
+ private ArrayList allEdges;
+
+ /**
+ * Constructor
+ * @param name Name of the vertex. Assumed to be per-graph unique
+ * @param waitTime Average time spent waiting at this vertex
+ */
+ public Vertex(String name) {
+ this.name = name;
+ this.incoming = new ArrayList();
+ this.outgoing = new ArrayList();
+ this.allEdges = new ArrayList();
+ }
+
+ /**
+ * Getter method for vertex name
+ * @return Vertex name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * Add an incoming edge to this vertex
+ * @param edge Incoming edge
+ */
+ public void addIncoming(Edge edge) {
+ incoming.add(edge);
+ allEdges.add(edge);
+ }
+
+ /**
+ * Add an outgoing edge to this vertex
+ * @param edge Outgoing edge
+ */
+ public void addOutgoing(Edge edge) {
+ outgoing.add(edge);
+ allEdges.add(edge);
+ }
+
+ /**
+ * Get a List of all outgoing edges of this vertex
+ * @return
+ */
+ public ArrayList getOutgoing() {
+ return outgoing;
+ }
+
+ public ArrayList getResidual() {
+ return allEdges;
+ }
+
+ public int getTotalFlow() {
+ int res = 0;
+ for(Edge edge : outgoing)
+ res += edge.flow();
+ for(Edge edge : incoming)
+ res -= edge.flow();
+ return res;
+ }
+
+ /**
+ * Get a dot code representation of this vertex
+ * @return Dot code representing the vertex
+ */
+ public String toDotCode() {
+ return this.name + "[label=\"" + this.name + "\"];";
+ }
+}