diff --git a/src/berack96/sim/util/graph/Edge.java b/src/berack96/sim/util/graph/Edge.java new file mode 100644 index 0000000..d002cef --- /dev/null +++ b/src/berack96/sim/util/graph/Edge.java @@ -0,0 +1,83 @@ +package berack96.sim.util.graph; + +/** + * Class used for retrieving the edges of the graph. + * + * @param the vertices + * @param the weight of the edge + * @author Berack96 + */ +public class Edge { + + /** + * The source vertex + */ + private final V source; + /** + * The destination vertex + */ + private final V destination; + /** + * The weight of this edge + */ + private final W weight; + + /** + * Create an final version of this object + * + * @param source the source of the edge + * @param destination the destination of the edge + * @param weight the weight od the edge + */ + public Edge(V source, V destination, W weight) { + this.source = source; + this.destination = destination; + this.weight = weight; + } + + /** + * The vertex where the edge goes + * + * @return the vertex + */ + public V getDestination() { + return destination; + } + + /** + * The vertex where the edge starts from + * + * @return the vertex + */ + public V getSource() { + return source; + } + + /** + * The weight of the edge + * + * @return the weight + */ + public W getWeight() { + return weight; + } + + @Override + public String toString() { + return "[" + source + " -> " + destination + ", " + weight + "]"; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + try { + return obj.getClass().equals(getClass()) && obj.toString().equals(toString()); + } catch (Exception e) { + return false; + } + } +} diff --git a/src/berack96/sim/util/graph/Graph.java b/src/berack96/sim/util/graph/Graph.java index dc1f4b3..6b88e23 100644 --- a/src/berack96/sim/util/graph/Graph.java +++ b/src/berack96/sim/util/graph/Graph.java @@ -1,10 +1,11 @@ package berack96.sim.util.graph; +import berack96.sim.util.graph.visit.VisitInfo; import berack96.sim.util.graph.visit.VisitStrategy; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; /** @@ -40,6 +41,17 @@ public interface Graph extends Iterable { */ boolean isDAG(); + /** + * Get an instance of the vertex linked with this graph.
+ * For more info see {@link Vertex} + * + * @param vertex the vertex + * @return a vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException; + /** * Add the vertex to the graph. If it's already in the graph it will be replaced.
* Of course the vertex added will have no edge to any other vertex nor form any other vertex. @@ -60,24 +72,23 @@ public interface Graph extends Iterable { boolean addVertexIfAbsent(V vertex) throws NullPointerException; /** - * Add all the vertices contained in the set to the graph.
- * If a vertex is contained in the set and in the graph is ignored and it will not be replaced.
+ * Add all the vertices contained in the collection to the graph.
+ * If a vertex is contained in the collection and in the graph is ignored and it will not be replaced.
* Null vertices will be ignored and they will not be added to the graph. * - * @param vertices a set containing the vertices + * @param vertices a collection of the vertices to add * @throws NullPointerException if the set is null */ - void addAllVertices(Set vertices) throws NullPointerException; + void addAllVertices(Collection vertices) throws NullPointerException; /** * Remove the selected vertex from the graph.
* After this method's call the vertex will be no longer present in the graph, and nether all his edges. * * @param vertex the vertex to remove - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph + * @throws NullPointerException if the vertex is null */ - void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException; + void removeVertex(V vertex) throws IllegalArgumentException; /** * Remove all the vertex contained in the graph.
@@ -96,6 +107,63 @@ public interface Graph extends Iterable { */ boolean contains(V vertex) throws NullPointerException; + /** + * Add to the specified vertex the mark passed.
+ * A vertex can have multiple marker. + * + * @param vertex the vertex to mark + * @param mark the mark to add + * @throws NullPointerException if one of the param is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void mark(V vertex, String mark) throws NullPointerException, IllegalArgumentException; + + /** + * Remove the selected mark from the vertex.
+ * + * @param vertex the vertex where remove the mark + * @param mark the mark to remove + * @throws NullPointerException if a param is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void unMark(V vertex, String mark) throws NullPointerException, IllegalArgumentException; + + /** + * Unmark the vertex selected.
+ * After this call the vertex will not have any marked object to himself. + * + * @param vertex the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + void unMark(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Get all the marker of this vertex.
+ * If the vertex doesn't have any mark, then it will return an empty set.
+ * Note: this set is linked to the marked vertex, so any changes to the set returned are reflected to the graph. + * + * @param vertex the vertex + * @return all the mark to the vertex or an empty collection if none + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Remove the selected mark from all the vertices + * + * @param mark the mark to remove + * @throws NullPointerException if the mark is null + */ + void unMarkAll(String mark) throws NullPointerException; + + /** + * Remove all the marker to all the vertex.
+ * After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set + */ + void unMarkAll(); + /** * Add an edge between the two vertex.
* The edge will be created from the vertex V1 and the vertex V2
@@ -105,12 +173,25 @@ public interface Graph extends Iterable { * @param vertex1 a vertex of the graph * @param vertex2 a vertex of the graph * @param weight the weight of the edge - * @return null or the previous value of the edge if there was already one + * @return null or the previous weight of the edge if there was already one * @throws NullPointerException if one of the parameter is null * @throws IllegalArgumentException if one of the vertex is not contained in the graph */ W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException; + /** + * Add an edge between the two vertex.
+ * The edge will be created from the vertex source of the edge and the vertex destination of it
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param edge the edge to add + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException; + /** * This particular function add an edge to the graph.
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
@@ -121,13 +202,26 @@ public interface Graph extends Iterable { * @param vertex1 a vertex of the graph * @param vertex2 a vertex of the graph * @param weight the weight of the edge - * @return null or the previous value of the edge if there was already one + * @return null or the previous weight of the edge if there was already one * @throws NullPointerException if one of the parameter is null */ W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException; /** - * Add all the edges of the set in the graph.
+ * This particular function add an edge to the graph.
+ * If one of the two, or both vertices of the edge aren't contained in the graph, then the vertices will be added.
+ * The edge will be created from the vertex source of the edge and the vertex destination of it
+ * This method will overwrite any existing edge between the two vertex.
+ * If there was a previous edge then it is returned + * + * @param edge the edge to add + * @return null or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + */ + W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException; + + /** + * Add all the edges of the collection to the graph.
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.
* Any null edges will be ignored.
* This method will overwrite any existing edge between the two vertex. @@ -135,7 +229,7 @@ public interface Graph extends Iterable { * @param edges the edges to add * @throws NullPointerException if the set is null */ - void addAllEdges(Set> edges) throws NullPointerException; + void addAllEdges(Collection> edges) throws NullPointerException; /** * Get the weight of the selected edge.
@@ -188,7 +282,7 @@ public interface Graph extends Iterable { * * @param vertex a vertex of the graph * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph + * @throws IllegalArgumentException if one of the vertex is not contained in the graph */ void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException; @@ -200,44 +294,66 @@ public interface Graph extends Iterable { /** * Check if the edge between the two vertex passed is contained in the graph or not.
- * An edge between V1 and V2 is contained in the graph if and only if i can travel from V1 to V2. + * An edge between V1 and V2 is contained in the graph if and only if i can travel from V1 to V2.
+ * If one of the two vertices is not contained in the graph, then even the edge isn't * * @param vertex1 a vertex of the graph * @param vertex2 a vertex of the graph * @return true if the edge is contained, false otherwise - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph + * @throws NullPointerException if one of the parameters is null */ - boolean containsEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; + boolean containsEdge(V vertex1, V vertex2) throws NullPointerException; /** * Get all the vertices in the graph.
- * If the graph doesn't contains vertices, it'll return an empty set.
- * Note that this set is completely different than the set used for the vertices, so any modification of this set will not change the graph. + * If the graph doesn't contains vertices, it'll return an empty collection.
+ * Note that this collection is completely different the object used for the vertices, so any modification to this collection will not change the graph. * - * @return a set that include all the vertices + * @return an array that include all the vertices */ - Set vertices(); + Collection vertices(); /** * Get all the edges in the graph.
- * If the graph doesn't contains edges, it'll return an empty set.
- * Note that this set is completely different than the set used for the edges, so any modification of this set will not change the graph. + * If the graph doesn't contains edges, it'll return an empty collection.
+ * Note that this collection is completely different than the object used for the edges, so any modification to this collection will not change the graph. * - * @return a set that include all the edges + * @return a collection that include all the edges */ - Set> edges(); + Collection> edges(); /** - * Retrieve all the edges from a particular vertex.
- * Note: the edges that is returned are the edges that goes IN this vertex AND the edges that goes OUT of it. + * Retrieve all the edges of a particular vertex.
+ * Note: the edges that are returned are the one that goes IN this vertex AND the edges that goes OUT of it. * * @param vertex a vertex of the graph - * @return a set of edges + * @return a collection of edges * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - Set> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException; + Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Retrieve all the edges of a particular vertex.
+ * Note: the edges that are returned are the one that have this vertex as destination and another as source. + * + * @param vertex a vertex of the graph + * @return a collection of edges + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException; + + /** + * Retrieve all the edges that goes OUT of a particular vertex.
+ * Note: the edges that are returned are the one that have this vertex as source and another one as destination. + * + * @param vertex a vertex of the graph + * @return a collection of edges + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException; /** * Get all the vertices that are children of the vertex passed as parameter.
@@ -245,34 +361,22 @@ public interface Graph extends Iterable { * where V1 is the source of that edge. * * @param vertex the source vertex - * @return a set of vertices + * @return an array of vertices * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - Set getChildren(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * This method will get all the child of the vertex selected.
- * The map created will be a {@link java.util.LinkedHashMap LinkedHashMap}
- * The order in which the vertex are iterated in the map will be from the vertex with the lowest weight to the one with the highest. - * - * @param vertex a vertex of the graph - * @return a map of all the child and their respective weight - * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is not contained in the graph - */ - Map getChildrenAndWeight(V vertex) throws NullPointerException, IllegalArgumentException; + Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException; /** * Get all the vertices that have the vertex passed as their child.
* Basically is the opposite of {@link #getChildren(Object)} * * @param vertex a vertex of the graph - * @return a set of ancestors of the vertex + * @return an array of ancestors of the vertex * @throws NullPointerException if one of the parameters is null * @throws IllegalArgumentException if one of the vertex is not contained in the graph */ - Set getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; + Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; /** * Tells the degree of all the edges that goes to this vertex.
@@ -333,7 +437,7 @@ public interface Graph extends Iterable { * @throws NullPointerException if one of the parameter is null (except the consumer) * @throws IllegalArgumentException if the vertex is not in the graph */ - void visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException; + VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException; /** * This method will create a new Graph that is the transposed version of the original.
@@ -356,9 +460,9 @@ public interface Graph extends Iterable { /** * The strongly connected components or diconnected components of an arbitrary directed graph form a partition into subgraphs that are themselves strongly connected. * - * @return a set containing the strongly connected components + * @return a collection containing the strongly connected components */ - Set> stronglyConnectedComponents(); + Collection> stronglyConnectedComponents(); /** * Get a sub-graph of the current one based on the maximum depth that is given.
@@ -371,10 +475,21 @@ public interface Graph extends Iterable { * @param depth the maximum depth (must be a positive number, if >=0 a graph containing only the source is returned) * @return a sub-graph of the original * @throws NullPointerException if the vertex is null - * @throws IllegalArgumentException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained */ Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException; + /** + * Get a sub-graph of the current one with only the vertex marked with the selected marker.
+ * Each vertex will have all his edges, but only the ones with the destination marked with the same marker.
+ * If the marker is null then the returning graph will have all the vertices that are not marked by any marker.
+ * If the graph doesn't contain any vertex with that marker then an empty graph is returned. + * + * @param marker the marker + * @return a sub-graph of the current graph + */ + Graph subGraph(String marker); + /** * Get the minimum path from the source vertex to the destination vertex.
* If the source vertex can't reach the destination, then an exception is thrown. @@ -400,75 +515,4 @@ public interface Graph extends Iterable { // TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser // TODO maybe, but i don't think so... STATIC DISTANCE V* -> V* - - /** - * Class used for retrieving the edges of the graph. - * - * @param the vertices - * @param the weight of the edge - */ - class Edge { - private final V source; - private final V destination; - private final W weight; - - /** - * Create an final version of this object - * - * @param source the source of the edge - * @param destination the destination of the edge - * @param weight the weight od the edge - */ - public Edge(V source, V destination, W weight) { - this.source = source; - this.destination = destination; - this.weight = weight; - } - - /** - * The vertex where the edge goes - * - * @return the vertex - */ - public V getDestination() { - return destination; - } - - /** - * The vertex where the edge starts from - * - * @return the vertex - */ - public V getSource() { - return source; - } - - /** - * The weight of the edge - * - * @return the weight - */ - public W getWeight() { - return weight; - } - - @Override - public String toString() { - return "[" + source + " -> " + destination + ", " + weight + "]"; - } - - @Override - public int hashCode() { - return toString().hashCode(); - } - - @Override - public boolean equals(Object obj) { - try { - return obj.getClass().equals(getClass()) && obj.toString().equals(toString()); - } catch (Exception e) { - return false; - } - } - } } diff --git a/src/berack96/sim/util/graph/MapGraph.java b/src/berack96/sim/util/graph/MapGraph.java index 69fe2ec..451eeb9 100644 --- a/src/berack96/sim/util/graph/MapGraph.java +++ b/src/berack96/sim/util/graph/MapGraph.java @@ -2,6 +2,7 @@ package berack96.sim.util.graph; import berack96.sim.util.graph.visit.Dijkstra; import berack96.sim.util.graph.visit.Tarjan; +import berack96.sim.util.graph.visit.VisitInfo; import berack96.sim.util.graph.visit.VisitStrategy; import java.util.*; @@ -17,6 +18,7 @@ import java.util.function.Consumer; * * @param the vertices * @param the weight of the edges + * @author Berack96 */ public class MapGraph implements Graph { @@ -27,6 +29,11 @@ public class MapGraph implements Graph { */ private final Map> edges = new HashMap<>(); + /** + * Map that contains the vertex as key and all the marker as the value associated with that vertex. + */ + private final Map> marked = new HashMap<>(); + /** * Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change */ @@ -47,6 +54,12 @@ public class MapGraph implements Graph { return !isCyclic(); } + @Override + public Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + return new Vertex<>(this, vertex); + } + @Override public void addVertex(V vertex) throws NullPointerException { checkNull(vertex); @@ -63,18 +76,18 @@ public class MapGraph implements Graph { } @Override - public void addAllVertices(Set vertices) throws NullPointerException { + public void addAllVertices(Collection vertices) throws NullPointerException { checkNull(vertices); vertices.forEach(this::addVertexIfAbsent); } @Override - public void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - graphChanged(); - edges.remove(vertex); - edges.forEach((v, map) -> map.remove(vertex)); + public void removeVertex(V vertex) throws NullPointerException { + if (contains(vertex)) { + graphChanged(); + edges.remove(vertex); + edges.forEach((v, map) -> map.remove(vertex)); + } } @Override @@ -89,6 +102,45 @@ public class MapGraph implements Graph { return edges.containsKey(vertex); } + @Override + public void mark(V vertex, String mark) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + checkNull(mark); + + Set set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>()); + set.add(mark); + } + + @Override + public void unMark(V vertex, String mark) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + checkNull(mark); + marked.get(vertex).remove(mark); + } + + @Override + public void unMark(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + marked.get(vertex).clear(); + } + + @Override + public Set getMarks(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + return marked.computeIfAbsent(vertex, (m) -> new HashSet<>()); + } + + @Override + public void unMarkAll(String mark) { + checkNull(mark); + marked.forEach((v, m) -> m.remove(mark)); + } + + @Override + public void unMarkAll() { + marked.clear(); + } + @Override public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { checkNullAndExist(vertex1); @@ -99,6 +151,11 @@ public class MapGraph implements Graph { return edges.get(vertex1).put(vertex2, weight); } + @Override + public W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { + return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight()); + } + @Override public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { addVertexIfAbsent(vertex1); @@ -107,7 +164,12 @@ public class MapGraph implements Graph { } @Override - public void addAllEdges(Set> edges) throws NullPointerException { + public W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException { + return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()); + } + + @Override + public void addAllEdges(Collection> edges) throws NullPointerException { edges.forEach((edge) -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight())); } @@ -146,6 +208,7 @@ public class MapGraph implements Graph { @Override public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); removeVertex(vertex); addVertex(vertex); } @@ -157,11 +220,8 @@ public class MapGraph implements Graph { } @Override - public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - - return edges.get(vertex1).get(vertex2) != null; + public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { + return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null; } @Override @@ -188,6 +248,27 @@ public class MapGraph implements Graph { return set; } + @Override + public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + Collection> collection = new HashSet<>(); + edges.forEach((source, edge) -> { + if (edge.get(vertex) != null) + collection.add(new Edge<>(source, vertex, edge.get(vertex))); + }); + + return collection; + } + + @Override + public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { + checkNullAndExist(vertex); + Collection> collection = new HashSet<>(); + edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); + + return collection; + } + @Override public Set getChildren(V vertex) throws NullPointerException, IllegalArgumentException { checkNullAndExist(vertex); @@ -195,13 +276,6 @@ public class MapGraph implements Graph { return new HashSet<>(edges.get(vertex).keySet()); } - @Override - public Map getChildrenAndWeight(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - return new HashMap<>(edges.get(vertex)); - } - @Override public Set getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { checkNullAndExist(vertex); @@ -252,8 +326,8 @@ public class MapGraph implements Graph { } @Override - public void visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException { - strategy.visit(this, source, visit); + public VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException { + return strategy.visit(this, source, visit); } @Override @@ -275,7 +349,7 @@ public class MapGraph implements Graph { } @Override - public Set> stronglyConnectedComponents() { + public Collection> stronglyConnectedComponents() { return getTarjan().getSCC(); } @@ -302,15 +376,16 @@ public class MapGraph implements Graph { vertices.add(child); } } + return null; }; strategy.visit(this, source, null); sub.addAllVertices(vertices); for (V vertex : vertices) - getChildrenAndWeight(vertex).forEach((child, weight) -> { + getEdgesOut(vertex).forEach((edge) -> { try { - sub.addEdge(vertex, child, weight); + sub.addEdge(edge); } catch (Exception ignored) { } }); @@ -318,6 +393,35 @@ public class MapGraph implements Graph { return sub; } + @Override + public Graph subGraph(final String marker) { + final Graph graph = new MapGraph<>(); + final Set allVertices = new HashSet<>(); + + marked.forEach((vertex, mark) -> { + if (mark.contains(marker) || (marker == null && !mark.isEmpty())) + allVertices.add(vertex); + }); + + if (marker == null) { + Collection toAdd = graph.vertices(); + toAdd.removeAll(allVertices); + allVertices.clear(); + allVertices.addAll(toAdd); + } + + graph.addAllVertices(allVertices); + for (V vertex : graph.vertices()) + getEdgesOut(vertex).forEach((edge) -> { + try { + graph.addEdge(edge); + } catch (Exception ignored) { + } + }); + + return graph; + } + @Override public List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { checkNullAndExist(source); diff --git a/src/berack96/sim/util/graph/Vertex.java b/src/berack96/sim/util/graph/Vertex.java new file mode 100644 index 0000000..7c36eb6 --- /dev/null +++ b/src/berack96/sim/util/graph/Vertex.java @@ -0,0 +1,264 @@ +package berack96.sim.util.graph; + +import berack96.sim.util.graph.visit.VisitInfo; +import berack96.sim.util.graph.visit.VisitStrategy; + +import java.util.Collection; +import java.util.HashSet; +import java.util.function.Consumer; + +/** + * Class used for represent a vertex of the graph.
+ * The vertex contained is linked with the graph, so if any changes are made to + * it, then they will be reflected here. + * + * @param the vertex + * @author Berack96 + */ +@SuppressWarnings("unchecked") +public class Vertex { + + public static final String REMOVED = "The vertex is no longer in the graph"; + + /** + * The vertex associated + */ + private final V vertex; + /** + * The graph associated + */ + private final Graph graph; + + /** + * Get a Vertex linked with the graph + * + * @param graph the graph of the vertex + * @param vertex the vertex + * @throws NullPointerException if one of the param is null + */ + public Vertex(Graph graph, V vertex) throws NullPointerException { + if (graph == null || vertex == null) + throw new NullPointerException(); + this.graph = (Graph) graph; + this.vertex = vertex; + } + + /** + * Get the vertex + * + * @return the vertex + */ + public V getValue() { + return vertex; + } + + /** + * Mark the vertex with the associated string + * + * @param mark the marker + * @throws NullPointerException if the marker is null + * @throws UnsupportedOperationException if the vertes is not in the graph anymore + */ + public void mark(String mark) throws NullPointerException, UnsupportedOperationException { + throwIfNotContained(); + graph.mark(vertex, mark); + } + + /** + * Remove all the marker from the vertex + * + * @throws UnsupportedOperationException if the vertes is not in the graph anymore + */ + public void unMark() throws UnsupportedOperationException { + throwIfNotContained(); + graph.unMark(vertex); + } + + /** + * Get all the marks that are associated with this vertex + * + * @return a set of marks + * @throws UnsupportedOperationException if the vertes is not in the graph anymore + */ + public Collection getMarks() throws UnsupportedOperationException { + throwIfNotContained(); + return graph.getMarks(vertex); + } + + /** + * Get all the vertex children of the current vertex + * + * @return all the children + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection getChildren() throws UnsupportedOperationException { + throwIfNotContained(); + return graph.getChildren(vertex); + } + + /** + * Get all the children of this vertex like {@link #getChildren()}, but as {@link Vertex}.
+ * In this way they are linked to the graph as this one.
+ * * This method allocate a new object for each vertex, so it is more heavy. + * + * @return a collection of vertices that are children of the current one + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection> getChildrenAsVertex() throws UnsupportedOperationException { + Collection children = getChildren(); + Collection> toReturn = new HashSet<>(); + for (V vertex : children) + toReturn.add(new Vertex<>(graph, vertex)); + + return toReturn; + } + + /** + * Get all the vertex ancestor of this vertex.
+ * The ancestors are all the vertices that have as destination this vertex. + * + * @return a collection of vertices + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection getAncestors() throws UnsupportedOperationException { + throwIfNotContained(); + return graph.getAncestors(vertex); + } + + /** + * Get all the ancestors of this vertex like {@link #getAncestors()}, but as {@link Vertex}.
+ * In this way they are linked to the graph as this one.
+ * This method allocate a new object for each vertex, so it is more heavy. + * + * @return a collection of vertices that are children of the current one + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection> getAncestorsAsVertex() throws UnsupportedOperationException { + Collection ancestors = getAncestors(); + Collection> toReturn = new HashSet<>(); + for (V vertex : ancestors) + toReturn.add(new Vertex<>(graph, vertex)); + + return toReturn; + } + + /** + * Get all the edge that goes OUT of this vertex + * + * @return a collection of edges with source this one + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection> getEdgesOut() throws UnsupportedOperationException { + throwIfNotContained(); + return graph.getEdgesOut(vertex); + } + + /** + * Get all the edge that goes INTO this vertex + * + * @return a collection of edges with destination this one + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public Collection> getEdgesIn() throws UnsupportedOperationException { + throwIfNotContained(); + return graph.getEdgesIn(vertex); + } + + /** + * Add a child to this vertex.
+ * The added child must be in the graph or it will return an exception. + * + * @param child the destination vertex of this edge + * @param weight the weight of the edge + * @throws NullPointerException if the param is null + * @throws IllegalArgumentException if the child vertex is not contained in the graph + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public void addChild(V child, Number weight) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + throwIfNotContained(); + graph.addEdge(vertex, child, weight); + } + + /** + * Removes a child of this vertex. + * If the vertex passed as param is not a child, then this call does nothing. + * + * @param child the child of the current vertex + * @throws NullPointerException if the param is null + * @throws IllegalArgumentException if the child vertex is not contained in the graph + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public void removeChild(V child) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + throwIfNotContained(); + graph.removeEdge(vertex, child); + } + + /** + * This call tell if the current vertex is still contained in the graph linked.
+ * While this function return false all the other methods will throw an exception. + * + * @return true if it is, false otherwise + */ + public boolean isStillContained() { + return graph.contains(vertex); + } + + /** + * Add the vertex to the graph only if it's not already in the graph. + */ + public void addIfAbsent() { + graph.addVertexIfAbsent(vertex); + } + + /** + * Remove the vertex from the graph.
+ * After this call all the other methods will throw an exception + */ + public void remove() { + if (graph.contains(vertex)) + graph.removeVertex(vertex); + } + + /** + * Visit the graph from this current vertex with the strategy assigned + * + * @param strategy the strategy of the visit + * @param visit the function to apply at each vertex (can be null) + * @return an info of the visit if supported by the strategy + * @throws NullPointerException if the strategy is null + * @throws UnsupportedOperationException if the vertex is not in the graph anymore + */ + public VisitInfo visit(final VisitStrategy strategy, final Consumer visit) throws NullPointerException, UnsupportedOperationException { + throwIfNotContained(); + return graph.visit(vertex, (VisitStrategy) strategy, visit); + } + + @Override + public String toString() { + return vertex.toString(); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + try { + return obj instanceof Vertex && (this.vertex.equals(obj) || this.vertex.equals(((Vertex) obj).vertex)); + } catch (Exception e) { + return false; + } + } + + /** + * Used for throwing the UnsupportedOperationException if the vertex is not contained anymore + * + * @throws UnsupportedOperationException if IllegalArgumentException is thrown by the runnable + */ + private void throwIfNotContained() throws UnsupportedOperationException { + if (!graph.contains(vertex)) + throw new UnsupportedOperationException(REMOVED); + } +} diff --git a/src/berack96/sim/util/graph/visit/BFS.java b/src/berack96/sim/util/graph/visit/BFS.java index 9eef55a..39048b1 100644 --- a/src/berack96/sim/util/graph/visit/BFS.java +++ b/src/berack96/sim/util/graph/visit/BFS.java @@ -11,42 +11,33 @@ import java.util.function.Consumer; * * @param the vertex of the graph * @param the weight of the graph + * @author Berack96 */ public class BFS implements VisitStrategy { - private VisitInfo lastVisit = null; - - /** - * Retrieve the info of the last visit - * - * @return an info of the visit - */ - public VisitInfo getLastVisit() { - return lastVisit; - } - @Override - public void visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { - lastVisit = new VisitInfo<>(source); + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + VisitInfo info = new VisitInfo<>(source); final LinkedList toVisitChildren = new LinkedList<>(); toVisitChildren.push(source); if (visit != null) visit.accept(source); - lastVisit.setVisited(source); + info.setVisited(source); while (!toVisitChildren.isEmpty()) { V current = toVisitChildren.removeFirst(); for (V child : graph.getChildren(current)) - if (!lastVisit.isDiscovered(child)) { + if (!info.isDiscovered(child)) { toVisitChildren.addLast(child); - lastVisit.setVisited(child); - lastVisit.setParent(current, child); + info.setVisited(child); + info.setParent(current, child); if (visit != null) visit.accept(child); } } + return info; } } diff --git a/src/berack96/sim/util/graph/visit/DFS.java b/src/berack96/sim/util/graph/visit/DFS.java index 9f34209..35c6274 100644 --- a/src/berack96/sim/util/graph/visit/DFS.java +++ b/src/berack96/sim/util/graph/visit/DFS.java @@ -12,23 +12,13 @@ import java.util.function.Consumer; * * @param the vertex of the graph * @param the weight of the graph + * @author Berack96 */ public class DFS implements VisitStrategy { - private VisitInfo lastVisit = null; - - /** - * Retrieve the info of the last visit - * - * @return an info of the visit - */ - public VisitInfo getLastVisit() { - return lastVisit; - } - @Override - public void visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { - lastVisit = new VisitInfo<>(source); + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + VisitInfo info = new VisitInfo<>(source); final Stack toVisit = new Stack<>(); toVisit.push(source); @@ -40,19 +30,20 @@ public class DFS implements VisitStrategy { while (iter.hasNext() && !hasChildToVisit) { V child = iter.next(); - if (!lastVisit.isDiscovered(child)) { + if (!info.isDiscovered(child)) { hasChildToVisit = true; toVisit.push(child); - lastVisit.setParent(current, child); + info.setParent(current, child); } } if (!hasChildToVisit) { toVisit.pop(); - lastVisit.setVisited(current); + info.setVisited(current); if (visit != null) visit.accept(current); } } + return info; } } diff --git a/src/berack96/sim/util/graph/visit/Dijkstra.java b/src/berack96/sim/util/graph/visit/Dijkstra.java index 312b3e9..d40cf37 100644 --- a/src/berack96/sim/util/graph/visit/Dijkstra.java +++ b/src/berack96/sim/util/graph/visit/Dijkstra.java @@ -1,45 +1,61 @@ package berack96.sim.util.graph.visit; +import berack96.sim.util.graph.Edge; import berack96.sim.util.graph.Graph; import java.util.*; import java.util.function.Consumer; -public class Dijkstra implements VisitStrategy { +/** + * Class that implements the Dijkstra algorithm and uses it for getting all the distance from a source + * + * @param vertex + * @param weight + * @author Berack96 + */ +public class Dijkstra implements VisitDistance { - private Map>> distance; + private Map>> distance = null; + private V source = null; - /** - * Get the last calculated distance to all the possible destinations
- * The map contains all the possible vertices that are reachable from the source set in the visit
- * If there is no path between the destination and the source, then null is returned as accordingly to the map interface
- * If the visit is not already been done, then the map is null. - * - * @return the last distance - */ - public Map>> getLastDistance() { + @Override + public Map>> getLastDistance() { return distance; } @Override - public void visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { - Queue> queue = new PriorityQueue<>(); + public V getLastSource() { + return source; + } + + @Override + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + VisitInfo info = new VisitInfo<>(source); + Queue queue = new PriorityQueue<>(); Map dist = new HashMap<>(); Map prev = new HashMap<>(); + this.source = source; dist.put(source, 0); // Initialization - queue.add(new QueueEntry<>(source, 0)); + queue.add(new QueueEntry(source, 0)); while (!queue.isEmpty()) { // The main loop - QueueEntry u = queue.poll(); // Remove and return best vertex - graph.getChildrenAndWeight(u.entry).forEach((vertex, weight) -> { - int alt = dist.get(u.entry) + weight.intValue(); - Integer distCurrent = dist.get(vertex); - if (distCurrent == null || alt < distCurrent) { - dist.put(vertex, alt); - prev.put(vertex, u.entry); + QueueEntry u = queue.poll(); // Remove and return best vertex - QueueEntry current = new QueueEntry<>(vertex, alt); + info.setVisited(u.entry); + if (visit != null) + visit.accept(u.entry); + + graph.getEdgesOut(u.entry).forEach((edge) -> { + V child = edge.getDestination(); + info.setDiscovered(child); + int alt = dist.get(u.entry) + edge.getWeight().intValue(); + Integer distCurrent = dist.get(child); + if (distCurrent == null || alt < distCurrent) { + dist.put(child, alt); + prev.put(child, u.entry); + + QueueEntry current = new QueueEntry(child, alt); queue.remove(current); queue.add(current); } @@ -49,25 +65,27 @@ public class Dijkstra implements VisitStrategy { /* Cleaning up the results */ distance = new HashMap<>(); for (V vertex : prev.keySet()) { - List> path = new LinkedList<>(); + List> path = new LinkedList<>(); V child = vertex; V father = prev.get(child); do { - Graph.Edge edge = new Graph.Edge<>(father, child, graph.getWeight(father, child)); + Edge edge = new Edge<>(father, child, graph.getWeight(father, child)); path.add(0, edge); + info.setParent(father, child); child = father; father = prev.get(child); } while (father != null); distance.put(vertex, new ArrayList<>(path)); } + return info; } - private class QueueEntry implements Comparable { + private class QueueEntry implements Comparable { final V entry; - final W weight; + final Integer weight; - QueueEntry(V entry, W weight) { + QueueEntry(V entry, Integer weight) { this.entry = entry; this.weight = weight; } @@ -83,7 +101,7 @@ public class Dijkstra implements VisitStrategy { @Override public int compareTo(QueueEntry queueEntry) { - return this.weight.intValue() - queueEntry.weight.intValue(); + return this.weight - queueEntry.weight; } } } diff --git a/src/berack96/sim/util/graph/visit/Tarjan.java b/src/berack96/sim/util/graph/visit/Tarjan.java index 10e1346..ef8c428 100644 --- a/src/berack96/sim/util/graph/visit/Tarjan.java +++ b/src/berack96/sim/util/graph/visit/Tarjan.java @@ -5,71 +5,77 @@ import berack96.sim.util.graph.Graph; import java.util.*; import java.util.function.Consumer; -public class Tarjan implements VisitStrategy { +/** + * Class that implements the Tarjan algorithm and uses it for getting the SCC and the topological sort + * + * @param vertex + * @param weight + * @author Berack96 + */ +public class Tarjan implements VisitSCC, VisitTopological { - private Set> SCC = null; + private Collection> SCC = null; private List topologicalSort = null; private Map indices = null; private Map lowLink = null; private Stack stack = null; + private VisitInfo info = null; - /** - * Return the latest calculated strongly connected components of the graph. - * - * @return the latest SCC - */ - public Set> getSCC() { + @Override + public Collection> getSCC() { return SCC; } - /** - * Return the latest calculated Topological sort of the graph.
- * If the latest visited graph is not a DAG, it will return null. - * - * @return the topological order of the DAG - */ + @Override public List getTopologicalSort() { return topologicalSort; } /** - * This particular visit strategy use only the graph, so the other parameters are useless. + * This particular visit strategy use only the graph and the visit, so the source param is not needed. * * @param graph the graph to visit - * @param source the source of the visit + * @param source not needed * @param visit the function to apply at each vertex when they are visited * @throws NullPointerException if the graph is null * @throws IllegalArgumentException doesn't throw this */ @Override - public void visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { SCC = new HashSet<>(); topologicalSort = new LinkedList<>(); + info = null; indices = new HashMap<>(); lowLink = new HashMap<>(); stack = new Stack<>(); Integer index = 0; - for (V vertex : graph) + for (V vertex : graph) { + if (info == null) + info = new VisitInfo<>(vertex); if (!indices.containsKey(vertex)) - strongConnect(graph, vertex, index); + strongConnect(graph, vertex, index, visit); + } topologicalSort = (graph.numberOfVertices() == SCC.size()) ? new ArrayList<>(topologicalSort) : null; + return info; } - private void strongConnect(Graph graph, V vertex, Integer index) { + private void strongConnect(Graph graph, V vertex, Integer index, Consumer visit) { // Set the depth index for v to the smallest unused index indices.put(vertex, index); lowLink.put(vertex, index); index++; stack.push(vertex); + info.setDiscovered(vertex); // Consider successors of v for (V child : graph.getChildren(vertex)) { if (!indices.containsKey(child)) { - strongConnect(graph, child, index); + info.setParent(vertex, child); + strongConnect(graph, child, index, visit); lowLink.put(vertex, Math.min(lowLink.get(vertex), lowLink.get(child))); } else if (stack.contains(child)) { // Successor w is in stack S and hence in the current SCC @@ -88,6 +94,11 @@ public class Tarjan implements VisitStrategy { temp = stack.pop(); topologicalSort.add(0, temp); newComponent.add(temp); + + info.setVisited(temp); + if (visit != null) + visit.accept(temp); + } while (!temp.equals(vertex)); SCC.add(newComponent); diff --git a/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java b/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java new file mode 100644 index 0000000..697ff57 --- /dev/null +++ b/src/berack96/sim/util/graph/visit/VisitDistSourceDest.java @@ -0,0 +1,29 @@ +package berack96.sim.util.graph.visit; + +import berack96.sim.util.graph.Edge; +import berack96.sim.util.graph.Graph; + +import java.util.List; + +/** + * Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others + * + * @param the vertex + * @param the weight + * @author Berack96 + */ +public interface VisitDistSourceDest extends VisitStrategy { + + /** + * Get the distance from the source to the destination
+ * The list contains the minimum path from the vertex marked as source to the destination vertex + * + * @param graph the graph were to find the min path + * @param source the source vertex + * @param destination the destination vertex + * @return the distance + * @throws NullPointerException if one of the vertex is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + List> distance(Graph graph, V source, V destination) throws NullPointerException, IllegalArgumentException; +} diff --git a/src/berack96/sim/util/graph/visit/VisitDistance.java b/src/berack96/sim/util/graph/visit/VisitDistance.java new file mode 100644 index 0000000..02eff96 --- /dev/null +++ b/src/berack96/sim/util/graph/visit/VisitDistance.java @@ -0,0 +1,35 @@ +package berack96.sim.util.graph.visit; + +import berack96.sim.util.graph.Edge; + +import java.util.List; +import java.util.Map; + +/** + * Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others + * + * @param the vertex + * @param the weight + * @author Berack96 + */ +public interface VisitDistance extends VisitStrategy { + + /** + * Get the last calculated distance to all the possible destinations
+ * The map contains all the possible vertices that are reachable from the source set in the visit
+ * If there is no path between the destination and the source, then null is returned as accordingly to the map interface
+ * If the visit is not already been done, then the map is null. + * + * @return the last distance + * @throws NullPointerException if the visit is not already been done + */ + Map>> getLastDistance() throws NullPointerException; + + /** + * Get the last source vertex of the visit for calculating the destinations.
+ * Returns null if the visit is not already been done + * + * @return the last vertex + */ + V getLastSource(); +} diff --git a/src/berack96/sim/util/graph/visit/VisitInfo.java b/src/berack96/sim/util/graph/visit/VisitInfo.java new file mode 100644 index 0000000..b919748 --- /dev/null +++ b/src/berack96/sim/util/graph/visit/VisitInfo.java @@ -0,0 +1,266 @@ +package berack96.sim.util.graph.visit; + +import java.util.*; +import java.util.function.Consumer; + +/** + * The class used for getting the info of the visit.
+ * It could be used with the algorithm of the visit for set some useful data. + * + * @param the vertex of the visit + * @author Berack96 + */ +public class VisitInfo { + private final Map discovered; + private final Map visited; + private final Map parent; + private final V source; + private long time; + + /** + * Need a source for initialize the basic values + * + * @param source the source of the visit + * @throws NullPointerException if the source is null + */ + public VisitInfo(V source) { + if (source == null) + throw new NullPointerException(); + + discovered = new Hashtable<>(); + visited = new Hashtable<>(); + parent = new Hashtable<>(); + + this.time = 0; + this.source = source; + setDiscovered(source); + } + + /** + * The time of the vertex when it is discovered in the visit.
+ * For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
+ * The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time
+ * + * @param vertex the vertex needed + * @return the time of it's discovery + * @throws IllegalArgumentException if the vertex is not discovered + * @throws NullPointerException if the vertex is null + */ + public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException { + Long time = discovered.get(vertex); + if (time == null) + throw new IllegalArgumentException(); + return time; + } + + /** + * The time when the vertex is visited by the algorithm
+ * For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
+ * The time starts at 0 and for each vertex discovered or visited is increased by one
+ * + * @param vertex the vertex needed + * @return the time of it's visit + * @throws IllegalArgumentException if the vertex is not visited + * @throws NullPointerException if the vertex is null + */ + public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException { + Long time = visited.get(vertex); + if (time == null) + throw new IllegalArgumentException(); + return time; + } + + /** + * Tells if a vertex is discovered or not + * + * @param vertex the vertex chosen + * @return true if is discovered + */ + public boolean isDiscovered(V vertex) throws NullPointerException { + try { + return discovered.containsKey(vertex); + } catch (NullPointerException e) { + return false; + } + } + + /** + * Tells if the vertex is visited or not + * + * @param vertex the vertex chosen + * @return true if is visited + */ + public boolean isVisited(V vertex) throws NullPointerException { + try { + return visited.containsKey(vertex); + } catch (NullPointerException e) { + return false; + } + } + + /** + * Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.
+ * Next this call it will be possible to get the time of visit of that vertex
+ * Does nothing if the vertex is already been visited. + * + * @param vertex the vertex that has been visited + */ + synchronized void setVisited(V vertex) { + setDiscovered(vertex); + if (!visited.containsKey(vertex)) + visited.put(vertex, time++); + } + + /** + * Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery
+ * Does nothing if the vertex is already been discovered. + * + * @param vertex the vertex that has been discovered + */ + synchronized void setDiscovered(V vertex) { + if (!discovered.containsKey(vertex)) + discovered.put(vertex, time++); + } + + /** + * Set the parent of a particular vertex
+ * The parent of a vertex is the one that has discovered it
+ * If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
+ * + * @param parent the vertex that is the parent + * @param child the vertex discovered + * @throws IllegalArgumentException if the parent is not already discovered + */ + synchronized void setParent(V parent, V child) throws IllegalArgumentException { + if (!isDiscovered(parent)) + throw new IllegalArgumentException(parent.toString()); + + setDiscovered(child); + this.parent.putIfAbsent(child, parent); + } + + /** + * Get the source of the visit. + * + * @return the source vertex where it's started the visit + */ + public V getSource() { + return source; + } + + /** + * Get the parent of a particular vertex.
+ * The parent of a vertex is the one that has discovered it
+ * If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned. + * + * @param vertex the child vertex + * @return the parent of the child + * @throws IllegalArgumentException if the vertex has not been discovered yet + */ + public V getParentOf(V vertex) throws IllegalArgumentException { + if (isDiscovered(vertex)) + return parent.get(vertex); + + throw new IllegalArgumentException(); + } + + /** + * Get all the visited vertices so far. + * + * @return the visited vertices + */ + public Set getVisited() { + return visited.keySet(); + } + + /** + * Get all the discovered vertices so far. + * + * @return the discovered vertices + */ + public Set getDiscovered() { + return discovered.keySet(); + } + + /** + * Iterate through all the vertices that are discovered.
+ * The vertices will be ordered by the time of their discover. + * + * @param consumer the function to apply to each + */ + public void forEachDiscovered(Consumer consumer) { + Set vertices = getDiscovered(); + Queue queue = new PriorityQueue<>(); + + vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), isVisited(vertex) ? getTimeVisit(vertex) : -1, false))); + + while (!queue.isEmpty()) + consumer.accept(queue.poll()); + } + + /** + * Iterate through all the vertices that are visited.
+ * The vertices will be ordered by the time of their visit. + * + * @param consumer the function to apply to each + */ + public void forEachVisited(Consumer consumer) { + Set vertices = getVisited(); + Queue queue = new PriorityQueue<>(); + + vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true))); + + while (!queue.isEmpty()) + consumer.accept(queue.poll()); + } + + /** + * Iterate through all the vertices discovered and visited with the correct timeline.
+ * The vertices will be visited in the order that they are discovered and visited, so a vertex can appear two times (one for the discovery, anc the other for the visit) + * + * @param consumer the function to apply at each vertex + */ + public void forEach(Consumer consumer) { + Set discovered = getDiscovered(); + Set visited = getVisited(); + Queue queue = new PriorityQueue<>(); + + discovered.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), false))); + visited.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true))); + + while (!queue.isEmpty()) + consumer.accept(queue.remove()); + } + + /** + * Class used mainly for storing the data of the visit + */ + public class VertexInfo implements Comparable { + public final V vertex; + public final V parent; + public final long timeDiscovered; + public final long timeVisited; + private final boolean compareVisited; + + private VertexInfo(V vertex, V parent, long timeDiscovered, long timeVisited, boolean compareVisited) { + this.vertex = vertex; + this.parent = parent; + this.timeDiscovered = timeDiscovered; + this.timeVisited = timeVisited; + this.compareVisited = compareVisited; + } + + @Override + public int compareTo(VertexInfo other) { + long compareThis = compareVisited ? timeVisited : timeDiscovered; + long compareOther = other.compareVisited ? other.timeVisited : other.timeDiscovered; + + return (int) (compareThis - compareOther); + } + + @Override + public String toString() { + return String.format("%s -> %s [D:%3d, V:%3d]", parent, vertex, timeDiscovered, timeVisited); + } + } +} diff --git a/src/berack96/sim/util/graph/visit/VisitSCC.java b/src/berack96/sim/util/graph/visit/VisitSCC.java new file mode 100644 index 0000000..0579049 --- /dev/null +++ b/src/berack96/sim/util/graph/visit/VisitSCC.java @@ -0,0 +1,21 @@ +package berack96.sim.util.graph.visit; + +import java.util.Collection; + +/** + * Interface that is helpful for implements visit that needs to retrieve the SCC + * + * @param the vertex + * @param the weight + * @author Berack96 + */ +public interface VisitSCC extends VisitStrategy { + + /** + * Return the latest calculated strongly connected components of the graph. + * + * @return the latest SCC + * @throws NullPointerException if there is no last calculated SCC + */ + Collection> getSCC(); +} diff --git a/src/berack96/sim/util/graph/visit/VisitStrategy.java b/src/berack96/sim/util/graph/visit/VisitStrategy.java index 1d08008..b3455b2 100644 --- a/src/berack96/sim/util/graph/visit/VisitStrategy.java +++ b/src/berack96/sim/util/graph/visit/VisitStrategy.java @@ -2,8 +2,6 @@ package berack96.sim.util.graph.visit; import berack96.sim.util.graph.Graph; -import java.util.Hashtable; -import java.util.Map; import java.util.function.Consumer; /** @@ -23,171 +21,10 @@ public interface VisitStrategy { * @param graph the graph to visit * @param source the source of the visit * @param visit the function to apply at each vertex when they are visited + * @return an info of the view * @throws NullPointerException if one of the arguments is null (only the consumers can be null) * @throws IllegalArgumentException if the source vertex is not in the graph * @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph */ - void visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; - - /** - * The class used for getting the info of the visit.
- * It could be used with the algorithm of the visit for set some useful data. - * - * @param the vertex of the visit - * @author Berack96 - */ - class VisitInfo { - private final Map discovered; - private final Map visited; - private final Map parent; - private final V source; - private long time; - - /** - * Need a source for initialize the basic values - * - * @param source the source of the visit - * @throws NullPointerException if the source is null - */ - public VisitInfo(V source) { - if (source == null) - throw new NullPointerException(); - - discovered = new Hashtable<>(); - visited = new Hashtable<>(); - parent = new Hashtable<>(); - - this.time = 0; - this.source = source; - setDiscovered(source); - } - - /** - * The time of the vertex when it is discovered in the visit.
- * For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
- * The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time
- * - * @param vertex the vertex needed - * @return the time of it's discovery - * @throws IllegalArgumentException if the vertex is not discovered - * @throws NullPointerException if the vertex is null - */ - public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException { - Long time = discovered.get(vertex); - if (time == null) - throw new IllegalArgumentException(); - return time; - } - - /** - * The time when the vertex is visited by the algorithm
- * For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
- * The time starts at 0 and for each vertex discovered or visited is increased by one
- * - * @param vertex the vertex needed - * @return the time of it's visit - * @throws IllegalArgumentException if the vertex is not visited - * @throws NullPointerException if the vertex is null - */ - public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException { - Long time = visited.get(vertex); - if (time == null) - throw new IllegalArgumentException(); - return time; - } - - /** - * Tells if a vertex is discovered or not - * - * @param vertex the vertex chosen - * @return true if is discovered - */ - public boolean isDiscovered(V vertex) throws NullPointerException { - try { - return discovered.containsKey(vertex); - } catch (NullPointerException e) { - return false; - } - } - - /** - * Tells if the vertex is visited or not - * - * @param vertex the vertex chosen - * @return true if is visited - */ - public boolean isVisited(V vertex) throws NullPointerException { - try { - return visited.containsKey(vertex); - } catch (NullPointerException e) { - return false; - } - } - - /** - * Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.
- * Next this call it will be possible to get the time of visit of that vertex
- * Does nothing if the vertex is already been visited. - * - * @param vertex the vertex that has been visited - */ - public synchronized void setVisited(V vertex) { - setDiscovered(vertex); - if (!visited.containsKey(vertex)) - visited.put(vertex, time++); - } - - /** - * Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery
- * Does nothing if the vertex is already been discovered. - * - * @param vertex the vertex that has been discovered - */ - public synchronized void setDiscovered(V vertex) { - if (!discovered.containsKey(vertex)) - discovered.put(vertex, time++); - } - - /** - * Set the parent of a particular vertex
- * The parent of a vertex is the one that has discovered it
- * If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
- * - * @param parent the vertex that is the parent - * @param child the vertex discovered - * @throws IllegalArgumentException if the parent is not already discovered - */ - public synchronized void setParent(V parent, V child) throws IllegalArgumentException { - if (!isDiscovered(parent)) - throw new IllegalArgumentException(parent.toString()); - - setDiscovered(child); - this.parent.putIfAbsent(child, parent); - } - - /** - * Get the source of the visit. - * - * @return the source vertex where it's started the visit - */ - public V getSource() { - return source; - } - - /** - * Get the parent of a particular vertex.
- * The parent of a vertex is the one that has discovered it
- * If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned. - * - * @param vertex the child vertex - * @return the parent of the child - * @throws IllegalArgumentException if the vertex has not been discovered yet - */ - public V getParentOf(V vertex) throws IllegalArgumentException { - if (isDiscovered(vertex)) - return parent.get(vertex); - - throw new IllegalArgumentException(); - } - } + VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; } diff --git a/src/berack96/sim/util/graph/visit/VisitTopological.java b/src/berack96/sim/util/graph/visit/VisitTopological.java new file mode 100644 index 0000000..978b41c --- /dev/null +++ b/src/berack96/sim/util/graph/visit/VisitTopological.java @@ -0,0 +1,22 @@ +package berack96.sim.util.graph.visit; + +import java.util.List; + +/** + * Interface that is helpful for implements visit that needs to retrieve the topological sort + * + * @param the vertex + * @param the weight + * @author Berack96 + */ +public interface VisitTopological extends VisitStrategy { + + /** + * Return the latest calculated Topological sort of the graph.
+ * If the latest visited graph is not a DAG, it will return null. + * + * @return the topological order of the DAG + * @throws NullPointerException if there is no last calculated topological sort + */ + List getTopologicalSort(); +} diff --git a/test/berack96/test/sim/TestGraph.java b/test/berack96/test/sim/TestGraph.java index 4c9c5ee..d74a29a 100644 --- a/test/berack96/test/sim/TestGraph.java +++ b/test/berack96/test/sim/TestGraph.java @@ -1,14 +1,17 @@ package berack96.test.sim; +import berack96.sim.util.graph.Edge; import berack96.sim.util.graph.Graph; import berack96.sim.util.graph.MapGraph; +import berack96.sim.util.graph.Vertex; import berack96.sim.util.graph.visit.BFS; import berack96.sim.util.graph.visit.DFS; -import berack96.sim.util.graph.visit.VisitStrategy; +import berack96.sim.util.graph.visit.VisitInfo; import org.junit.Before; import org.junit.Test; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; @@ -18,6 +21,7 @@ public class TestGraph { private final Exception nullException = new NullPointerException(Graph.PARAM_NULL); private final Exception notException = new IllegalArgumentException(Graph.VERTEX_NOT_CONTAINED); + private final Exception unsuppException = new UnsupportedOperationException(Vertex.REMOVED); @Before public void before() { @@ -90,6 +94,10 @@ public class TestGraph { shouldThrow(nullException, () -> graph.addEdge(null, "2", 1)); shouldThrow(nullException, () -> graph.addEdge(null, null, 1)); shouldThrow(nullException, () -> graph.addEdge("1", null, 1)); + shouldThrow(new NullPointerException(), () -> graph.addEdge(null)); + shouldThrow(nullException, () -> graph.addEdge(new Edge<>("1", null, 1))); + shouldThrow(nullException, () -> graph.addEdge(new Edge<>(null, null, 1))); + shouldThrow(nullException, () -> graph.addEdge(new Edge<>(null, "2", 1))); shouldThrow(nullException, () -> graph.containsEdge(null, "2")); shouldThrow(nullException, () -> graph.containsEdge(null, null)); shouldThrow(nullException, () -> graph.containsEdge("1", null)); @@ -103,9 +111,6 @@ public class TestGraph { shouldThrow(notException, () -> graph.addEdge("0", "2", 1)); shouldThrow(notException, () -> graph.addEdge("2", "8", 1)); shouldThrow(notException, () -> graph.addEdge("9", "6", 1)); - shouldThrow(notException, () -> graph.containsEdge("01", "4")); - shouldThrow(notException, () -> graph.containsEdge("3", "8132")); - shouldThrow(notException, () -> graph.containsEdge("9423", "516")); shouldThrow(notException, () -> graph.removeEdge("012", "2")); shouldThrow(notException, () -> graph.removeEdge("2", "28")); shouldThrow(notException, () -> graph.removeEdge("4329", "62")); @@ -116,14 +121,18 @@ public class TestGraph { assertEquals(0, graph.numberOfEdges()); assertNull(graph.addEdge("1", "2", 1)); - assertNull(graph.addEdge("1", "3", 1)); + assertNull(graph.addEdge(new Edge<>("1", "3", 1))); assertNull(graph.addEdge("2", "5", 4)); - assertNull(graph.addEdge("3", "5", 2)); - assertNull(graph.addEdge("5", "3", 2)); + assertNull(graph.addEdge(new Edge<>("3", "5", 2))); + assertNull(graph.addEdge(new Edge<>("5", "3", 2))); assertNull(graph.addEdge("5", "4", 3)); assertEquals(6, graph.numberOfEdges()); + assertFalse(graph.containsEdge("01", "4")); + assertFalse(graph.containsEdge("3", "8132")); + assertFalse(graph.containsEdge("9423", "516")); + // All this calls should do nothing graph.removeEdge("1", "5"); graph.removeEdge("1", "4"); @@ -145,6 +154,9 @@ public class TestGraph { assertEquals(new Integer(1), graph.addEdge("1", "2", 102)); assertEquals(new Integer(102), graph.addEdge("1", "2", 3)); assertEquals(new Integer(3), graph.addEdge("1", "2", 1)); + assertEquals(new Integer(1), graph.addEdge(new Edge<>("1", "2", 102))); + assertEquals(new Integer(102), graph.addEdge(new Edge<>("1", "2", 3))); + assertEquals(new Integer(3), graph.addEdge(new Edge<>("1", "2", 1))); assertEquals(6, graph.numberOfEdges()); assertTrue(graph.containsEdge("1", "2")); @@ -196,19 +208,45 @@ public class TestGraph { assertFalse(graph.containsEdge("5", "4")); assertEquals(0, graph.numberOfEdges()); - shouldThrow(notException, () -> graph.containsEdge("2", "323")); - graph.addEdgeAndVertices("2", "323", 3); + assertFalse(graph.containsEdge("2", "323")); + assertNull(graph.addEdgeAndVertices("2", "323", 3)); assertTrue(graph.containsEdge("2", "323")); - shouldThrow(notException, () -> graph.containsEdge("2aa", "323")); - graph.addEdgeAndVertices("2aa", "323", 3); + assertFalse(graph.containsEdge("2aa", "323")); + assertNull(graph.addEdgeAndVertices("2aa", "323", 35)); assertTrue(graph.containsEdge("2aa", "323")); - shouldThrow(notException, () -> graph.containsEdge("2bbb", "323bbb")); - graph.addEdgeAndVertices("2bbb", "323bbb", 3); + assertFalse(graph.containsEdge("2bbb", "323bbb")); + assertNull(graph.addEdgeAndVertices("2bbb", "323bbb", 135)); assertTrue(graph.containsEdge("2bbb", "323bbb")); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, "1", 1)); shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, null, 1)); shouldThrow(nullException, () -> graph.addEdgeAndVertices("2", null, 1)); + assertEquals(3, graph.addEdgeAndVertices("2", "323", 50).intValue()); + assertEquals(35, graph.addEdgeAndVertices("2aa", "323", 5).intValue()); + assertEquals(50, graph.addEdgeAndVertices("2", "323", 500).intValue()); + + graph.removeAllEdge(); + + assertFalse(graph.containsEdge("2", "323")); + assertNull(graph.addEdgeAndVertices(new Edge<>("2", "323", 3))); + assertTrue(graph.containsEdge("2", "323")); + assertFalse(graph.containsEdge("2aa", "323")); + assertNull(graph.addEdgeAndVertices(new Edge<>("2aa", "323", 35))); + assertTrue(graph.containsEdge("2aa", "323")); + assertFalse(graph.containsEdge("2bbb", "323bbb")); + assertNull(graph.addEdgeAndVertices(new Edge<>("2bbb", "323bbb", 135))); + assertTrue(graph.containsEdge("2bbb", "323bbb")); + + shouldThrow(nullException, () -> graph.addEdgeAndVertices(new Edge<>(null, "1", 1))); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(new Edge<>(null, null, 1))); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(new Edge<>("2", null, 1))); + shouldThrow(new NullPointerException(), () -> graph.addEdgeAndVertices(null)); + + assertEquals(3, graph.addEdgeAndVertices(new Edge<>("2", "323", 50)).intValue()); + assertEquals(35, graph.addEdgeAndVertices(new Edge<>("2aa", "323", 5)).intValue()); + assertEquals(50, graph.addEdgeAndVertices(new Edge<>("2", "323", 500)).intValue()); + graph.removeAllVertex(); graph.addVertex("aaa"); graph.addVertex("1"); @@ -217,19 +255,19 @@ public class TestGraph { shouldContain(graph.vertices(), "1", "2", "aaa"); shouldContain(graph.edges()); - Set> edges = new HashSet<>(); - edges.add(new Graph.Edge<>("aaa", "bbb", 3)); - edges.add(new Graph.Edge<>("bbb", "ccc", 4)); - edges.add(new Graph.Edge<>("ccc", "aaa", 5)); - edges.add(new Graph.Edge<>("1", "2", 2)); + Set> edges = new HashSet<>(); + edges.add(new Edge<>("aaa", "bbb", 3)); + edges.add(new Edge<>("bbb", "ccc", 4)); + edges.add(new Edge<>("ccc", "aaa", 5)); + edges.add(new Edge<>("1", "2", 2)); graph.addAllEdges(edges); shouldContain(graph.vertices(), "1", "2", "aaa", "bbb", "ccc"); shouldContain(graph.edges(), - new Graph.Edge<>("aaa", "bbb", 3), - new Graph.Edge<>("bbb", "ccc", 4), - new Graph.Edge<>("ccc", "aaa", 5), - new Graph.Edge<>("1", "2", 2)); + new Edge<>("aaa", "bbb", 3), + new Edge<>("bbb", "ccc", 4), + new Edge<>("ccc", "aaa", 5), + new Edge<>("1", "2", 2)); } @Test @@ -276,12 +314,19 @@ public class TestGraph { shouldContain(graph.getAncestors("5"), "2", "3"); shouldContain(graph.getAncestors("6"), "2", "4"); - shouldContain(graph.getChildrenAndWeight("1").entrySet(), new AbstractMap.SimpleEntry<>("2", 1), new AbstractMap.SimpleEntry<>("3", 1)); - shouldContain(graph.getChildrenAndWeight("2").entrySet(), new AbstractMap.SimpleEntry<>("5", 4), new AbstractMap.SimpleEntry<>("6", 5)); - shouldContain(graph.getChildrenAndWeight("3").entrySet(), new AbstractMap.SimpleEntry<>("5", 2)); - shouldContain(graph.getChildrenAndWeight("4").entrySet(), new AbstractMap.SimpleEntry<>("6", 6)); - shouldContain(graph.getChildrenAndWeight("5").entrySet(), new AbstractMap.SimpleEntry<>("3", 9), new AbstractMap.SimpleEntry<>("4", 5)); - shouldContain(graph.getChildrenAndWeight("6").entrySet()); + shouldContain(graph.getEdgesOut("1"), new Edge<>("1", "2", 1), new Edge<>("1", "3", 1)); + shouldContain(graph.getEdgesOut("2"), new Edge<>("2", "5", 4), new Edge<>("2", "6", 5)); + shouldContain(graph.getEdgesOut("3"), new Edge<>("3", "5", 2)); + shouldContain(graph.getEdgesOut("4"), new Edge<>("4", "6", 6)); + shouldContain(graph.getEdgesOut("5"), new Edge<>("5", "3", 9), new Edge<>("5", "4", 5)); + shouldContain(graph.getEdgesOut("6")); + + shouldContain(graph.getEdgesIn("1")); + shouldContain(graph.getEdgesIn("2"), new Edge<>("1", "2", 1)); + shouldContain(graph.getEdgesIn("3"), new Edge<>("1", "3", 1), new Edge<>("5", "3", 9)); + shouldContain(graph.getEdgesIn("4"), new Edge<>("5", "4", 5)); + shouldContain(graph.getEdgesIn("5"), new Edge<>("2", "5", 4), new Edge<>("3", "5", 2)); + shouldContain(graph.getEdgesIn("6"), new Edge<>("4", "6", 6), new Edge<>("2", "6", 5)); assertEquals(0, graph.degreeIn("1")); assertEquals(1, graph.degreeIn("2")); @@ -305,27 +350,27 @@ public class TestGraph { assertEquals(2, graph.degree("6")); shouldContain(graph.edges(), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("1", "3", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("2", "6", 5), - new Graph.Edge<>("3", "5", 2), - new Graph.Edge<>("4", "6", 6), - new Graph.Edge<>("5", "3", 9), - new Graph.Edge<>("5", "4", 5)); + new Edge<>("1", "2", 1), + new Edge<>("1", "3", 1), + new Edge<>("2", "5", 4), + new Edge<>("2", "6", 5), + new Edge<>("3", "5", 2), + new Edge<>("4", "6", 6), + new Edge<>("5", "3", 9), + new Edge<>("5", "4", 5)); shouldThrow(nullException, () -> graph.edgesOf(null)); shouldThrow(notException, () -> graph.edgesOf("rew")); shouldContain(graph.edgesOf("5"), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("3", "5", 2), - new Graph.Edge<>("5", "3", 9), - new Graph.Edge<>("5", "4", 5)); + new Edge<>("2", "5", 4), + new Edge<>("3", "5", 2), + new Edge<>("5", "3", 9), + new Edge<>("5", "4", 5)); } @Test public void preBasicVisit() { - VisitStrategy.VisitInfo info = new VisitStrategy.VisitInfo<>(0); + VisitInfo info = new VisitInfo<>(0); assertTrue(info.isDiscovered(0)); assertFalse(info.isVisited(0)); assertEquals(0, info.getTimeDiscover(0)); @@ -383,8 +428,7 @@ public class TestGraph { shouldThrow(notException, () -> graph.visit("1010", new DFS<>(), null)); DFS dfs = new DFS<>(); - graph.visit("1", dfs, null); - VisitStrategy.VisitInfo visitDFS = dfs.getLastVisit(); + VisitInfo visitDFS = graph.visit("1", dfs, null); assertEquals(0, visitDFS.getTimeDiscover("1")); assertEquals(1, visitDFS.getTimeDiscover("2")); assertEquals(2, visitDFS.getTimeDiscover("5")); @@ -400,9 +444,25 @@ public class TestGraph { assertFalse(visitDFS.isDiscovered("7")); assertFalse(visitDFS.isDiscovered("8")); + int[] discoverTime = {0, 1, 2, 3, 5, 6}; + String[] verticesDiscovered = {"1", "2", "5", "3", "4", "6"}; + AtomicInteger integer = new AtomicInteger(0); + visitDFS.forEachDiscovered(vertexInfo -> { + assertEquals(discoverTime[integer.get()], vertexInfo.timeDiscovered); + assertEquals(verticesDiscovered[integer.get()], vertexInfo.vertex); + integer.incrementAndGet(); + }); + integer.set(0); + int[] visitTime = {4, 7, 8, 9, 10, 11}; + String[] verticesVisited = {"3", "6", "4", "5", "2", "1"}; + visitDFS.forEachVisited(vertexInfo -> { + assertEquals(visitTime[integer.get()], vertexInfo.timeVisited); + assertEquals(verticesVisited[integer.get()], vertexInfo.vertex); + integer.incrementAndGet(); + }); + BFS bfs = new BFS<>(); - graph.visit("1", bfs, null); - VisitStrategy.VisitInfo visitBFS = bfs.getLastVisit(); + VisitInfo visitBFS = graph.visit("1", bfs, null); assertEquals(0, visitBFS.getTimeDiscover("1")); assertEquals(1, visitBFS.getTimeVisit("1")); assertEquals(2, visitBFS.getTimeDiscover("2")); @@ -642,8 +702,7 @@ public class TestGraph { Graph transposed = graph.transpose(); DFS dfs = new DFS<>(); - transposed.visit("6", dfs, null); - VisitStrategy.VisitInfo visitDFS = dfs.getLastVisit(); + VisitInfo visitDFS = transposed.visit("6", dfs, null); assertEquals(0, visitDFS.getTimeDiscover("6")); assertEquals(1, visitDFS.getTimeDiscover("4")); assertEquals(2, visitDFS.getTimeDiscover("5")); @@ -658,8 +717,7 @@ public class TestGraph { assertFalse(visitDFS.isDiscovered("7")); assertFalse(visitDFS.isDiscovered("8")); - transposed.visit("8", dfs, null); - visitDFS = dfs.getLastVisit(); + visitDFS = transposed.visit("8", dfs, null); assertEquals(0, visitDFS.getTimeDiscover("8")); assertEquals(1, visitDFS.getTimeDiscover("7")); assertEquals(2, visitDFS.getTimeVisit("7")); @@ -733,23 +791,23 @@ public class TestGraph { graph.addEdge("6", "2", 2); graph.addEdge("7", "8", 8); - List> distance = graph.distance("1", "6"); - int sum = distance.stream().mapToInt(Graph.Edge::getWeight).sum(); + List> distance = graph.distance("1", "6"); + int sum = distance.stream().mapToInt(Edge::getWeight).sum(); assertEquals(13, sum); shouldContainInOrder(distance, - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "4", 3), - new Graph.Edge<>("4", "6", 5)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3), + new Edge<>("4", "6", 5)); distance = graph.distance("1", "3"); - sum = distance.stream().mapToInt(Graph.Edge::getWeight).sum(); + sum = distance.stream().mapToInt(Edge::getWeight).sum(); assertEquals(8, sum); shouldContainInOrder(distance, - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "3", 3)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "3", 3)); - shouldContainInOrder(graph.distance("7", "8"), new Graph.Edge<>("7", "8", 8)); + shouldContainInOrder(graph.distance("7", "8"), new Edge<>("7", "8", 8)); shouldThrow(nullException, () -> graph.distance(null, "1")); shouldThrow(nullException, () -> graph.distance(null, null)); @@ -792,32 +850,146 @@ public class TestGraph { graph.addEdge("6", "2", 2); graph.addEdge("7", "8", 8); - Map>> distance = graph.distance("1"); + Map>> distance = graph.distance("1"); assertNull(distance.get("1")); shouldContainInOrder(distance.get("2"), - new Graph.Edge<>("1", "2", 1)); + new Edge<>("1", "2", 1)); shouldContainInOrder(distance.get("3"), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "3", 3)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "3", 3)); shouldContain(distance.get("4"), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "4", 3)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3)); shouldContain(distance.get("5"), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4)); shouldContain(distance.get("6"), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "4", 3), - new Graph.Edge<>("4", "6", 5)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3), + new Edge<>("4", "6", 5)); assertNull(distance.get("7")); shouldContain(distance.get("8"), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "4", 3), - new Graph.Edge<>("4", "8", 2)); + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3), + new Edge<>("4", "8", 2)); + } + + @Test + public void marker() { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + + graph.addVertexIfAbsent("1"); + graph.addVertexIfAbsent("2"); + graph.addVertexIfAbsent("3"); + graph.addVertexIfAbsent("4"); + graph.addVertexIfAbsent("5"); + graph.addVertexIfAbsent("6"); + graph.addVertexIfAbsent("7"); + graph.addVertexIfAbsent("8"); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 1); + graph.addEdge("2", "5", 4); + graph.addEdge("4", "6", 5); + graph.addEdge("5", "3", 6); + graph.addEdge("5", "4", 3); + graph.addEdge("6", "2", 2); + graph.addEdge("7", "8", 8); + graph.addEdge("8", "7", 8); + + shouldThrow(nullException, () -> graph.mark(null, null)); + shouldThrow(nullException, () -> graph.mark("1", null)); + shouldThrow(nullException, () -> graph.mark(null, "yellow")); + shouldThrow(nullException, () -> graph.unMark(null)); + shouldThrow(nullException, () -> graph.getMarks(null)); + shouldThrow(nullException, () -> graph.unMark(null, null)); + shouldThrow(nullException, () -> graph.unMark("1", null)); + shouldThrow(nullException, () -> graph.unMark(null, "blue")); + shouldThrow(nullException, () -> graph.unMarkAll(null)); + + shouldThrow(notException, () -> graph.mark("324", "yellow")); + shouldThrow(notException, () -> graph.unMark("32423")); + shouldThrow(notException, () -> graph.getMarks("hw7389")); + + shouldContain(graph.getMarks("1")); + graph.mark("1", "red"); + shouldContain(graph.getMarks("1"), "red"); + graph.mark("1", "yellow"); + graph.mark("1", "blue"); + shouldContain(graph.getMarks("1"), "red", "yellow", "blue"); + graph.mark("1", "red"); + shouldContain(graph.getMarks("1"), "red", "yellow", "blue"); + + shouldContain(graph.getMarks("2")); + graph.mark("2", "red"); + shouldContain(graph.getMarks("8")); + graph.mark("8", "blue"); + shouldContain(graph.getMarks("2"), "red"); + shouldContain(graph.getMarks("8"), "blue"); + + graph.unMark("2"); + shouldContain(graph.getMarks("2")); + graph.unMark("1"); + shouldContain(graph.getMarks("1")); + + graph.mark("2", "red"); + graph.mark("2", "blue"); + shouldContain(graph.getMarks("2"), "red", "blue"); + graph.mark("4", "green"); + shouldContain(graph.getMarks("4"), "green"); + graph.mark("5", "green"); + shouldContain(graph.getMarks("5"), "green"); + + graph.unMarkAll(); + shouldContain(graph.getMarks("1")); + shouldContain(graph.getMarks("2")); + shouldContain(graph.getMarks("3")); + shouldContain(graph.getMarks("4")); + shouldContain(graph.getMarks("5")); + shouldContain(graph.getMarks("6")); + shouldContain(graph.getMarks("7")); + shouldContain(graph.getMarks("8")); + + graph.mark("1", "mark"); + graph.mark("2", "mark"); + graph.mark("3", "mark2"); + graph.mark("1", "mark2"); + shouldContain(graph.getMarks("1"), "mark", "mark2"); + shouldContain(graph.getMarks("2"), "mark"); + shouldContain(graph.getMarks("3"), "mark2"); + + graph.unMark("1", "mark"); + shouldContain(graph.getMarks("1"), "mark2"); + shouldContain(graph.getMarks("2"), "mark"); + shouldContain(graph.getMarks("3"), "mark2"); + + graph.unMarkAll("mark2"); + shouldContain(graph.getMarks("1")); + shouldContain(graph.getMarks("2"), "mark"); + shouldContain(graph.getMarks("3")); + + graph.unMark("1", "mark"); + graph.unMark("2", "mark2"); + shouldContain(graph.getMarks("1")); + shouldContain(graph.getMarks("2"), "mark"); + shouldContain(graph.getMarks("3")); + + graph.unMark("2", "mark"); + shouldContain(graph.getMarks("1")); + shouldContain(graph.getMarks("2")); + shouldContain(graph.getMarks("3")); } @Test @@ -847,6 +1019,24 @@ public class TestGraph { graph.addEdge("5", "4", 5); graph.addEdge("6", "2", 2); + graph.mark("1", "blue"); + graph.mark("3", "blue"); + graph.mark("5", "blue"); + + graph.mark("2", "even"); + graph.mark("4", "even"); + graph.mark("6", "even"); + + graph.mark("2", "circle"); + graph.mark("4", "circle"); + graph.mark("5", "circle"); + graph.mark("6", "circle"); + + graph.mark("1", "z"); + graph.mark("2", "z"); + graph.mark("5", "z"); + graph.mark("4", "z"); + Graph sub = graph.subGraph("1", -541); shouldContain(sub.vertices(), "1"); shouldContain(sub.edges()); @@ -858,34 +1048,175 @@ public class TestGraph { sub = graph.subGraph("1", 1); shouldContain(sub.vertices(), "1", "2", "3"); shouldContain(sub.edges(), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("1", "3", 1)); + new Edge<>("1", "2", 1), + new Edge<>("1", "3", 1)); sub = graph.subGraph("1", 3); shouldContain(sub.vertices(), "1", "2", "3", "5", "4"); shouldContain(sub.edges(), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("1", "3", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("5", "3", 2), - new Graph.Edge<>("5", "4", 5)); + new Edge<>("1", "2", 1), + new Edge<>("1", "3", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "3", 2), + new Edge<>("5", "4", 5)); sub = graph.subGraph("6", 2); shouldContain(sub.vertices(), "6", "2", "5"); shouldContain(sub.edges(), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("6", "2", 2)); + new Edge<>("2", "5", 4), + new Edge<>("6", "2", 2)); sub = graph.subGraph("1", 77689); shouldContain(sub.vertices(), "1", "2", "3", "5", "4", "6"); shouldContain(sub.edges(), - new Graph.Edge<>("1", "2", 1), - new Graph.Edge<>("1", "3", 1), - new Graph.Edge<>("2", "5", 4), - new Graph.Edge<>("4", "6", 6), - new Graph.Edge<>("5", "3", 2), - new Graph.Edge<>("5", "4", 5), - new Graph.Edge<>("6", "2", 2)); + new Edge<>("1", "2", 1), + new Edge<>("1", "3", 1), + new Edge<>("2", "5", 4), + new Edge<>("4", "6", 6), + new Edge<>("5", "3", 2), + new Edge<>("5", "4", 5), + new Edge<>("6", "2", 2)); + + /* MARKED */ + sub = graph.subGraph("z"); + shouldContain(sub.vertices(), "1", "2", "5", "4"); + shouldContain(sub.edges(), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 5)); + + sub = graph.subGraph("circle"); + shouldContain(sub.vertices(), "2", "5", "4", "6"); + shouldContain(sub.edges(), + new Edge<>("2", "5", 4), + new Edge<>("4", "6", 6), + new Edge<>("5", "4", 5), + new Edge<>("6", "2", 2)); + + sub = graph.subGraph("blue"); + shouldContain(sub.vertices(), "1", "3", "5"); + shouldContain(sub.edges(), + new Edge<>("1", "3", 1), + new Edge<>("5", "3", 2)); + + sub = graph.subGraph("even"); + shouldContain(sub.vertices(), "2", "4", "6"); + shouldContain(sub.edges(), + new Edge<>("4", "6", 6), + new Edge<>("6", "2", 2)); + } + + @Test + public void vertexClass() { + Vertex vertex = new Vertex<>(graph, "stronzo"); + + assertEquals("stronzo", vertex.getValue()); + assertEquals(0, graph.numberOfVertices()); + + shouldThrow(unsuppException, () -> vertex.addChild(null, null)); + shouldThrow(unsuppException, () -> vertex.mark(null)); + shouldThrow(unsuppException, () -> vertex.removeChild(null)); + shouldThrow(unsuppException, () -> vertex.visit(null, null)); + shouldThrow(unsuppException, vertex::unMark); + shouldThrow(unsuppException, vertex::getAncestors); + shouldThrow(unsuppException, vertex::getChildren); + shouldThrow(unsuppException, vertex::getEdgesOut); + shouldThrow(unsuppException, vertex::getEdgesIn); + shouldThrow(unsuppException, vertex::getChildrenAsVertex); + shouldThrow(unsuppException, vertex::getAncestorsAsVertex); + shouldThrow(unsuppException, vertex::getMarks); + + vertex.addIfAbsent(); + assertEquals(1, graph.numberOfVertices()); + vertex.addIfAbsent(); + assertEquals(1, graph.numberOfVertices()); + vertex.addIfAbsent(); + assertEquals(1, graph.numberOfVertices()); + + assertEquals(vertex, graph.getVertex("stronzo")); + shouldThrow(nullException, () -> graph.getVertex(null)); + shouldThrow(notException, () -> graph.getVertex("stronzo1")); + + shouldThrow(nullException, () -> vertex.addChild(null, 3)); + shouldThrow(nullException, () -> vertex.addChild(null, null)); + shouldThrow(nullException, () -> vertex.mark(null)); + shouldThrow(nullException, () -> vertex.removeChild(null)); + shouldThrow(new NullPointerException(), () -> vertex.visit(null, null)); + + shouldThrow(notException, () -> vertex.addChild("1", null)); + shouldThrow(notException, () -> vertex.addChild("ssdsad", 2)); + shouldThrow(notException, () -> vertex.removeChild("234")); + + shouldContain(vertex.getMarks()); + shouldContain(vertex.getAncestors()); + shouldContain(vertex.getChildren()); + shouldContain(vertex.getChildrenAsVertex()); + shouldContain(vertex.getEdgesIn()); + shouldContain(vertex.getEdgesOut()); + + graph.addVertex("1"); + graph.addVertex("2"); + graph.addVertex("3"); + + graph.addEdge("1", "2", 2); + graph.addEdge("3", "stronzo", 6); + graph.addEdge("stronzo", "2", 1); + graph.addEdge("stronzo", "1", 3); + + shouldContain(vertex.getMarks()); + shouldContain(vertex.getAncestors(), "3"); + shouldContain(vertex.getChildren(), "1", "2"); + shouldContain(vertex.getChildrenAsVertex(), new Vertex<>(graph, "1"), new Vertex<>(graph, "2")); + shouldContain(vertex.getAncestorsAsVertex(), new Vertex<>(graph, "3")); + shouldContain(vertex.getEdgesIn(), + new Edge<>("3", "stronzo", 6)); + shouldContain(graph.getEdgesIn(vertex.getValue()), + new Edge<>("3", "stronzo", 6)); + shouldContain(vertex.getEdgesOut(), + new Edge<>("stronzo", "1", 3), + new Edge<>("stronzo", "2", 1)); + shouldContain(graph.getEdgesOut(vertex.getValue()), + new Edge<>("stronzo", "1", 3), + new Edge<>("stronzo", "2", 1)); + + vertex.mark("ciao"); + vertex.mark("ciao2"); + shouldContain(vertex.getMarks(), "ciao", "ciao2"); + shouldContain(graph.getMarks(vertex.getValue()), "ciao", "ciao2"); + vertex.unMark(); + shouldContain(vertex.getMarks()); + + vertex.removeChild("1"); + shouldContain(vertex.getChildren(), "2"); + vertex.addChild("3", 23); + shouldContain(vertex.getChildren(), "2", "3"); + shouldContain(vertex.getAncestors(), "3"); + shouldContain(vertex.getEdgesOut(), new Edge<>("stronzo", "3", 23), new Edge<>("stronzo", "2", 1)); + shouldContain(graph.getEdgesOut(vertex.getValue()), new Edge<>("stronzo", "3", 23), new Edge<>("stronzo", "2", 1)); + shouldContain(vertex.getEdgesIn(), new Edge<>("3", "stronzo", 6)); + shouldContain(graph.getEdgesIn(vertex.getValue()), new Edge<>("3", "stronzo", 6)); + + assertTrue(vertex.isStillContained()); + vertex.remove(); + assertFalse(vertex.isStillContained()); + assertFalse(graph.contains(vertex.getValue())); + assertEquals(3, graph.numberOfVertices()); + + shouldThrow(unsuppException, () -> vertex.addChild(null, null)); + shouldThrow(unsuppException, () -> vertex.mark(null)); + shouldThrow(unsuppException, () -> vertex.removeChild(null)); + shouldThrow(unsuppException, () -> vertex.visit(null, null)); + shouldThrow(unsuppException, vertex::unMark); + shouldThrow(unsuppException, vertex::getAncestors); + shouldThrow(unsuppException, vertex::getChildren); + shouldThrow(unsuppException, vertex::getEdgesOut); + shouldThrow(unsuppException, vertex::getEdgesIn); + shouldThrow(unsuppException, vertex::getChildrenAsVertex); + shouldThrow(unsuppException, vertex::getAncestorsAsVertex); + shouldThrow(unsuppException, vertex::getMarks); + + vertex.addIfAbsent(); + assertEquals(4, graph.numberOfVertices()); } // TODO test saveFile