diff --git a/src/berack96/lib/graph/Edge.java b/src/berack96/lib/graph/Edge.java index 17a3946..239bddd 100644 --- a/src/berack96/lib/graph/Edge.java +++ b/src/berack96/lib/graph/Edge.java @@ -1,13 +1,15 @@ package berack96.lib.graph; +import java.util.Collection; +import java.util.List; + /** * Class used for retrieving the edges of the graph. * * @param the vertices - * @param the weight of the edge * @author Berack96 */ -public class Edge { +public class Edge implements Comparable> { /** * The source vertex @@ -20,21 +22,44 @@ public class Edge { /** * The weight of this edge */ - private final W weight; + private final int weight; /** - * Create an final version of this object + * Create a final version of this object with weight 1 + * + * @param source the source of the edge + * @param destination the destination of the edge + */ + public Edge(V source, V destination) { + this(source, destination, 1); + } + + /** + * Create a final version of this object * * @param source the source of the edge * @param destination the destination of the edge * @param weight the weight of the edge */ - public Edge(V source, V destination, W weight) { + public Edge(V source, V destination, int weight) { this.source = source; this.destination = destination; this.weight = weight; } + /** + * + */ + public Collection getVertices() { + if (source == null && destination == null) + return List.of(); + if (source == null) + return List.of(destination); + if (destination == null) + return List.of(source); + return List.of(source, destination); + } + /** * The vertex where the edge goes * @@ -58,7 +83,7 @@ public class Edge { * * @return the weight */ - public W getWeight() { + public int getWeight() { return weight; } @@ -80,4 +105,9 @@ public class Edge { return false; } } + + @Override + public int compareTo(Edge edge) { + return weight - edge.weight; + } } diff --git a/src/berack96/lib/graph/Graph.java b/src/berack96/lib/graph/Graph.java index 4a300e1..7d58ece 100644 --- a/src/berack96/lib/graph/Graph.java +++ b/src/berack96/lib/graph/Graph.java @@ -1,56 +1,49 @@ package berack96.lib.graph; import berack96.lib.graph.visit.VisitStrategy; +import berack96.lib.graph.visit.impl.BFS; +import berack96.lib.graph.visit.impl.Dijkstra; import berack96.lib.graph.visit.impl.VisitInfo; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Consumer; /** - * An interface for the graphs.
- * This interface is used for the graphs with Directed edges.
- * A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.
+ * An abstract class for the graphs.
+ * This class is used for the graphs in general.
+ * There are more specific {@link GraphDirected} and {@link GraphUndirected} edges graph interfaces.
* * @param The Object that represent a vertex - * @param The Object that represent the edge (more specifically the weight of the edge) * @author Berack96 */ -public interface Graph extends Iterable { +public abstract class Graph implements Iterable { - String NOT_DAG = "The graph is not a DAG"; - String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; - String PARAM_NULL = "The parameter must not be null"; - String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; - - /** - * Tells if the graph has some cycle.
- * A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G), - * the visit can return to V1 in any point. - * - * @return true if has cycle, false otherwise - */ - boolean isCyclic(); + public static final int NO_EDGE = 0; + public final static String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; + public final static String PARAM_NULL = "The parameter must not be null"; + public final static String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; /** - * Tells if the graph has the property of DAG (Directed Acyclic Graph).
- * A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} ) - * - * @return true if is a DAG, false otherwise + * Map that contains the vertex as key and a set of all the marker associated with it. */ - boolean isDAG(); + private final Map> markers = new HashMap<>(); + + /** + * Get a new instance of this Graph. + * + * @return A new instance of the graph + */ + protected abstract Graph getNewInstance(); /** * Check if the vertex passed is contained in the graph or not.
* The vertex V1 is contained in the graph G, if and only if:
- * exist V2 in G such that V2.equals(V1) + * exist V2 in G such that V2 == V1 * * @param vertex the vertex to check * @return true if the vertex is contained, false otherwise - * @throws NullPointerException if the vertex is null */ - boolean contains(V vertex) throws NullPointerException; + public abstract boolean contains(V vertex) throws NullPointerException; /** * Get an instance of the vertex linked with this graph.
@@ -58,29 +51,36 @@ public interface Graph extends Iterable { * * @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 get(V vertex) throws NullPointerException, IllegalArgumentException; + public final Vertex get(V vertex) throws IllegalArgumentException { + checkVert(vertex); + return new Vertex<>(this, vertex); + } /** - * Add the vertex to the graph. If it's already in the graph it will be replaced and all its edges will be resetted.
- * Of course the vertex added will have no edge to any other vertex nor form any other vertex. + * Add the vertex to the graph. If it's already in the graph it will be replaced and all its edges will be reset.
+ * Of course the vertex added will have no marks nor edge to any other vertex nor form any other vertex. * * @param vertex the vertex to add * @throws NullPointerException if the vertex is null */ - void add(V vertex) throws NullPointerException; + public abstract void add(V vertex) throws NullPointerException; /** * Add the specified vertex to the graph only if the graph doesn't contains it.
* The graph contains a vertex only if the method {@link #contains(Object)} returns true. * * @param vertex the vertex to add - * @return true if the vertex is added, false if the graph contains the vertex and therefore the new one is not added + * @return true if it adds a vertex, false if it was already in the graph * @throws NullPointerException if the vertex is null */ - boolean addIfAbsent(V vertex) throws NullPointerException; + public final boolean addIfAbsent(V vertex) throws NullPointerException { + if (contains(vertex)) + return false; + add(vertex); + return true; + } /** * Add all the vertices contained in the collection to the graph.
@@ -90,214 +90,178 @@ public interface Graph extends Iterable { * @param vertices a collection of the vertices to add * @throws NullPointerException if the set is null */ - void addAll(Collection vertices) throws NullPointerException; + public void addAll(@SuppressWarnings("ConstantConditions") Collection vertices) throws NullPointerException { + check(vertices); + for (V vertex : vertices) + addIfAbsent(vertex); + } /** * 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. + * After this method's call the vertex will be no longer present in the graph, and nether all his edges and marks. * * @param vertex the vertex to remove - * @throws NullPointerException if the vertex is null + * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained */ - void remove(V vertex) throws NullPointerException, IllegalArgumentException; + public abstract void remove(V vertex) throws NullPointerException, IllegalArgumentException; /** * Remove all the vertex contained in the graph.
* After this method's call the graph will be empty; no vertices nor edges. */ - void removeAll(); + public void removeAll() { + unMarkAll(); + for (V vertex : vertices()) + remove(vertex); + } /** - * Get all the marks of this graph.
- * Specifically it will return a collection of marks where every mark
- * as associated at least one vertex of the graph.
- * If the graph doesn't have vertex marked then it is returned an empty collection. - * - * @return a collection of marks - */ - Collection marks(); - - /** - * 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, Object 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, Object 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 vertices that are marked with the specific mark passed.
- * If there aren't vertices with that mark then it is returned an empty set.
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. - * - * @param mark the mark - * @return all the vertices that are marked with that specific mark - * @throws NullPointerException if the mark is null - */ - Collection getMarkedWith(Object mark) throws NullPointerException; - - /** - * Get all the marker of this vertex.
- * If the vertex doesn't have any mark, then it will return an empty set.
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be 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(Object 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
- * This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned + * Check if the edge between the two vertex passed is contained in the graph or not.
+ * 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 - * @param weight the weight of the edge - * @return null or the previous weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null + * @return true if the edge is contained, false otherwise + * @throws NullPointerException if one of the parameters is null + */ + public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { + try { + return getWeight(vertex1, vertex2) != NO_EDGE; + } catch (IllegalArgumentException ignore) { + return false; + } + } + + /** + * Get the weight of the selected edge.
+ * If the edge doesn't exist, then 0 is returned + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @return the weight previously set, or 0 if the edge doesn't exist + * @throws NullPointerException if one of the parameters 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; + public abstract int getWeight(V vertex1, V vertex2) 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 + * @return 0 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; + public final int addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { + return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight()); + } /** - * 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.
- * The edge will be created from the vertex V1 and the vertex V2
+ * Add an edge between the two vertex.
+ * This method will overwrite any existing edge between the two vertices.
+ * By default using this method will set the edge to the value 1. + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @throws NullPointerException if one of the parameter is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + public final void addEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { + addEdge(vertex1, vertex2, 1); + } + + /** + * Add an edge between the two vertex.
* This method will overwrite any existing edge between the two vertex.
- * If there was a previous edge then it is returned + * If there was a previous edge then it's value is returned.
+ * If the weight passed is equals to 0 or {@link Graph#NO_EDGE}, then + * the edge will be removed. * * @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 weight of the edge if there was already one - * @throws NullPointerException if one of the parameter is null + * @return 0 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 addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException; + public abstract int addEdge(V vertex1, V vertex2, int weight) throws NullPointerException, IllegalArgumentException; /** * 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.
+ * This method will overwrite any existing edge between the two vertices.
* 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 + * @return 0 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; + public final int addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException { + return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()); + } + + /** + * 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.
+ * This method will overwrite any existing edge between the two vertices.
+ * By default using this method will set the edge to the value 1. + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @throws NullPointerException if one of the parameter is null + */ + public final int addEdgeAndVertices(V vertex1, V vertex2) throws NullPointerException { + return addEdgeAndVertices(vertex1, vertex2, 1); + } + + /** + * 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.
+ * This method will overwrite any existing edge between the two vertices.
+ * If there was a previous edge then it is returned + * + * @param vertex1 a vertex of the graph + * @param vertex2 a vertex of the graph + * @param weight the weight of the edge + * @return 0 or the previous weight of the edge if there was already one + * @throws NullPointerException if one of the parameter is null + */ + public final int addEdgeAndVertices(V vertex1, V vertex2, int weight) throws NullPointerException { + addIfAbsent(vertex1); + addIfAbsent(vertex2); + return addEdge(vertex1, vertex2, weight); + } /** * 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. + * This method will overwrite any existing edge between the two vertices. * * @param edges the edges to add * @throws NullPointerException if the set is null */ - void addAllEdges(Collection> edges) throws NullPointerException; + public void addAllEdges(Collection> edges) throws NullPointerException { + edges.forEach(edge -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight())); + } /** - * Get the weight of the selected edge.
- * If the edge doesn't exist, then null is returned - * - * @param vertex1 a vertex of the graph - * @param vertex2 a vertex of the graph - * @return the weight previously set, or null if the edge doesn't exist - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; - - /** - * Remove the edge between the two vertex.
+ * Remove the edge between the two vertex by setting it's value to 0.
* If the edge doesn't exist, then this call does nothing.
- * After this method's call it will be no longer possible to travel from V1 to V2, nether from V2 to V1. + * This method is equivalent to calling {@link Graph#addEdge(Object, Object, int)} )} + * with the weight set to {@link Graph#NO_EDGE} * * @param vertex1 a vertex of the graph * @param vertex2 a vertex of the graph * @throws NullPointerException if one of the parameters is null * @throws IllegalArgumentException if one of the vertex is not contained in the graph */ - void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all the edges that goes in the vertex.
- * After this method's call it will be no longer possible travel to this vertex. - * - * @param vertex a vertex of the graph - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException; - - /** - * Remove all the edges that start from this vertex.
- * After this method's call it will be no longer possible travel to any vertex from this one. - * - * @param vertex a vertex of the graph - * @throws NullPointerException if one of the parameters is null - * @throws IllegalArgumentException if one of the vertex is not contained in the graph - */ - void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException; + public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { + addEdge(vertex1, vertex2, NO_EDGE); + } /** * Remove all edges form a particular vertex of the graph.
@@ -308,133 +272,199 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if one of the vertex is not contained in the graph */ - void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException; + public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { + unMark(vertex); + remove(vertex); + add(vertex); + } /** * Remove all the edges of the graph.
* After this method's call the graph will have only vertices, and no edge. */ - void removeAllEdge(); - - /** - * 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.
- * 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 - */ - 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 collection.
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. - * - * @return an array that include all the vertices - */ - Collection vertices(); - - /** - * Get all the edges in the graph.
- * If the graph doesn't contains edges, it'll return an empty collection.
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. - * - * @return a collection that include all the edges - */ - Collection> edges(); + public void removeAllEdge() { + Collection vertices = vertices(); + removeAll(); + addAll(vertices); + } /** * 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.
- * Note2: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. * * @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> 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.
- * Note2: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. - * - * @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.
- * Note2: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. - * - * @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; + public abstract Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException; /** * Get all the vertices that are children of the vertex passed as parameter.
* The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge - * where V1 is the source of that edge.
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. + * where V1 is the source of that edge. * * @param vertex the source vertex * @return an array of vertices * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - Collection getChildrens(V vertex) throws NullPointerException, IllegalArgumentException; + public abstract 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 #getChildrens(Object)}
- * Note: depending on the implementation, modifying the returned collection
- * could affect the graph behavior and the changes could be reflected to the graph. + * Basically is the opposite of {@link #getChildren(Object)} * * @param vertex a vertex of the graph * @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 */ - Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; + public abstract Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException; /** - * Tells the degree of all the edges that goes to this vertex.
- * Basically, it'll count how many edge towards himself it have. + * Get all the marks of this graph.
+ * Specifically it will return a collection of marks where every mark
+ * as associated at least one vertex of the graph.
+ * If the graph doesn't have vertex marked then it is returned an empty collection. * - * @param vertex a vertex of the graph - * @return the in degree of the vertex + * @return a collection of marks + */ + public final Collection marks() { + Collection ret = new HashSet<>(); + markers.forEach((v, set) -> ret.addAll(set)); + return ret; + } + + /** + * Add to the specified vertex the mark passed.
+ * A vertex can have multiple marker.
+ * The null marker cannot be used. + * + * @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 + */ + public final void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { + check(mark); + checkVert(vertex); + Set marks = markers.computeIfAbsent(vertex, v -> new HashSet<>()); + marks.add(mark); + } + + /** + * 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 + */ + public final void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { + check(mark); + checkVert(vertex); + markers.computeIfPresent(vertex, (v, set) -> { + set.remove(mark); + if (set.size() > 0) + return set; + return null; + }); + } + + /** + * 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 */ - int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException; + public final void unMark(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + try { + markers.remove(vertex).clear(); + } catch (Exception ignore) { + } + } /** - * Tells the degree of all the edges that goes form this vertex to others.
- * Basically, it'll count how many edge towards any other vertex it have. + * Get all the vertices that are marked with the specific mark passed.
+ * If there aren't vertices with that mark then it is returned an empty set.
* - * @param vertex a vertex of the graph - * @return the out degree of the vertex + * @param mark the mark + * @return all the vertices that are marked with that specific mark + * @throws NullPointerException if the mark is null + */ + public final Collection getMarkedWith(Object mark) throws NullPointerException { + check(mark); + Collection vertices = new ArrayList<>(markers.size()); + markers.forEach((v, set) -> { + if (set.contains(mark)) + vertices.add(v); + }); + return vertices; + } + + /** + * Get all the marker of this vertex.
+ * If the vertex doesn't have any mark, then it will return an empty set.
+ * Note: modifying the returned collection affect the marker of the vertex. + * + * @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 */ - int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException; + public final Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + return markers.getOrDefault(vertex, new HashSet<>()); + } + + /** + * Remove the selected mark from all the vertices + * + * @param mark the mark to remove + * @throws NullPointerException if the mark is null + */ + public final void unMarkAll(Object mark) throws NullPointerException { + check(mark); + Collection toRemove = new ArrayList<>(markers.size()); + markers.forEach((v, set) -> { + set.remove(mark); + if (set.size() == 0) + toRemove.add(v); + }); + markers.keySet().removeAll(toRemove); + } + + /** + * Remove all the marker to all the vertex.
+ * After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set + */ + public final void unMarkAll() { + markers.values().forEach(Set::clear); + markers.clear(); + } + + /** + * Get all the vertices in the graph.
+ * If the graph doesn't contains vertices, it'll return an empty collection.
+ * + * @return an array that include all the vertices + */ + public Collection vertices() { + Collection collection = new ArrayList<>(); + forEach(collection::add); + return collection; + } + + /** + * Get all the edges in the graph.
+ * If the graph doesn't contains edges, it'll return an empty collection.
+ * + * @return a collection that include all the edges + */ + public abstract Collection> edges(); /** * Tells the degree of a vertex.
@@ -446,63 +476,36 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - int degree(V vertex) throws NullPointerException, IllegalArgumentException; + public abstract int degree(V vertex) throws NullPointerException, IllegalArgumentException; /** * Tells how many vertices are in the graph. * * @return the number of vertices */ - int size(); + public abstract int size(); /** * Tells how many edges are in the graph. * * @return the number of edges */ - int numberOfEdges(); + public abstract int numberOfEdges(); /** * Visit the graph accordingly to the strategy that is passed.
- * This method visit the graph from the source to all the vertex that are reachable form the source.
* Some strategy can accept a source vertex null, because they visit all the graph anyway. * - * @param source the source vertex of the visit + * @param source the starting vertex for the visit * @param strategy the algorithm for visiting the graph - * @param visit the function to apply at each vertex - * @return an info of the visit - * @throws NullPointerException if one of the parameter is null (except the consumer) - * @throws IllegalArgumentException if the vertex is not in the graph + * @param visit the function to apply at each vertex visited + * @return an info of the visit if provided by the strategy + * @throws NullPointerException if one of the parameter is null (except the consumer) + * @throws UnsupportedOperationException in the case the visit cannot be applied to the graph */ - 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.
- * At the end of this method the new graph will have all the edges inverted in orientation.
- * Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges, - * the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges. - * - * @return a transposed graph of this instance - */ - Graph transpose(); - - /** - * If the current graph is a DAG, it returns a topological sort of this graph.
- * A topological ordering of a graph is a linear ordering of its vertices such that for - * every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering. - * - * @return a list containing the topological order of the vertices - * @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()}) - */ - List topologicalSort() throws UnsupportedOperationException; - - /** - * The strongly connected components or disconnected components of an arbitrary directed graph - * form a partition into subgraphs that are themselves strongly connected. - * - * @return a collection containing the strongly connected components - */ - Collection> stronglyConnectedComponents(); + public final VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, UnsupportedOperationException { + return strategy.visit(this, source, visit); + } /** * Get a sub-graph of the current one based on the maximum depth that is given.
@@ -517,18 +520,57 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained */ - Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException; + public final Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { + checkVert(source); + Graph sub = getNewInstance(); + Set vertices = new HashSet<>(this.size() + 1, 1); + new BFS().setMaxDepth(Math.max(depth, 0)).visit(this, source, vertices::add); + + sub.addAll(vertices); + for (V src : vertices) + for (V dest : getChildren(src)) + if (sub.contains(dest)) + sub.addEdge(new Edge<>(src, dest, this.getWeight(src, dest))); + return sub; + } /** * Get a sub-graph of the current one with only the vertex marked with the selected markers.
- * Each vertex will have all his edges, but only the ones with the destination marked with the same marker.
+ * Each vertex will have all his markers and his edges, but only the ones with the destination marked with the same marker.
* If the marker is not specified or 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 one or more markers * @return a sub-graph of the current graph */ - Graph subGraph(Object...marker); + public final Graph subGraph(Object... marker) { + final Graph sub = getNewInstance(); + final Set allVertices = new HashSet<>(); + final Set allMarkers = new HashSet<>(); + final boolean isEmpty = (marker == null || marker.length == 0); + + if (!isEmpty) { + Collections.addAll(allMarkers, marker); + markers.forEach((v, set) -> { + if (!Collections.disjoint(allMarkers, set)) + allVertices.add(v); + }); + } else { + Collection toAdd = vertices(); + toAdd.removeAll(markers.keySet()); + allVertices.addAll(toAdd); + } + + sub.addAll(allVertices); + for (V src : sub.vertices()) { + for (Object mark : getMarks(src)) + sub.mark(src, mark); + for (V dest : getChildren(src)) + if (sub.contains(dest)) + sub.addEdge(src, dest, getWeight(src, dest)); + } + return sub; + } /** * Get the minimum path from the source vertex to the destination vertex.
@@ -541,7 +583,13 @@ public interface Graph extends Iterable { * @throws IllegalArgumentException if the vertex is not in the graph * @throws UnsupportedOperationException if from the source it's not possible to reach the destination */ - List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; + public final List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + checkVert(source, destination); + List> path = distance(source).get(destination); + if (path == null) + throw new UnsupportedOperationException(NOT_CONNECTED); + return path; + } /** * Get the minimum path from the source vertex to all the possible reachable vertices. @@ -551,7 +599,40 @@ 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 */ - Map>> distance(V source) throws NullPointerException, IllegalArgumentException; + public final Map>> distance(V source) throws NullPointerException, IllegalArgumentException { + checkVert(source); + Dijkstra dijkstra = new Dijkstra<>(); + dijkstra.visit(this, source, null); + return dijkstra.getLastDistance(); + } - // TODO maybe, but i don't think so... STATIC DISTANCE V* -> V* + + /** + * Check if the object passed is not null. + * If it's null then throws eventual exception + * + * @param objects the objects to test + */ + protected final void check(Object... objects) { + for (Object obj : objects) + if (obj == null) + throw new NullPointerException(PARAM_NULL); + } + + /** + * Check if the vertex passed is not null and if exist in the graph. + * If not then throws eventual exception + * + * @param vertices the vertices to test + */ + @SafeVarargs + protected final void checkVert(V... vertices) { + check((Object[]) vertices); + for (V vert : vertices) + try { + if (!contains(vert)) + throw new IllegalArgumentException(VERTEX_NOT_CONTAINED); + } catch (ClassCastException ignore) { + } + } } diff --git a/src/berack96/lib/graph/GraphDirected.java b/src/berack96/lib/graph/GraphDirected.java new file mode 100644 index 0000000..bd2abe5 --- /dev/null +++ b/src/berack96/lib/graph/GraphDirected.java @@ -0,0 +1,207 @@ +package berack96.lib.graph; + +import berack96.lib.graph.visit.VisitSCC; +import berack96.lib.graph.visit.VisitTopological; +import berack96.lib.graph.visit.impl.Tarjan; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * This is a more specific interface for an implementation of a Undirected Graph.
+ * An Undirected Graph is a Graph where an arc or edge can be traversed in both ways.
+ * For example an arc between A and B can be traversed even from B to A.
+ * + * @param The Object that represent a vertex + * @author Berack96 + */ +public abstract class GraphDirected extends Graph { + String NOT_DAG = "The graph is not a DAG"; + + /** + * Tells if the graph has some cycle.
+ * A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G), + * the visit can return to V1 in any point. + * + * @return true if has cycle, false otherwise + */ + public final boolean isCyclic() { + return stronglyConnectedComponents().size() != size(); + } + + /** + * Tells if the graph has the property of DAG (Directed Acyclic Graph).
+ * A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} ) + * + * @return true if is a DAG, false otherwise + */ + public final boolean isDAG() { + return !isCyclic(); + } + + /** + * Remove all the edges that goes in the vertex.
+ * After this method's call it will be no longer possible travel to this vertex. + * + * @param vertex a vertex of the graph + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { + for (V ancestor : getAncestors(vertex)) + removeEdge(ancestor, vertex); + } + + /** + * Remove all the edges that start from this vertex.
+ * After this method's call it will be no longer possible travel to any vertex from this one. + * + * @param vertex a vertex of the graph + * @throws NullPointerException if one of the parameters is null + * @throws IllegalArgumentException if one of the vertex is not contained in the graph + */ + public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { + for (V child : getChildren(vertex)) + removeEdge(vertex, child); + } + + /** + * 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.
+ * Note2: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @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 + */ + public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { + Collection ancestors = getAncestors(vertex); + Collection> edgesIn = new HashSet<>(ancestors.size()); + + for (V ancestor : ancestors) { + int weight = getWeight(ancestor, vertex); + if (weight != NO_EDGE) + edgesIn.add(new Edge<>(ancestor, vertex, weight)); + } + return edgesIn; + } + + /** + * 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.
+ * Note2: depending on the implementation, modifying the returned collection
+ * could affect the graph behavior and the changes could be reflected to the graph. + * + * @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 + */ + public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { + Collection children = getChildren(vertex); + Collection> edgesOut = new HashSet<>(children.size()); + + for (V child : children) { + int weight = getWeight(vertex, child); + if (weight != NO_EDGE) + edgesOut.add(new Edge<>(vertex, child, weight)); + } + return edgesOut; + } + + /** + * Tells the degree of all the edges that goes to this vertex.
+ * Basically, it'll count how many edge towards himself it have. + * + * @param vertex a vertex of the graph + * @return the in degree of the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { + return getAncestors(vertex).size(); + } + + /** + * Tells the degree of all the edges that goes form this vertex to others.
+ * Basically, it'll count how many edge towards any other vertex it have. + * + * @param vertex a vertex of the graph + * @return the out degree of the vertex + * @throws NullPointerException if the vertex is null + * @throws IllegalArgumentException if the vertex is not contained in the graph + */ + public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { + return getChildren(vertex).size(); + } + + /** + * This method will create a new Graph that is the transposed version of the original.
+ * At the end of this method the new graph will have all the edges inverted in orientation.
+ * Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges, + * the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges. + * + * @return a transposed graph of this instance + */ + public final GraphDirected transpose() { + GraphDirected transposed = (GraphDirected) getNewInstance(); + transposed.addAll(vertices()); + + for (V vertex : transposed) + for (V child : getChildren(vertex)) + transposed.addEdge(child, vertex, getWeight(vertex, child)); + return transposed; + } + + /** + * If the current graph is a DAG, it returns a topological sort of this graph.
+ * A topological ordering of a graph is a linear ordering of its vertices such that for + * every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering. + * + * @return a list containing the topological order of the vertices + * @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()}) + */ + public final List topologicalSort() throws UnsupportedOperationException { + VisitTopological visit = new Tarjan<>(); + visit.visit(this, null, null); + + if (visit.getTopologicalSort() == null) + throw new UnsupportedOperationException(NOT_DAG); + return visit.getTopologicalSort(); + } + + /** + * The strongly connected components or disconnected components of an arbitrary directed graph + * form a partition into subgraphs that are themselves strongly connected. + * + * @return a collection containing the strongly connected components + */ + public final Collection> stronglyConnectedComponents() { + VisitSCC visit = new Tarjan<>(); + visit.visit(this, null, null); + return visit.getSCC(); + } + + @Override + public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { + Collection> edges = getEdgesIn(vertex); + edges.addAll(getEdgesOut(vertex)); + return edges; + } + + @Override + public Collection> edges() { + Collection> collection = new ArrayList<>(); + forEach(v -> collection.addAll(getEdgesIn(v))); + return collection; + } + + @Override + public final int degree(V vertex) throws NullPointerException, IllegalArgumentException { + return degreeIn(vertex) + degreeOut(vertex); + } +} diff --git a/src/berack96/lib/graph/GraphUndirected.java b/src/berack96/lib/graph/GraphUndirected.java new file mode 100644 index 0000000..e07fb47 --- /dev/null +++ b/src/berack96/lib/graph/GraphUndirected.java @@ -0,0 +1,46 @@ +package berack96.lib.graph; + +import berack96.lib.graph.visit.VisitMST; +import berack96.lib.graph.visit.impl.Prim; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * This is a more specific interface for an implementation of a Directed Graph.
+ * A Directed Graph is a Graph where an arc or edge can be traversed in only one way.
+ * A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.
+ * + * @param The Object that represent a vertex + * @author Berack96 + */ +public abstract class GraphUndirected extends Graph { + + /** + * The connected components of an arbitrary undirected graph form a partition into subgraphs that are themselves connected. + * + * @return a collection containing the strongly connected components + */ + public Collection> connectedComponents() { + return null; + } + + /** + * minimum spanning forest or minimum spamming tree of the graph + * + * @return A collection of edges representing the M.S.F. + */ + public Collection> minimumSpanningForest() { + VisitMST visit = new Prim<>(); + visit.visit(this, iterator().next(), null); + return visit.getMST(); + } + + @Override + public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + Collection> edges = new LinkedList<>(); + getChildren(vertex).forEach(v -> edges.add(new Edge<>(vertex, v, getWeight(vertex, v)))); + return edges; + } +} diff --git a/src/berack96/lib/graph/Vertex.java b/src/berack96/lib/graph/Vertex.java index 570abf0..34e8e91 100644 --- a/src/berack96/lib/graph/Vertex.java +++ b/src/berack96/lib/graph/Vertex.java @@ -3,8 +3,9 @@ package berack96.lib.graph; import berack96.lib.graph.visit.VisitStrategy; import berack96.lib.graph.visit.impl.VisitInfo; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; +import java.util.Objects; import java.util.function.Consumer; /** @@ -15,7 +16,6 @@ import java.util.function.Consumer; * @param the vertex * @author Berack96 */ -@SuppressWarnings({"unchecked", "rawtypes"}) public class Vertex { public static final String REMOVED = "The vertex is no longer in the graph"; @@ -27,7 +27,7 @@ public class Vertex { /** * The graph associated */ - private final Graph graph; + private final Graph graph; /** * Get a Vertex linked with the graph @@ -36,10 +36,10 @@ public class Vertex { * @param vertex the vertex * @throws NullPointerException if one of the param is null */ - public Vertex(Graph graph, V vertex) throws NullPointerException { + public Vertex(Graph graph, V vertex) throws NullPointerException { if (graph == null || vertex == null) throw new NullPointerException(); - this.graph = (Graph) graph; + this.graph = graph; this.vertex = vertex; } @@ -48,7 +48,7 @@ public class Vertex { * * @return the vertex */ - public V getValue() { + public V get() { return vertex; } @@ -105,22 +105,22 @@ public class Vertex { */ public Collection getChildren() throws UnsupportedOperationException { throwIfNotContained(); - return graph.getChildrens(vertex); + 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. + * 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<>(); + Collection> toReturn = new ArrayList<>(children.size()); for (V vertex : children) - toReturn.add(new Vertex<>(graph, vertex)); + toReturn.add(graph.get(vertex)); return toReturn; } @@ -147,33 +147,24 @@ public class Vertex { */ public Collection> getAncestorsAsVertex() throws UnsupportedOperationException { Collection ancestors = getAncestors(); - Collection> toReturn = new HashSet<>(); + Collection> toReturn = new ArrayList<>(ancestors.size()); for (V vertex : ancestors) - toReturn.add(new Vertex<>(graph, vertex)); + toReturn.add(graph.get(vertex)); return toReturn; } /** - * Get all the edge that goes OUT of this vertex + * This method will return the weight of the edge that connects
+ * this vertex to the vertex inserted.
+ * In the case that the two vertices aren't connected this method will return 0. * - * @return a collection of edges with source this one - * @throws UnsupportedOperationException if the vertex is not in the graph anymore + * @param child a child of this vertex + * @return the weight to the child or 0 if the edge doesn't exist. */ - public Collection> getEdgesOut() throws UnsupportedOperationException { + public int getChildWeight(V child) { 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); + return graph.getWeight(vertex, child); } /** @@ -186,11 +177,26 @@ public class Vertex { * @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 { + public void addChild(V child, int weight) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { throwIfNotContained(); graph.addEdge(vertex, child, weight); } + /** + * Add a child to this vertex.
+ * The added child must be in the graph or it will return an exception.
+ * This method will add the basic value for the weight provided by the graph. {@link Graph#addEdge(V, V)} + * + * @param child the destination vertex of this 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) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { + throwIfNotContained(); + graph.addEdge(vertex, child); + } + /** * Removes a child of this vertex. * If the vertex passed as param is not a child, then this call does nothing. @@ -240,9 +246,10 @@ public class Vertex { * @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 { + @SuppressWarnings("ConstantConditions") + public VisitInfo visit(final VisitStrategy strategy, final Consumer visit) throws NullPointerException, UnsupportedOperationException { throwIfNotContained(); - return graph.visit(vertex, (VisitStrategy) strategy, visit); + return graph.visit(vertex, strategy, visit); } @Override @@ -252,13 +259,13 @@ public class Vertex { @Override public int hashCode() { - return toString().hashCode(); + return vertex.hashCode(); } @Override public boolean equals(Object obj) { try { - return obj instanceof Vertex && (this.vertex.equals(obj) || this.vertex.equals(((Vertex) obj).vertex)); + return obj instanceof Vertex && (Objects.equals(vertex, obj) || Objects.equals(vertex, ((Vertex) obj).vertex)); } catch (Exception e) { return false; } diff --git a/src/berack96/lib/graph/impl/AGraph.java b/src/berack96/lib/graph/impl/AGraph.java deleted file mode 100644 index 52a0190..0000000 --- a/src/berack96/lib/graph/impl/AGraph.java +++ /dev/null @@ -1,579 +0,0 @@ -package berack96.lib.graph.impl; - -import berack96.lib.graph.Edge; -import berack96.lib.graph.Graph; -import berack96.lib.graph.Vertex; -import berack96.lib.graph.visit.VisitStrategy; -import berack96.lib.graph.visit.impl.Depth; -import berack96.lib.graph.visit.impl.Dijkstra; -import berack96.lib.graph.visit.impl.Tarjan; -import berack96.lib.graph.visit.impl.VisitInfo; - -import java.util.*; -import java.util.function.Consumer; - -/** - * An abstract class used for a basic implementation of a graph.
- * It implements the visits, the markers and some other stupid to implement methods.
- * It might not be super efficient but it works and you can always overwrite its methods for better performance - * - * @param the vertex - * @param the weight - * @author Berack96 - */ -public abstract class AGraph implements Graph { - - /** - * Map that contains the marker as key and a set of all the vertices that has it as the value.
- * This map is build like this for performance in creating the marker for multiple vertices.
- * If you flip the parameters (object and set) then has more performance over the single vertex. - */ - private final Map> markers = new HashMap<>(); - - /** - * Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change - */ - private Tarjan tarjan = null; - - /** - * Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change - */ - final private Map> dijkstra = new HashMap<>(); - - /** - * Get a new instance of this Graph. - * - * @return A new instance of the graph - */ - protected abstract Graph getNewInstance(); - - /** - * Add a vertex to the graph - * - * @param vertex the vertex to add - */ - protected abstract void addVertex(V vertex); - - /** - * Check if a vertex is in the graph - * - * @param vertex the vertex to check - * @return true if is contained, false otherwise - */ - protected abstract boolean containsVertex(V vertex); - - /** - * Remove a vertex from the graph - * - * @param vertex the vertex to remove - */ - protected abstract void removeVertex(V vertex); - - /** - * Remove all vertices from the graph - */ - protected abstract void removeAllVertices(); - - /** - * Check if the edge is in the graph - * @param vertex1 the source vertex - * @param vertex2 the destination vertex - * @return true if the edge is in the graph, false otherwise - */ - protected abstract boolean containsEdgeImpl(V vertex1, V vertex2); - - /** - * Add a new edge to the graph.
- * If the edge already exist then replace the weight and returns the old one. - * - * @param vertex1 the source vertex - * @param vertex2 the destination vertex - * @param weight the weight of the new edge - * @return the old weight, null otherwise - */ - protected abstract W addEdgeImpl(V vertex1, V vertex2, W weight); - - /** - * Get the weight of the edge - * - * @param vertex1 the source vertex - * @param vertex2 the destination vertex - * @return the weight of the edge - */ - protected abstract W getWeightImpl(V vertex1, V vertex2); - - /** - * Retrieves all the edges that goes out of a vertex.
- * (where the vertex is the source) - * - * @param vertex the source vertex - * @return a collection of edges - */ - protected abstract Collection> getEdgesOutImpl(V vertex); - - /** - * Retrieves all the edges that goes in of a vertex.
- * (where the vertex is the destination) - * - * @param vertex the destination vertex - * @return a collection of edges - */ - protected abstract Collection> getEdgesInImpl(V vertex); - - /** - * Remove the edge from the graph - * @param vertex1 the source vertex - * @param vertex2 the destination vertex - */ - protected abstract void removeEdgeImpl(V vertex1, V vertex2); - - /** - * Removes all the edges that goes out of a vertex.
- * (where the vertex is the source) - * @param vertex the source vertex - */ - protected abstract void removeAllOutEdgeImpl(V vertex); - - /** - * Removes all the edges that goes in of a vertex.
- * (where the vertex is the destination) - * - * @param vertex the destination vertex - */ - protected abstract void removeAllInEdgeImpl(V vertex); - - @Override - public boolean isCyclic() { - return stronglyConnectedComponents().size() != size(); - } - - @Override - public boolean isDAG() { - return !isCyclic(); - } - - @Override - public Vertex get(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - return new Vertex<>(this, vertex); - } - - @Override - public boolean contains(V vertex) throws NullPointerException { - checkNull(vertex); - return containsVertex(vertex); - } - - @Override - public void add(V vertex) throws NullPointerException { - checkNull(vertex); - if(containsVertex(vertex)) - remove(vertex); - addVertex(vertex); - graphChanged(); - } - - @Override - public boolean addIfAbsent(V vertex) throws NullPointerException { - if(contains(vertex)) - return false; - add(vertex); - return true; - } - - @Override - public void addAll(Collection vertices) throws NullPointerException { - checkNull(vertices); - vertices.forEach(this::addIfAbsent); - } - - @Override - public void remove(V vertex) throws NullPointerException, IllegalArgumentException { - unMark(vertex); - removeVertex(vertex); - graphChanged(); - } - - @Override - public void removeAll() { - unMarkAll(); - removeAllVertices(); - graphChanged(); - } - - @Override - public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { - checkNull(vertex1); - checkNull(vertex2); - return containsEdgeImpl(vertex1, vertex2); - } - - @Override - public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - graphChanged(); - return addEdgeImpl(vertex1, vertex2, weight); - } - - @Override - public W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { - checkNull(edge); - return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight()); - } - - @Override - public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { - addIfAbsent(vertex1); - addIfAbsent(vertex2); - return addEdge(vertex1, vertex2, weight); - } - - @Override - 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())); - } - - @Override - public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - return getWeightImpl(vertex1, vertex2); - } - - @Override - public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - return getEdgesOutImpl(vertex); - } - - @Override - public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - return getEdgesInImpl(vertex); - } - - @Override - public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - removeEdgeImpl(vertex1, vertex2); - graphChanged(); - } - - @Override - public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - removeAllOutEdgeImpl(vertex); - graphChanged(); - } - - @Override - public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - removeAllInEdgeImpl(vertex); - graphChanged(); - } - - @Override - public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - removeVertex(vertex); - addVertex(vertex); - } - - @Override - public void removeAllEdge() { - Collection vert = vertices(); - removeAllVertices(); - addAll(vert); - graphChanged(); - } - - @Override - public Collection vertices() { - Set set = new HashSet<>(); - this.forEach(set::add); - return set; - } - - @Override - public Collection> edges() { - Set> set = new HashSet<>(); - this.forEach( v -> set.addAll(this.getEdgesOut(v))); - return set; - } - - @Override - public int size() { - return vertices().size(); - } - - @Override - public int numberOfEdges() { - return edges().size(); - } - - @Override - public int degree(V vertex) throws NullPointerException, IllegalArgumentException { - return degreeIn(vertex) + degreeOut(vertex); - } - - @Override - public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { - return getAncestors(vertex).size(); - } - - @Override - public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { - return getChildrens(vertex).size(); - } - - @Override - public Collection marks() { - Collection ret = new HashSet<>(); - markers.forEach((m, v) -> { - if(v.size() > 0) - ret.add(m); - }); - - return ret; - } - - @Override - public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - checkNull(mark); - - Set set = markers.computeIfAbsent(mark, (v) -> new HashSet<>()); - set.add(vertex); - } - - @Override - public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - checkNull(mark); - markers.get(mark).remove(vertex); - } - - @Override - public void unMark(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - markers.forEach( (mark, set) -> set.remove(vertex) ); - } - - @Override - public Collection getMarkedWith(Object mark) throws NullPointerException { - checkNull(mark); - return markers.computeIfAbsent(mark, (v) -> new HashSet<>()); - } - - @Override - public Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Collection marks = new HashSet<>(); - markers.forEach( (mark, set) -> { - if (set.contains(vertex)) - marks.add(mark); - }); - - return marks; - } - - @Override - public void unMarkAll(Object mark) { - checkNull(mark); - markers.remove(mark); - } - - @Override - public void unMarkAll() { - markers.clear(); - } - - @Override - public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Collection> coll = getEdgesIn(vertex); - coll.addAll(getEdgesOut(vertex)); - return coll; - } - - @Override - public Collection getChildrens(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set set = new HashSet<>(); - getEdgesOut(vertex).forEach(e -> set.add(e.getDestination())); - return set; - } - - @Override - public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set set = new HashSet<>(); - getEdgesIn(vertex).forEach(e -> set.add(e.getSource())); - return set; - } - - @Override - public VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) throws NullPointerException, IllegalArgumentException { - return strategy.visit(this, source, visit); - } - - @Override - public Graph transpose() { - Graph graph = getNewInstance(); - graph.addAll(vertices()); - for(Edge edge : edges()) - graph.addEdge(edge.getDestination(), edge.getSource(), edge.getWeight()); - - return graph; - } - - @Override - public List topologicalSort() throws UnsupportedOperationException { - if (!isDAG()) - throw new UnsupportedOperationException(NOT_DAG); - return getTarjan().getTopologicalSort(); - } - - @Override - public Collection> stronglyConnectedComponents() { - return getTarjan().getSCC(); - } - - @Override - public Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { - Graph sub = getNewInstance(); - - Set vertices = new HashSet<>(); - new Depth(Math.max(depth, 0)).visit(this, source, vertices::add); - - sub.addAll(vertices); - for (V vertex : vertices) - getEdgesOut(vertex).forEach((edge) -> { - if(sub.contains(edge.getSource()) && sub.contains(edge.getDestination())) - sub.addEdge(edge); - }); - - return sub; - } - - @Override - public Graph subGraph(final Object...marker) { - final Graph sub = getNewInstance(); - final Set allVertices = new HashSet<>(); - final Set allMarkers = new HashSet<>(); - final boolean isEmpty = (marker == null || marker.length == 0); - - if (!isEmpty) - Collections.addAll(allMarkers, marker); - - markers.forEach( (mark, set) -> { - if (isEmpty || allMarkers.contains(mark)) - allVertices.addAll(set); - }); - - if (isEmpty) { - Collection toAdd = vertices(); - toAdd.removeAll(allVertices); - allVertices.clear(); - allVertices.addAll(toAdd); - } - - sub.addAll(allVertices); - for (V vertex : sub.vertices()) - this.edgesOf(vertex).forEach( (edge) -> { - if(sub.contains(edge.getSource()) && sub.contains(edge.getDestination())) - sub.addEdge(edge); - }); - - return sub; - } - - @Override - public List> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { - checkNullAndExist(source); - checkNullAndExist(destination); - - Dijkstra dijkstra = getDijkstra(source); /* Cached */ - List> path = dijkstra.getLastDistance().get(destination); - if (path == null) - throw new UnsupportedOperationException(NOT_CONNECTED); - return new ArrayList<>(path); - } - - @Override - public Map>> distance(V source) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(source); - return new HashMap<>(getDijkstra(source).getLastDistance()); /* Cached */ - } - - /** - * Simple function that reset all the caching variables if the graph changed - */ - private void graphChanged() { - tarjan = null; - dijkstra.clear(); - } - - /** - * Test if the object passed is null. - * If it is throw an exception. - * @param object the object to test - */ - private void checkNull(Object object) { - if (object == null) - throw new NullPointerException(PARAM_NULL); - } - - /** - * Check if the vertex passed is null and if exist in the graph. - * If not then throws eventual exception - * @param vertex the vertex to test - */ - private void checkNullAndExist(V vertex) { - checkNull(vertex); - if (!contains(vertex)) - throw new IllegalArgumentException(VERTEX_NOT_CONTAINED); - } - - /** - * Simple function that return the result of the Dijkstra visit, with the starting point as source.
- * It also cache it, so multiple call will return always the same value unless the graph has changed. - * @param source the source of the visit - * @return the complete visit - */ - private Dijkstra getDijkstra(V source) { - if (dijkstra.get(source) == null) { - Dijkstra newDijkstra = new Dijkstra<>(); - newDijkstra.visit(this, source, null); - dijkstra.put(source, newDijkstra); - } - - return dijkstra.get(source); - } - - /** - * Simple function that return the result of the Tarjan visit.
- * It also cache it, so multiple call will return always the same value unless the graph has changed. - * @return the tarjan visit - */ - private Tarjan getTarjan() { - if (tarjan == null) { - tarjan = new Tarjan<>(); - tarjan.visit(this, null, null); - } - - return tarjan; - } -} diff --git a/src/berack96/lib/graph/impl/ListGraph.java b/src/berack96/lib/graph/impl/ListGraph.java index 948c680..fb29b81 100644 --- a/src/berack96/lib/graph/impl/ListGraph.java +++ b/src/berack96/lib/graph/impl/ListGraph.java @@ -1,20 +1,20 @@ package berack96.lib.graph.impl; -import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphDirected; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** * An implementation of the graph using an adjacent list for representing the edges * * @param the vertex - * @param the weight * @author Berack96 */ -public class ListGraph extends AGraph { +public class ListGraph extends GraphDirected { - final private Map> adj = new HashMap<>(); + final private Map> adj = new Hashtable<>(); @Override public Iterator iterator() { @@ -22,121 +22,137 @@ public class ListGraph extends AGraph { } @Override - protected Graph getNewInstance() { + protected Graph getNewInstance() { return new ListGraph<>(); } @Override - protected void addVertex(V vertex) { - adj.put(vertex, new LinkedList<>()); + public void add(V vertex) { + check(vertex); + if (adj.containsKey(vertex)) + removeAllEdge(vertex); + else + adj.put(vertex, new LinkedList<>()); } @Override - protected boolean containsVertex(V vertex) { + public boolean contains(V vertex) { + check(vertex); return adj.containsKey(vertex); } @Override - protected void removeVertex(V vertex) { + public void remove(V vertex) { + checkVert(vertex); adj.remove(vertex); - adj.forEach((v, l) -> { - Set set = new HashSet<>(); - l.forEach(adj -> { - if(adj.vertex.equals(vertex)) - set.add(adj); - }); - l.removeAll(set); - }); + adj.forEach((v, list) -> list.remove(getAdj(list, vertex))); } @Override - protected void removeAllVertices() { + public int addEdge(V vertex1, V vertex2, int weight) { + checkVert(vertex1, vertex2); + + List list = adj.get(vertex1); + Adj a = getAdj(list, vertex2); + int old = a == null ? NO_EDGE : a.weight; + + if (weight == NO_EDGE) + list.remove(a); + else if (old == NO_EDGE) + list.add(new Adj(vertex2, weight)); + else + a.weight = weight; + return old; + } + + @Override + public int getWeight(V vertex1, V vertex2) { + checkVert(vertex1, vertex2); + Adj a = getAdj(adj.get(vertex1), vertex2); + return a == null ? NO_EDGE : a.weight; + } + + @Override + public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + Collection children = new HashSet<>(); + for (Adj adj : adj.get(vertex)) + children.add(adj.vertex); + return children; + } + + @Override + public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + Collection ancestors = new HashSet<>(); + adj.forEach((v, list) -> { + if (getAdj(list, vertex) != null) + ancestors.add(v); + }); + + return ancestors; + } + + + /** + * From here on there are some optimization for the methods of the generic DirectedGraph + **/ + + @Override + public int size() { + return adj.size(); + } + + @Override + public int numberOfEdges() { + AtomicInteger size = new AtomicInteger(0); + adj.values().forEach(list -> size.addAndGet(list.size())); + return size.get(); + } + + @Override + public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + AtomicInteger degree = new AtomicInteger(0); + adj.values().forEach(list -> degree.addAndGet(getAdj(list, vertex) != null ? 1 : 0)); + return degree.get(); + } + + @Override + public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + return adj.get(vertex).size(); + } + + @Override + public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + adj.get(vertex).clear(); + adj.forEach((v, list) -> list.remove(getAdj(list, vertex))); + } + + @Override + public void removeAllEdge() { + adj.forEach((v, list) -> list.clear()); + } + + @Override + public void removeAll() { adj.clear(); } - @Override - protected boolean containsEdgeImpl(V vertex1, V vertex2) { - if(!adj.containsKey(vertex1)) - return false; - - for(Adj a : adj.get(vertex1)) - if(a.vertex.equals(vertex2)) - return true; - return false; + private Adj getAdj(List list, V vertex) { + for (Adj adj : list) + if (Objects.equals(adj.vertex, vertex)) + return adj; + return null; } - @Override - protected W addEdgeImpl(V vertex1, V vertex2, W weight) { - W ret = null; - List l = adj.get(vertex1); - for(Adj a : l) - if(a.vertex.equals(vertex2)) { - ret = a.weight; - a.weight = weight; - } - if(ret == null) - l.add(new Adj(vertex2, weight)); - return ret; - } - - @Override - protected W getWeightImpl(V vertex1, V vertex2) { - W ret = null; - for(Adj a : adj.get(vertex1)) - if(a.vertex.equals(vertex2)) - ret = a.weight; - return ret; - } - - @Override - protected Collection> getEdgesOutImpl(V vertex) { - Set> set = new HashSet<>(); - adj.get(vertex).forEach(a -> set.add(new Edge<>(vertex, a.vertex, a.weight))); - return set; - } - - @Override - protected Collection> getEdgesInImpl(V vertex) { - Set> set = new HashSet<>(); - adj.forEach((v, l) -> l.forEach(a -> { - if(a.vertex.equals(vertex)) - set.add(new Edge<>(v, a.vertex, a.weight)); - })); - return set; - } - - @Override - protected void removeEdgeImpl(V vertex1, V vertex2) { - Adj ret = null; - List l = adj.get(vertex1); - for(Adj a : l) - if(a.vertex.equals(vertex2)) - ret = a; - l.remove(ret); - } - - @Override - protected void removeAllOutEdgeImpl(V vertex) { - adj.compute(vertex,(v, l) -> new LinkedList<>()); - } - - @Override - protected void removeAllInEdgeImpl(V vertex) { - adj.forEach((v, l) -> { - Set set = new HashSet<>(); - l.forEach(adj -> { - if(adj.vertex.equals(vertex)) - set.add(adj); - }); - l.removeAll(set); - }); - } - private class Adj { private final V vertex; - private W weight; - - private Adj(V vertex, W weight) { + private int weight; + + private Adj(V vertex, int weight) { this.vertex = vertex; this.weight = weight; } diff --git a/src/berack96/lib/graph/impl/MapGraph.java b/src/berack96/lib/graph/impl/MapGraph.java index 2584093..a477fa2 100644 --- a/src/berack96/lib/graph/impl/MapGraph.java +++ b/src/berack96/lib/graph/impl/MapGraph.java @@ -1,7 +1,7 @@ package berack96.lib.graph.impl; -import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphDirected; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -14,97 +14,92 @@ import java.util.concurrent.atomic.AtomicInteger; * This happen if the HashMap is not reallocated. So in the end each operation of adding or removing has O(n) * * @param the vertices - * @param the weight of the edges * @author Berack96 */ -public class MapGraph extends AGraph { +public class MapGraph extends GraphDirected { - /** - * Map that contains the edges from a vertex to another
- * The first vertex is the vertex where start the edge, the second one is where the edge goes
- * If an edge exist, then it's weight is returned - */ - private final Map> edges = new HashMap<>(); - - @Override - public Iterator iterator() { - return edges.keySet().iterator(); - } + /** + * Map that contains the edges from a vertex to another
+ * The first vertex is the vertex where start the edge, the second one is where the edge goes
+ * If an edge exist, then it's weight is returned + */ + private final Map> edges = new HashMap<>(); + + @Override + public Iterator iterator() { + return edges.keySet().iterator(); + } @Override - protected Graph getNewInstance() { + protected Graph getNewInstance() { return new MapGraph<>(); } @Override - protected void addVertex(V vertex) { - edges.put(vertex, new HashMap<>()); + public void add(V vertex) { + check(vertex); + edges.computeIfAbsent(vertex, v -> new HashMap<>()); + edges.forEach((v, adj) -> adj.remove(vertex)); + edges.get(vertex).clear(); } @Override - protected boolean containsVertex(V vertex) { + public boolean contains(V vertex) { + check(vertex); return edges.containsKey(vertex); } @Override - protected void removeVertex(V vertex) { + public void remove(V vertex) { + checkVert(vertex); edges.remove(vertex); - edges.forEach((v, map) -> map.remove(vertex)); + edges.forEach((v, map) -> map.remove(vertex)); } @Override - protected void removeAllVertices() { - edges.clear(); + public int addEdge(V vertex1, V vertex2, int weight) { + checkVert(vertex1, vertex2); + Map edge = edges.get(vertex1); + Integer old = edge.get(vertex2); + old = old == null ? NO_EDGE : old; + + if (weight == NO_EDGE) + edge.remove(vertex2); + else + edge.put(vertex2, weight); + return old; } @Override - protected boolean containsEdgeImpl(V vertex1, V vertex2) { - return contains(vertex1) && contains(vertex2) && edges.get(vertex1).containsKey(vertex2); + public int getWeight(V vertex1, V vertex2) { + checkVert(vertex1, vertex2); + Integer weight = edges.get(vertex1).get(vertex2); + return weight == null ? NO_EDGE : weight; } @Override - protected W addEdgeImpl(V vertex1, V vertex2, W weight) { - return edges.get(vertex1).put(vertex2, weight); + public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + return new HashSet<>(edges.get(vertex).keySet()); } @Override - protected W getWeightImpl(V vertex1, V vertex2) { - return edges.get(vertex1).get(vertex2); - } - - @Override - protected Collection> getEdgesOutImpl(V vertex) { - Collection> collection = new HashSet<>(); - edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); - return collection; - } - - @Override - protected Collection> getEdgesInImpl(V 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; + public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + Collection ancestors = new HashSet<>(); + edges.forEach((v, adj) -> { + if (adj.containsKey(vertex)) + ancestors.add(v); + }); + return ancestors; } @Override - protected void removeEdgeImpl(V vertex1, V vertex2) { - edges.get(vertex1).remove(vertex2); + public void removeAll() { + edges.clear(); } - @Override - protected void removeAllOutEdgeImpl(V vertex) { - edges.put(vertex, new HashMap<>()); - } - - @Override - protected void removeAllInEdgeImpl(V vertex) { - edges.forEach((v, map) -> map.remove(vertex)); - } - @Override public int size() { return edges.size(); diff --git a/src/berack96/lib/graph/impl/MatrixGraph.java b/src/berack96/lib/graph/impl/MatrixGraph.java index fd5d615..6023d32 100644 --- a/src/berack96/lib/graph/impl/MatrixGraph.java +++ b/src/berack96/lib/graph/impl/MatrixGraph.java @@ -1,7 +1,7 @@ package berack96.lib.graph.impl; -import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphDirected; import java.util.*; @@ -9,13 +9,12 @@ import java.util.*; * An implementation of the graph using a matrix for representing the edges * * @param the vertex - * @param the weight * @author Berack96 */ -public class MatrixGraph extends AGraph { +public class MatrixGraph extends GraphDirected { - final Map map = new HashMap<>(); - final List> matrix = new ArrayList<>(); + private final Map map = new HashMap<>(); + private int[][] matrix = new int[0][0]; @Override public Iterator iterator() { @@ -23,110 +22,175 @@ public class MatrixGraph extends AGraph { } @Override - protected Graph getNewInstance() { + protected Graph getNewInstance() { return new MatrixGraph<>(); } @Override - protected void addVertex(V vertex) { - map.put(vertex, map.size()); - - List newVert = new ArrayList<>(map.size()); - for (int i=0; i list.add(null)); - matrix.add(newVert); + public void add(V vertex) { + check(vertex); + if (map.containsKey(vertex)) + removeAllEdge(vertex); + else { + map.put(vertex, map.size()); + matrix = modifyMatrix(map.size()); + } } @Override - protected boolean containsVertex(V vertex) { + public boolean contains(V vertex) { + check(vertex); return map.containsKey(vertex); } @Override - protected void removeVertex(V vertex) { + public void remove(V vertex) { + checkVert(vertex); int x = map.remove(vertex); - map.replaceAll((vert, index) -> index>x? index-1:index); + int newSize = map.size(); - matrix.remove(x); - matrix.forEach(list -> { - int i; - for(i=x; i0) - list.remove(i); - }); + int[][] newMatrix = new int[newSize][newSize]; + for (int i = 0; i < newSize; i++) + for (int j = 0; j < newSize; j++) { + int indexI = i + (i < x ? 0 : 1); + int indexJ = j + (j < x ? 0 : 1); + + newMatrix[i][j] = matrix[indexI][indexJ]; + } + + matrix = newMatrix; + map.replaceAll((vert, index) -> index > x ? index - 1 : index); } @Override - protected void removeAllVertices() { + public int addEdge(V vertex1, V vertex2, int weight) { + checkVert(vertex1, vertex2); + int i = map.get(vertex1); + int j = map.get(vertex2); + + int old = matrix[i][j]; + matrix[i][j] = weight; + return old; + } + + @Override + public int getWeight(V vertex1, V vertex2) { + checkVert(vertex1, vertex2); + return matrix[map.get(vertex1)][map.get(vertex2)]; + } + + @Override + public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.get(vertex); + Collection children = new HashSet<>(); + Map invert = getInverted(); + + for (int i = 0; i < matrix.length; i++) + if (matrix[x][i] != NO_EDGE) + children.add(invert.get(i)); + return children; + } + + @Override + public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.get(vertex); + Collection ancestors = new HashSet<>(); + Map invert = getInverted(); + + for (int i = 0; i < matrix.length; i++) + if (matrix[i][x] != NO_EDGE) + ancestors.add(invert.get(i)); + return ancestors; + } + + /** + * From here on there are some optimization for the methods of the generic DirectedGraph + **/ + + @Override + public int size() { + return map.size(); + } + + @Override + public int numberOfEdges() { + int sum = 0; + for (int[] adj : matrix) + for (int edge : adj) + if (edge != NO_EDGE) + sum++; + return sum; + } + + @Override + public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int degree = 0, x = map.get(vertex); + for (int[] ints : matrix) degree += ints[x] == NO_EDGE ? 0 : 1; + return degree; + } + + @Override + public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int degree = 0, x = map.get(vertex); + for (int ints : matrix[x]) degree += ints == NO_EDGE ? 0 : 1; + return degree; + } + + @Override + public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.get(vertex); + Arrays.fill(matrix[x], NO_EDGE); + for (int[] ints : matrix) ints[x] = NO_EDGE; + } + + @Override + public void removeAllEdge() { + for (int[] ints : matrix) + Arrays.fill(ints, NO_EDGE); + } + + @Override + public void removeAll() { map.clear(); - matrix.clear(); + matrix = new int[0][0]; } @Override - protected boolean containsEdgeImpl(V vertex1, V vertex2) { - try { - return matrix.get(map.get(vertex1)).get(map.get(vertex2)) != null; - } catch (Exception ignore) { - return false; - } + public void addAll(Collection vertices) throws NullPointerException { + check(vertices); + for (V vert : vertices) + if (vert != null) + map.compute(vert, (v, i) -> { + if (i == null) + return map.size(); + removeAllEdge(vert); + return i; + }); + matrix = modifyMatrix(map.size()); } - @Override - protected W addEdgeImpl(V vertex1, V vertex2, W weight) { - return matrix.get(map.get(vertex1)).set(map.get(vertex2), weight); + private int[][] modifyMatrix(int newSize) { + int oldSize = matrix.length; + if (newSize <= oldSize) + return matrix; + + int[][] newMatrix = new int[newSize][newSize]; + for (int[] ints : newMatrix) + Arrays.fill(ints, NO_EDGE); + for (int i = 0; i < oldSize; i++) + System.arraycopy(matrix[i], 0, newMatrix[i], 0, oldSize); + + return newMatrix; } - @Override - protected W getWeightImpl(V vertex1, V vertex2) { - return matrix.get(map.get(vertex1)).get(map.get(vertex2)); - } - - @Override - protected Collection> getEdgesOutImpl(V vertex) { - Set> set = new HashSet<>(); - Map inverted = new HashMap<>(); - map.keySet().forEach(v -> inverted.put(map.get(v), v)); - - List list = matrix.get(map.get(vertex)); - for(int i=0; i(vertex, inverted.get(i), weight)); - } - return set; - } - - @Override - protected Collection> getEdgesInImpl(V vertex) { - Set> set = new HashSet<>(); - Map inverted = new HashMap<>(); - map.keySet().forEach(v -> inverted.put(map.get(v), v)); - - int x = map.get(vertex); - for(int i=0; i(inverted.get(i), vertex, weight)); - } - return set; - } - - @Override - protected void removeEdgeImpl(V vertex1, V vertex2) { - matrix.get(map.get(vertex1)).set(map.get(vertex2), null); - } - - @Override - protected void removeAllOutEdgeImpl(V vertex) { - matrix.get(map.get(vertex)).replaceAll(var -> null); - } - - @Override - protected void removeAllInEdgeImpl(V vertex) { - int x = map.get(vertex); - matrix.forEach(list -> list.set(x, null)); + private Map getInverted() { + Map invert = new HashMap<>(map.size() + 1, 1); + map.forEach((v, i) -> invert.put(i, v)); + return invert; } } diff --git a/src/berack96/lib/graph/impl/MatrixUndGraph.java b/src/berack96/lib/graph/impl/MatrixUndGraph.java new file mode 100644 index 0000000..327af48 --- /dev/null +++ b/src/berack96/lib/graph/impl/MatrixUndGraph.java @@ -0,0 +1,176 @@ +package berack96.lib.graph.impl; + +import berack96.lib.graph.Edge; +import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphUndirected; + +import java.util.*; + +public class MatrixUndGraph extends GraphUndirected { + + Map map = new HashMap<>(); + private int[][] matrix = new int[0][0]; + + @Override + protected Graph getNewInstance() { + return new MatrixUndGraph<>(); + } + + @Override + public boolean contains(V vertex) throws NullPointerException { + check(vertex); + return map.containsKey(vertex); + } + + @Override + public void add(V vertex) throws NullPointerException { + check(vertex); + if (map.containsKey(vertex)) + removeAllEdge(vertex); + else { + map.put(vertex, map.size()); + matrix = modifyMatrix(map.size()); + } + } + + @Override + public void remove(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.remove(vertex); + int newSize = map.size(); + + int[][] newMatrix = new int[newSize][]; + for (int i = 0; i < newSize; i++) + newMatrix[i] = i < x ? matrix[i] : new int[i]; + + for (int i = x; i < newSize; i++) + for (int j = 0; j < newMatrix[i].length; j++) + newMatrix[i][j] = matrix[i + 1][j + (j < x ? 0 : 1)]; + + matrix = newMatrix; + map.replaceAll((vert, index) -> index > x ? index - 1 : index); + } + + @Override + public int getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { + checkVert(vertex1, vertex2); + int x = map.get(vertex1); + int y = map.get(vertex2); + return x == y ? 0 : matrix[Math.max(x, y)][Math.min(x, y)]; + } + + @Override + public int addEdge(V vertex1, V vertex2, int weight) throws NullPointerException, IllegalArgumentException { + checkVert(vertex1, vertex2); + int x = map.get(vertex1); + int y = map.get(vertex2); + int max = Math.max(x, y); + int min = Math.min(x, y); + + int old = matrix[max][min]; + matrix[max][min] = weight; + return old; + } + + @Override + public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + Collection collection = new HashSet<>(); + Map inverted = getInverted(); + int x = map.get(vertex); + for (int i = 0; i < matrix.length; i++) + if (i < x && matrix[x][i] != 0) + collection.add(inverted.get(i)); + else if (i > x && matrix[i][x] != 0) + collection.add(inverted.get(i)); + return collection; + } + + @Override + public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { + return getChildren(vertex); + } + + @Override + public Collection> edges() { + Map inverted = getInverted(); + Collection> edges = new LinkedList<>(); + + for (int i = 0; i < matrix.length; i++) + for (int j = 0; j < matrix[i].length; j++) + if (matrix[i][j] != NO_EDGE) + edges.add(new Edge<>(inverted.get(i), inverted.get(j), matrix[i][j])); + return edges; + } + + @Override + public int degree(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.get(vertex); + int degree = 0; + for (int i = 0; i < x; i++) + if (matrix[x][i] != NO_EDGE) + degree++; + for (int i = x; i < matrix.length; i++) + if (matrix[i][x] != NO_EDGE) + degree++; + return degree; + } + + @Override + public int size() { + return map.size(); + } + + @Override + public int numberOfEdges() { + int num = 0; + for (int[] ints : matrix) + for (int edge : ints) + if (edge != NO_EDGE) + num++; + return num; + } + + @Override + public Iterator iterator() { + return map.keySet().iterator(); + } + + @Override + public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { + checkVert(vertex); + int x = map.get(vertex); + + Arrays.fill(matrix[x], NO_EDGE); + for (int i = x + 1; i < matrix.length; i++) + matrix[i][x] = NO_EDGE; + } + + @Override + public void removeAllEdge() { + for (int[] adj : matrix) + Arrays.fill(adj, NO_EDGE); + } + + private int[][] modifyMatrix(int newSize) { + int oldSize = matrix.length; + if (newSize <= oldSize) + return matrix; + int[][] newMatrix = new int[newSize][]; + System.arraycopy(matrix, 0, newMatrix, 0, oldSize); + + for (int i = oldSize; i < newSize; i++) { + newMatrix[i] = new int[i]; + Arrays.fill(newMatrix[i], NO_EDGE); + } + + return newMatrix; + } + + private Map getInverted() { + Map invert = new HashMap<>(map.size() + 1, 1); + map.forEach((v, i) -> invert.put(i, v)); + return invert; + } +} diff --git a/src/berack96/lib/graph/models/EdgeSaveStructure.java b/src/berack96/lib/graph/models/EdgeSaveStructure.java index 1865da1..e8b161d 100644 --- a/src/berack96/lib/graph/models/EdgeSaveStructure.java +++ b/src/berack96/lib/graph/models/EdgeSaveStructure.java @@ -2,18 +2,17 @@ package berack96.lib.graph.models; /** * Support class used for saving a Graph in a file. - * - * @author Berack96 * + * @author Berack96 */ public class EdgeSaveStructure { - protected EdgeSaveStructure(String s, String d, String w) { - this.src = s; - this.dest = d; - this.weight = w; - } - - public String src; - public String dest; - public String weight; + protected EdgeSaveStructure(String s, String d, int w) { + this.src = s; + this.dest = d; + this.weight = w; + } + + public String src; + public String dest; + public int weight; } \ No newline at end of file diff --git a/src/berack96/lib/graph/models/GraphSaveStructure.java b/src/berack96/lib/graph/models/GraphSaveStructure.java index 751e65a..4d2cc4f 100644 --- a/src/berack96/lib/graph/models/GraphSaveStructure.java +++ b/src/berack96/lib/graph/models/GraphSaveStructure.java @@ -9,53 +9,54 @@ import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; import java.io.*; -import java.lang.reflect.Type; /** * Support class used for saving a Graph in a file. - * - * @author Berack96 * + * @author Berack96 */ -public class GraphSaveStructure { - - final public Gson gson = new Gson(); +public class GraphSaveStructure { + final static public Gson gson = new Gson(); public String[] vertices; public EdgeSaveStructure[] edges; //public MarkSaveStructure[] marks; - /* + /** * Load the graph saved in this class in an instance of a graph passed. * Before loading the graph, it is emptied. + * + * @param graph the graph where insert the data + * @param fileName the file path and name + * @param classV the class of the vertices + * @throws FileNotFoundException in the case the file is not found + * @throws NullPointerException in the case the graph is null */ - public final void load(Graph graph, String fileName, Class classV, Class classW) throws FileNotFoundException { - Gson gson = new GsonBuilder().registerTypeAdapter(this.getClass(), new Creator(this)).create(); + public final void load(@SuppressWarnings("ConstantConditions") Graph graph, String fileName, Class classV) throws FileNotFoundException, NullPointerException { + //this way i use this class for the load + InstanceCreator> creator = type -> this; + Gson gson = new GsonBuilder().registerTypeAdapter(this.getClass(), creator).create(); JsonReader reader = new JsonReader(new FileReader(fileName)); - gson.fromJson(reader, this.getClass()); - loadGraph(graph, classV, classW); + gson.fromJson(reader, GraphSaveStructure.class); + loadGraph(graph, classV); } /** * This method can be used by sub-classes for saving other stuff from the graph * - * @param graph the graph to load with + * @param graph the graph to load with * @param classV the class used for the Vertex - * @param classW the class used for the Weight * @throws NullPointerException if the graph is null - * @throws JsonSyntaxException if the file is malformed or corrupted + * @throws JsonSyntaxException if the file is malformed or corrupted */ - protected void loadGraph(Graph graph, Class classV, Class classW) throws NullPointerException, JsonSyntaxException { + protected void loadGraph(Graph graph, Class classV) throws NullPointerException, JsonSyntaxException { graph.removeAll(); - for(String str : vertices) + for (String str : vertices) graph.add(gson.fromJson(str, classV)); for (EdgeSaveStructure edge : edges) - graph.addEdge( - gson.fromJson(edge.src, classV), - gson.fromJson(edge.dest, classV), - gson.fromJson(edge.weight, classW) - ); + graph.addEdge(gson.fromJson(edge.src, classV), gson.fromJson(edge.dest, classV), edge.weight); } + /** * Save the Graph passed as input to a file inserted as parameter.
* The resulting file is a Json string representing all the graph.
@@ -68,46 +69,37 @@ public class GraphSaveStructure { * @param file the name of the file * @throws IOException for various reason that appear in the message, but the most common is that the file is not found. */ - public final void save(Graph graph, String file) throws IOException { - saveGraph(graph); + public final void save(Graph graph, String file) throws IOException { + saveGraph(graph); int slash = file.lastIndexOf("\\"); - if(slash == -1) - slash = file.lastIndexOf("/"); - if(slash != -1) { - String dir = file.substring(0, slash); - File fDir = new File(dir); + if (slash == -1) + slash = file.lastIndexOf("/"); + if (slash != -1) { + String dir = file.substring(0, slash); + File fDir = new File(dir); //noinspection ResultOfMethodCallIgnored fDir.mkdirs(); - } + } - FileWriter writer = new FileWriter(file); - gson.toJson(this, writer); - writer.close(); - } + FileWriter writer = new FileWriter(file); + gson.toJson(this, writer); + writer.close(); + } /** * This method can be used by sub-classes for saving other stuff from the graph + * * @param graph the graph to save */ - protected void saveGraph(Graph graph) { + protected void saveGraph(Graph graph) { this.vertices = new String[graph.size()]; int i = 0; - for(Object o: graph.vertices()) + for (Object o : graph.vertices()) this.vertices[i++] = gson.toJson(o); this.edges = new EdgeSaveStructure[graph.numberOfEdges()]; i = 0; - for (Edge edge : graph.edges()) - this.edges[i++] = new EdgeSaveStructure( - gson.toJson(edge.getSource()), - gson.toJson(edge.getDestination()), - gson.toJson(edge.getWeight()) - ); - } - - private class Creator implements InstanceCreator> { - private final GraphSaveStructure save; - public Creator(GraphSaveStructure save) { this.save = save; } - public GraphSaveStructure createInstance(Type type) { return save; } + for (Edge edge : graph.edges()) + this.edges[i++] = new EdgeSaveStructure(gson.toJson(edge.getSource()), gson.toJson(edge.getDestination()), edge.getWeight()); } } \ No newline at end of file diff --git a/src/berack96/lib/graph/struct/QuickFind.java b/src/berack96/lib/graph/struct/QuickFind.java new file mode 100644 index 0000000..51cc7de --- /dev/null +++ b/src/berack96/lib/graph/struct/QuickFind.java @@ -0,0 +1,76 @@ +package berack96.lib.graph.struct; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +/** + * Simple implementation of the {@link UnionFind} interface with priority to the find function. + * + * @param the elements to search and merge + */ +public class QuickFind implements UnionFind { + Map> struct = new HashMap<>(); + + @Override + public int size() { + return struct.size(); + } + + @Override + public void makeSetAll(Collection elements) throws NullPointerException { + Map> temp = new HashMap<>(elements.size() + 1, 1); + for (X elem : elements) + temp.computeIfAbsent(elem, new AddElement()); + struct.putAll(temp); + } + + @Override + public void makeSet(X element) throws NullPointerException { + if (element == null) + throw new NullPointerException(); + struct.computeIfAbsent(element, new AddElement()); + } + + @Override + public boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException { + element1 = find(element1); + element2 = find(element2); + if (element1 == null || element2 == null) + throw new IllegalArgumentException(); + if (element1 == element2) + return false; + + return struct.get(element1).addAll(struct.remove(element2)); + } + + @Override + public X find(X element) throws NullPointerException { + if (element == null) + throw new NullPointerException(); + if (struct.containsKey(element)) + return element; + + AtomicReference toReturn = new AtomicReference<>(null); + struct.forEach((key, collection) -> { + if (collection.contains(element)) + toReturn.set(key); + }); + return toReturn.get(); + } + + /** + * Stupid class for implementing the adding of a new element + */ + private class AddElement implements Function> { + @Override + public Collection apply(X x) { + Collection coll = new HashSet<>(); + coll.add(x); + return coll; + } + } +} diff --git a/src/berack96/lib/graph/struct/UnionFind.java b/src/berack96/lib/graph/struct/UnionFind.java new file mode 100644 index 0000000..db38669 --- /dev/null +++ b/src/berack96/lib/graph/struct/UnionFind.java @@ -0,0 +1,57 @@ +package berack96.lib.graph.struct; + +import java.util.Collection; + +/** + * Basic interface for the UnionFind tree sets + * + * @param the object + * @author Berack96 + */ +public interface UnionFind { + + /** + * Indicate how many different sets there are. + * + * @return the number of sets + */ + int size(); + + /** + * It creates the single element set for every element in the collection + * + * @param elements the collection of the elements + * @throws NullPointerException in the case of the set being null + */ + void makeSetAll(Collection elements) throws NullPointerException; + + /** + * Creates the single element set for the element + * + * @param element the element to insert + * @throws NullPointerException in the case of a null element + */ + void makeSet(X element) throws NullPointerException; + + /** + * Merge the tho elements into a single set.
+ * In the case that the two elements are in the same set it returns false. + * + * @param element1 an element of a set + * @param element2 an element of another set + * @return true in the case of a successful merge, false otherwise + * @throws NullPointerException in the case of a null element + * @throws IllegalArgumentException in the case of an element not in the sets + */ + boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException; + + /** + * Returns the element representing the set in which the element passed resides.
+ * In case of an element not found then it's returned null + * + * @param element the element in the set + * @return the representing element of the set found + * @throws NullPointerException in the case of a null element + */ + X find(X element) throws NullPointerException; +} diff --git a/src/berack96/lib/graph/view/GraphInfo.java b/src/berack96/lib/graph/view/GraphInfo.java index 72af7d2..40ee762 100644 --- a/src/berack96/lib/graph/view/GraphInfo.java +++ b/src/berack96/lib/graph/view/GraphInfo.java @@ -14,28 +14,28 @@ import java.io.Serial; import java.util.List; import java.util.*; -public class GraphInfo extends JPanel { +public class GraphInfo extends JPanel { - @Serial + @Serial private static final long serialVersionUID = 1L; - + private final Map> visits; - public GraphInfo(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener, Set> visits) { + public GraphInfo(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener, Set> visits) { this.visits = new HashMap<>(); /* ZERO (DESCRIPTION) */ JLabel listenerDescription = new JLabel(); - + JPanel panelDescription = new JPanel(); panelDescription.setOpaque(false); panelDescription.add(listenerDescription); - + /* FIRST (GRAPH INFO) */ JLabel vNumber = new JLabel(String.valueOf(graphPanel.getGraph().size())); JLabel eNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfEdges())); - JLabel gCyclic = new JLabel(String.valueOf(graphPanel.getGraph().isCyclic())); + //JLabel gCyclic = new JLabel(String.valueOf(graphPanel.getGraph().isCyclic())); List components = new LinkedList<>(); JLabel selected = new JLabel(); @@ -59,7 +59,7 @@ public class GraphInfo extends JPanel { }); comboBox.addItem("None"); - for(VisitStrategy strategy: visits) { + for (VisitStrategy strategy : visits) { String clazz = strategy.getClass().getSimpleName(); VisitListener visit = new VisitListener<>(graphPanel, strategy); comboBox.addItem(clazz); @@ -75,7 +75,7 @@ public class GraphInfo extends JPanel { components.add(new JLabel("Edge Number: ")); components.add(eNumber); components.add(new JLabel("Is Cyclic: ")); - components.add(gCyclic); + //components.add(gCyclic); JPanel panelInfo = new JPanel(); panelInfo.setOpaque(false); @@ -182,21 +182,21 @@ public class GraphInfo extends JPanel { modVertex.doClick(); graphPanel.addObserver((o, arg) -> { - Graph graph = graphPanel.getGraph(); + Graph graph = graphPanel.getGraph(); if(arg.equals(graph)) { vNumber.setText(String.valueOf(graph.size())); eNumber.setText(String.valueOf(graph.numberOfEdges())); - gCyclic.setText(String.valueOf(graph.isCyclic())); + //gCyclic.setText(String.valueOf(graph.isCyclic())); /* There should be only one */ for(V v : graph.getMarkedWith("selected")) { - int inE = graph.getEdgesIn(v).size(); - int outE = graph.getEdgesOut(v).size(); - - vEdgesInNumber.setText(String.valueOf(inE)); - vEdgesOutNumber.setText(String.valueOf(outE)); - vEdgesNumber.setText(String.valueOf(inE + outE)); - vVertex.setText(v.toString()); + int inE = graph.getAncestors(v).size(); + int outE = graph.getChildren(v).size(); + + vEdgesInNumber.setText(String.valueOf(inE)); + vEdgesOutNumber.setText(String.valueOf(outE)); + vEdgesNumber.setText(String.valueOf(inE + outE)); + vVertex.setText(v.toString()); } } }); diff --git a/src/berack96/lib/graph/view/GraphPanel.java b/src/berack96/lib/graph/view/GraphPanel.java index 7350228..fe85d68 100644 --- a/src/berack96/lib/graph/view/GraphPanel.java +++ b/src/berack96/lib/graph/view/GraphPanel.java @@ -1,6 +1,5 @@ package berack96.lib.graph.view; -import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; import berack96.lib.graph.Vertex; import berack96.lib.graph.impl.MapGraph; @@ -19,31 +18,29 @@ import java.util.Observer; import java.util.Set; @SuppressWarnings({"unchecked", "deprecation"}) -public class GraphPanel extends Component { +public class GraphPanel extends Component { - @Serial + @Serial private static final long serialVersionUID = 1L; - private final GraphicalView> vertexRender; - private final GraphicalView> edgeRender; + private final GraphicalView> vertexRender; + private final GraphicalView> edgeRender; private final Class classV; - private final Class classW; final Container vertices = new Container(); final Container edges = new Container(); - private final Graph graph = new MapGraph<>(); + private final Graph graph = new MapGraph<>(); private final Set observers = new HashSet<>(); - + private GraphListener old = null; - public GraphPanel(GraphicalView> vertexRender, GraphicalView> edgeRender, Class classV, Class classW) { + public GraphPanel(GraphicalView> vertexRender, GraphicalView> edgeRender, Class classV) { this.vertexRender = vertexRender; this.edgeRender = edgeRender; this.classV = classV; - this.classW = classW; } - public Graph getGraph() { + public Graph getGraph() { return graph; } @@ -89,7 +86,8 @@ public class GraphPanel extends Component { VertexComponent component = getVertexAt(center); component.vertex.remove(); vertices.remove(component); - } catch (Exception ignore) {} + } catch (Exception ignore) { + } } public void moveVertex(VertexComponent vertex, Point destination) { @@ -97,38 +95,38 @@ public class GraphPanel extends Component { vertex.setLocation(rectangle.x, rectangle.y); } - public void addEdge(Edge edge) { - VertexComponent vSource = null; - VertexComponent vDest = null; - for (Component comp : vertices.getComponents()) { - VertexComponent temp = (VertexComponent) comp; - V vTemp = temp.vertex.getValue(); - if (vSource == null && vTemp.equals(edge.getSource())) - vSource = temp; - if (vDest == null && vTemp.equals(edge.getDestination())) - vDest = temp; - } - addEdge(vSource, vDest, edge.getWeight()); + public void addEdge(V source, V destination, int weight) { + VertexComponent vSource = null; + VertexComponent vDest = null; + for (Component comp : vertices.getComponents()) { + VertexComponent temp = (VertexComponent) comp; + V vTemp = temp.vertex.get(); + if (vSource == null && vTemp.equals(source)) + vSource = temp; + if (vDest == null && vTemp.equals(destination)) + vDest = temp; + } + addEdge(vSource, vDest, weight); } - - public void addEdge(VertexComponent source, VertexComponent dest, W weight) { + + public void addEdge(VertexComponent source, VertexComponent dest, int weight) { try { Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY())); - EdgeComponent edgeComponent = new EdgeComponent<>(source, dest, weight); + EdgeComponent edgeComponent = new EdgeComponent<>(source, dest, weight); edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center)); edges.add(edgeComponent); graph.addEdge(edgeComponent.edge); } catch (Exception e) { - e.printStackTrace(); + e.printStackTrace(); } } public void removeEdge(VertexComponent source, VertexComponent dest) { try { - graph.removeEdge(source.vertex.getValue(), dest.vertex.getValue()); - EdgeComponent toRemove = null; + graph.removeEdge(source.vertex.get(), dest.vertex.get()); + EdgeComponent toRemove = null; for (Component c : edges.getComponents()) { - EdgeComponent edge = (EdgeComponent) c; + EdgeComponent edge = (EdgeComponent) c; if (edge.source.equals(source) && edge.destination.equals(dest)) toRemove = edge; } @@ -137,7 +135,7 @@ public class GraphPanel extends Component { } catch (Exception ignore) {} } - public void modEdge(VertexComponent source, VertexComponent dest, W weight) { + public void modEdge(VertexComponent source, VertexComponent dest, int weight) { removeEdge(source, dest); addEdge(source, dest, weight); } @@ -147,9 +145,9 @@ public class GraphPanel extends Component { return component instanceof VertexComponent ? (VertexComponent) component : null; } - public EdgeComponent getEdgeAt(Point point) { + public EdgeComponent getEdgeAt(Point point) { Component component = edges.getComponentAt(point); - return component instanceof EdgeComponent ? (EdgeComponent) component : null; + return component instanceof EdgeComponent ? (EdgeComponent) component : null; } public void addObserver(Observer observer) { @@ -165,7 +163,7 @@ public class GraphPanel extends Component { } public void load(String fileName) throws IOException { - new GraphPointsSave<>(this).load(graph, fileName, classV, classW); + new GraphPointsSave<>(this).load(graph, fileName, classV); } @Override @@ -180,24 +178,23 @@ public class GraphPanel extends Component { Graphics2D g2 = (Graphics2D) g.create(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - Collection> toRemove = new HashSet<>(); + Collection> toRemove = new HashSet<>(); for (Component component : edges.getComponents()) { - EdgeComponent edge = (EdgeComponent) component; + EdgeComponent edge = (EdgeComponent) component; Vertex source = edge.source.vertex; Vertex dest = edge.destination.vertex; - if (source.isStillContained() && dest.isStillContained() && graph.containsEdge(source.getValue(), dest.getValue())) { + if (source.isStillContained() && dest.isStillContained() && graph.containsEdge(source.get(), dest.get())) { Point center = new Point(edge.getX() + edge.getWidth() / 2, edge.getY() + edge.getHeight() / 2); edge.setBounds(edgeRender.getBox(edge, center)); edgeRender.paint((Graphics2D) g2.create(), edge, center); - } - else + } else toRemove.add(edge); } toRemove.forEach(edges::remove); for (Component component : vertices.getComponents()) { VertexComponent vertex = (VertexComponent) component; - if (graph.contains(vertex.vertex.getValue())) { + if (graph.contains(vertex.vertex.get())) { Point center = new Point(vertex.getX() + vertex.getWidth() / 2, vertex.getY() + vertex.getHeight() / 2); vertexRender.paint((Graphics2D) g2.create(), vertex, center); } diff --git a/src/berack96/lib/graph/view/GraphPointsSave.java b/src/berack96/lib/graph/view/GraphPointsSave.java index 60b96f7..032ce70 100644 --- a/src/berack96/lib/graph/view/GraphPointsSave.java +++ b/src/berack96/lib/graph/view/GraphPointsSave.java @@ -7,21 +7,21 @@ import java.awt.*; import java.util.LinkedList; import java.util.List; -public class GraphPointsSave extends GraphSaveStructure { +public class GraphPointsSave extends GraphSaveStructure { - final private GraphPanel panel; + final private GraphPanel panel; public Point[] points; - public GraphPointsSave(GraphPanel panel) { + public GraphPointsSave(GraphPanel panel) { this.panel = panel; } @Override - protected void saveGraph(Graph graph) { + protected void saveGraph(Graph graph) { super.saveGraph(graph); List p = new LinkedList<>(); - for(Component vertex : panel.vertices.getComponents()) { + for (Component vertex : panel.vertices.getComponents()) { Point temp = new Point(vertex.getX(), vertex.getY()); temp.x += vertex.getWidth() / 2; temp.y += vertex.getHeight() / 2; @@ -35,20 +35,21 @@ public class GraphPointsSave extends GraphSaveStructure graph, Class classV, Class classW) { - super.loadGraph(graph, classV, classW); - + public void loadGraph(Graph graph, Class classV) { + super.loadGraph(graph, classV); panel.vertices.removeAll(); panel.edges.removeAll(); - for(int i = 0; i panel.addEdge(src, child, graph.getWeight(src, child))); + } panel.repaint(); } } diff --git a/src/berack96/lib/graph/view/GraphWindow.java b/src/berack96/lib/graph/view/GraphWindow.java index fe2eb41..8abd77c 100644 --- a/src/berack96/lib/graph/view/GraphWindow.java +++ b/src/berack96/lib/graph/view/GraphWindow.java @@ -19,25 +19,25 @@ import java.util.Set; * * @author Berack96 */ -public class GraphWindow extends JFrame { +public class GraphWindow extends JFrame { - @Serial + @Serial private static final long serialVersionUID = 1L; - private final GraphPanel graphPanel; + private final GraphPanel graphPanel; - public GraphWindow(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener) { + public GraphWindow(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener) { this.setTitle("Grafo"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLayout(new BorderLayout()); - Set> strats = new LinkedHashSet<>(); + Set> strats = new LinkedHashSet<>(); strats.add(new DFS<>()); strats.add(new BFS<>()); strats.add(new Dijkstra<>()); strats.add(new Tarjan<>()); - GraphInfo infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats); + GraphInfo infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats); this.graphPanel = graphPanel; this.add(infoPanel, BorderLayout.EAST); this.add(graphPanel); @@ -47,7 +47,7 @@ public class GraphWindow extends JFrame { VisitListener.changeRefresh(millis); } - public GraphPanel getGraphPanel() { + public GraphPanel getGraphPanel() { return graphPanel; } } diff --git a/src/berack96/lib/graph/view/Main.java b/src/berack96/lib/graph/view/Main.java index 669bcb4..254462f 100644 --- a/src/berack96/lib/graph/view/Main.java +++ b/src/berack96/lib/graph/view/Main.java @@ -1,25 +1,23 @@ package berack96.lib.graph.view; -import java.awt.Dimension; -import java.awt.Toolkit; - import berack96.lib.graph.view.edge.EdgeIntListener; import berack96.lib.graph.view.edge.EdgeView; import berack96.lib.graph.view.vertex.VertexIntListener; import berack96.lib.graph.view.vertex.VertexView; +import java.awt.*; + public class Main { + public static void main(String[] args) { + GraphPanel panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>(), Integer.class); + GraphWindow win = new GraphWindow<>(panel, new VertexIntListener(panel), new EdgeIntListener<>(panel)); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // full screen + dim.setSize(dim.width / 2, dim.height / 2); + win.setSize(dim); + win.setLocationRelativeTo(null); //centered + win.visitRefresh(500); - public static void main(String[] args) { - GraphPanel panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>(), Integer.class, Integer.class); - GraphWindow win = new GraphWindow<>(panel, new VertexIntListener(panel), new EdgeIntListener<>(panel)); - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // full screen - dim.setSize(dim.width / 2, dim.height / 2); - win.setSize(dim); - win.setLocationRelativeTo(null); //centered - win.visitRefresh(500); - - win.setVisible(true); - } + win.setVisible(true); + } } diff --git a/src/berack96/lib/graph/view/VisitListener.java b/src/berack96/lib/graph/view/VisitListener.java index e827648..0934977 100644 --- a/src/berack96/lib/graph/view/VisitListener.java +++ b/src/berack96/lib/graph/view/VisitListener.java @@ -14,13 +14,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class VisitListener implements GraphListener { - private final GraphPanel panel; - private final VisitStrategy strategy; + private final GraphPanel panel; + private final VisitStrategy strategy; private final Set timers = new HashSet<>(); private static int refreshTime = 1000; - public VisitListener(GraphPanel panel, VisitStrategy strategy) { + public VisitListener(GraphPanel panel, VisitStrategy strategy) { this.panel = panel; this.strategy = strategy; } @@ -46,8 +46,8 @@ public class VisitListener implements GraphListener { @Override public void mousePressed(MouseEvent e) { this.remove(); - - Graph graph = panel.getGraph(); + + Graph graph = panel.getGraph(); graph.unMarkAll(); panel.repaint(); diff --git a/src/berack96/lib/graph/view/edge/EdgeComponent.java b/src/berack96/lib/graph/view/edge/EdgeComponent.java index a50da8c..96d68c7 100644 --- a/src/berack96/lib/graph/view/edge/EdgeComponent.java +++ b/src/berack96/lib/graph/view/edge/EdgeComponent.java @@ -4,19 +4,21 @@ import berack96.lib.graph.Edge; import berack96.lib.graph.view.vertex.VertexComponent; import java.awt.*; +import java.io.Serial; -public class EdgeComponent extends Component { - private static final long serialVersionUID = 1L; - - public final VertexComponent source; +public class EdgeComponent extends Component { + @Serial + private static final long serialVersionUID = 1L; + + public final VertexComponent source; public final VertexComponent destination; - public final W weight; - public final Edge edge; + public final int weight; + public final Edge edge; - public EdgeComponent(VertexComponent source, VertexComponent destination, W weight) { + public EdgeComponent(VertexComponent source, VertexComponent destination, int weight) { this.source = source; this.destination = destination; this.weight = weight; - this.edge = new Edge<>(source.vertex.getValue(), destination.vertex.getValue(), weight); + this.edge = new Edge<>(source.vertex.get(), destination.vertex.get(), weight); } } diff --git a/src/berack96/lib/graph/view/edge/EdgeIntListener.java b/src/berack96/lib/graph/view/edge/EdgeIntListener.java index a2fa3eb..4b94e18 100644 --- a/src/berack96/lib/graph/view/edge/EdgeIntListener.java +++ b/src/berack96/lib/graph/view/edge/EdgeIntListener.java @@ -3,22 +3,23 @@ package berack96.lib.graph.view.edge; import berack96.lib.graph.Vertex; import berack96.lib.graph.view.GraphPanel; -public class EdgeIntListener extends EdgeListener { +public class EdgeIntListener extends EdgeListener { - public EdgeIntListener(GraphPanel graphPanel) { + public EdgeIntListener(GraphPanel graphPanel) { super(graphPanel); } @Override - public void remove() {} - + public void remove() { + } + @Override - protected Integer buildNewEdge(Vertex vertex, Vertex vertex1) { + protected int buildNewEdge(Vertex vertex, Vertex vertex1) { return (int) (Math.random() * 100); } @Override - protected Integer buildEdgeFrom(String string) { + protected int buildEdgeFrom(String string) { return Integer.parseInt(string.replaceAll("[^0-9]+", "")); } } diff --git a/src/berack96/lib/graph/view/edge/EdgeListener.java b/src/berack96/lib/graph/view/edge/EdgeListener.java index 52f5095..83b6286 100644 --- a/src/berack96/lib/graph/view/edge/EdgeListener.java +++ b/src/berack96/lib/graph/view/edge/EdgeListener.java @@ -9,21 +9,21 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.util.concurrent.atomic.AtomicReference; -public abstract class EdgeListener implements GraphListener { +public abstract class EdgeListener implements GraphListener { - private final GraphPanel graphPanel; + private final GraphPanel graphPanel; private final AtomicReference> componentPressed = new AtomicReference<>(); private final AtomicReference buttonPressed = new AtomicReference<>(); - private final AtomicReference> edge = new AtomicReference<>(); + private final AtomicReference> edge = new AtomicReference<>(); private final StringBuilder string = new StringBuilder(); - public EdgeListener(GraphPanel graphPanel) { + public EdgeListener(GraphPanel graphPanel) { this.graphPanel = graphPanel; } - protected abstract W buildNewEdge(Vertex vertex, Vertex vertex1); + protected abstract int buildNewEdge(Vertex vertex, Vertex vertex1); - protected abstract W buildEdgeFrom(String string); + protected abstract int buildEdgeFrom(String string); @Override public String getDescription() { @@ -54,8 +54,8 @@ public abstract class EdgeListener implements GraphListener VertexComponent source = componentPressed.get(); VertexComponent destination = graphPanel.getVertexAt(e.getPoint()); - if (!graphPanel.getGraph().containsEdge(source.vertex.getValue(), destination.vertex.getValue()) - && !source.vertex.equals(destination.vertex)) + if (!graphPanel.getGraph().containsEdge(source.vertex.get(), destination.vertex.get()) + && !source.vertex.equals(destination.vertex)) graphPanel.addEdge(source, destination, buildNewEdge(source.vertex, destination.vertex)); } catch (Exception ignore) { } diff --git a/src/berack96/lib/graph/view/edge/EdgeView.java b/src/berack96/lib/graph/view/edge/EdgeView.java index 90135d5..21e07ed 100644 --- a/src/berack96/lib/graph/view/edge/EdgeView.java +++ b/src/berack96/lib/graph/view/edge/EdgeView.java @@ -1,18 +1,18 @@ package berack96.lib.graph.view.edge; +import berack96.lib.graph.view.GraphicalView; +import berack96.lib.graph.view.stuff.Arrow; + import java.awt.*; import java.awt.geom.Point2D; import java.util.Collection; -import berack96.lib.graph.view.GraphicalView; -import berack96.lib.graph.view.stuff.Arrow; - -public class EdgeView implements GraphicalView> { +public class EdgeView implements GraphicalView> { private static final Font FONT = new Font("Papyrus", Font.BOLD, 14); @Override - public Rectangle getBox(EdgeComponent edge, Point center) { + public Rectangle getBox(EdgeComponent edge, Point center) { /* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */ Point srcLoc = edge.source.getLocation(); Point desLoc = edge.destination.getLocation(); @@ -29,7 +29,7 @@ public class EdgeView implements GraphicalView implements GraphicalView edge, Point center) { + public void paint(Graphics2D g2, EdgeComponent edge, Point center) { /* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */ Point srcLoc = edge.source.getLocation(); Point desLoc = edge.destination.getLocation(); @@ -55,7 +55,7 @@ public class EdgeView implements GraphicalView implements GraphicalView marksD = edge.destination.vertex.getMarks(); Collection marksS = edge.source.vertex.getMarks(); - - boolean isChild = marksD.contains(edge.source.vertex.getValue()); + + boolean isChild = marksD.contains(edge.source.vertex.get()); boolean selected = marksS.contains("selected"); boolean isMod = marksD.contains("modD") && marksS.contains("modS"); @@ -92,7 +92,7 @@ public class EdgeView implements GraphicalView { - public VertexIntListener(GraphPanel panel) { + public VertexIntListener(GraphPanel panel) { super(panel); } @Override public void remove() {} - + @Override - protected Integer buildNewVertex(Graph graph) { - int counter = 0; - Integer[] vertices = graph.vertices().toArray(new Integer[graph.size()]); - Arrays.sort(vertices); + protected Integer buildNewVertex(Graph graph) { + int counter = 0; + Integer[] vertices = graph.vertices().toArray(new Integer[graph.size()]); + Arrays.sort(vertices); for (Integer vertex : vertices) { if (!vertex.equals(counter)) diff --git a/src/berack96/lib/graph/view/vertex/VertexListener.java b/src/berack96/lib/graph/view/vertex/VertexListener.java index 895c555..c4dd7bb 100644 --- a/src/berack96/lib/graph/view/vertex/VertexListener.java +++ b/src/berack96/lib/graph/view/vertex/VertexListener.java @@ -10,14 +10,14 @@ import java.util.concurrent.atomic.AtomicReference; public abstract class VertexListener implements GraphListener { - protected final GraphPanel panel; + protected final GraphPanel panel; private final AtomicReference> componentPressed = new AtomicReference<>(); - public VertexListener(GraphPanel panel) { + public VertexListener(GraphPanel panel) { this.panel = panel; } - protected abstract V buildNewVertex(Graph graph); + protected abstract V buildNewVertex(Graph graph); @Override public String getDescription() { diff --git a/src/berack96/lib/graph/view/vertex/VertexView.java b/src/berack96/lib/graph/view/vertex/VertexView.java index 8965f9c..81ed773 100644 --- a/src/berack96/lib/graph/view/vertex/VertexView.java +++ b/src/berack96/lib/graph/view/vertex/VertexView.java @@ -12,7 +12,7 @@ public class VertexView implements GraphicalView> { @Override public Rectangle getBox(VertexComponent obj, Point center) { FontMetrics metrics = obj.getFontMetrics(FONT); - int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString()); + int stringPixels = metrics.stringWidth(obj.vertex.get().toString()); int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING; return new Rectangle(center.x - size / 2, center.y - size / 2, size, size); @@ -23,9 +23,9 @@ public class VertexView implements GraphicalView> { boolean discovered = obj.vertex.getMarks().contains("discovered"); boolean visited = obj.vertex.getMarks().contains("visited"); boolean selected = obj.vertex.getMarks().contains("selected"); - + FontMetrics metrics = obj.getFontMetrics(FONT); - int stringPixels = metrics.stringWidth(obj.vertex.getValue().toString()); + int stringPixels = metrics.stringWidth(obj.vertex.get().toString()); int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING; center.x = center.x - size / 2; @@ -37,6 +37,6 @@ public class VertexView implements GraphicalView> { g2.setColor(visited || discovered || selected ? Color.ORANGE : Color.YELLOW); g2.fillOval(center.x + PADDING / 2, center.y + PADDING / 2, size - PADDING, size - PADDING); g2.setColor(Color.BLACK); - g2.drawString(obj.vertex.getValue().toString(), center.x + PADDING + (size - 2 * PADDING - stringPixels) / 2, center.y + (size) / 2 + PADDING); + g2.drawString(obj.vertex.get().toString(), center.x + PADDING + (size - 2 * PADDING - stringPixels) / 2, center.y + (size) / 2 + PADDING); } } diff --git a/src/berack96/lib/graph/visit/VisitDistSourceDest.java b/src/berack96/lib/graph/visit/VisitDistSourceDest.java index fb1dccd..49f15ea 100644 --- a/src/berack96/lib/graph/visit/VisitDistSourceDest.java +++ b/src/berack96/lib/graph/visit/VisitDistSourceDest.java @@ -1,18 +1,17 @@ package berack96.lib.graph.visit; -import java.util.List; - import berack96.lib.graph.Edge; import berack96.lib.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 { +public interface VisitDistSourceDest extends VisitStrategy { /** * Get the distance from the source to the destination
@@ -25,5 +24,5 @@ public interface VisitDistSourceDest extends VisitStrategy< * @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; + List> distance(Graph graph, V source, V destination) throws NullPointerException, IllegalArgumentException; } diff --git a/src/berack96/lib/graph/visit/VisitDistance.java b/src/berack96/lib/graph/visit/VisitDistance.java index 0a690a9..63b6ed2 100644 --- a/src/berack96/lib/graph/visit/VisitDistance.java +++ b/src/berack96/lib/graph/visit/VisitDistance.java @@ -1,18 +1,17 @@ package berack96.lib.graph.visit; +import berack96.lib.graph.Edge; + import java.util.List; import java.util.Map; -import berack96.lib.graph.Edge; - /** * 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 { +public interface VisitDistance extends VisitStrategy { /** * Get the last calculated distance to all the possible destinations
@@ -23,7 +22,7 @@ public interface VisitDistance extends VisitStrategy * @return the last distance * @throws NullPointerException if the visit is not already been done */ - Map>> getLastDistance() throws NullPointerException; + Map>> getLastDistance() throws NullPointerException; /** * Get the last source vertex of the visit for calculating the destinations.
diff --git a/src/berack96/lib/graph/visit/VisitMST.java b/src/berack96/lib/graph/visit/VisitMST.java new file mode 100644 index 0000000..1f28ab0 --- /dev/null +++ b/src/berack96/lib/graph/visit/VisitMST.java @@ -0,0 +1,19 @@ +package berack96.lib.graph.visit; + +import berack96.lib.graph.Edge; + +import java.util.Collection; + +/** + * @param + */ +public interface VisitMST extends VisitStrategy { + + /** + * Return the latest calculated MST. + * + * @return the latest MST + * @throws NullPointerException if there is no last calculated MST + */ + Collection> getMST(); +} diff --git a/src/berack96/lib/graph/visit/VisitSCC.java b/src/berack96/lib/graph/visit/VisitSCC.java index fbcfccc..95a22b0 100644 --- a/src/berack96/lib/graph/visit/VisitSCC.java +++ b/src/berack96/lib/graph/visit/VisitSCC.java @@ -6,10 +6,9 @@ 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 { +public interface VisitSCC extends VisitStrategy { /** * Return the latest calculated strongly connected components of the graph. diff --git a/src/berack96/lib/graph/visit/VisitStrategy.java b/src/berack96/lib/graph/visit/VisitStrategy.java index 22f9c2b..fda52e8 100644 --- a/src/berack96/lib/graph/visit/VisitStrategy.java +++ b/src/berack96/lib/graph/visit/VisitStrategy.java @@ -1,18 +1,18 @@ package berack96.lib.graph.visit; -import java.util.function.Consumer; - import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphDirected; import berack96.lib.graph.visit.impl.VisitInfo; +import java.util.function.Consumer; + /** * This class is used for define some strategy for the visit of a graph. * * @param The Object that represent a vertex - * @param The Object that represent the edge (more specifically the weight of the edge) * @author Berack96 */ -public interface VisitStrategy { +public interface VisitStrategy { /** * With this the graph will be visited accordingly to the strategy of the visit.
@@ -20,12 +20,25 @@ public interface VisitStrategy { * If you want to stop the visit of the graph, you just have to throw any exception in the visit function, but be sure to catch it * * @param graph the graph to visit - * @param source the source of the visit + * @param source the vertex where the visit starts * @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 NullPointerException if the graph is null * @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph */ - VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException; + VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, UnsupportedOperationException; + + /** + * Method used for checking if the graph is Directed.
+ * It's useful when the algorithm can only be applied to Directed graph. + * + * @param graph the instance of the graph to check + * @return the instance of the graph casted to a {@link GraphDirected} + * @throws UnsupportedOperationException in the case it's not a directed graph + */ + default GraphDirected checkDirected(Graph graph) { + if (graph instanceof GraphDirected) + return (GraphDirected) graph; + throw new UnsupportedOperationException(); + } } diff --git a/src/berack96/lib/graph/visit/VisitTopological.java b/src/berack96/lib/graph/visit/VisitTopological.java index 2ece307..6bdf42a 100644 --- a/src/berack96/lib/graph/visit/VisitTopological.java +++ b/src/berack96/lib/graph/visit/VisitTopological.java @@ -6,10 +6,9 @@ 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 { +public interface VisitTopological extends VisitStrategy { /** * Return the latest calculated Topological sort of the graph.
diff --git a/src/berack96/lib/graph/visit/impl/BFS.java b/src/berack96/lib/graph/visit/impl/BFS.java index 299ceea..f33aafb 100644 --- a/src/berack96/lib/graph/visit/impl/BFS.java +++ b/src/berack96/lib/graph/visit/impl/BFS.java @@ -11,13 +11,19 @@ import java.util.function.Consumer; * The algorithm starts at the root node and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level. * * @param the vertex of the graph - * @param the weight of the graph * @author Berack96 */ -public class BFS implements VisitStrategy { +public class BFS implements VisitStrategy { + + private int maxDepth = -1; + + public BFS setMaxDepth(int depth) { + this.maxDepth = depth; + return this; + } @Override - public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { VisitInfo info = new VisitInfo<>(source); final LinkedList toVisitChildren = new LinkedList<>(); @@ -28,8 +34,10 @@ public class BFS implements VisitStrategy { while (!toVisitChildren.isEmpty()) { V current = toVisitChildren.removeFirst(); + if (maxDepth > -1 && info.getDepth(current) >= maxDepth) + break; - for (V child : graph.getChildrens(current)) + for (V child : graph.getChildren(current)) if (!info.isDiscovered(child)) { toVisitChildren.addLast(child); diff --git a/src/berack96/lib/graph/visit/impl/DFS.java b/src/berack96/lib/graph/visit/impl/DFS.java index a1b899b..b6bca53 100644 --- a/src/berack96/lib/graph/visit/impl/DFS.java +++ b/src/berack96/lib/graph/visit/impl/DFS.java @@ -12,13 +12,12 @@ import java.util.function.Consumer; * The algorithm starts at the root node and explores as far as possible along each branch before backtracking. * * @param the vertex of the graph - * @param the weight of the graph * @author Berack96 */ -public class DFS implements VisitStrategy { +public class DFS implements VisitStrategy { @Override - public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { VisitInfo info = new VisitInfo<>(source); final Stack toVisit = new Stack<>(); @@ -27,7 +26,7 @@ public class DFS implements VisitStrategy { while (!toVisit.isEmpty()) { V current = toVisit.peek(); boolean hasChildToVisit = false; - Iterator iter = graph.getChildrens(current).iterator(); + Iterator iter = graph.getChildren(current).iterator(); while (iter.hasNext() && !hasChildToVisit) { V child = iter.next(); diff --git a/src/berack96/lib/graph/visit/impl/Depth.java b/src/berack96/lib/graph/visit/impl/Depth.java deleted file mode 100644 index d497389..0000000 --- a/src/berack96/lib/graph/visit/impl/Depth.java +++ /dev/null @@ -1,53 +0,0 @@ -package berack96.lib.graph.visit.impl; - -import berack96.lib.graph.Graph; -import berack96.lib.graph.visit.VisitStrategy; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Consumer; - -public class Depth implements VisitStrategy { - - private long finalDepth; - - public Depth(long depth) { - this.finalDepth = depth; - } - - public void setDepth(long depth) { - this.finalDepth = depth; - } - - @Override - public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { - VisitInfo info = new VisitInfo<>(source); - long currentDepth = info.getDepth(source); - - if(visit != null) - visit.accept(source); - info.setVisited(source); - - List toVisit = new LinkedList<>(); - toVisit.add(source); - - while (!toVisit.isEmpty() && currentDepth < finalDepth) { - V current = toVisit.remove(0); - currentDepth = info.getDepth(current) + 1; - - for (V child : graph.getChildrens(current)) - if (!info.isDiscovered(child)) { - if(visit != null) - visit.accept(child); - - info.setVisited(child); - info.setParent(current, child); - toVisit.add(child); - - } - } - - return info; - } - -} diff --git a/src/berack96/lib/graph/visit/impl/Dijkstra.java b/src/berack96/lib/graph/visit/impl/Dijkstra.java index 97bd084..45665f9 100644 --- a/src/berack96/lib/graph/visit/impl/Dijkstra.java +++ b/src/berack96/lib/graph/visit/impl/Dijkstra.java @@ -1,26 +1,25 @@ package berack96.lib.graph.visit.impl; -import java.util.*; -import java.util.function.Consumer; - import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; import berack96.lib.graph.visit.VisitDistance; +import java.util.*; +import java.util.function.Consumer; + /** * 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 { +public class Dijkstra implements VisitDistance { - private Map>> distance = null; + private Map>> distance = null; private V source = null; @Override - public Map>> getLastDistance() { + public Map>> getLastDistance() { return distance; } @@ -30,15 +29,15 @@ public class Dijkstra implements VisitDistance { } @Override - public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, IllegalArgumentException { + 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 dist = new HashMap<>(); Map prev = new HashMap<>(); this.source = source; - dist.put(source, 0.0); // Initialization - queue.add(new QueueEntry(source, 0.0)); + dist.put(source, 0); // Initialization + queue.add(new QueueEntry(source, 0)); while (!queue.isEmpty()) { // The main loop QueueEntry u = queue.poll(); // Remove and return best vertex @@ -47,11 +46,11 @@ public class Dijkstra implements VisitDistance { if (visit != null) visit.accept(u.entry); - graph.getEdgesOut(u.entry).forEach((edge) -> { - V child = edge.getDestination(); + for (V child : graph.getChildren(u.entry)) { info.setDiscovered(child); - double alt = dist.get(u.entry) + edge.getWeight().doubleValue(); - Double distCurrent = dist.get(child); + int alt = dist.get(u.entry) + graph.getWeight(u.entry, child); + Integer distCurrent = dist.get(child); + if (distCurrent == null || alt < distCurrent) { dist.put(child, alt); prev.put(child, u.entry); @@ -60,17 +59,17 @@ public class Dijkstra implements VisitDistance { queue.remove(current); queue.add(current); } - }); + } } /* 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 { - Edge edge = new 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; @@ -84,9 +83,9 @@ public class Dijkstra implements VisitDistance { private class QueueEntry implements Comparable { final V entry; - final Double weight; + final int weight; - QueueEntry(V entry, Double weight) { + QueueEntry(V entry, int weight) { this.entry = entry; this.weight = weight; } diff --git a/src/berack96/lib/graph/visit/impl/Kruskal.java b/src/berack96/lib/graph/visit/impl/Kruskal.java new file mode 100644 index 0000000..e744935 --- /dev/null +++ b/src/berack96/lib/graph/visit/impl/Kruskal.java @@ -0,0 +1,44 @@ +package berack96.lib.graph.visit.impl; + +import berack96.lib.graph.Edge; +import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphUndirected; +import berack96.lib.graph.struct.QuickFind; +import berack96.lib.graph.struct.UnionFind; +import berack96.lib.graph.visit.VisitMST; + +import java.util.*; +import java.util.function.Consumer; + +/** + * Class that implement the algorithm discovered by Kruskal for the minimum spanning forest + * for a given {@link GraphUndirected} + * + * @param The vertex of the graph + */ +public class Kruskal implements VisitMST { + private Collection> mst; + + @Override + public Collection> getMST() { + return mst; + } + + @Override + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, UnsupportedOperationException { + UnionFind sets = new QuickFind<>(); + sets.makeSetAll(graph.vertices()); + + List> edges = new ArrayList<>(graph.edges()); + Collections.sort(edges); + + mst = new HashSet<>(graph.size(), 1); + Iterator> iter = edges.iterator(); + while (iter.hasNext() && sets.size() > 1) { + Edge edge = iter.next(); + if (sets.union(edge.getSource(), edge.getDestination())) + mst.add(edge); + } + return null; + } +} diff --git a/src/berack96/lib/graph/visit/impl/Prim.java b/src/berack96/lib/graph/visit/impl/Prim.java new file mode 100644 index 0000000..0ce648e --- /dev/null +++ b/src/berack96/lib/graph/visit/impl/Prim.java @@ -0,0 +1,63 @@ +package berack96.lib.graph.visit.impl; + +import berack96.lib.graph.Edge; +import berack96.lib.graph.Graph; +import berack96.lib.graph.GraphUndirected; +import berack96.lib.graph.visit.VisitMST; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.function.Consumer; + +/** + * Class that implement the algorithm discovered by Prim for the minimum spanning forest + * for a given {@link GraphUndirected} + * + * @param The vertex of the graph + */ +public class Prim implements VisitMST { + + private Collection> mst; + + @Override + public Collection> getMST() { + return mst; + } + + @Override + public VisitInfo visit(Graph graph, V source, Consumer visit) throws NullPointerException, UnsupportedOperationException { + mst = new LinkedList<>(); + Collection vertices = graph.vertices(); + + if (source == null) + source = vertices.iterator().next(); + VisitInfo info = new VisitInfo<>(source); + V current = source; + + do { + if (current == null) + current = vertices.iterator().next(); + + Edge min = null; + for (Edge edge : graph.edgesOf(current)) + if (vertices.contains(edge.getDestination())) + min = (min == null || edge.getWeight() < min.getWeight() ? edge : min); + + info.setParent(source, current); + info.setVisited(current); + if (visit != null) + visit.accept(current); + + if (min == null) + current = null; + else { + vertices.remove(current); + source = min.getSource(); + current = min.getDestination(); + mst.add(min); + } + } while (vertices.size() != 0); + + return info; + } +} diff --git a/src/berack96/lib/graph/visit/impl/Tarjan.java b/src/berack96/lib/graph/visit/impl/Tarjan.java index 426c43d..ff9b29d 100644 --- a/src/berack96/lib/graph/visit/impl/Tarjan.java +++ b/src/berack96/lib/graph/visit/impl/Tarjan.java @@ -11,10 +11,9 @@ import java.util.function.Consumer; * 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 { +public class Tarjan implements VisitSCC, VisitTopological { private Collection> SCC = null; private List topologicalSort = null; @@ -44,15 +43,15 @@ public class Tarjan implements VisitSCC, VisitTopolog * @throws IllegalArgumentException doesn't throw this */ @Override - public VisitInfo 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<>(); + topologicalSort = new ArrayList<>(graph.size()); info = null; indices = new HashMap<>(); lowLink = new HashMap<>(); stack = new Stack<>(); - Integer index = 0; + int index = 0; for (V vertex : graph) { if (info == null) @@ -61,11 +60,11 @@ public class Tarjan implements VisitSCC, VisitTopolog strongConnect(graph, vertex, index, visit); } - topologicalSort = (graph.size() == SCC.size()) ? new ArrayList<>(topologicalSort) : null; + topologicalSort = (graph.size() == SCC.size()) ? topologicalSort : null; return info; } - private void strongConnect(Graph graph, V vertex, Integer index, Consumer visit) { + 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); @@ -74,7 +73,7 @@ public class Tarjan implements VisitSCC, VisitTopolog info.setDiscovered(vertex); // Consider successors of v - for (V child : graph.getChildrens(vertex)) { + for (V child : graph.getChildren(vertex)) { if (!indices.containsKey(child)) { info.setParent(vertex, child); strongConnect(graph, child, index, visit); diff --git a/src/berack96/lib/graph/visit/impl/VisitInfo.java b/src/berack96/lib/graph/visit/impl/VisitInfo.java index 06f1305..87359e7 100644 --- a/src/berack96/lib/graph/visit/impl/VisitInfo.java +++ b/src/berack96/lib/graph/visit/impl/VisitInfo.java @@ -12,9 +12,9 @@ import java.util.function.Consumer; * @param the vertex of the visit * @author Berack96 */ -public class VisitInfo { +public class VisitInfo implements Iterable.VertexInfo> { private static final int NOT_SET = -1; - + private final Map vertices; private final V source; private long time; @@ -229,11 +229,11 @@ public class VisitInfo { * * @param consumer the function to apply to each */ - public void forEachDiscovered(Consumer consumer) { + public void forEachDiscovered(Consumer consumer) { Queue queue = new PriorityQueue<>(); - vertices.forEach((v, info) -> { - if(info.timeDiscovered != NOT_SET) - queue.offer(new VertexInfo(info, false)); + vertices.forEach((v, info) -> { + if (info.timeDiscovered != NOT_SET) + queue.offer(new VertexInfo(info, false)); }); while (!queue.isEmpty()) @@ -246,41 +246,35 @@ public class VisitInfo { * * @param consumer the function to apply to each */ - public void forEachVisited(Consumer consumer) { + public void forEachVisited(Consumer consumer) { Queue queue = new PriorityQueue<>(); - vertices.forEach((v, info) -> { - if(info.timeVisited != NOT_SET) - queue.offer(new VertexInfo(info, true)); + vertices.forEach((v, info) -> { + if (info.timeVisited != NOT_SET) + queue.offer(new VertexInfo(info, 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) { - Queue queue = new PriorityQueue<>(); - vertices.forEach((v, info) -> { - if(info.timeDiscovered != NOT_SET) - queue.offer(new VertexInfo(info, false)); - if(info.timeVisited != NOT_SET) - queue.offer(new VertexInfo(info, true)); + @Override + public Iterator iterator() { + List list = new ArrayList<>(vertices.size() * 2); + vertices.forEach((v, info) -> { + if (info.timeDiscovered != NOT_SET) + list.add(new VertexInfo(info, false)); + if (info.timeVisited != NOT_SET) + list.add(new VertexInfo(info, true)); }); - - while (!queue.isEmpty()) - consumer.accept(queue.remove()); + Collections.sort(list); + return list.iterator(); } - + /** * Class used mainly for storing the data of the visit */ public class VertexInfo implements Comparable { - public V vertex; + public V vertex; public V parent; public long timeDiscovered; public long timeVisited; diff --git a/test/berack96/test/lib/TestGraph.java b/test/berack96/test/lib/TestGraph.java index 9650eca..a6492fb 100644 --- a/test/berack96/test/lib/TestGraph.java +++ b/test/berack96/test/lib/TestGraph.java @@ -1,9 +1,13 @@ package berack96.test.lib; -import berack96.lib.graph.Edge; -import berack96.lib.graph.Graph; -import berack96.lib.graph.Vertex; +import berack96.lib.graph.*; +import berack96.lib.graph.impl.ListGraph; +import berack96.lib.graph.impl.MapGraph; +import berack96.lib.graph.impl.MatrixGraph; +import berack96.lib.graph.impl.MatrixUndGraph; import berack96.lib.graph.models.GraphSaveStructure; +import berack96.lib.graph.struct.QuickFind; +import berack96.lib.graph.struct.UnionFind; import berack96.lib.graph.visit.impl.BFS; import berack96.lib.graph.visit.impl.DFS; import berack96.lib.graph.visit.impl.VisitInfo; @@ -12,6 +16,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -19,53 +25,201 @@ import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; -@Timeout(value = 2) +@Timeout(value = 10) +@SuppressWarnings("ConstantConditions,ConfusingArgumentToVarargsMethod") public class TestGraph { - - /* I decided to only try this for sake of simplicity */ - private Graph graph; - private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - + private final String encoding = "UTF-8"; - private final Exception nullException = new NullPointerException(Graph.PARAM_NULL); + private final Exception nullException = new NullPointerException(); + private final Exception illegalException = new IllegalArgumentException(); private final Exception notException = new IllegalArgumentException(Graph.VERTEX_NOT_CONTAINED); private final Exception unSuppException = new UnsupportedOperationException(Vertex.REMOVED); + private final Exception notConnException = new UnsupportedOperationException(Graph.NOT_CONNECTED); + + //TODO tests for GraphUndirected minimum spanning forest + public static Stream> getGraphsUnDir() { + return Stream.of(new MatrixUndGraph<>()); + } + + public static Stream> getGraphsDir() { + return Stream.of(new MapGraph<>(), new MatrixGraph<>(), new ListGraph<>()); + } + + public static Stream> getGraphs() { + return Stream.concat(getGraphsDir(), getGraphsUnDir()); + } + + public static Stream> getUnionFind() { + return Stream.of(new QuickFind<>()); + } @BeforeEach public void before() { - // Change here the instance for changing all the test for that particular class - //graph = new berack96.lib.graph.impl.MapGraph<>(); - //graph = new berack96.lib.graph.impl.MatrixGraph<>(); - graph = new berack96.lib.graph.impl.ListGraph<>(); - PrintStream p; try { - p = new PrintStream(bytes, true, encoding); - System.setErr(p); - System.setOut(p); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + p = new PrintStream(bytes, true, encoding); + System.setErr(p); + System.setOut(p); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } } @AfterEach public void after() { - try { - String printed = bytes.toString(encoding); - bytes.reset(); - if (!printed.isEmpty()) - fail("Remove the printed string in the methods: " + printed); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } + try { + String printed = bytes.toString(encoding); + bytes.reset(); + if (!printed.isEmpty()) + fail("!!_____________!!_____________!!\nRemove the printed string:\n" + printed); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } } @Test - public void basicVertex() { + public void preBasicVisit() { + VisitInfo info = new VisitInfo<>(0); + assertTrue(info.isDiscovered(0)); + assertFalse(info.isVisited(0)); + assertEquals(0, info.getDepth(0)); + assertEquals(0, info.getTimeDiscover(0)); + assertEquals(Integer.valueOf(0), info.getSource()); + assertNull(info.getParentOf(0)); + + assertFalse(info.isVisited(null)); + assertFalse(info.isDiscovered(null)); + + shouldThrow(illegalException, () -> info.getTimeVisit(0)); + shouldThrow(illegalException, () -> info.getTimeDiscover(1)); + shouldThrow(illegalException, () -> info.getParentOf(2)); + shouldThrow(illegalException, () -> info.getDepth(2)); + + shouldThrow(nullException, () -> info.getTimeDiscover(null)); + shouldThrow(nullException, () -> info.getTimeVisit(null)); + shouldThrow(nullException, () -> info.getParentOf(null)); + shouldThrow(nullException, () -> info.getDepth(null)); + } + + @Test + public void preEdges() { + Edge edge = new Edge<>(null, null); + assertEquals(edge.getWeight(), 1); + assertNull(edge.getSource()); + assertNull(edge.getDestination()); + shouldContain(edge.getVertices()); + + edge = new Edge<>("a", "b"); + assertEquals(edge.getWeight(), 1); + assertEquals(edge.getSource(), "a"); + assertEquals(edge.getDestination(), "b"); + shouldContain(edge.getVertices(), "a", "b"); + + edge = new Edge<>("23", "51", 0); + assertEquals(edge.getWeight(), 0); + assertEquals(edge.getSource(), "23"); + assertEquals(edge.getDestination(), "51"); + shouldContain(edge.getVertices(), "23", "51"); + + edge = new Edge<>("JHfkOHFbeH", "SdGjhFdSaAv", -894656); + assertEquals(edge.getWeight(), -894656); + assertEquals(edge.getSource(), "JHfkOHFbeH"); + assertEquals(edge.getDestination(), "SdGjhFdSaAv"); + shouldContain(edge.getVertices(), "JHfkOHFbeH", "SdGjhFdSaAv"); + + edge = new Edge<>("", "1", 546815); + assertEquals(edge.getWeight(), 546815); + assertEquals(edge.getSource(), ""); + assertEquals(edge.getDestination(), "1"); + shouldContain(edge.getVertices(), "", "1"); + } + + @ParameterizedTest + @MethodSource("getUnionFind") + public void preMSF(UnionFind tree) { + shouldThrow(nullException, () -> tree.find(null)); + shouldThrow(nullException, () -> tree.union(null, null)); + shouldThrow(nullException, () -> tree.union("2", null)); + shouldThrow(nullException, () -> tree.union(null, "5")); + shouldThrow(illegalException, () -> tree.union("67", "24")); + shouldThrow(nullException, () -> tree.makeSet(null)); + shouldThrow(nullException, () -> tree.makeSetAll(null)); + + assertEquals(tree.size(), 0); + assertNull(tree.find("")); + assertNull(tree.find("0")); + assertNull(tree.find("1")); + assertNull(tree.find("2")); + assertNull(tree.find("3")); + + tree.makeSetAll(List.of("1", "2", "3")); + assertEquals(tree.size(), 3); + assertNull(tree.find("")); + assertNull(tree.find("0")); + assertEquals(tree.find("1"), "1"); + assertEquals(tree.find("2"), "2"); + assertEquals(tree.find("3"), "3"); + + tree.makeSet("0"); + assertEquals(tree.size(), 4); + assertNull(tree.find("")); + assertEquals(tree.find("0"), "0"); + assertEquals(tree.find("1"), "1"); + assertEquals(tree.find("2"), "2"); + assertEquals(tree.find("3"), "3"); + + assertFalse(tree.union("0", "0")); + assertFalse(tree.union("1", "1")); + assertFalse(tree.union("2", "2")); + assertFalse(tree.union("3", "3")); + shouldThrow(illegalException, () -> tree.union("", "")); + shouldThrow(illegalException, () -> tree.union("4", "4")); + + assertTrue(tree.union("0", "1")); + assertEquals(tree.size(), 3); + String find0 = tree.find("0"); + assertEquals(tree.find("0"), find0); + assertEquals(tree.find("1"), find0); + assertEquals(tree.find("2"), "2"); + assertEquals(tree.find("3"), "3"); + assertFalse(tree.union("0", "1")); + assertFalse(tree.union("1", "0")); + + assertTrue(tree.union("2", "3")); + assertEquals(tree.size(), 2); + String find2 = tree.find("2"); + assertEquals(tree.find("0"), find0); + assertEquals(tree.find("1"), find0); + assertEquals(tree.find("2"), find2); + assertEquals(tree.find("3"), find2); + assertFalse(tree.union("2", "3")); + assertFalse(tree.union("3", "2")); + + assertTrue(tree.union("3", "0")); + assertEquals(tree.size(), 1); + find2 = tree.find("2"); + assertEquals(tree.find("0"), find2); + assertEquals(tree.find("1"), find2); + assertEquals(tree.find("2"), find2); + assertEquals(tree.find("3"), find2); + assertFalse(tree.union("0", "3")); + assertFalse(tree.union("3", "0")); + assertFalse(tree.union("0", "2")); + assertFalse(tree.union("2", "0")); + assertFalse(tree.union("1", "3")); + assertFalse(tree.union("3", "1")); + assertFalse(tree.union("1", "2")); + assertFalse(tree.union("2", "1")); + } + + @ParameterizedTest + @MethodSource("getGraphs") + public void basicVertex(Graph graph) { assertEquals(0, graph.size()); graph.add("1"); @@ -98,7 +252,7 @@ public class TestGraph { graph.removeAll(); shouldContain(graph.vertices()); - Set vertices = new HashSet<>(Arrays.asList("1", "5", "24", "2", "3")); + Collection vertices = Set.of("1", "5", "24", "2", "3"); graph.addAll(vertices); shouldContain(graph.vertices(), vertices.toArray()); graph.remove("1"); @@ -110,344 +264,9 @@ public class TestGraph { shouldThrow(nullException, () -> graph.addAll(null)); } - @Test - public void basicEdge() { - /* - * This graph should be like this - * - * 1 -> 2 - * | | - * v v - * 3 <-> 5 -> 4 - */ - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - - shouldThrow(nullException, () -> graph.addEdge(null, "2", 1)); - shouldThrow(nullException, () -> graph.addEdge(null, null, 1)); - shouldThrow(nullException, () -> graph.addEdge("1", null, 1)); - shouldThrow(nullException, () -> 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)); - shouldThrow(nullException, () -> graph.removeEdge(null, "2")); - shouldThrow(nullException, () -> graph.removeEdge(null, null)); - shouldThrow(nullException, () -> graph.removeEdge("1", null)); - shouldThrow(nullException, () -> graph.removeAllEdge(null)); - shouldThrow(nullException, () -> graph.removeAllOutEdge(null)); - shouldThrow(nullException, () -> graph.removeAllInEdge(null)); - - shouldThrow(notException, () -> graph.addEdge("0", "2", 1)); - shouldThrow(notException, () -> graph.addEdge("2", "8", 1)); - shouldThrow(notException, () -> graph.addEdge("9", "6", 1)); - shouldThrow(notException, () -> graph.removeEdge("012", "2")); - shouldThrow(notException, () -> graph.removeEdge("2", "28")); - shouldThrow(notException, () -> graph.removeEdge("4329", "62")); - shouldThrow(notException, () -> graph.removeAllEdge("0")); - shouldThrow(notException, () -> graph.removeAllInEdge("011")); - shouldThrow(notException, () -> graph.removeAllOutEdge("9")); - - assertEquals(0, graph.numberOfEdges()); - - assertNull(graph.addEdge("1", "2", 1)); - assertNull(graph.addEdge(new Edge<>("1", "3", 1))); - assertNull(graph.addEdge("2", "5", 4)); - 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"); - graph.removeEdge("2", "3"); - graph.removeEdge("3", "1"); - graph.removeEdge("4", "5"); - - assertEquals(6, graph.numberOfEdges()); - - assertEquals(Integer.valueOf(1), graph.getWeight("1", "2")); - assertEquals(Integer.valueOf(1), graph.getWeight("1", "3")); - assertEquals(Integer.valueOf(4), graph.getWeight("2", "5")); - assertEquals(Integer.valueOf(2), graph.getWeight("3", "5")); - assertEquals(Integer.valueOf(2), graph.getWeight("5", "3")); - assertEquals(Integer.valueOf(3), graph.getWeight("5", "4")); - - assertNull(graph.getWeight("1", "4")); - - assertEquals(Integer.valueOf(1), graph.addEdge("1", "2", 102)); - assertEquals(Integer.valueOf(102), graph.addEdge("1", "2", 3)); - assertEquals(Integer.valueOf(3), graph.addEdge("1", "2", 1)); - assertEquals(Integer.valueOf(1), graph.addEdge(new Edge<>("1", "2", 102))); - assertEquals(Integer.valueOf(102), graph.addEdge(new Edge<>("1", "2", 3))); - assertEquals(Integer.valueOf(3), graph.addEdge(new Edge<>("1", "2", 1))); - - assertEquals(6, graph.numberOfEdges()); - assertTrue(graph.containsEdge("1", "2")); - assertFalse(graph.containsEdge("4", "3")); - assertFalse(graph.containsEdge("2", "1")); - assertFalse(graph.containsEdge("1", "4")); - assertTrue(graph.containsEdge("1", "3")); - assertTrue(graph.containsEdge("3", "5")); - assertTrue(graph.containsEdge("2", "5")); - - graph.removeEdge("2", "5"); - assertFalse(graph.containsEdge("2", "5")); - assertEquals(5, graph.numberOfEdges()); - - graph.removeEdge("1", "2"); - assertFalse(graph.containsEdge("1", "2")); - assertTrue(graph.containsEdge("1", "3")); - assertEquals(4, graph.numberOfEdges()); - graph.addEdge("1", "2", 2); - - graph.removeAllOutEdge("1"); - assertFalse(graph.containsEdge("1", "2")); - assertFalse(graph.containsEdge("1", "3")); - assertEquals(3, graph.numberOfEdges()); - graph.addEdge("1", "2", 2); - graph.addEdge("1", "3", 2); - assertEquals(5, graph.numberOfEdges()); - - graph.removeAllInEdge("3"); - assertFalse(graph.containsEdge("5", "3")); - assertFalse(graph.containsEdge("1", "3")); - assertTrue(graph.containsEdge("3", "5")); - assertEquals(3, graph.numberOfEdges()); - graph.addEdge("1", "3", 2); - graph.addEdge("5", "3", 2); - - graph.removeAllEdge("3"); - assertFalse(graph.containsEdge("5", "3")); - assertFalse(graph.containsEdge("1", "3")); - assertFalse(graph.containsEdge("3", "5")); - assertEquals(2, graph.numberOfEdges()); - - graph.removeAllEdge(); - assertFalse(graph.containsEdge("1", "2")); - assertFalse(graph.containsEdge("1", "3")); - assertFalse(graph.containsEdge("2", "5")); - assertFalse(graph.containsEdge("3", "5")); - assertFalse(graph.containsEdge("5", "3")); - assertFalse(graph.containsEdge("5", "4")); - assertEquals(0, graph.numberOfEdges()); - - assertFalse(graph.containsEdge("2", "323")); - assertNull(graph.addEdgeAndVertices("2", "323", 3)); - assertTrue(graph.containsEdge("2", "323")); - assertFalse(graph.containsEdge("2aa", "323")); - assertNull(graph.addEdgeAndVertices("2aa", "323", 35)); - assertTrue(graph.containsEdge("2aa", "323")); - 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.removeAll(); - graph.add("aaa"); - graph.add("1"); - graph.add("2"); - - shouldContain(graph.vertices(), "1", "2", "aaa"); - shouldContain(graph.edges()); - - 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 Edge<>("aaa", "bbb", 3), - new Edge<>("bbb", "ccc", 4), - new Edge<>("ccc", "aaa", 5), - new Edge<>("1", "2", 2)); - } - - @Test - public void advancedEdge() { - /* - * This graph should be like this - * - * 1 -> 2 -> 6 - * ^ - * | | | - * v v - * 3 <-> 5 -> 4 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - - shouldContain(graph.edges()); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 1); - graph.addEdge("2", "5", 4); - graph.addEdge("2", "6", 5); - graph.addEdge("3", "5", 2); - graph.addEdge("4", "6", 6); - graph.addEdge("5", "3", 9); - graph.addEdge("5", "4", 5); - - shouldContain(graph.getChildrens("1"), "2", "3"); - shouldContain(graph.getChildrens("2"), "5", "6"); - shouldContain(graph.getChildrens("3"), "5"); - shouldContain(graph.getChildrens("4"), "6"); - shouldContain(graph.getChildrens("5"), "3", "4"); - shouldContain(graph.getChildrens("6")); - - shouldContain(graph.getAncestors("1")); - shouldContain(graph.getAncestors("2"), "1"); - shouldContain(graph.getAncestors("3"), "1", "5"); - shouldContain(graph.getAncestors("4"), "5"); - shouldContain(graph.getAncestors("5"), "2", "3"); - shouldContain(graph.getAncestors("6"), "2", "4"); - - 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")); - assertEquals(2, graph.degreeIn("3")); - assertEquals(1, graph.degreeIn("4")); - assertEquals(2, graph.degreeIn("5")); - assertEquals(2, graph.degreeIn("6")); - - assertEquals(2, graph.degreeOut("1")); - assertEquals(2, graph.degreeOut("2")); - assertEquals(1, graph.degreeOut("3")); - assertEquals(1, graph.degreeOut("4")); - assertEquals(2, graph.degreeOut("5")); - assertEquals(0, graph.degreeOut("6")); - - assertEquals(2, graph.degree("1")); - assertEquals(3, graph.degree("2")); - assertEquals(3, graph.degree("3")); - assertEquals(2, graph.degree("4")); - assertEquals(4, graph.degree("5")); - assertEquals(2, graph.degree("6")); - - shouldContain(graph.edges(), - 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 Edge<>("2", "5", 4), - new Edge<>("3", "5", 2), - new Edge<>("5", "3", 9), - new Edge<>("5", "4", 5)); - - /* Weird case in the add */ - graph.addIfAbsent("2"); - shouldContain(graph.edges(), - 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)); - graph.add("2"); - shouldContain(graph.edges(), - new Edge<>("1", "3", 1), - new Edge<>("3", "5", 2), - new Edge<>("4", "6", 6), - new Edge<>("5", "3", 9), - new Edge<>("5", "4", 5)); - } - - @Test - public void preBasicVisit() { - VisitInfo info = new VisitInfo<>(0); - assertTrue(info.isDiscovered(0)); - assertFalse(info.isVisited(0)); - assertEquals(0, info.getDepth(0)); - assertEquals(0, info.getTimeDiscover(0)); - assertEquals(Integer.valueOf(0), info.getSource()); - assertNull(info.getParentOf(0)); - - assertFalse(info.isVisited(null)); - assertFalse(info.isDiscovered(null)); - - shouldThrow(new IllegalArgumentException(), () -> info.getTimeVisit(0)); - shouldThrow(new IllegalArgumentException(), () -> info.getTimeDiscover(1)); - shouldThrow(new IllegalArgumentException(), () -> info.getParentOf(2)); - shouldThrow(new IllegalArgumentException(), () -> info.getDepth(2)); - - shouldThrow(new NullPointerException(), () -> info.getTimeDiscover(null)); - shouldThrow(new NullPointerException(), () -> info.getTimeVisit(null)); - shouldThrow(new NullPointerException(), () -> info.getParentOf(null)); - shouldThrow(new NullPointerException(), () -> info.getDepth(null)); - } - - @Test - public void basicVisit() { + @ParameterizedTest + @MethodSource("getGraphs") + public void marker(Graph graph) { /* * This graph should be like this * @@ -458,526 +277,7 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("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); - - Exception nullP = new NullPointerException(); - shouldThrow(nullP, () -> graph.visit(null, new DFS<>(), null)); - shouldThrow(nullP, () -> graph.visit(null, null, null)); - shouldThrow(nullP, () -> graph.visit("1", null, null)); - - shouldThrow(notException, () -> graph.visit("1010", new DFS<>(), null)); - - DFS dfs = new DFS<>(); - VisitInfo visitDFS = graph.visit("1", dfs, null); - assertEquals(0, visitDFS.getTimeDiscover("1")); - assertEquals(1, visitDFS.getTimeDiscover("2")); - assertEquals(2, visitDFS.getTimeDiscover("5")); - assertEquals(3, visitDFS.getTimeDiscover("3")); - assertEquals(4, visitDFS.getTimeVisit("3")); - assertEquals(5, visitDFS.getTimeDiscover("4")); - assertEquals(6, visitDFS.getTimeDiscover("6")); - assertEquals(7, visitDFS.getTimeVisit("6")); - assertEquals(8, visitDFS.getTimeVisit("4")); - assertEquals(9, visitDFS.getTimeVisit("5")); - assertEquals(10, visitDFS.getTimeVisit("2")); - assertEquals(11, visitDFS.getTimeVisit("1")); - 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(); - }); - - String[] vertices = {"1", "2", "5", "3", "3", "4", "6", "6", "4", "5", "2", "1"}; - boolean[] found = new boolean[graph.size()]; - integer.set(0); - visitDFS.forEach(vertexInfo -> { - int i = integer.get(); - assertEquals(vertices[i], vertexInfo.vertex, "Iter " + i); - int vert = Integer.parseInt(vertexInfo.vertex); - - if(found[vert]) - assertEquals(i, vertexInfo.timeVisited, "Iter " + i); - else { - assertEquals(i, vertexInfo.timeDiscovered, "Iter " + i); - found[vert] = true; - } - - integer.incrementAndGet(); - }); - - BFS bfs = new BFS<>(); - VisitInfo visitBFS = graph.visit("1", bfs, null); - assertEquals(0, visitBFS.getTimeDiscover("1")); - assertEquals(1, visitBFS.getTimeVisit("1")); - assertEquals(2, visitBFS.getTimeDiscover("2")); - assertEquals(3, visitBFS.getTimeVisit("2")); - assertEquals(4, visitBFS.getTimeDiscover("3")); - assertEquals(5, visitBFS.getTimeVisit("3")); - assertEquals(6, visitBFS.getTimeDiscover("5")); - assertEquals(7, visitBFS.getTimeVisit("5")); - assertEquals(8, visitBFS.getTimeDiscover("4")); - assertEquals(9, visitBFS.getTimeVisit("4")); - assertEquals(10, visitBFS.getTimeDiscover("6")); - assertEquals(11, visitBFS.getTimeVisit("6")); - assertFalse(visitBFS.isDiscovered("7")); - assertFalse(visitBFS.isDiscovered("8")); - } - - @Test - public void iterable() { - /* - * This graph should be like this - * - * 1 -> 2 <- 6 7 - * ^ ^ - * | | | | - * v v v - * 3 <- 5 -> 4 8 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("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); - - Set vertices = new HashSet<>(); - - Iterator iter = graph.iterator(); - assertNotNull(iter, "This should not be null!"); - while (iter.hasNext()) - vertices.add(iter.next()); - shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); - vertices.clear(); - - for (String vertex : graph) - vertices.add(vertex); - shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); - - vertices.clear(); - graph.forEach(vertices::add); - shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); - } - - @Test - public void scc() { - /* - * This graph should be like this - * - * 1 -> 2 -> 6 - * ^ - * | | | - * v v - * 3 <-> 5 -> 4 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 1); - graph.addEdge("2", "5", 4); - graph.addEdge("2", "6", 5); - graph.addEdge("3", "5", 2); - graph.addEdge("4", "6", 6); - graph.addEdge("5", "3", 9); - graph.addEdge("5", "4", 5); - - shouldContain(graph.stronglyConnectedComponents(), new HashSet<>(Collections.singletonList("6")), new HashSet<>(Arrays.asList("3", "5")), new HashSet<>(Collections.singletonList("4")), new HashSet<>(Collections.singletonList("1")), new HashSet<>(Collections.singletonList("2"))); - - /* - * This graph should be like this - * - * 1 -> 2 <- 6 7 - * ^ ^ - * | | | | - * v v v - * 3 <- 5 -> 4 8 - */ - before(); - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("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); - - shouldContain(graph.stronglyConnectedComponents(), new HashSet<>(Arrays.asList("7", "8")), new HashSet<>(Arrays.asList("2", "5", "4", "6")), new HashSet<>(Collections.singletonList("3")), new HashSet<>(Collections.singletonList("1"))); - } - - @Test - public void cyclic() { - /* - * This graph should be like this - * - * 1 -> 2 -> 6 - * ^ - * | | | - * v v - * 3 -> 5 -> 4 - */ - - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 1); - graph.addEdge("2", "5", 4); - graph.addEdge("2", "6", 5); - graph.addEdge("3", "5", 2); - graph.addEdge("4", "6", 6); - graph.addEdge("5", "4", 5); - - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - - /* - * This graph should be like this - * - * 1 -> 2 <- 6 - * ^ - * | | | - * v v - * 3 <- 5 -> 4 - */ - before(); - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - - - graph.addEdge("1", "2", 1); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("1", "3", 1); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("2", "5", 4); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("4", "6", 5); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("5", "3", 6); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("5", "4", 3); - assertFalse(graph.isCyclic()); - assertTrue(graph.isDAG()); - graph.addEdge("6", "2", 2); - assertTrue(graph.isCyclic()); - assertFalse(graph.isDAG()); - } - - @Test - public void transpose() { - /* - * This graph should be like this - * - * 1 -> 2 <- 6 7 - * ^ - * | | | | - * v v v - * 3 <- 5 -> 4 8 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("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 transposed = graph.transpose(); - assertNotNull(transposed, "This should not be null!"); - - DFS dfs = new DFS<>(); - VisitInfo visitDFS = transposed.visit("6", dfs, null); - assertEquals(0, visitDFS.getTimeDiscover("6")); - assertEquals(1, visitDFS.getTimeDiscover("4")); - assertEquals(2, visitDFS.getTimeDiscover("5")); - assertEquals(3, visitDFS.getTimeDiscover("2")); - assertEquals(4, visitDFS.getTimeDiscover("1")); - assertEquals(5, visitDFS.getTimeVisit("1")); - assertEquals(6, visitDFS.getTimeVisit("2")); - assertEquals(7, visitDFS.getTimeVisit("5")); - assertEquals(8, visitDFS.getTimeVisit("4")); - assertEquals(9, visitDFS.getTimeVisit("6")); - assertFalse(visitDFS.isDiscovered("3")); - assertFalse(visitDFS.isDiscovered("7")); - assertFalse(visitDFS.isDiscovered("8")); - - visitDFS = transposed.visit("8", dfs, null); - assertEquals(0, visitDFS.getTimeDiscover("8")); - assertEquals(1, visitDFS.getTimeDiscover("7")); - assertEquals(2, visitDFS.getTimeVisit("7")); - assertEquals(3, visitDFS.getTimeVisit("8")); - assertFalse(visitDFS.isDiscovered("1")); - assertFalse(visitDFS.isDiscovered("2")); - assertFalse(visitDFS.isDiscovered("3")); - assertFalse(visitDFS.isDiscovered("4")); - assertFalse(visitDFS.isDiscovered("5")); - assertFalse(visitDFS.isDiscovered("6")); - } - - @Test - public void topologicalSort() { - /* - * This graph should be like this - * - * 1 -> 2 -> 6 - * ^ - * | | | - * v v - * 3 -> 5 -> 4 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 1); - graph.addEdge("2", "5", 4); - graph.addEdge("2", "6", 5); - graph.addEdge("3", "5", 2); - graph.addEdge("4", "6", 6); - graph.addEdge("5", "4", 5); - - shouldContainInOrder(graph.topologicalSort(), "1", "3", "2", "5", "4", "6"); - - } - - @Test - public void distanceVV() { - /* - * This graph should be like this - * - * 1 -> 2 <- 6 7 - * ^ - * | | | | - * v v v - * 3 <- 5 -> 4 8 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("8"); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 10); - graph.addEdge("2", "5", 4); - graph.addEdge("4", "6", 5); - graph.addEdge("5", "3", 3); - graph.addEdge("5", "4", 3); - graph.addEdge("6", "2", 2); - graph.addEdge("7", "8", 8); - - List> distance = graph.distance("1", "6"); - assertNotNull(distance, "This should not be null!"); - int sum = distance.stream().mapToInt(Edge::getWeight).sum(); - assertEquals(13, sum); - shouldContainInOrder(distance, - 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(Edge::getWeight).sum(); - assertEquals(8, sum); - shouldContainInOrder(distance, - new Edge<>("1", "2", 1), - new Edge<>("2", "5", 4), - new Edge<>("5", "3", 3)); - - shouldContainInOrder(graph.distance("7", "8"), new Edge<>("7", "8", 8)); - - shouldThrow(nullException, () -> graph.distance(null, "1")); - shouldThrow(nullException, () -> graph.distance(null, null)); - shouldThrow(nullException, () -> graph.distance("1", null)); - shouldThrow(notException, () -> graph.distance("34", "1")); - shouldThrow(notException, () -> graph.distance("2", "36")); - shouldThrow(notException, () -> graph.distance("689", "374")); - shouldThrow(new UnsupportedOperationException(Graph.NOT_CONNECTED), () -> graph.distance("1", "7")); - shouldThrow(new UnsupportedOperationException(Graph.NOT_CONNECTED), () -> graph.distance("3", "2")); - } - - @Test - public void distanceVtoAll() { - /* - * This graph should be like this - * - * 1 -> 2 <- 6 7 - * ^ - * | | | | - * v v v - * 3 <- 5 -> 4 -> 8 - */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("8"); - - graph.addEdge("1", "2", 1); - graph.addEdge("1", "3", 10); - graph.addEdge("2", "5", 4); - graph.addEdge("4", "6", 5); - graph.addEdge("4", "8", 2); - graph.addEdge("5", "3", 3); - graph.addEdge("5", "4", 3); - graph.addEdge("6", "2", 2); - graph.addEdge("7", "8", 8); - - Map>> distance = graph.distance("1"); - assertNotNull(distance, "This should not be null!"); - assertNull(distance.get("1")); - shouldContainInOrder(distance.get("2"), - new Edge<>("1", "2", 1)); - shouldContainInOrder(distance.get("3"), - new Edge<>("1", "2", 1), - new Edge<>("2", "5", 4), - new Edge<>("5", "3", 3)); - shouldContain(distance.get("4"), - new Edge<>("1", "2", 1), - new Edge<>("2", "5", 4), - new Edge<>("5", "4", 3)); - shouldContain(distance.get("5"), - new Edge<>("1", "2", 1), - new Edge<>("2", "5", 4)); - shouldContain(distance.get("6"), - 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 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.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("8"); + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "8")); graph.addEdge("1", "2", 1); graph.addEdge("1", "3", 1); @@ -1086,7 +386,7 @@ public class TestGraph { shouldContain(graph.getMarkedWith("mark"), "2"); shouldContain(graph.getMarkedWith("mark2")); shouldContain(graph.getMarkedWith(3), "1"); - + graph.unMark("1", "mark"); graph.unMark("2", "mark2"); shouldContain(graph.marks(), "mark", 3); @@ -1105,7 +405,7 @@ public class TestGraph { shouldContain(graph.getMarkedWith("mark")); shouldContain(graph.getMarkedWith("mark2")); shouldContain(graph.getMarkedWith(3), "1"); - + graph.unMarkAll(3); shouldContain(graph.marks()); shouldContain(graph.getMarks("1")); @@ -1116,8 +416,1015 @@ public class TestGraph { shouldContain(graph.getMarkedWith(3)); } - @Test - public void subGraph() { + @ParameterizedTest + @MethodSource("getGraphs") + public void basicEdge(Graph graph) { + /* + * This graph should be like this + * + * 1 -> 2 + * | | + * v v + * 3 <-> 5 -> 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5")); + + shouldThrow(nullException, () -> graph.addEdge(null, "2", 1)); + shouldThrow(nullException, () -> graph.addEdge(null, null, 1)); + shouldThrow(nullException, () -> graph.addEdge("1", null, 1)); + shouldThrow(nullException, () -> 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)); + shouldThrow(nullException, () -> graph.removeEdge(null, "2")); + shouldThrow(nullException, () -> graph.removeEdge(null, null)); + shouldThrow(nullException, () -> graph.removeEdge("1", null)); + shouldThrow(nullException, () -> graph.removeAllEdge(null)); + + shouldThrow(notException, () -> graph.addEdge("0", "2", 1)); + shouldThrow(notException, () -> graph.addEdge("2", "8", 1)); + shouldThrow(notException, () -> graph.addEdge("9", "6", 1)); + shouldThrow(notException, () -> graph.removeEdge("012", "2")); + shouldThrow(notException, () -> graph.removeEdge("2", "28")); + shouldThrow(notException, () -> graph.removeEdge("4329", "62")); + shouldThrow(notException, () -> graph.removeAllEdge("0")); + + assertEquals(0, graph.numberOfEdges()); + + assertEquals(0, graph.addEdge("1", "2", 1)); + assertEquals(0, graph.addEdge(new Edge<>("1", "3", 1))); + assertEquals(0, graph.addEdge("2", "5", 4)); + assertEquals(0, graph.addEdge(new Edge<>("3", "5", 2))); + assertEquals(0, graph.addEdge("5", "4", 3)); + + assertEquals(5, 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"); + graph.removeEdge("2", "3"); + graph.removeEdge("4", "3"); + graph.removeEdge("5", "1"); + graph.removeEdge("4", "1"); + + assertEquals(5, graph.numberOfEdges()); + + assertEquals(1, graph.getWeight("1", "2")); + assertEquals(1, graph.getWeight("1", "3")); + assertEquals(4, graph.getWeight("2", "5")); + assertEquals(2, graph.getWeight("3", "5")); + assertEquals(3, graph.getWeight("5", "4")); + + assertEquals(0, graph.getWeight("1", "4")); + + assertEquals(1, graph.addEdge("1", "2", 102)); + assertEquals(102, graph.addEdge("1", "2", 3)); + assertEquals(3, graph.addEdge("1", "2", 1)); + assertEquals(1, graph.addEdge(new Edge<>("1", "2", 102))); + assertEquals(102, graph.addEdge(new Edge<>("1", "2", 3))); + assertEquals(3, graph.addEdge(new Edge<>("1", "2", 1))); + + assertTrue(graph.containsEdge("1", "2")); + assertEquals(1, graph.addEdge("1", "2", 0)); + assertFalse(graph.containsEdge("1", "2")); + graph.addEdge("1", "2", 1); + assertTrue(graph.containsEdge("5", "4")); + assertEquals(3, graph.addEdge("5", "4", 0)); + assertFalse(graph.containsEdge("5", "4")); + graph.addEdge("5", "4", 1); + + assertEquals(5, graph.numberOfEdges()); + assertTrue(graph.containsEdge("1", "2")); + assertFalse(graph.containsEdge("4", "3")); + assertFalse(graph.containsEdge("1", "4")); + assertTrue(graph.containsEdge("1", "3")); + assertTrue(graph.containsEdge("3", "5")); + assertTrue(graph.containsEdge("2", "5")); + + graph.removeEdge("2", "5"); + assertFalse(graph.containsEdge("2", "5")); + assertEquals(4, graph.numberOfEdges()); + + graph.removeEdge("1", "2"); + assertFalse(graph.containsEdge("1", "2")); + assertTrue(graph.containsEdge("1", "3")); + assertEquals(3, graph.numberOfEdges()); + graph.addEdge("1", "2", 2); + + graph.removeAllEdge("3"); + assertFalse(graph.containsEdge("1", "3")); + assertEquals(2, graph.numberOfEdges()); + + graph.removeAllEdge(); + assertFalse(graph.containsEdge("1", "2")); + assertFalse(graph.containsEdge("1", "3")); + assertFalse(graph.containsEdge("2", "5")); + assertFalse(graph.containsEdge("3", "5")); + assertFalse(graph.containsEdge("5", "3")); + assertFalse(graph.containsEdge("5", "4")); + assertEquals(0, graph.numberOfEdges()); + + assertFalse(graph.containsEdge("2", "323")); + assertEquals(0, graph.addEdgeAndVertices("2", "323", 3)); + assertTrue(graph.containsEdge("2", "323")); + assertFalse(graph.containsEdge("2aa", "323")); + assertEquals(0, graph.addEdgeAndVertices("2aa", "323", 35)); + assertTrue(graph.containsEdge("2aa", "323")); + assertFalse(graph.containsEdge("2bbb", "323bbb")); + assertEquals(0, graph.addEdgeAndVertices("2bbb", "323bbb", 135)); + assertTrue(graph.containsEdge("2bbb", "323bbb")); + + assertFalse(graph.containsEdge("aff5", "444")); + assertEquals(0, graph.addEdgeAndVertices("aff5", "444")); + assertTrue(graph.containsEdge("aff5", "444")); + assertFalse(graph.containsEdge("444", "455")); + assertEquals(0, graph.addEdgeAndVertices("444", "455")); + assertTrue(graph.containsEdge("444", "455")); + assertFalse(graph.containsEdge("333", "455")); + assertEquals(0, graph.addEdgeAndVertices("333", "455")); + assertTrue(graph.containsEdge("333", "455")); + + shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, "1", 1)); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, null, 1)); + shouldThrow(nullException, () -> graph.addEdgeAndVertices("2", null, 1)); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, "1")); + shouldThrow(nullException, () -> graph.addEdgeAndVertices(null, null)); + shouldThrow(nullException, () -> graph.addEdgeAndVertices("3", null)); + + assertEquals(3, graph.addEdgeAndVertices("2", "323", 50)); + assertEquals(35, graph.addEdgeAndVertices("2aa", "323", 5)); + assertEquals(50, graph.addEdgeAndVertices("2", "323", 500)); + assertEquals(500, graph.addEdgeAndVertices("2", "323")); + assertEquals(5, graph.addEdgeAndVertices("2aa", "323")); + + graph.removeAllEdge(); + + assertFalse(graph.containsEdge("2", "323")); + assertEquals(0, graph.addEdgeAndVertices(new Edge<>("2", "323", 3))); + assertTrue(graph.containsEdge("2", "323")); + assertFalse(graph.containsEdge("2aa", "323")); + assertEquals(0, graph.addEdgeAndVertices(new Edge<>("2aa", "323", 35))); + assertTrue(graph.containsEdge("2aa", "323")); + assertFalse(graph.containsEdge("2bbb", "323bbb")); + assertEquals(0, 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(nullException, () -> graph.addEdgeAndVertices(null)); + + assertEquals(3, graph.addEdgeAndVertices(new Edge<>("2", "323", 50))); + assertEquals(35, graph.addEdgeAndVertices(new Edge<>("2aa", "323", 5))); + assertEquals(50, graph.addEdgeAndVertices(new Edge<>("2", "323", 500))); + + graph.removeAll(); + graph.add("aaa"); + graph.add("1"); + graph.add("2"); + + shouldContain(graph.vertices(), "1", "2", "aaa"); + shouldContain(graph.edges()); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void basicEdgeDir01(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 + * | | + * v v + * 3 <-> 5 -> 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5")); + + shouldThrow(nullException, () -> graph.removeAllOutEdge(null)); + shouldThrow(nullException, () -> graph.removeAllInEdge(null)); + shouldThrow(notException, () -> graph.removeAllInEdge("011")); + shouldThrow(notException, () -> graph.removeAllOutEdge("9")); + + graph.addEdge("1", "2"); + graph.addEdge("1", "3"); + graph.addEdge("2", "5"); + graph.addEdge("3", "5"); + graph.addEdge("5", "3"); + graph.addEdge("5", "4"); + + graph.removeAllOutEdge("1"); + assertFalse(graph.containsEdge("1", "2")); + assertFalse(graph.containsEdge("1", "3")); + assertEquals(4, graph.numberOfEdges()); + graph.addEdge("1", "2"); + graph.addEdge("1", "3"); + assertEquals(6, graph.numberOfEdges()); + + graph.removeAllInEdge("3"); + assertFalse(graph.containsEdge("5", "3")); + assertFalse(graph.containsEdge("1", "3")); + assertTrue(graph.containsEdge("3", "5")); + assertEquals(4, graph.numberOfEdges()); + graph.addEdge("1", "3"); + graph.addEdge("5", "3"); + assertEquals(6, graph.numberOfEdges()); + + graph.removeAllInEdge("5"); + assertFalse(graph.containsEdge("3", "5")); + assertFalse(graph.containsEdge("2", "5")); + assertTrue(graph.containsEdge("5", "3")); + assertTrue(graph.containsEdge("5", "4")); + assertEquals(4, graph.numberOfEdges()); + + graph.removeAll(); + 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 Edge<>("aaa", "bbb", 3), + new Edge<>("bbb", "ccc", 4), + new Edge<>("ccc", "aaa", 5), + new Edge<>("1", "2", 2)); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void basicEdgeDir02(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 -> 6 + * ^ + * | | | + * v v + * 3 <-> 5 -> 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6")); + shouldContain(graph.edges()); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 1); + graph.addEdge("2", "5", 4); + graph.addEdge("2", "6", 5); + graph.addEdge("3", "5", 2); + graph.addEdge("4", "6", 6); + graph.addEdge("5", "3", 9); + graph.addEdge("5", "4", 5); + + assertEquals(8, graph.numberOfEdges()); + + shouldContain(graph.getChildren("1"), "2", "3"); + shouldContain(graph.getChildren("2"), "5", "6"); + shouldContain(graph.getChildren("3"), "5"); + shouldContain(graph.getChildren("4"), "6"); + shouldContain(graph.getChildren("5"), "3", "4"); + shouldContain(graph.getChildren("6")); + + shouldContain(graph.getAncestors("1")); + shouldContain(graph.getAncestors("2"), "1"); + shouldContain(graph.getAncestors("3"), "1", "5"); + shouldContain(graph.getAncestors("4"), "5"); + shouldContain(graph.getAncestors("5"), "2", "3"); + shouldContain(graph.getAncestors("6"), "2", "4"); + + 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")); + assertEquals(2, graph.degreeIn("3")); + assertEquals(1, graph.degreeIn("4")); + assertEquals(2, graph.degreeIn("5")); + assertEquals(2, graph.degreeIn("6")); + + assertEquals(2, graph.degreeOut("1")); + assertEquals(2, graph.degreeOut("2")); + assertEquals(1, graph.degreeOut("3")); + assertEquals(1, graph.degreeOut("4")); + assertEquals(2, graph.degreeOut("5")); + assertEquals(0, graph.degreeOut("6")); + + assertEquals(2, graph.degree("1")); + assertEquals(3, graph.degree("2")); + assertEquals(3, graph.degree("3")); + assertEquals(2, graph.degree("4")); + assertEquals(4, graph.degree("5")); + assertEquals(2, graph.degree("6")); + + shouldContain(graph.edges(), + 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 Edge<>("2", "5", 4), + new Edge<>("3", "5", 2), + new Edge<>("5", "3", 9), + new Edge<>("5", "4", 5)); + + /* Weird case in the add */ + graph.addIfAbsent("2"); + shouldContain(graph.edges(), + 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)); + graph.add("2"); + shouldContain(graph.edges(), + new Edge<>("1", "3", 1), + new Edge<>("3", "5", 2), + new Edge<>("4", "6", 6), + new Edge<>("5", "3", 9), + new Edge<>("5", "4", 5)); + } + + @ParameterizedTest + @MethodSource("getGraphsUnDir") + public void basicEdgeUnDir01(GraphUndirected graph) { + /* + * This graph should be like this + * + * 1 - 2 + * | | + * 3 - 5 - 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5")); + + graph.addEdge("1", "2"); + graph.addEdge("1", "3"); + graph.addEdge("2", "5"); + graph.addEdge("3", "5"); + graph.addEdge("5", "4"); + assertEquals(5, graph.numberOfEdges()); + assertEquals(5, graph.size()); + + assertTrue(graph.containsEdge("1", "2")); + assertTrue(graph.containsEdge("1", "3")); + assertTrue(graph.containsEdge("2", "1")); + assertTrue(graph.containsEdge("3", "1")); + graph.removeAllEdge("1"); + assertEquals(5, graph.size()); + assertEquals(3, graph.numberOfEdges()); + assertFalse(graph.containsEdge("1", "2")); + assertFalse(graph.containsEdge("1", "3")); + assertFalse(graph.containsEdge("2", "1")); + assertFalse(graph.containsEdge("3", "1")); + + graph.addEdge("1", "2"); + graph.addEdge("1", "3"); + assertEquals(5, graph.size()); + assertEquals(5, graph.numberOfEdges()); + + assertTrue(graph.containsEdge("3", "5")); + assertTrue(graph.containsEdge("5", "3")); + assertTrue(graph.containsEdge("1", "3")); + assertTrue(graph.containsEdge("3", "1")); + graph.removeAllEdge("3"); + assertEquals(5, graph.size()); + assertEquals(3, graph.numberOfEdges()); + assertFalse(graph.containsEdge("5", "3")); + assertFalse(graph.containsEdge("3", "5")); + assertFalse(graph.containsEdge("1", "3")); + assertFalse(graph.containsEdge("3", "1")); + graph.addEdge("1", "3"); + graph.addEdge("5", "3"); + assertEquals(5, graph.size()); + assertEquals(5, graph.numberOfEdges()); + + assertTrue(graph.containsEdge("3", "5")); + assertTrue(graph.containsEdge("5", "3")); + assertTrue(graph.containsEdge("2", "5")); + assertTrue(graph.containsEdge("5", "2")); + assertTrue(graph.containsEdge("4", "5")); + assertTrue(graph.containsEdge("5", "4")); + graph.removeAllEdge("5"); + assertEquals(5, graph.size()); + assertEquals(2, graph.numberOfEdges()); + assertFalse(graph.containsEdge("3", "5")); + assertFalse(graph.containsEdge("5", "3")); + assertFalse(graph.containsEdge("2", "5")); + assertFalse(graph.containsEdge("5", "2")); + assertFalse(graph.containsEdge("4", "5")); + assertFalse(graph.containsEdge("5", "4")); + + graph.removeAll(); + assertEquals(0, graph.size()); + assertEquals(0, graph.numberOfEdges()); + + 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"); + shouldContainUnDir(graph.edges(), + new Edge<>("aaa", "bbb", 3), + new Edge<>("bbb", "ccc", 4), + new Edge<>("ccc", "aaa", 5), + new Edge<>("1", "2", 2)); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void basicVisitDir(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "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.visit(null, new DFS<>(), null)); + shouldThrow(nullException, () -> graph.visit(null, null, null)); + shouldThrow(nullException, () -> graph.visit("1", null, null)); + + shouldThrow(notException, () -> graph.visit("1010", new DFS<>(), null)); + + DFS dfs = new DFS<>(); + VisitInfo visitDFS = graph.visit("1", dfs, null); + assertEquals(0, visitDFS.getTimeDiscover("1")); + assertEquals(1, visitDFS.getTimeDiscover("2")); + assertEquals(2, visitDFS.getTimeDiscover("5")); + assertEquals(3, visitDFS.getTimeDiscover("3")); + assertEquals(4, visitDFS.getTimeVisit("3")); + assertEquals(5, visitDFS.getTimeDiscover("4")); + assertEquals(6, visitDFS.getTimeDiscover("6")); + assertEquals(7, visitDFS.getTimeVisit("6")); + assertEquals(8, visitDFS.getTimeVisit("4")); + assertEquals(9, visitDFS.getTimeVisit("5")); + assertEquals(10, visitDFS.getTimeVisit("2")); + assertEquals(11, visitDFS.getTimeVisit("1")); + 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(); + }); + + String[] vertices = {"1", "2", "5", "3", "3", "4", "6", "6", "4", "5", "2", "1"}; + boolean[] found = new boolean[graph.size()]; + integer.set(0); + visitDFS.forEach(vertexInfo -> { + int i = integer.get(); + assertEquals(vertices[i], vertexInfo.vertex, "Iter " + i); + int vert = Integer.parseInt(vertexInfo.vertex); + + if (found[vert]) + assertEquals(i, vertexInfo.timeVisited, "Iter " + i); + else { + assertEquals(i, vertexInfo.timeDiscovered, "Iter " + i); + found[vert] = true; + } + + integer.incrementAndGet(); + }); + + BFS bfs = new BFS<>(); + VisitInfo visitBFS = graph.visit("1", bfs, null); + assertEquals(0, visitBFS.getTimeDiscover("1")); + assertEquals(1, visitBFS.getTimeVisit("1")); + assertEquals(2, visitBFS.getTimeDiscover("2")); + assertEquals(3, visitBFS.getTimeVisit("2")); + assertEquals(4, visitBFS.getTimeDiscover("3")); + assertEquals(5, visitBFS.getTimeVisit("3")); + assertEquals(6, visitBFS.getTimeDiscover("5")); + assertEquals(7, visitBFS.getTimeVisit("5")); + assertEquals(8, visitBFS.getTimeDiscover("4")); + assertEquals(9, visitBFS.getTimeVisit("4")); + assertEquals(10, visitBFS.getTimeDiscover("6")); + assertEquals(11, visitBFS.getTimeVisit("6")); + assertFalse(visitBFS.isDiscovered("7")); + assertFalse(visitBFS.isDiscovered("8")); + } + + @ParameterizedTest + @MethodSource("getGraphsUnDir") + public void basicVisitUnDir(GraphUndirected graph) { + /* + * This graph should be like this + * + * 1 - 2 6 7 + * | | | | + * 3 - 5 - 4 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "8")); + + graph.addEdge("1", "2"); + graph.addEdge("1", "3"); + graph.addEdge("2", "5"); + graph.addEdge("3", "5"); + graph.addEdge("4", "6"); + graph.addEdge("5", "4"); + graph.addEdge("7", "8"); + + shouldThrow(nullException, () -> graph.visit(null, new DFS<>(), null)); + shouldThrow(nullException, () -> graph.visit(null, null, null)); + shouldThrow(nullException, () -> graph.visit("1", null, null)); + shouldThrow(notException, () -> graph.visit("1010", new DFS<>(), null)); + + DFS dfs = new DFS<>(); + VisitInfo visitDFS = graph.visit("1", dfs, null); + assertEquals(0, visitDFS.getTimeDiscover("1")); + assertEquals(1, visitDFS.getTimeDiscover("2")); + assertEquals(2, visitDFS.getTimeDiscover("5")); + assertEquals(3, visitDFS.getTimeDiscover("3")); + assertEquals(4, visitDFS.getTimeVisit("3")); + assertEquals(5, visitDFS.getTimeDiscover("4")); + assertEquals(6, visitDFS.getTimeDiscover("6")); + assertEquals(7, visitDFS.getTimeVisit("6")); + assertEquals(8, visitDFS.getTimeVisit("4")); + assertEquals(9, visitDFS.getTimeVisit("5")); + assertEquals(10, visitDFS.getTimeVisit("2")); + assertEquals(11, visitDFS.getTimeVisit("1")); + 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(); + }); + + String[] vertices = {"1", "2", "5", "3", "3", "4", "6", "6", "4", "5", "2", "1"}; + boolean[] found = new boolean[graph.size()]; + integer.set(0); + visitDFS.forEach(vertexInfo -> { + int i = integer.get(); + assertEquals(vertices[i], vertexInfo.vertex, "Iter " + i); + int vert = Integer.parseInt(vertexInfo.vertex); + + if (found[vert]) + assertEquals(i, vertexInfo.timeVisited, "Iter " + i); + else { + assertEquals(i, vertexInfo.timeDiscovered, "Iter " + i); + found[vert] = true; + } + + integer.incrementAndGet(); + }); + + BFS bfs = new BFS<>(); + VisitInfo visitBFS = graph.visit("1", bfs, null); + assertEquals(0, visitBFS.getTimeDiscover("1")); + assertEquals(1, visitBFS.getTimeVisit("1")); + assertEquals(2, visitBFS.getTimeDiscover("2")); + assertEquals(3, visitBFS.getTimeVisit("2")); + assertEquals(4, visitBFS.getTimeDiscover("3")); + assertEquals(5, visitBFS.getTimeVisit("3")); + assertEquals(6, visitBFS.getTimeDiscover("5")); + assertEquals(7, visitBFS.getTimeVisit("5")); + assertEquals(8, visitBFS.getTimeDiscover("4")); + assertEquals(9, visitBFS.getTimeVisit("4")); + assertEquals(10, visitBFS.getTimeDiscover("6")); + assertEquals(11, visitBFS.getTimeVisit("6")); + assertFalse(visitBFS.isDiscovered("7")); + assertFalse(visitBFS.isDiscovered("8")); + } + + @ParameterizedTest + @MethodSource("getGraphs") + public void iterable(Graph graph) { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "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); + + Set vertices = new HashSet<>(); + + Iterator iter = graph.iterator(); + assertNotNull(iter, "This should not be null!"); + while (iter.hasNext()) + vertices.add(iter.next()); + shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); + vertices.clear(); + + for (String vertex : graph) + vertices.add(vertex); + shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); + + vertices.clear(); + graph.forEach(vertices::add); + shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); + } + + //TODO tests for GraphUndirected cc + @ParameterizedTest + @MethodSource("getGraphsDir") + public void scc(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 -> 6 + * ^ + * | | | + * v v + * 3 <-> 5 -> 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6")); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 1); + graph.addEdge("2", "5", 4); + graph.addEdge("2", "6", 5); + graph.addEdge("3", "5", 2); + graph.addEdge("4", "6", 6); + graph.addEdge("5", "3", 9); + graph.addEdge("5", "4", 5); + + shouldContain(graph.stronglyConnectedComponents(), new HashSet<>(Collections.singletonList("6")), new HashSet<>(Arrays.asList("3", "5")), new HashSet<>(Collections.singletonList("4")), new HashSet<>(Collections.singletonList("1")), new HashSet<>(Collections.singletonList("2"))); + graph.removeAll(); + + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + graph.addIfAbsent("1"); + graph.addIfAbsent("2"); + graph.addIfAbsent("3"); + graph.addIfAbsent("4"); + graph.addIfAbsent("5"); + graph.addIfAbsent("6"); + graph.addIfAbsent("7"); + graph.addIfAbsent("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); + + shouldContain(graph.stronglyConnectedComponents(), new HashSet<>(Arrays.asList("7", "8")), new HashSet<>(Arrays.asList("2", "5", "4", "6")), new HashSet<>(Collections.singletonList("3")), new HashSet<>(Collections.singletonList("1"))); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void cyclic(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 -> 6 + * ^ + * | | | + * v v + * 3 -> 5 -> 4 + */ + + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + + graph.addAll(List.of("1", "2", "3", "4", "5", "6")); + + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 1); + graph.addEdge("2", "5", 4); + graph.addEdge("2", "6", 5); + graph.addEdge("3", "5", 2); + graph.addEdge("4", "6", 6); + graph.addEdge("5", "4", 5); + + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.removeAll(); + + /* + * This graph should be like this + * + * 1 -> 2 <- 6 + * ^ + * | | | + * v v + * 3 <- 5 -> 4 + */ + graph.addIfAbsent("1"); + graph.addIfAbsent("2"); + graph.addIfAbsent("3"); + graph.addIfAbsent("4"); + graph.addIfAbsent("5"); + graph.addIfAbsent("6"); + + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + + graph.addEdge("1", "2", 1); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("1", "3", 1); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("2", "5", 4); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("4", "6", 5); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("5", "3", 6); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("5", "4", 3); + assertFalse(graph.isCyclic()); + assertTrue(graph.isDAG()); + graph.addEdge("6", "2", 2); + assertTrue(graph.isCyclic()); + assertFalse(graph.isDAG()); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void transpose(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "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 transposed = graph.transpose(); + assertNotNull(transposed, "This should not be null!"); + + DFS dfs = new DFS<>(); + VisitInfo visitDFS = transposed.visit("6", dfs, null); + assertEquals(0, visitDFS.getTimeDiscover("6")); + assertEquals(1, visitDFS.getTimeDiscover("4")); + assertEquals(2, visitDFS.getTimeDiscover("5")); + assertEquals(3, visitDFS.getTimeDiscover("2")); + assertEquals(4, visitDFS.getTimeDiscover("1")); + assertEquals(5, visitDFS.getTimeVisit("1")); + assertEquals(6, visitDFS.getTimeVisit("2")); + assertEquals(7, visitDFS.getTimeVisit("5")); + assertEquals(8, visitDFS.getTimeVisit("4")); + assertEquals(9, visitDFS.getTimeVisit("6")); + assertFalse(visitDFS.isDiscovered("3")); + assertFalse(visitDFS.isDiscovered("7")); + assertFalse(visitDFS.isDiscovered("8")); + + visitDFS = transposed.visit("8", dfs, null); + assertEquals(0, visitDFS.getTimeDiscover("8")); + assertEquals(1, visitDFS.getTimeDiscover("7")); + assertEquals(2, visitDFS.getTimeVisit("7")); + assertEquals(3, visitDFS.getTimeVisit("8")); + assertFalse(visitDFS.isDiscovered("1")); + assertFalse(visitDFS.isDiscovered("2")); + assertFalse(visitDFS.isDiscovered("3")); + assertFalse(visitDFS.isDiscovered("4")); + assertFalse(visitDFS.isDiscovered("5")); + assertFalse(visitDFS.isDiscovered("6")); + } + + @ParameterizedTest + @MethodSource("getGraphsDir") + public void topologicalSort(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 -> 6 + * ^ + * | | | + * v v + * 3 -> 5 -> 4 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6")); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 1); + graph.addEdge("2", "5", 4); + graph.addEdge("2", "6", 5); + graph.addEdge("3", "5", 2); + graph.addEdge("4", "6", 6); + graph.addEdge("5", "4", 5); + + shouldContainInOneOrder(graph.topologicalSort(), + new String[]{"1", "2", "3", "5", "4", "6"}, + new String[]{"1", "3", "2", "5", "4", "6"}); + } + + //TODO tests for GraphUndirected distanceVV + @ParameterizedTest + @MethodSource("getGraphsDir") + public void distanceVV(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ + * | | | | + * v v v + * 3 <- 5 -> 4 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "8")); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 10); + graph.addEdge("2", "5", 4); + graph.addEdge("4", "6", 5); + graph.addEdge("5", "3", 3); + graph.addEdge("5", "4", 3); + graph.addEdge("6", "2", 2); + graph.addEdge("7", "8", 8); + + List> distance = graph.distance("1", "6"); + assertNotNull(distance, "This should not be null!"); + int sum = distance.stream().mapToInt(Edge::getWeight).sum(); + assertEquals(13, sum); + shouldContainInOrder(distance, + 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(Edge::getWeight).sum(); + assertEquals(8, sum); + shouldContainInOrder(distance, + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "3", 3)); + + shouldContainInOrder(graph.distance("7", "8"), new Edge<>("7", "8", 8)); + + shouldThrow(nullException, () -> graph.distance(null, "1")); + shouldThrow(nullException, () -> graph.distance(null, null)); + shouldThrow(nullException, () -> graph.distance("1", null)); + shouldThrow(notException, () -> graph.distance("34", "1")); + shouldThrow(notException, () -> graph.distance("2", "36")); + shouldThrow(notException, () -> graph.distance("689", "374")); + shouldThrow(notConnException, () -> graph.distance("1", "7")); + shouldThrow(notConnException, () -> graph.distance("3", "2")); + } + + //TODO tests for GraphUndirected distanceVtoAll + @ParameterizedTest + @MethodSource("getGraphsDir") + public void distanceVtoAll(GraphDirected graph) { + /* + * This graph should be like this + * + * 1 -> 2 <- 6 7 + * ^ + * | | | | + * v v v + * 3 <- 5 -> 4 -> 8 + */ + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "8")); + + graph.addEdge("1", "2", 1); + graph.addEdge("1", "3", 10); + graph.addEdge("2", "5", 4); + graph.addEdge("4", "6", 5); + graph.addEdge("4", "8", 2); + graph.addEdge("5", "3", 3); + graph.addEdge("5", "4", 3); + graph.addEdge("6", "2", 2); + graph.addEdge("7", "8", 8); + + Map>> distance = graph.distance("1"); + assertNotNull(distance, "This should not be null!"); + assertNull(distance.get("1")); + shouldContainInOrder(distance.get("2"), + new Edge<>("1", "2", 1)); + shouldContainInOrder(distance.get("3"), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "3", 3)); + shouldContainInOrder(distance.get("4"), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3)); + shouldContainInOrder(distance.get("5"), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4)); + shouldContainInOrder(distance.get("6"), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3), + new Edge<>("4", "6", 5)); + assertNull(distance.get("7")); + shouldContainInOrder(distance.get("8"), + new Edge<>("1", "2", 1), + new Edge<>("2", "5", 4), + new Edge<>("5", "4", 3), + new Edge<>("4", "8", 2)); + } + + //TODO tests for GraphUndirected subgraph + @ParameterizedTest + @MethodSource("getGraphsDir") + public void subGraphDir(GraphDirected graph) { /* * This graph should look like this * @@ -1127,15 +1434,7 @@ public class TestGraph { * v v * 3 <- 5 -> 4 8 */ - - graph.addIfAbsent("1"); - graph.addIfAbsent("2"); - graph.addIfAbsent("3"); - graph.addIfAbsent("4"); - graph.addIfAbsent("5"); - graph.addIfAbsent("6"); - graph.addIfAbsent("7"); - graph.addIfAbsent("8"); + graph.addAll(List.of("1", "2", "3", "4", "5", "6", "7", "8")); graph.addEdge("1", "2", 1); graph.addEdge("1", "3", 1); @@ -1164,7 +1463,7 @@ public class TestGraph { graph.mark("5", "z"); graph.mark("4", "z"); - Graph sub = graph.subGraph("1", -541); + Graph sub = graph.subGraph("1", -541); assertNotNull(sub, "This should not be null!"); shouldContain(sub.vertices(), "1"); shouldContain(sub.edges()); @@ -1243,33 +1542,33 @@ public class TestGraph { new Edge<>("5", "3", 2), new Edge<>("5", "4", 5), new Edge<>("6", "2", 2)); - + sub = graph.subGraph(); shouldContain(sub.vertices(), "7", "8"); shouldContain(sub.edges(), new Edge<>("8", "7", 9)); - //noinspection ConfusingArgumentToVarargsMethod sub = graph.subGraph(null); shouldContain(sub.vertices(), "7", "8"); shouldContain(sub.edges(), new Edge<>("8", "7", 9)); } - @Test - public void vertexClass() { + @ParameterizedTest + @MethodSource("getGraphsDir") + public void vertexClass(GraphDirected graph) { Vertex vertex = new Vertex<>(graph, "stronzo"); - assertEquals("stronzo", vertex.getValue()); + assertEquals("stronzo", vertex.get()); assertEquals(0, graph.size()); - shouldThrow(unSuppException, () -> vertex.addChild(null, null)); + shouldThrow(unSuppException, () -> vertex.addChild(null)); + shouldThrow(unSuppException, () -> vertex.addChild(null, 3)); shouldThrow(unSuppException, () -> vertex.mark(null)); shouldThrow(unSuppException, () -> vertex.removeChild(null)); + shouldThrow(unSuppException, () -> vertex.getChildWeight(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); @@ -1285,53 +1584,52 @@ public class TestGraph { shouldThrow(nullException, () -> graph.get(null)); shouldThrow(notException, () -> graph.get("stronzo1")); + shouldThrow(nullException, () -> vertex.addChild(null)); shouldThrow(nullException, () -> vertex.addChild(null, 3)); - shouldThrow(nullException, () -> vertex.addChild(null, null)); shouldThrow(nullException, () -> vertex.mark(null)); shouldThrow(nullException, () -> vertex.unMark(null)); shouldThrow(nullException, () -> vertex.removeChild(null)); - shouldThrow(new NullPointerException(), () -> vertex.visit(null, null)); + shouldThrow(nullException, () -> vertex.getChildWeight(null)); + shouldThrow(nullException, () -> vertex.visit(null, null)); - shouldThrow(notException, () -> vertex.addChild("1", null)); + shouldThrow(notException, () -> vertex.addChild("1")); + shouldThrow(notException, () -> vertex.addChild("1", 3)); shouldThrow(notException, () -> vertex.addChild("ssdsad", 2)); shouldThrow(notException, () -> vertex.removeChild("234")); + shouldThrow(notException, () -> vertex.getChildWeight("73")); shouldContain(vertex.getMarks()); shouldContain(vertex.getAncestors()); shouldContain(vertex.getChildren()); shouldContain(vertex.getChildrenAsVertex()); - shouldContain(vertex.getEdgesIn()); - shouldContain(vertex.getEdgesOut()); - graph.add("1"); - graph.add("2"); - graph.add("3"); + graph.addAll(List.of("1", "2", "3")); + + assertEquals(0, vertex.getChildWeight("1")); + assertEquals(0, vertex.getChildWeight("2")); + assertEquals(0, vertex.getChildWeight("3")); + assertEquals(0, vertex.getChildWeight("stronzo")); graph.addEdge("1", "2", 2); graph.addEdge("3", "stronzo", 6); graph.addEdge("stronzo", "2", 1); graph.addEdge("stronzo", "1", 3); + assertEquals(3, vertex.getChildWeight("1")); + assertEquals(1, vertex.getChildWeight("2")); + assertEquals(0, vertex.getChildWeight("3")); + assertEquals(0, vertex.getChildWeight("stronzo")); + 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"); + shouldContain(graph.getMarks(vertex.get()), "ciao", "ciao2"); vertex.unMark(); shouldContain(vertex.getMarks()); vertex.mark("cio"); @@ -1347,26 +1645,22 @@ public class TestGraph { 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())); + assertFalse(graph.contains(vertex.get())); assertEquals(3, graph.size()); - shouldThrow(unSuppException, () -> vertex.addChild(null, null)); + shouldThrow(unSuppException, () -> vertex.addChild(null)); + shouldThrow(unSuppException, () -> vertex.addChild(null, 3)); shouldThrow(unSuppException, () -> vertex.mark(null)); shouldThrow(unSuppException, () -> vertex.removeChild(null)); + shouldThrow(unSuppException, () -> vertex.getChildWeight(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); @@ -1375,9 +1669,11 @@ public class TestGraph { assertEquals(4, graph.size()); } - @Test - public void saveLoad() { - /* + //TODO tests for GraphUndirected save/load + @ParameterizedTest + @MethodSource("getGraphsDir") + public void saveLoadDir(final GraphDirected graph) { + /* * This graph should be like this * * 1 -> 2 <- 6 7 @@ -1387,99 +1683,109 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - String fileName = "test/resources/test.json"; - Set vertices = new HashSet<>(); - Set> edges = new HashSet<>(); - Map> marks = new HashMap<>(); - Set temp = new HashSet<>(); - - vertices.add("1"); - vertices.add("2"); - vertices.add("3"); - vertices.add("4"); - vertices.add("5"); - vertices.add("6"); - vertices.add("7"); - vertices.add("8"); + String fileName = "test/resources/test.json"; + Set vertices = Set.of("1", "2", "3", "4", "5", "6", "7", "8"); + Set> edges = new HashSet<>(); + Map> marks = new HashMap<>(); + Set temp = new HashSet<>(); + + edges.add(new Edge<>("1", "2", 1)); + edges.add(new Edge<>("1", "3", 1)); + edges.add(new Edge<>("2", "5", 4)); + edges.add(new Edge<>("4", "6", 6)); + edges.add(new Edge<>("5", "3", 2)); + edges.add(new Edge<>("5", "4", 5)); + edges.add(new Edge<>("6", "2", 2)); + edges.add(new Edge<>("8", "7", 9)); + + temp.add(1); + marks.put("2", new HashSet<>(temp)); + temp.add("blue"); + marks.put("1", new HashSet<>(temp)); + marks.put("7", new HashSet<>(temp)); + temp.remove(1); + temp.add(4.0); + marks.put("4", new HashSet<>(temp)); + marks.put("8", new HashSet<>(temp)); + temp.remove(4.0); + temp.add("red"); + marks.put("5", new HashSet<>(temp)); + temp.remove("blue"); + marks.put("6", new HashSet<>(temp)); + temp.clear(); - edges.add(new Edge<>("1", "2", 1)); - edges.add(new Edge<>("1", "3", 1)); - edges.add(new Edge<>("2", "5", 4)); - edges.add(new Edge<>("4", "6", 6)); - edges.add(new Edge<>("5", "3", 2)); - edges.add(new Edge<>("5", "4", 5)); - edges.add(new Edge<>("6", "2", 2)); - edges.add(new Edge<>("8", "7", 9)); - - temp.add(1); - marks.put("2", new HashSet<>(temp)); - temp.add("blue"); - marks.put("1", new HashSet<>(temp)); - marks.put("7", new HashSet<>(temp)); - temp.remove(1); - temp.add(4.0); - marks.put("4", new HashSet<>(temp)); - marks.put("8", new HashSet<>(temp)); - temp.remove(4.0); - temp.add("red"); - marks.put("5", new HashSet<>(temp)); - temp.remove("blue"); - marks.put("6", new HashSet<>(temp)); - temp.clear(); - graph.addAll(vertices); graph.addAllEdges(edges); marks.forEach((v, m) -> m.forEach(mk -> graph.mark(v, mk))); - GraphSaveStructure struct = new GraphSaveStructure<>(); - - try { - struct.save(graph, fileName); - struct.load(graph, fileName, String.class, Integer.class); - shouldContain(graph.vertices(), vertices.toArray()); - shouldContain(graph.edges(), edges.toArray()); - //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); - - graph.removeAll(); - struct.load(graph, fileName, String.class, Integer.class); - shouldContain(graph.vertices(), vertices.toArray()); - shouldContain(graph.edges(), edges.toArray()); - //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); - } catch (Exception e) { - e.printStackTrace(System.err); - fail(e.getMessage()); - } - + GraphSaveStructure struct = new GraphSaveStructure<>(); try { - struct.load(graph, "sadadafacensi", String.class, Integer.class); - fail("Should have been thrown IOException"); - } catch (Exception e) { - if (!(e instanceof IOException)) - fail("Should have been thrown IOException " + e.getMessage()); - } - - shouldContain(graph.vertices(), vertices.toArray()); - shouldContain(graph.edges(), edges.toArray()); - //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); - + struct.save(graph, fileName); + struct.load(graph, fileName, String.class); + shouldContain(graph.vertices(), vertices.toArray()); + shouldContain(graph.edges(), edges.toArray()); + //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); + + graph.removeAll(); + struct.load(graph, fileName, String.class); + shouldContain(graph.vertices(), vertices.toArray()); + shouldContain(graph.edges(), edges.toArray()); + //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); + } catch (Exception e) { + e.printStackTrace(System.err); + fail(e.getMessage()); + } + + try { - struct.load(graph, fileName + ".fail", String.class, Integer.class); - fail("Should have been thrown JsonSyntaxException"); - } catch (Exception e) { - if (!(e instanceof JsonSyntaxException)) - fail("Should have been thrown JsonSyntaxException " + e.getMessage()); - } - - graph = null; - shouldThrow(new NullPointerException(), () -> { try { - struct.load(graph, fileName, String.class, Integer.class); - } catch (IOException e) { - fail(); - e.printStackTrace(); - } }); + struct.load(graph, "sadadafacensi", String.class); + fail("Should have been thrown IOException"); + } catch (Exception e) { + if (!(e instanceof IOException)) + fail("Should have been thrown IOException " + e.getMessage()); + } + + shouldContain(graph.vertices(), vertices.toArray()); + shouldContain(graph.edges(), edges.toArray()); + //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); + + try { + struct.load(graph, fileName + ".fail", String.class); + fail("Should have been thrown JsonSyntaxException"); + } catch (Exception e) { + if (!(e instanceof JsonSyntaxException)) + fail("Should have been thrown JsonSyntaxException " + e.getMessage()); + } + + shouldThrow(nullException, () -> { + try { + struct.load(null, fileName, String.class); + } catch (IOException e) { + fail(); + e.printStackTrace(); + } + }); + } + + + @SafeVarargs + private void shouldContainUnDir(Collection> actual, Edge... expected) { + assertNotNull(actual, "You should pass me a collection!"); + assertEquals(expected.length, actual.size(), "They have not the same number of elements\nActual: " + actual); + + for (Edge edge : expected) { + Edge found = null; + for (Edge edgeAc : actual) { + Collection vert = edgeAc.getVertices(); + if (vert.contains(edge.getSource()) && vert.contains(edge.getDestination())) + found = edgeAc; + } + + if (found == null) + fail("The undirected edge " + edge + " couldn't be found in " + actual); + assertEquals(edge.getWeight(), found.getWeight()); + } } - - private void shouldContain(Collection actual, Object... expected) { assertNotNull(actual, "You should pass me a collection!"); @@ -1490,11 +1796,23 @@ public class TestGraph { } private void shouldContainInOrder(List actual, Object... expected) { - assertNotNull(actual, "You should pass me a list!"); - assertEquals(expected.length, actual.size(), "They have not the same number of elements\nActual: " + actual); + shouldContainInOneOrder(actual, expected); + } - for (int i = 0; i < actual.size(); i++) - assertEquals(expected[i], actual.get(i), "Index: " + i); + private void shouldContainInOneOrder(List actual, Object[]... expected) { + assertNotNull(actual, "You should pass me a list!"); + + boolean ok = false; + for (int j = 0; j < expected.length && !ok; j++) { + Object[] probable = expected[j]; + assertEquals(probable.length, actual.size(), "They have not the same number of elements\nActual: " + actual); + + boolean check = false; + for (int i = 0; i < actual.size() && !check; i++) + check = Objects.equals(probable[i], actual.get(i)); + ok = check; + } + assertTrue(ok, "The list passed doesn't match any expected arrays\nList: " + actual); } private void shouldThrow(Exception expected, Runnable runnable) { diff --git a/test/resources/test.json b/test/resources/test.json index 3862700..624e195 100644 --- a/test/resources/test.json +++ b/test/resources/test.json @@ -1 +1,54 @@ -{"gson":{"calls":{"threadLocalHashCode":865977613},"typeTokenCache":{"com.google.gson.reflect.TypeToken\u003c?\u003e":{},"com.google.gson.InstanceCreator\u003c?\u003e":{},"java.util.Map\u003ccom.google.gson.reflect.TypeToken\u003c?\u003e, com.google.gson.TypeAdapter\u003c?\u003e\u003e":{},"java.lang.Class\u003c?\u003e":{},"com.google.gson.internal.reflect.ReflectionAccessor":{},"com.google.gson.internal.ConstructorConstructor":{},"java.util.List\u003ccom.google.gson.ExclusionStrategy\u003e":{},"com.google.gson.internal.Excluder":{},"java.util.Map\u003cjava.lang.reflect.Type, com.google.gson.InstanceCreator\u003c?\u003e\u003e":{},"com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory":{},"berack96.lib.graph.models.EdgeSaveStructure":{},"berack96.lib.graph.models.EdgeSaveStructure[]":{},"java.util.List\u003ccom.google.gson.TypeAdapterFactory\u003e":{},"com.google.gson.FieldNamingStrategy":{},"com.google.gson.Gson":{},"double":{},"java.lang.String":{},"java.lang.String[]":{},"java.lang.reflect.Type":{},"int":{},"com.google.gson.ExclusionStrategy":{},"com.google.gson.TypeAdapter\u003c?\u003e":{},"java.lang.Integer":{},"com.google.gson.TypeAdapterFactory":{},"java.lang.ThreadLocal\u003cjava.util.Map\u003ccom.google.gson.reflect.TypeToken\u003c?\u003e, com.google.gson.Gson$FutureTypeAdapter\u003c?\u003e\u003e\u003e":{},"boolean":{},"com.google.gson.LongSerializationPolicy":{},"berack96.lib.graph.models.GraphSaveStructure":{}},"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}},"jsonAdapterFactory":{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}}},"factories":[null,null,{"version":-1.0,"modifiers":136,"serializeInnerClasses":true,"requireExpose":false,"serializationStrategies":[],"deserializationStrategies":[]},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}}},{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}},"complexMapKeySerialization":false},{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}}},null,{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}},"fieldNamingPolicy":"IDENTITY","excluder":{"version":-1.0,"modifiers":136,"serializeInnerClasses":true,"requireExpose":false,"serializationStrategies":[],"deserializationStrategies":[]},"jsonAdapterFactory":{"constructorConstructor":{"instanceCreators":{},"accessor":{"theUnsafe":{}}}},"accessor":{"theUnsafe":{}}}],"excluder":{"version":-1.0,"modifiers":136,"serializeInnerClasses":true,"requireExpose":false,"serializationStrategies":[],"deserializationStrategies":[]},"fieldNamingStrategy":"IDENTITY","instanceCreators":{},"serializeNulls":false,"complexMapKeySerialization":false,"generateNonExecutableJson":false,"htmlSafe":true,"prettyPrinting":false,"lenient":false,"serializeSpecialFloatingPointValues":false,"dateStyle":2,"timeStyle":2,"longSerializationPolicy":"DEFAULT","builderFactories":[],"builderHierarchyFactories":[]},"vertices":["\"1\"","\"2\"","\"3\"","\"4\"","\"5\"","\"6\"","\"7\"","\"8\""],"edges":[{"src":"\"1\"","dest":"\"2\"","weight":"1"},{"src":"\"1\"","dest":"\"3\"","weight":"1"},{"src":"\"5\"","dest":"\"4\"","weight":"5"},{"src":"\"6\"","dest":"\"2\"","weight":"2"},{"src":"\"5\"","dest":"\"3\"","weight":"2"},{"src":"\"8\"","dest":"\"7\"","weight":"9"},{"src":"\"4\"","dest":"\"6\"","weight":"6"},{"src":"\"2\"","dest":"\"5\"","weight":"4"}]} \ No newline at end of file +{ + "vertices": [ + "\"6\"", + "\"5\"", + "\"4\"", + "\"3\"", + "\"2\"", + "\"1\"", + "\"8\"", + "\"7\"" + ], + "edges": [ + { + "src": "\"4\"", + "dest": "\"6\"", + "weight": 6 + }, + { + "src": "\"2\"", + "dest": "\"5\"", + "weight": 4 + }, + { + "src": "\"5\"", + "dest": "\"4\"", + "weight": 5 + }, + { + "src": "\"5\"", + "dest": "\"3\"", + "weight": 2 + }, + { + "src": "\"1\"", + "dest": "\"3\"", + "weight": 1 + }, + { + "src": "\"6\"", + "dest": "\"2\"", + "weight": 2 + }, + { + "src": "\"1\"", + "dest": "\"2\"", + "weight": 1 + }, + { + "src": "\"8\"", + "dest": "\"7\"", + "weight": 9 + } + ] +} \ No newline at end of file