From 7f16252890f95d979a277045270689af180c7c62 Mon Sep 17 00:00:00 2001 From: Giacomo Bertolazzi <20015159@studenti.uniupo.it> Date: Wed, 6 Jan 2021 16:28:01 +0100 Subject: [PATCH] Updating graph * Implemented MatrixGraph * Changed save/load logics (test not passing) --- build.gradle | 15 +- src/berack96/lib/graph/Graph.java | 133 +--- src/berack96/lib/graph/Vertex.java | 14 +- src/berack96/lib/graph/impl/AGraph.java | 579 ++++++++++++++++++ src/berack96/lib/graph/impl/AdjGraph.java | 319 ---------- src/berack96/lib/graph/impl/ListGraph.java | 144 +++++ src/berack96/lib/graph/impl/MapGraph.java | 560 ++--------------- src/berack96/lib/graph/impl/MatrixGraph.java | 353 +++-------- .../lib/graph/models/EdgeSaveStructure.java | 1 - .../lib/graph/models/GraphSaveStructure.java | 124 +++- .../lib/graph/models/MarkSaveStructure.java | 1 - src/berack96/lib/graph/view/GraphInfo.java | 36 +- .../lib/graph/view/GraphListener.java | 4 +- src/berack96/lib/graph/view/GraphPanel.java | 99 +-- .../lib/graph/view/GraphPointsSave.java | 54 ++ src/berack96/lib/graph/view/GraphWindow.java | 22 +- .../lib/graph/view/VisitListener.java | 6 +- .../graph/view/vertex/VertexComponent.java | 6 +- .../graph/view/vertex/VertexIntListener.java | 18 +- .../lib/graph/view/vertex/VertexView.java | 6 +- src/berack96/lib/graph/visit/impl/BFS.java | 8 +- src/berack96/lib/graph/visit/impl/DFS.java | 8 +- src/berack96/lib/graph/visit/impl/Depth.java | 53 ++ src/berack96/lib/graph/visit/impl/Tarjan.java | 10 +- .../lib/graph/visit/impl/VisitInfo.java | 351 ++++++----- test/berack96/test/lib/TestGraph.java | 523 ++++++++-------- test/resources/test.json | 2 +- 27 files changed, 1678 insertions(+), 1771 deletions(-) create mode 100644 src/berack96/lib/graph/impl/AGraph.java delete mode 100644 src/berack96/lib/graph/impl/AdjGraph.java create mode 100644 src/berack96/lib/graph/impl/ListGraph.java create mode 100644 src/berack96/lib/graph/view/GraphPointsSave.java create mode 100644 src/berack96/lib/graph/visit/impl/Depth.java diff --git a/build.gradle b/build.gradle index bc2999e..58ef372 100644 --- a/build.gradle +++ b/build.gradle @@ -14,13 +14,9 @@ test { useJUnitPlatform() } -sourceSets { - main { - java { - srcDirs "src" - srcDirs "test" - } - } +sourceSets.main.java { + srcDirs "src" + srcDirs "test" } repositories { @@ -28,8 +24,9 @@ repositories { } dependencies { - compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + implementation 'org.junit.jupiter:junit-jupiter:5.5.2' + compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5' /*compile group: 'commons-collections', name: 'commons-collections', version: '3.2'*/ - testCompile 'junit:junit:4.4' + testCompile 'junit:junit:5.5.2' } \ No newline at end of file diff --git a/src/berack96/lib/graph/Graph.java b/src/berack96/lib/graph/Graph.java index 65ee5b0..4a300e1 100644 --- a/src/berack96/lib/graph/Graph.java +++ b/src/berack96/lib/graph/Graph.java @@ -1,21 +1,13 @@ package berack96.lib.graph; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import berack96.lib.graph.visit.VisitStrategy; +import berack96.lib.graph.visit.impl.VisitInfo; + import java.util.Collection; import java.util.List; import java.util.Map; import java.util.function.Consumer; -import com.google.gson.Gson; -import com.google.gson.JsonSyntaxException; - -import berack96.lib.graph.models.GraphSaveStructure; -import berack96.lib.graph.visit.VisitStrategy; -import berack96.lib.graph.visit.impl.VisitInfo; - /** * An interface for the graphs.
* This interface is used for the graphs with Directed edges.
@@ -27,11 +19,10 @@ import berack96.lib.graph.visit.impl.VisitInfo; */ public interface Graph extends Iterable { - final String NOT_DAG = "The graph is not a DAG"; - final String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination"; - final String PARAM_NULL = "The parameter must not be null"; - final String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph"; - final Gson GSON = new Gson(); + 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.
@@ -70,16 +61,16 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException; + Vertex get(V vertex) throws NullPointerException, IllegalArgumentException; /** - * Add the vertex to the graph. If it's already in the graph it will be replaced.
+ * 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. * * @param vertex the vertex to add * @throws NullPointerException if the vertex is null */ - void addVertex(V vertex) throws NullPointerException; + void add(V vertex) throws NullPointerException; /** * Add the specified vertex to the graph only if the graph doesn't contains it.
@@ -89,7 +80,7 @@ public interface Graph extends Iterable { * @return true if the vertex is added, false if the graph contains the vertex and therefore the new one is not added * @throws NullPointerException if the vertex is null */ - boolean addVertexIfAbsent(V vertex) throws NullPointerException; + boolean addIfAbsent(V vertex) throws NullPointerException; /** * Add all the vertices contained in the collection to the graph.
@@ -99,7 +90,7 @@ public interface Graph extends Iterable { * @param vertices a collection of the vertices to add * @throws NullPointerException if the set is null */ - void addAllVertices(Collection vertices) throws NullPointerException; + void addAll(Collection vertices) throws NullPointerException; /** * Remove the selected vertex from the graph.
@@ -109,13 +100,13 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained */ - void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException; + 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 removeAllVertex(); + void removeAll(); /** * Get all the marks of this graph.
@@ -408,11 +399,11 @@ public interface Graph extends Iterable { * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained in the graph */ - Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException; + Collection getChildrens(V vertex) throws NullPointerException, IllegalArgumentException; /** * Get all the vertices that have the vertex passed as their child.
- * Basically is the opposite of {@link #getChildren(Object)}
+ * 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. * @@ -462,7 +453,7 @@ public interface Graph extends Iterable { * * @return the number of vertices */ - int numberOfVertices(); + int size(); /** * Tells how many edges are in the graph. @@ -500,7 +491,7 @@ public interface Graph extends Iterable { * 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 an array containing the topological order of the vertices + * @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; @@ -521,7 +512,7 @@ public interface Graph extends Iterable { * Of course the sub-graph will contain the edges that link the vertices, but only the one selected. * * @param source the source vertex - * @param depth the maximum depth (must be a positive number, if >=0 a graph containing only the source is returned) + * @param depth the maximum depth (must be a positive number, if <=0 a graph containing only the source is returned) * @return a sub-graph of the original * @throws NullPointerException if the vertex is null * @throws IllegalArgumentException if the vertex is not contained @@ -562,91 +553,5 @@ public interface Graph extends Iterable { */ Map>> distance(V source) throws NullPointerException, IllegalArgumentException; - /** - * Save the Graph passed as input to a file inserted as parameter.
- * The resulting file is a Json string representing all the graph.
- * If the directory for getting through the file do not exist,
- * then it is created.
- * For now the marks are not included. - * - * @param graph the graph to save - * @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. - */ - static void save(Graph graph, String file) throws IOException { - save(graph, "", file); - } - - /** - * Save the Graph passed as input to a file inserted as parameter.
- * The resulting file is a Json string representing all the graph.
- * If the directory for getting through the file do not exist,
- * then it is created.
- * For now the marks are not included.
- * The additional parameter is used if you want to save other as well as the graph. - * - * @param graph the graph to save - * @param other other things to save - * @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. - */ - static void save(Graph graph, String other, String file) throws IOException { - GraphSaveStructure save = new GraphSaveStructure(graph, other); - int slash = file.lastIndexOf("\\"); - if(slash == -1) - slash = file.lastIndexOf("/"); - if(slash != -1) { - String dir = file.substring(0, slash); - File fDir = new File(dir); - fDir.mkdirs(); - } - - FileWriter writer = new FileWriter(file); - - GSON.toJson(save, writer); - writer.close(); - } - - /** - * Load an already saved graph in an instance of a graph. - * Before loading the graph, it is emptied. - * - * @param the parameter needed for the vertex - * @param the parameter needed for the weight - * @param graph the graph to load with - * @param file the file where the graph is saved - * @param classV the class used for the Vertex - * @param classW the class used for the Weight - * @return the string saved in other, if any - * @throws IOException for any possible reason, the most common: the file doesn't exist - * @throws NullPointerException if the graph is null - * @throws JsonSyntaxException if the file is malformed or corrupted - */ - static String load(Graph graph, String file, Class classV, Class classW) throws IOException, NullPointerException, JsonSyntaxException { - FileReader reader = new FileReader(file); - StringBuilder fileContent = new StringBuilder(); - int c; - - while((c = reader.read()) != -1) - fileContent.append((char)c); - reader.close(); - GraphSaveStructure save = GSON.fromJson(fileContent.toString(), GraphSaveStructure.class); - - graph.removeAllVertex(); - for(String str : save.vertices) - graph.addVertex(GSON.fromJson(str, classV)); - - for(int i = 0; i V* } diff --git a/src/berack96/lib/graph/Vertex.java b/src/berack96/lib/graph/Vertex.java index 79433ac..570abf0 100644 --- a/src/berack96/lib/graph/Vertex.java +++ b/src/berack96/lib/graph/Vertex.java @@ -1,12 +1,12 @@ package berack96.lib.graph; +import berack96.lib.graph.visit.VisitStrategy; +import berack96.lib.graph.visit.impl.VisitInfo; + import java.util.Collection; import java.util.HashSet; import java.util.function.Consumer; -import berack96.lib.graph.visit.VisitStrategy; -import berack96.lib.graph.visit.impl.VisitInfo; - /** * Class used for represent a vertex of the graph.
* The vertex contained is linked with the graph, so if any changes are made to @@ -105,7 +105,7 @@ public class Vertex { */ public Collection getChildren() throws UnsupportedOperationException { throwIfNotContained(); - return graph.getChildren(vertex); + return graph.getChildrens(vertex); } /** @@ -219,7 +219,7 @@ public class Vertex { * Add the vertex to the graph only if it's not already in the graph. */ public void addIfAbsent() { - graph.addVertexIfAbsent(vertex); + graph.addIfAbsent(vertex); } /** @@ -228,7 +228,7 @@ public class Vertex { */ public void remove() { if (graph.contains(vertex)) - graph.removeVertex(vertex); + graph.remove(vertex); } /** @@ -240,7 +240,7 @@ 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 { + public VisitInfo visit(final VisitStrategy strategy, final Consumer visit) throws NullPointerException, UnsupportedOperationException { throwIfNotContained(); return graph.visit(vertex, (VisitStrategy) strategy, visit); } diff --git a/src/berack96/lib/graph/impl/AGraph.java b/src/berack96/lib/graph/impl/AGraph.java new file mode 100644 index 0000000..52a0190 --- /dev/null +++ b/src/berack96/lib/graph/impl/AGraph.java @@ -0,0 +1,579 @@ +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/AdjGraph.java b/src/berack96/lib/graph/impl/AdjGraph.java deleted file mode 100644 index de3d4fa..0000000 --- a/src/berack96/lib/graph/impl/AdjGraph.java +++ /dev/null @@ -1,319 +0,0 @@ -package berack96.lib.graph.impl; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -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.VisitInfo; - -public class AdjGraph implements Graph { - - @Override - public Iterator iterator() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isCyclic() { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isDAG() { - // TODO Auto-generated method stub - return false; - } - - @Override - public Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void addVertex(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public boolean addVertexIfAbsent(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - return false; - } - - @Override - public void addAllVertices(Collection vertices) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllVertex() { - // TODO Auto-generated method stub - - } - - @Override - public boolean contains(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - return false; - } - - @Override - public Collection marks() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void unMark(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public Collection getMarkedWith(Object mark) throws NullPointerException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void unMarkAll(Object mark) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public void unMarkAll() { - // TODO Auto-generated method stub - - } - - @Override - public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void addAllEdges(Collection> edges) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllEdge() { - // TODO Auto-generated method stub - - } - - @Override - public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { - // TODO Auto-generated method stub - return false; - } - - @Override - public Collection vertices() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> edges() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int degree(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int numberOfVertices() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int numberOfEdges() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) - throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph transpose() { - // TODO Auto-generated method stub - return null; - } - - @Override - public List topologicalSort() throws UnsupportedOperationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> stronglyConnectedComponents() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph subGraph(Object... marker) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List> distance(V source, V destination) - throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map>> distance(V source) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/src/berack96/lib/graph/impl/ListGraph.java b/src/berack96/lib/graph/impl/ListGraph.java new file mode 100644 index 0000000..948c680 --- /dev/null +++ b/src/berack96/lib/graph/impl/ListGraph.java @@ -0,0 +1,144 @@ +package berack96.lib.graph.impl; + +import berack96.lib.graph.Edge; +import berack96.lib.graph.Graph; + +import java.util.*; + +/** + * 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 { + + final private Map> adj = new HashMap<>(); + + @Override + public Iterator iterator() { + return adj.keySet().iterator(); + } + + @Override + protected Graph getNewInstance() { + return new ListGraph<>(); + } + + @Override + protected void addVertex(V vertex) { + adj.put(vertex, new LinkedList<>()); + } + + @Override + protected boolean containsVertex(V vertex) { + return adj.containsKey(vertex); + } + + @Override + protected void removeVertex(V 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); + }); + } + + @Override + protected void removeAllVertices() { + 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; + } + + @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) { + 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 6718030..2584093 100644 --- a/src/berack96/lib/graph/impl/MapGraph.java +++ b/src/berack96/lib/graph/impl/MapGraph.java @@ -1,16 +1,10 @@ package berack96.lib.graph.impl; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - 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.Dijkstra; -import berack96.lib.graph.visit.impl.Tarjan; -import berack96.lib.graph.visit.impl.VisitInfo; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** * Graph that uses HashMap for vertices and edges
@@ -23,7 +17,7 @@ import berack96.lib.graph.visit.impl.VisitInfo; * @param the weight of the edges * @author Berack96 */ -public class MapGraph implements Graph { +public class MapGraph extends AGraph { /** * Map that contains the edges from a vertex to another
@@ -31,321 +25,88 @@ public class MapGraph implements Graph { * If an edge exist, then it's weight is returned */ private final Map> edges = new HashMap<>(); - - /** - * 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 - */ - private Map> dijkstra = new HashMap<>(); - - @Override - public boolean isCyclic() { - return stronglyConnectedComponents().size() != numberOfVertices(); - } - - @Override - public boolean isDAG() { - return !isCyclic(); - } - - @Override - public Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - return new Vertex<>(this, vertex); - } - - @Override - public void addVertex(V vertex) throws NullPointerException { - checkNull(vertex); - edges.put(vertex, new HashMap<>()); - graphChanged(); - } - - @Override - public boolean addVertexIfAbsent(V vertex) throws NullPointerException { - if (contains(vertex)) - return false; - addVertex(vertex); - return true; - } - - @Override - public void addAllVertices(Collection vertices) throws NullPointerException { - checkNull(vertices); - vertices.forEach(this::addVertexIfAbsent); - } - - @Override - public void removeVertex(V vertex) throws NullPointerException { - if (contains(vertex)) { - edges.remove(vertex); - edges.forEach((v, map) -> map.remove(vertex)); - markers.forEach((mark, set) -> set.remove(vertex)); - graphChanged(); - } - } - - @Override - public void removeAllVertex() { - edges.clear(); - markers.clear(); - graphChanged(); - } - - @Override - public boolean contains(V vertex) throws NullPointerException { - checkNull(vertex); - return edges.containsKey(vertex); - } @Override - public Collection marks() { - Collection ret = new HashSet<>(); - markers.forEach((m, v) -> { - if(v.size() > 0) - ret.add(m); - }); - - return ret; + public Iterator iterator() { + return edges.keySet().iterator(); } - @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<>()); + protected Graph getNewInstance() { + return new MapGraph<>(); + } + + @Override + protected void addVertex(V vertex) { + edges.put(vertex, new HashMap<>()); + } + + @Override + protected boolean containsVertex(V vertex) { + return edges.containsKey(vertex); + } + + @Override + protected void removeVertex(V vertex) { + edges.remove(vertex); + edges.forEach((v, map) -> map.remove(vertex)); + } + + @Override + protected void removeAllVertices() { + edges.clear(); + } + + @Override + protected boolean containsEdgeImpl(V vertex1, V vertex2) { + return contains(vertex1) && contains(vertex2) && edges.get(vertex1).containsKey(vertex2); + } + + @Override + protected W addEdgeImpl(V vertex1, V vertex2, W weight) { + return edges.get(vertex1).put(vertex2, weight); + } + + @Override + protected W getWeightImpl(V vertex1, V vertex2) { + return edges.get(vertex1).get(vertex2); } @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 W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - checkNull(weight); - - W old = edges.get(vertex1).put(vertex2, weight); - graphChanged(); - return old; - } - - @Override - public W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { - return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight()); - } - - @Override - public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { - addVertexIfAbsent(vertex1); - addVertexIfAbsent(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 edges.get(vertex1).get(vertex2); - } - - @Override - public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex1); - checkNullAndExist(vertex2); - - edges.get(vertex1).remove(vertex2); - graphChanged(); - } - - @Override - public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - edges.forEach((v, map) -> map.remove(vertex)); - graphChanged(); - } - - @Override - public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - edges.put(vertex, new HashMap<>()); - graphChanged(); - } - - @Override - public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - removeVertex(vertex); - addVertex(vertex); - } - - @Override - public void removeAllEdge() { - edges.forEach((v, map) -> map.clear()); - graphChanged(); - } - - @Override - public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { - return (contains(vertex1) && contains(vertex2)) && edges.get(vertex1).get(vertex2) != null; - } - - @Override - public Collection vertices() { - return new HashSet<>(edges.keySet()); - } - - @Override - public Collection> edges() { - Set> allEdges = new HashSet<>(); - edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight)))); - return allEdges; - } - - @Override - public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set> set = new HashSet<>(); - edges.forEach((source, map) -> map.forEach((destination, weight) -> { - if (destination.equals(vertex) || source.equals(vertex)) - set.add(new Edge<>(source, destination, weight)); - })); - return set; - } - - @Override - public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); + 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; - } + } - @Override - public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - Collection> collection = new HashSet<>(); - edges.get(vertex).forEach((dest, weight) -> collection.add(new Edge<>(vertex, dest, weight))); + @Override + protected void removeEdgeImpl(V vertex1, V vertex2) { + edges.get(vertex1).remove(vertex2); + } - return collection; - } + @Override + protected void removeAllOutEdgeImpl(V vertex) { + edges.put(vertex, new HashMap<>()); + } - @Override - public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - return new HashSet<>(edges.get(vertex).keySet()); - } - - @Override - public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - Set set = new HashSet<>(); - edges.forEach((v, map) -> { - if (map.containsKey(vertex)) set.add(v); - }); - return set; - } - - @Override - public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - AtomicInteger sum = new AtomicInteger(); - edges.forEach((v, map) -> { - if (map.containsKey(vertex)) - sum.getAndIncrement(); - }); - - return sum.get(); - } - - @Override - public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { - checkNullAndExist(vertex); - - return edges.get(vertex).size(); - } - - @Override - public int degree(V vertex) throws NullPointerException, IllegalArgumentException { - return degreeIn(vertex) + degreeOut(vertex); - } - - @Override - public int numberOfVertices() { + @Override + protected void removeAllInEdgeImpl(V vertex) { + edges.forEach((v, map) -> map.remove(vertex)); + } + + @Override + public int size() { return edges.size(); } @@ -356,189 +117,4 @@ public class MapGraph implements Graph { return sum.get(); } - - @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 = new MapGraph<>(); - for (V vertex : edges.keySet()) - graph.addVertex(vertex); - - edges.forEach((source, map) -> map.forEach((destination, weight) -> graph.addEdge(destination, source, weight))); - - 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 = new MapGraph<>(); - Set vertices = new HashSet<>(); - - int finalDepth = depth > 0 ? depth : 0; - VisitStrategy strategy = (graph, sourceVertex, visit) -> { - int currentDepth = 0; - final LinkedList> toVisitChildren = new LinkedList<>(); - toVisitChildren.add(new AbstractMap.SimpleEntry<>(sourceVertex, 0)); - vertices.add(source); - - while (!toVisitChildren.isEmpty() && currentDepth + 1 <= finalDepth) { - final Map.Entry current = toVisitChildren.removeFirst(); - currentDepth = current.getValue() + 1; - final int finalCurrentDepth = currentDepth; - - for (V child : graph.getChildren(current.getKey())) - if (!vertices.contains(child)) { - toVisitChildren.addLast(new AbstractMap.SimpleEntry<>(child, finalCurrentDepth)); - vertices.add(child); - } - } - return null; - }; - - strategy.visit(this, source, null); - - sub.addAllVertices(vertices); - for (V vertex : vertices) - getEdgesOut(vertex).forEach((edge) -> { - try { - sub.addEdge(edge); - } catch (Exception ignored) { - } - }); - - return sub; - } - - @Override - public Graph subGraph(final Object...marker) { - final Graph sub = new MapGraph<>(); - final Set allVertices = new HashSet<>(); - final Set allMarkers = new HashSet<>(); - final boolean isEmpty = (marker == null || marker.length == 0); - - if (!isEmpty) - for (Object mark: marker) - allMarkers.add(mark); - - 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.addAllVertices(allVertices); - for (V vertex : sub.vertices()) - edges.get(vertex).forEach( (dest, weight) -> { - try { - sub.addEdge(vertex, dest, weight); - } catch (Exception ignored) {} - }); - - 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 */ - } - - @Override - public Iterator iterator() { - return edges.keySet().iterator(); - } - - - /** - * Simple function that reset all the caching variables if the graph changed - */ - private void graphChanged() { - tarjan = null; - dijkstra.clear(); - } - - /** - * 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; - } - - /** - * 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 (!edges.containsKey(vertex)) - throw new IllegalArgumentException(VERTEX_NOT_CONTAINED); - } } diff --git a/src/berack96/lib/graph/impl/MatrixGraph.java b/src/berack96/lib/graph/impl/MatrixGraph.java index 896cf96..fd5d615 100644 --- a/src/berack96/lib/graph/impl/MatrixGraph.java +++ b/src/berack96/lib/graph/impl/MatrixGraph.java @@ -1,319 +1,132 @@ package berack96.lib.graph.impl; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - 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.VisitInfo; -public class MatrixGraph implements Graph { +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 { + + final Map map = new HashMap<>(); + final List> matrix = new ArrayList<>(); @Override public Iterator iterator() { - // TODO Auto-generated method stub - return null; + return map.keySet().iterator(); } @Override - public boolean isCyclic() { - // TODO Auto-generated method stub - return false; + protected Graph getNewInstance() { + return new MatrixGraph<>(); } @Override - public boolean isDAG() { - // TODO Auto-generated method stub - return false; + 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); } @Override - public Vertex getVertex(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; + protected boolean containsVertex(V vertex) { + return map.containsKey(vertex); } @Override - public void addVertex(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - + protected void removeVertex(V vertex) { + int x = map.remove(vertex); + map.replaceAll((vert, index) -> index>x? index-1:index); + + matrix.remove(x); + matrix.forEach(list -> { + int i; + for(i=x; i0) + list.remove(i); + }); } @Override - public boolean addVertexIfAbsent(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - return false; + protected void removeAllVertices() { + map.clear(); + matrix.clear(); } @Override - public void addAllVertices(Collection vertices) throws NullPointerException { - // TODO Auto-generated method stub - + protected boolean containsEdgeImpl(V vertex1, V vertex2) { + try { + return matrix.get(map.get(vertex1)).get(map.get(vertex2)) != null; + } catch (Exception ignore) { + return false; + } } @Override - public void removeVertex(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - + protected W addEdgeImpl(V vertex1, V vertex2, W weight) { + return matrix.get(map.get(vertex1)).set(map.get(vertex2), weight); } @Override - public void removeAllVertex() { - // TODO Auto-generated method stub - + protected W getWeightImpl(V vertex1, V vertex2) { + return matrix.get(map.get(vertex1)).get(map.get(vertex2)); } @Override - public boolean contains(V vertex) throws NullPointerException { - // TODO Auto-generated method stub - return false; + 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 - public Collection marks() { - // TODO Auto-generated method stub - return null; + 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 - public void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - + protected void removeEdgeImpl(V vertex1, V vertex2) { + matrix.get(map.get(vertex1)).set(map.get(vertex2), null); } @Override - public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - + protected void removeAllOutEdgeImpl(V vertex) { + matrix.get(map.get(vertex)).replaceAll(var -> null); } @Override - public void unMark(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - + protected void removeAllInEdgeImpl(V vertex) { + int x = map.get(vertex); + matrix.forEach(list -> list.set(x, null)); } - - @Override - public Collection getMarkedWith(Object mark) throws NullPointerException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getMarks(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void unMarkAll(Object mark) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public void unMarkAll() { - // TODO Auto-generated method stub - - } - - @Override - public W addEdge(V vertex1, V vertex2, W weight) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdge(Edge edge) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdgeAndVertices(V vertex1, V vertex2, W weight) throws NullPointerException { - // TODO Auto-generated method stub - return null; - } - - @Override - public W addEdgeAndVertices(Edge edge) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void addAllEdges(Collection> edges) throws NullPointerException { - // TODO Auto-generated method stub - - } - - @Override - public W getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - - } - - @Override - public void removeAllEdge() { - // TODO Auto-generated method stub - - } - - @Override - public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException { - // TODO Auto-generated method stub - return false; - } - - @Override - public Collection vertices() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> edges() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getChildren(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection getAncestors(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int degree(V vertex) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int numberOfVertices() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public int numberOfEdges() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public VisitInfo visit(V source, VisitStrategy strategy, Consumer visit) - throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph transpose() { - // TODO Auto-generated method stub - return null; - } - - @Override - public List topologicalSort() throws UnsupportedOperationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Collection> stronglyConnectedComponents() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Graph subGraph(Object... marker) { - // TODO Auto-generated method stub - return null; - } - - @Override - public List> distance(V source, V destination) - throws NullPointerException, IllegalArgumentException, UnsupportedOperationException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Map>> distance(V source) throws NullPointerException, IllegalArgumentException { - // TODO Auto-generated method stub - return null; - } - } diff --git a/src/berack96/lib/graph/models/EdgeSaveStructure.java b/src/berack96/lib/graph/models/EdgeSaveStructure.java index 9d12eca..1865da1 100644 --- a/src/berack96/lib/graph/models/EdgeSaveStructure.java +++ b/src/berack96/lib/graph/models/EdgeSaveStructure.java @@ -7,7 +7,6 @@ package berack96.lib.graph.models; * */ public class EdgeSaveStructure { - public EdgeSaveStructure() {} protected EdgeSaveStructure(String s, String d, String w) { this.src = s; this.dest = d; diff --git a/src/berack96/lib/graph/models/GraphSaveStructure.java b/src/berack96/lib/graph/models/GraphSaveStructure.java index 76fa155..751e65a 100644 --- a/src/berack96/lib/graph/models/GraphSaveStructure.java +++ b/src/berack96/lib/graph/models/GraphSaveStructure.java @@ -2,6 +2,14 @@ package berack96.lib.graph.models; import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.InstanceCreator; +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. @@ -9,33 +17,97 @@ import berack96.lib.graph.Graph; * @author Berack96 * */ -public class GraphSaveStructure { - public GraphSaveStructure() {} - public GraphSaveStructure(Graph graph, String other) { - this.vertices = new String[graph.numberOfVertices()]; - int i = 0; - for(Object o: graph.vertices()) { - this.vertices[i] = Graph.GSON.toJson(o); - i++; - } - - this.edges = new EdgeSaveStructure[graph.numberOfEdges()]; - i = 0; - for (Edge edge : graph.edges()) { - this.edges[i] = new EdgeSaveStructure( - Graph.GSON.toJson(edge.getSource()), - Graph.GSON.toJson(edge.getDestination()), - Graph.GSON.toJson(edge.getWeight()) - ); - i++; - } - - - this.other = other; - } - +public class GraphSaveStructure { + + final public Gson gson = new Gson(); public String[] vertices; public EdgeSaveStructure[] edges; //public MarkSaveStructure[] marks; - public String other; + + /* + * Load the graph saved in this class in an instance of a graph passed. + * Before loading the graph, it is emptied. + */ + 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(); + JsonReader reader = new JsonReader(new FileReader(fileName)); + gson.fromJson(reader, this.getClass()); + loadGraph(graph, classV, classW); + } + + /** + * This method can be used by sub-classes for saving other stuff from the graph + * + * @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 + */ + protected void loadGraph(Graph graph, Class classV, Class classW) throws NullPointerException, JsonSyntaxException { + graph.removeAll(); + 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) + ); + } + /** + * Save the Graph passed as input to a file inserted as parameter.
+ * The resulting file is a Json string representing all the graph.
+ * If the directory for getting through the file do not exist,
+ * then it is created.
+ * Marks are not included.
+ * The additional parameter is used if you want to save other as well as the graph. + * + * @param graph the graph to save + * @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); + int slash = file.lastIndexOf("\\"); + 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(); + } + + /** + * 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) { + this.vertices = new String[graph.size()]; + int i = 0; + 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; } + } } \ No newline at end of file diff --git a/src/berack96/lib/graph/models/MarkSaveStructure.java b/src/berack96/lib/graph/models/MarkSaveStructure.java index 557791b..3b3c125 100644 --- a/src/berack96/lib/graph/models/MarkSaveStructure.java +++ b/src/berack96/lib/graph/models/MarkSaveStructure.java @@ -7,7 +7,6 @@ package berack96.lib.graph.models; * */ public class MarkSaveStructure { - public MarkSaveStructure() {} protected MarkSaveStructure(String v, Object m) { this.vert = v; this.mark = m; diff --git a/src/berack96/lib/graph/view/GraphInfo.java b/src/berack96/lib/graph/view/GraphInfo.java index 73e47e4..72af7d2 100644 --- a/src/berack96/lib/graph/view/GraphInfo.java +++ b/src/berack96/lib/graph/view/GraphInfo.java @@ -1,33 +1,23 @@ package berack96.lib.graph.view; -import java.awt.Color; -import java.awt.Component; -import java.awt.GridLayout; -import java.awt.event.ItemEvent; -import java.io.IOException; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; - import berack96.lib.graph.Graph; import berack96.lib.graph.view.edge.EdgeListener; import berack96.lib.graph.view.vertex.VertexListener; import berack96.lib.graph.visit.VisitStrategy; +import javax.swing.*; +import javax.swing.border.BevelBorder; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.io.IOException; +import java.io.Serial; +import java.util.List; +import java.util.*; + public class GraphInfo extends JPanel { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; private final Map> visits; @@ -43,7 +33,7 @@ public class GraphInfo extends JPanel { /* FIRST (GRAPH INFO) */ - JLabel vNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfVertices())); + 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())); @@ -194,7 +184,7 @@ public class GraphInfo extends JPanel { graphPanel.addObserver((o, arg) -> { Graph graph = graphPanel.getGraph(); if(arg.equals(graph)) { - vNumber.setText(String.valueOf(graph.numberOfVertices())); + vNumber.setText(String.valueOf(graph.size())); eNumber.setText(String.valueOf(graph.numberOfEdges())); gCyclic.setText(String.valueOf(graph.isCyclic())); diff --git a/src/berack96/lib/graph/view/GraphListener.java b/src/berack96/lib/graph/view/GraphListener.java index 4469db0..e3ae356 100644 --- a/src/berack96/lib/graph/view/GraphListener.java +++ b/src/berack96/lib/graph/view/GraphListener.java @@ -16,12 +16,12 @@ public interface GraphListener extends MouseListener, MouseMotionListener, KeyLi * This function is called when the listener is removed to the graph. * Here you could remove any other thing that you have done. */ - public void remove(); + void remove(); /** * Get the description of this listener, in a way to interact with the user. * * @return a string describing the functionalities of this listener */ - public String getDescription(); + String getDescription(); } diff --git a/src/berack96/lib/graph/view/GraphPanel.java b/src/berack96/lib/graph/view/GraphPanel.java index 9296dd3..7350228 100644 --- a/src/berack96/lib/graph/view/GraphPanel.java +++ b/src/berack96/lib/graph/view/GraphPanel.java @@ -1,23 +1,5 @@ package berack96.lib.graph.view; -import java.awt.Component; -import java.awt.Container; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.event.KeyListener; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Observer; -import java.util.Set; - import berack96.lib.graph.Edge; import berack96.lib.graph.Graph; import berack96.lib.graph.Vertex; @@ -25,17 +7,29 @@ import berack96.lib.graph.impl.MapGraph; import berack96.lib.graph.view.edge.EdgeComponent; import berack96.lib.graph.view.vertex.VertexComponent; +import java.awt.*; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.IOException; +import java.io.Serial; +import java.util.Collection; +import java.util.HashSet; +import java.util.Observer; +import java.util.Set; + @SuppressWarnings({"unchecked", "deprecation"}) public class GraphPanel extends Component { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; private final GraphicalView> vertexRender; private final GraphicalView> edgeRender; private final Class classV; private final Class classW; - private final Container vertices = new Container(); - private final Container edges = new Container(); + final Container vertices = new Container(); + final Container edges = new Container(); private final Graph graph = new MapGraph<>(); private final Set observers = new HashSet<>(); @@ -78,8 +72,10 @@ public class GraphPanel extends Component { boolean isContained = false; for(Component comp : vertices.getComponents()) - if (comp.equals(v)) - isContained = true; + if (comp.equals(v)) { + isContained = true; + break; + } if (!isContained) { v.setBounds(vertexRender.getBox(v, center)); @@ -122,8 +118,8 @@ public class GraphPanel extends Component { edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center)); edges.add(edgeComponent); graph.addEdge(edgeComponent.edge); - } catch (Exception ignore) { - ignore.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); } } @@ -165,26 +161,11 @@ public class GraphPanel extends Component { } public void save(String fileName) throws IOException { - GraphGraphicalSave save = new GraphGraphicalSave(vertices); - Graph.save(graph, Graph.GSON.toJson(save), fileName); + new GraphPointsSave<>(this).save(graph, fileName); } public void load(String fileName) throws IOException { - String saveContent = Graph.load(graph, fileName, classV, classW); - GraphGraphicalSave save = Graph.GSON.fromJson(saveContent, GraphGraphicalSave.class); - vertices.removeAll(); - edges.removeAll(); - - for(int i = 0; i addEdge(e)); - - repaint(); + new GraphPointsSave<>(this).load(graph, fileName, classV, classW); } @Override @@ -229,37 +210,5 @@ public class GraphPanel extends Component { observers.forEach(observer -> observer.update(null, this.graph)); } - class GraphGraphicalSave { - public GraphGraphicalSave() {} - protected GraphGraphicalSave(Container vertices) { - List v = new LinkedList<>(); - List p = new LinkedList<>(); - - for(Component vertex : vertices.getComponents()) { - Point temp = new Point(vertex.getX(), vertex.getY()); - temp.x += vertex.getWidth() / 2; - temp.y += vertex.getHeight() / 2; - - p.add(temp); - v.add(Graph.GSON.toJson(((VertexComponent) vertex).vertex.getValue())); - } - - int i = 0; - this.vertices = new String[v.size()]; - for(String s : v) { - this.vertices[i] = s; - i++; - } - - i = 0; - this.points = new Point[p.size()]; - for(Point pt : p) { - this.points[i] = pt; - i++; - } - } - - public String[] vertices; - public Point[] points; - } + } diff --git a/src/berack96/lib/graph/view/GraphPointsSave.java b/src/berack96/lib/graph/view/GraphPointsSave.java new file mode 100644 index 0000000..60b96f7 --- /dev/null +++ b/src/berack96/lib/graph/view/GraphPointsSave.java @@ -0,0 +1,54 @@ +package berack96.lib.graph.view; + +import berack96.lib.graph.Graph; +import berack96.lib.graph.models.GraphSaveStructure; + +import java.awt.*; +import java.util.LinkedList; +import java.util.List; + +public class GraphPointsSave extends GraphSaveStructure { + + final private GraphPanel panel; + public Point[] points; + + public GraphPointsSave(GraphPanel panel) { + this.panel = panel; + } + + @Override + protected void saveGraph(Graph graph) { + super.saveGraph(graph); + List p = new LinkedList<>(); + + for(Component vertex : panel.vertices.getComponents()) { + Point temp = new Point(vertex.getX(), vertex.getY()); + temp.x += vertex.getWidth() / 2; + temp.y += vertex.getHeight() / 2; + p.add(temp); + } + + int i = 0; + this.points = new Point[p.size()]; + for(Point pt : p) + this.points[i++] = pt; + } + + @Override + public void loadGraph(Graph graph, Class classV, Class classW) { + super.loadGraph(graph, classV, classW); + + panel.vertices.removeAll(); + panel.edges.removeAll(); + + for(int i = 0; i extends JFrame { - private static final long serialVersionUID = 1L; + @Serial + private static final long serialVersionUID = 1L; private final GraphPanel graphPanel; - private final GraphInfo infoPanel; public GraphWindow(GraphPanel graphPanel, VertexListener vListener, EdgeListener eListener) { this.setTitle("Grafo"); @@ -37,14 +37,14 @@ public class GraphWindow extends JFrame { strats.add(new Dijkstra<>()); strats.add(new Tarjan<>()); - this.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); } - public void visitRefresh(int millisec) { - VisitListener.changeRefresh(millisec); + public void visitRefresh(int millis) { + VisitListener.changeRefresh(millis); } public GraphPanel getGraphPanel() { diff --git a/src/berack96/lib/graph/view/VisitListener.java b/src/berack96/lib/graph/view/VisitListener.java index fb28d7e..e827648 100644 --- a/src/berack96/lib/graph/view/VisitListener.java +++ b/src/berack96/lib/graph/view/VisitListener.java @@ -25,13 +25,13 @@ public class VisitListener implements GraphListener { this.strategy = strategy; } - public static void changeRefresh(int millisec) { - refreshTime = millisec; + public static void changeRefresh(int mills) { + refreshTime = mills; } @Override public void remove() { - timers.forEach(t -> t.stop()); + timers.forEach(Timer::stop); timers.clear(); } diff --git a/src/berack96/lib/graph/view/vertex/VertexComponent.java b/src/berack96/lib/graph/view/vertex/VertexComponent.java index 10195e6..ae91bec 100644 --- a/src/berack96/lib/graph/view/vertex/VertexComponent.java +++ b/src/berack96/lib/graph/view/vertex/VertexComponent.java @@ -1,10 +1,12 @@ package berack96.lib.graph.view.vertex; -import java.awt.*; - import berack96.lib.graph.Vertex; +import java.awt.*; +import java.io.Serial; + public class VertexComponent extends Component { + @Serial private static final long serialVersionUID = 1L; public final Vertex vertex; diff --git a/src/berack96/lib/graph/view/vertex/VertexIntListener.java b/src/berack96/lib/graph/view/vertex/VertexIntListener.java index 37d8ee7..19a8ed5 100644 --- a/src/berack96/lib/graph/view/vertex/VertexIntListener.java +++ b/src/berack96/lib/graph/view/vertex/VertexIntListener.java @@ -1,10 +1,10 @@ package berack96.lib.graph.view.vertex; -import java.util.Arrays; - import berack96.lib.graph.Graph; import berack96.lib.graph.view.GraphPanel; +import java.util.Arrays; + public class VertexIntListener extends VertexListener { public VertexIntListener(GraphPanel panel) { @@ -17,14 +17,14 @@ public class VertexIntListener extends VertexListener { @Override protected Integer buildNewVertex(Graph graph) { int counter = 0; - Integer[] vertices = graph.vertices().toArray(new Integer[graph.numberOfVertices()]); + Integer[] vertices = graph.vertices().toArray(new Integer[graph.size()]); Arrays.sort(vertices); - - for(int i = 0; i implements GraphicalView> { private static final Font FONT = new Font("Comic Sans MS", Font.BOLD, 17); @@ -23,7 +23,7 @@ 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 size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING; diff --git a/src/berack96/lib/graph/visit/impl/BFS.java b/src/berack96/lib/graph/visit/impl/BFS.java index c11db04..299ceea 100644 --- a/src/berack96/lib/graph/visit/impl/BFS.java +++ b/src/berack96/lib/graph/visit/impl/BFS.java @@ -1,11 +1,11 @@ package berack96.lib.graph.visit.impl; -import java.util.LinkedList; -import java.util.function.Consumer; - import berack96.lib.graph.Graph; import berack96.lib.graph.visit.VisitStrategy; +import java.util.LinkedList; +import java.util.function.Consumer; + /** * Breadth-first search
* 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. @@ -29,7 +29,7 @@ public class BFS implements VisitStrategy { while (!toVisitChildren.isEmpty()) { V current = toVisitChildren.removeFirst(); - for (V child : graph.getChildren(current)) + for (V child : graph.getChildrens(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 14586c1..a1b899b 100644 --- a/src/berack96/lib/graph/visit/impl/DFS.java +++ b/src/berack96/lib/graph/visit/impl/DFS.java @@ -1,12 +1,12 @@ package berack96.lib.graph.visit.impl; +import berack96.lib.graph.Graph; +import berack96.lib.graph.visit.VisitStrategy; + import java.util.Iterator; import java.util.Stack; import java.util.function.Consumer; -import berack96.lib.graph.Graph; -import berack96.lib.graph.visit.VisitStrategy; - /** * Depth-first search
* The algorithm starts at the root node and explores as far as possible along each branch before backtracking. @@ -27,7 +27,7 @@ public class DFS implements VisitStrategy { while (!toVisit.isEmpty()) { V current = toVisit.peek(); boolean hasChildToVisit = false; - Iterator iter = graph.getChildren(current).iterator(); + Iterator iter = graph.getChildrens(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 new file mode 100644 index 0000000..d497389 --- /dev/null +++ b/src/berack96/lib/graph/visit/impl/Depth.java @@ -0,0 +1,53 @@ +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/Tarjan.java b/src/berack96/lib/graph/visit/impl/Tarjan.java index c9960b8..426c43d 100644 --- a/src/berack96/lib/graph/visit/impl/Tarjan.java +++ b/src/berack96/lib/graph/visit/impl/Tarjan.java @@ -1,12 +1,12 @@ package berack96.lib.graph.visit.impl; -import java.util.*; -import java.util.function.Consumer; - import berack96.lib.graph.Graph; import berack96.lib.graph.visit.VisitSCC; import berack96.lib.graph.visit.VisitTopological; +import java.util.*; +import java.util.function.Consumer; + /** * Class that implements the Tarjan algorithm and uses it for getting the SCC and the topological sort * @@ -61,7 +61,7 @@ public class Tarjan implements VisitSCC, VisitTopolog strongConnect(graph, vertex, index, visit); } - topologicalSort = (graph.numberOfVertices() == SCC.size()) ? new ArrayList<>(topologicalSort) : null; + topologicalSort = (graph.size() == SCC.size()) ? new ArrayList<>(topologicalSort) : null; return info; } @@ -74,7 +74,7 @@ public class Tarjan implements VisitSCC, VisitTopolog info.setDiscovered(vertex); // Consider successors of v - for (V child : graph.getChildren(vertex)) { + for (V child : graph.getChildrens(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 5fa189f..06f1305 100644 --- a/src/berack96/lib/graph/visit/impl/VisitInfo.java +++ b/src/berack96/lib/graph/visit/impl/VisitInfo.java @@ -1,10 +1,10 @@ package berack96.lib.graph.visit.impl; +import berack96.lib.graph.visit.VisitStrategy; + import java.util.*; import java.util.function.Consumer; -import berack96.lib.graph.visit.VisitStrategy; - /** * The class used for getting the info of the visit.
* It could be used with the algorithm of the visit for set some useful data. @@ -13,9 +13,9 @@ import berack96.lib.graph.visit.VisitStrategy; * @author Berack96 */ public class VisitInfo { - private final Map discovered; - private final Map visited; - private final Map parent; + private static final int NOT_SET = -1; + + private final Map vertices; private final V source; private long time; @@ -29,118 +29,12 @@ public class VisitInfo { if (source == null) throw new NullPointerException(); - discovered = new Hashtable<>(); - visited = new Hashtable<>(); - parent = new Hashtable<>(); - + this.vertices = new Hashtable<>(); this.time = 0; this.source = source; setDiscovered(source); } - /** - * The time of the vertex when it is discovered in the visit.
- * For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
- * The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time
- * - * @param vertex the vertex needed - * @return the time of it's discovery - * @throws IllegalArgumentException if the vertex is not discovered - * @throws NullPointerException if the vertex is null - */ - public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException { - Long time = discovered.get(vertex); - if (time == null) - throw new IllegalArgumentException(); - return time; - } - - /** - * The time when the vertex is visited by the algorithm
- * For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
- * The time starts at 0 and for each vertex discovered or visited is increased by one
- * - * @param vertex the vertex needed - * @return the time of it's visit - * @throws IllegalArgumentException if the vertex is not visited - * @throws NullPointerException if the vertex is null - */ - public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException { - Long time = visited.get(vertex); - if (time == null) - throw new IllegalArgumentException(); - return time; - } - - /** - * Tells if a vertex is discovered or not - * - * @param vertex the vertex chosen - * @return true if is discovered - */ - public boolean isDiscovered(V vertex) throws NullPointerException { - try { - return discovered.containsKey(vertex); - } catch (NullPointerException e) { - return false; - } - } - - /** - * Tells if the vertex is visited or not - * - * @param vertex the vertex chosen - * @return true if is visited - */ - public boolean isVisited(V vertex) throws NullPointerException { - try { - return visited.containsKey(vertex); - } catch (NullPointerException e) { - return false; - } - } - - /** - * Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.
- * Next this call it will be possible to get the time of visit of that vertex
- * Does nothing if the vertex is already been visited. - * - * @param vertex the vertex that has been visited - */ - synchronized void setVisited(V vertex) { - setDiscovered(vertex); - if (!visited.containsKey(vertex)) - visited.put(vertex, time++); - } - - /** - * Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery
- * Does nothing if the vertex is already been discovered. - * - * @param vertex the vertex that has been discovered - */ - synchronized void setDiscovered(V vertex) { - if (!discovered.containsKey(vertex)) - discovered.put(vertex, time++); - } - - /** - * Set the parent of a particular vertex
- * The parent of a vertex is the one that has discovered it
- * If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
- * - * @param parent the vertex that is the parent - * @param child the vertex discovered - * @throws IllegalArgumentException if the parent is not already discovered - */ - synchronized void setParent(V parent, V child) throws IllegalArgumentException { - if (!isDiscovered(parent)) - throw new IllegalArgumentException(parent.toString()); - - setDiscovered(child); - this.parent.putIfAbsent(child, parent); - } - /** * Get the source of the visit. * @@ -157,13 +51,148 @@ public class VisitInfo { * * @param vertex the child vertex * @return the parent of the child - * @throws IllegalArgumentException if the vertex has not been discovered yet + * @throws IllegalArgumentException if the vertex has not been discovered yet or is null */ public V getParentOf(V vertex) throws IllegalArgumentException { - if (isDiscovered(vertex)) - return parent.get(vertex); + VertexInfo info = vertices.get(vertex); + if (!isDiscovered(vertex)) + throw new IllegalArgumentException(); + return info.parent; + } + + /** + * The time of the vertex when it is discovered in the visit.
+ * For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
+ * The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time
+ * + * @param vertex the vertex needed + * @return the time of it's discovery + * @throws IllegalArgumentException if the vertex is not discovered + * @throws NullPointerException if the vertex is null + */ + public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException { + VertexInfo info = vertices.get(vertex); + long time = (info == null) ? NOT_SET : info.timeDiscovered; + + if(time == NOT_SET) + throw new IllegalArgumentException(); + return time; + } - throw new IllegalArgumentException(); + /** + * The time when the vertex is visited by the algorithm
+ * For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
+ * The time starts at 0 and for each vertex discovered or visited is increased by one
+ * + * @param vertex the vertex needed + * @return the time of it's visit + * @throws IllegalArgumentException if the vertex is not visited + * @throws NullPointerException if the vertex is null + */ + public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException { + VertexInfo info = vertices.get(vertex); + long time = (info == null) ? NOT_SET : info.timeVisited; + + if(time == NOT_SET) + throw new IllegalArgumentException(); + return time; + } + + /** + * The depth of the vertex when it was first discovered. + * + * @param vertex the vertex needed + * @return the depth of it's discovery + * @throws IllegalArgumentException if the vertex is not discovered + * @throws NullPointerException if the vertex is null + */ + public long getDepth(V vertex) throws IllegalArgumentException, NullPointerException { + VertexInfo info = vertices.get(vertex); + long depth = (info == null) ? NOT_SET : info.depth; + + if(depth == NOT_SET) + throw new IllegalArgumentException(); + return depth; + } + + /** + * Tells if a vertex is discovered or not + * + * @param vertex the vertex chosen + * @return true if is discovered + */ + public boolean isDiscovered(V vertex) throws NullPointerException { + try { + return vertices.get(vertex).timeDiscovered != NOT_SET; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Tells if the vertex is visited or not + * + * @param vertex the vertex chosen + * @return true if is visited + */ + public boolean isVisited(V vertex) throws NullPointerException { + try { + return vertices.get(vertex).timeVisited != NOT_SET; + } catch (NullPointerException e) { + return false; + } + } + + /** + * Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.
+ * Next this call it will be possible to get the time of visit of that vertex
+ * Does nothing if the vertex has already been visited. + * + * @param vertex the vertex that has been visited + */ + synchronized void setVisited(V vertex) { + setDiscovered(vertex); + VertexInfo info = vertices.get(vertex); + if(info.timeVisited != NOT_SET) + return; + + info.timeVisited = time; + time++; + } + + /** + * Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery
+ * Does nothing if the vertex has already been discovered. + * + * @param vertex the vertex that has been discovered + */ + synchronized void setDiscovered(V vertex) { + VertexInfo info = vertices.computeIfAbsent(vertex, (v) -> new VertexInfo(vertex)); + if(info.timeDiscovered != NOT_SET) + return; + + info.timeDiscovered = time; + info.depth = 0; + time++; + } + + /** + * Set the parent of a particular vertex
+ * The parent of a vertex is the one that has discovered it
+ * If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
+ * + * @param parent the vertex that is the parent + * @param child the vertex discovered + * @throws IllegalArgumentException if the parent is not already discovered + */ + synchronized void setParent(V parent, V child) throws IllegalArgumentException { + if (!isDiscovered(parent)) + throw new IllegalArgumentException(parent.toString()); + + setDiscovered(child); + VertexInfo info = vertices.get(child); + info.parent = parent; + info.depth = vertices.get(parent).depth + 1; } /** @@ -172,7 +201,12 @@ public class VisitInfo { * @return the visited vertices */ public Set getVisited() { - return visited.keySet(); + Set visited = new HashSet<>(vertices.size()); + vertices.forEach((vert, info) -> { + if(info.timeVisited != NOT_SET) + visited.add(vert); + }); + return visited; } /** @@ -181,7 +215,12 @@ public class VisitInfo { * @return the discovered vertices */ public Set getDiscovered() { - return discovered.keySet(); + Set discovered = new HashSet<>(vertices.size()); + vertices.forEach((vert, info) -> { + if(info.timeDiscovered != NOT_SET) + discovered.add(vert); + }); + return discovered; } /** @@ -191,10 +230,11 @@ public class VisitInfo { * @param consumer the function to apply to each */ public void forEachDiscovered(Consumer consumer) { - Set vertices = getDiscovered(); Queue queue = new PriorityQueue<>(); - - vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), isVisited(vertex) ? getTimeVisit(vertex) : -1, false))); + vertices.forEach((v, info) -> { + if(info.timeDiscovered != NOT_SET) + queue.offer(new VertexInfo(info, false)); + }); while (!queue.isEmpty()) consumer.accept(queue.poll()); @@ -207,10 +247,11 @@ public class VisitInfo { * @param consumer the function to apply to each */ public void forEachVisited(Consumer consumer) { - Set vertices = getVisited(); Queue queue = new PriorityQueue<>(); - - vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true))); + vertices.forEach((v, info) -> { + if(info.timeVisited != NOT_SET) + queue.offer(new VertexInfo(info, true)); + }); while (!queue.isEmpty()) consumer.accept(queue.poll()); @@ -223,46 +264,70 @@ public class VisitInfo { * @param consumer the function to apply at each vertex */ public void forEach(Consumer consumer) { - Set discovered = getDiscovered(); - Set visited = getVisited(); Queue queue = new PriorityQueue<>(); - - discovered.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), false))); - visited.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true))); - + 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)); + }); + while (!queue.isEmpty()) consumer.accept(queue.remove()); } - + /** * Class used mainly for storing the data of the visit */ public class VertexInfo implements Comparable { - public final V vertex; - public final V parent; - public final long timeDiscovered; - public final long timeVisited; + public V vertex; + public V parent; + public long timeDiscovered; + public long timeVisited; + public long depth; private final boolean compareVisited; - private VertexInfo(V vertex, V parent, long timeDiscovered, long timeVisited, boolean compareVisited) { - this.vertex = vertex; - this.parent = parent; - this.timeDiscovered = timeDiscovered; - this.timeVisited = timeVisited; - this.compareVisited = compareVisited; + private VertexInfo(V vertex) { + this.vertex = vertex; + this.timeDiscovered = NOT_SET; + this.timeVisited = NOT_SET; + this.depth = NOT_SET; + this.compareVisited = false; + } + + private VertexInfo(VertexInfo info, boolean compare) { + this.vertex = info.vertex; + this.parent = info.parent; + this.timeDiscovered = info.timeDiscovered; + this.timeVisited = info.timeVisited; + this.depth = info.depth; + this.compareVisited = compare; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + @Override + public boolean equals(Object obj) { + try { + return obj instanceof VisitInfo.VertexInfo && obj.toString().equals(toString()); + } catch (Exception e) { + return false; + } } @Override public int compareTo(VertexInfo other) { long compareThis = compareVisited ? timeVisited : timeDiscovered; long compareOther = other.compareVisited ? other.timeVisited : other.timeDiscovered; - return (int) (compareThis - compareOther); } @Override public String toString() { - return String.format("%s -> %s [D:%3d, V:%3d]", parent, vertex, timeDiscovered, timeVisited); + return String.format("%s -> %s (%3d) [D:%3d, V:%3d]", parent, vertex, depth, timeDiscovered, timeVisited); } } } diff --git a/test/berack96/test/lib/TestGraph.java b/test/berack96/test/lib/TestGraph.java index b636e0b..9650eca 100644 --- a/test/berack96/test/lib/TestGraph.java +++ b/test/berack96/test/lib/TestGraph.java @@ -1,46 +1,31 @@ package berack96.test.lib; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import berack96.lib.graph.Edge; +import berack96.lib.graph.Graph; +import berack96.lib.graph.Vertex; +import berack96.lib.graph.models.GraphSaveStructure; +import berack96.lib.graph.visit.impl.BFS; +import berack96.lib.graph.visit.impl.DFS; +import berack96.lib.graph.visit.impl.VisitInfo; +import com.google.gson.JsonSyntaxException; +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 java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; -import com.google.gson.JsonSyntaxException; - -import berack96.lib.graph.Edge; -import berack96.lib.graph.Graph; -import berack96.lib.graph.Vertex; -import berack96.lib.graph.impl.AdjGraph; -import berack96.lib.graph.impl.MapGraph; -import berack96.lib.graph.impl.MatrixGraph; -import berack96.lib.graph.visit.impl.BFS; -import berack96.lib.graph.visit.impl.DFS; -import berack96.lib.graph.visit.impl.VisitInfo; - -@SuppressWarnings("deprecation") +@Timeout(value = 2) public class TestGraph { - /* We only try this for sake of simplicity */ + /* I decided to only try this for sake of simplicity */ private Graph graph; private final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); @@ -48,17 +33,16 @@ public class TestGraph { private final String encoding = "UTF-8"; private final Exception nullException = new NullPointerException(Graph.PARAM_NULL); private final Exception notException = new IllegalArgumentException(Graph.VERTEX_NOT_CONTAINED); - private final Exception unsuppException = new UnsupportedOperationException(Vertex.REMOVED); + private final Exception unSuppException = new UnsupportedOperationException(Vertex.REMOVED); - - @Before + @BeforeEach public void before() { // Change here the instance for changing all the test for that particular class - graph = new MapGraph<>(); -// graph = new MatrixGraph<>(); -// graph = new AdjGraph<>(); + //graph = new berack96.lib.graph.impl.MapGraph<>(); + //graph = new berack96.lib.graph.impl.MatrixGraph<>(); + graph = new berack96.lib.graph.impl.ListGraph<>(); - PrintStream p = null; + PrintStream p; try { p = new PrintStream(bytes, true, encoding); System.setErr(p); @@ -68,7 +52,7 @@ public class TestGraph { } } - @After + @AfterEach public void after() { try { String printed = bytes.toString(encoding); @@ -82,48 +66,48 @@ public class TestGraph { @Test public void basicVertex() { - assertEquals(0, graph.numberOfVertices()); + assertEquals(0, graph.size()); - graph.addVertex("1"); - graph.addVertex("2"); - shouldThrow(nullException, () -> graph.addVertex(null)); + graph.add("1"); + graph.add("2"); + shouldThrow(nullException, () -> graph.add(null)); assertTrue(graph.contains("1")); assertFalse(graph.contains("0")); assertTrue(graph.contains("2")); assertFalse(graph.contains("3")); - assertEquals(2, graph.numberOfVertices()); + assertEquals(2, graph.size()); - graph.removeVertex("1"); + graph.remove("1"); assertFalse(graph.contains("1")); assertTrue(graph.contains("2")); - assertEquals(1, graph.numberOfVertices()); + assertEquals(1, graph.size()); - graph.addVertex("3"); + graph.add("3"); assertTrue(graph.contains("3")); shouldThrow(nullException, () -> graph.contains(null)); - shouldThrow(nullException, () -> graph.addVertexIfAbsent(null)); + shouldThrow(nullException, () -> graph.addIfAbsent(null)); - assertTrue(graph.addVertexIfAbsent("4")); - assertFalse(graph.addVertexIfAbsent("4")); - assertFalse(graph.addVertexIfAbsent("2")); + assertTrue(graph.addIfAbsent("4")); + assertFalse(graph.addIfAbsent("4")); + assertFalse(graph.addIfAbsent("2")); - assertEquals(3, graph.numberOfVertices()); + assertEquals(3, graph.size()); shouldContain(graph.vertices(), "2", "3", "4"); - graph.removeAllVertex(); + graph.removeAll(); shouldContain(graph.vertices()); Set vertices = new HashSet<>(Arrays.asList("1", "5", "24", "2", "3")); - graph.addAllVertices(vertices); + graph.addAll(vertices); shouldContain(graph.vertices(), vertices.toArray()); - graph.removeVertex("1"); - graph.removeVertex("24"); + graph.remove("1"); + graph.remove("24"); shouldContain(graph.vertices(), "5", "2", "3"); - graph.addAllVertices(vertices); + graph.addAll(vertices); shouldContain(graph.vertices(), vertices.toArray()); - shouldThrow(nullException, () -> graph.addAllVertices(null)); + shouldThrow(nullException, () -> graph.addAll(null)); } @Test @@ -136,16 +120,16 @@ public class TestGraph { * v v * 3 <-> 5 -> 4 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); + 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(new NullPointerException(), () -> graph.addEdge(null)); + 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))); @@ -193,21 +177,21 @@ public class TestGraph { assertEquals(6, graph.numberOfEdges()); - assertEquals(new Integer(1), graph.getWeight("1", "2")); - assertEquals(new Integer(1), graph.getWeight("1", "3")); - assertEquals(new Integer(4), graph.getWeight("2", "5")); - assertEquals(new Integer(2), graph.getWeight("3", "5")); - assertEquals(new Integer(2), graph.getWeight("5", "3")); - assertEquals(new Integer(3), graph.getWeight("5", "4")); + 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(new Integer(1), graph.addEdge("1", "2", 102)); - assertEquals(new Integer(102), graph.addEdge("1", "2", 3)); - assertEquals(new Integer(3), graph.addEdge("1", "2", 1)); - assertEquals(new Integer(1), graph.addEdge(new Edge<>("1", "2", 102))); - assertEquals(new Integer(102), graph.addEdge(new Edge<>("1", "2", 3))); - assertEquals(new Integer(3), graph.addEdge(new Edge<>("1", "2", 1))); + assertEquals(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")); @@ -298,10 +282,10 @@ public class TestGraph { assertEquals(35, graph.addEdgeAndVertices(new Edge<>("2aa", "323", 5)).intValue()); assertEquals(50, graph.addEdgeAndVertices(new Edge<>("2", "323", 500)).intValue()); - graph.removeAllVertex(); - graph.addVertex("aaa"); - graph.addVertex("1"); - graph.addVertex("2"); + graph.removeAll(); + graph.add("aaa"); + graph.add("1"); + graph.add("2"); shouldContain(graph.vertices(), "1", "2", "aaa"); shouldContain(graph.edges()); @@ -333,12 +317,12 @@ public class TestGraph { * 3 <-> 5 -> 4 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); + graph.addIfAbsent("1"); + graph.addIfAbsent("2"); + graph.addIfAbsent("3"); + graph.addIfAbsent("4"); + graph.addIfAbsent("5"); + graph.addIfAbsent("6"); shouldContain(graph.edges()); @@ -351,12 +335,12 @@ public class TestGraph { graph.addEdge("5", "3", 9); graph.addEdge("5", "4", 5); - 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.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"); @@ -417,6 +401,25 @@ public class TestGraph { 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 @@ -424,8 +427,9 @@ public class TestGraph { VisitInfo info = new VisitInfo<>(0); assertTrue(info.isDiscovered(0)); assertFalse(info.isVisited(0)); + assertEquals(0, info.getDepth(0)); assertEquals(0, info.getTimeDiscover(0)); - assertEquals(new Integer(0), info.getSource()); + assertEquals(Integer.valueOf(0), info.getSource()); assertNull(info.getParentOf(0)); assertFalse(info.isVisited(null)); @@ -434,10 +438,12 @@ public class TestGraph { shouldThrow(new IllegalArgumentException(), () -> info.getTimeVisit(0)); shouldThrow(new IllegalArgumentException(), () -> info.getTimeDiscover(1)); shouldThrow(new IllegalArgumentException(), () -> info.getParentOf(2)); - shouldThrow(new IllegalArgumentException(), () -> info.getParentOf(null)); + 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 @@ -452,14 +458,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -503,6 +509,7 @@ public class TestGraph { 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"}; @@ -511,6 +518,24 @@ public class TestGraph { 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); @@ -542,14 +567,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -564,7 +589,7 @@ public class TestGraph { Set vertices = new HashSet<>(); Iterator iter = graph.iterator(); - assertTrue("This should not be null!", iter != null); + assertNotNull(iter, "This should not be null!"); while (iter.hasNext()) vertices.add(iter.next()); shouldContain(vertices, "1", "2", "3", "4", "5", "6", "7", "8"); @@ -591,12 +616,12 @@ public class TestGraph { * 3 <-> 5 -> 4 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); + 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); @@ -619,14 +644,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ before(); - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -656,12 +681,12 @@ public class TestGraph { assertFalse(graph.isCyclic()); assertTrue(graph.isDAG()); - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); + graph.addIfAbsent("1"); + graph.addIfAbsent("2"); + graph.addIfAbsent("3"); + graph.addIfAbsent("4"); + graph.addIfAbsent("5"); + graph.addIfAbsent("6"); assertFalse(graph.isCyclic()); assertTrue(graph.isDAG()); @@ -687,12 +712,12 @@ public class TestGraph { * 3 <- 5 -> 4 */ before(); - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); + graph.addIfAbsent("1"); + graph.addIfAbsent("2"); + graph.addIfAbsent("3"); + graph.addIfAbsent("4"); + graph.addIfAbsent("5"); + graph.addIfAbsent("6"); assertFalse(graph.isCyclic()); assertTrue(graph.isDAG()); @@ -733,14 +758,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -752,7 +777,7 @@ public class TestGraph { graph.addEdge("7", "8", 8); Graph transposed = graph.transpose(); - assertTrue("This should not be null!", transposed != null); + assertNotNull(transposed, "This should not be null!"); DFS dfs = new DFS<>(); VisitInfo visitDFS = transposed.visit("6", dfs, null); @@ -795,12 +820,12 @@ public class TestGraph { * 3 -> 5 -> 4 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); + 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); @@ -826,14 +851,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -845,7 +870,7 @@ public class TestGraph { graph.addEdge("7", "8", 8); List> distance = graph.distance("1", "6"); - assertTrue("This should not be null!", distance != null); + assertNotNull(distance, "This should not be null!"); int sum = distance.stream().mapToInt(Edge::getWeight).sum(); assertEquals(13, sum); shouldContainInOrder(distance, @@ -885,14 +910,14 @@ public class TestGraph { * 3 <- 5 -> 4 -> 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -905,7 +930,7 @@ public class TestGraph { graph.addEdge("7", "8", 8); Map>> distance = graph.distance("1"); - assertTrue("This should not be null!", distance != null); + assertNotNull(distance, "This should not be null!"); assertNull(distance.get("1")); shouldContainInOrder(distance.get("2"), new Edge<>("1", "2", 1)); @@ -945,14 +970,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -1094,7 +1119,7 @@ public class TestGraph { @Test public void subGraph() { /* - * This graph should be like this + * This graph should look like this * * 1 -> 2 <- 6 7 * ^ ^ @@ -1103,14 +1128,14 @@ public class TestGraph { * 3 <- 5 -> 4 8 */ - graph.addVertexIfAbsent("1"); - graph.addVertexIfAbsent("2"); - graph.addVertexIfAbsent("3"); - graph.addVertexIfAbsent("4"); - graph.addVertexIfAbsent("5"); - graph.addVertexIfAbsent("6"); - graph.addVertexIfAbsent("7"); - graph.addVertexIfAbsent("8"); + graph.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); @@ -1140,7 +1165,7 @@ public class TestGraph { graph.mark("4", "z"); Graph sub = graph.subGraph("1", -541); - assertTrue("This should not be null!", sub != null); + assertNotNull(sub, "This should not be null!"); shouldContain(sub.vertices(), "1"); shouldContain(sub.edges()); @@ -1222,7 +1247,8 @@ public class TestGraph { 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)); @@ -1233,31 +1259,31 @@ public class TestGraph { Vertex vertex = new Vertex<>(graph, "stronzo"); assertEquals("stronzo", vertex.getValue()); - assertEquals(0, graph.numberOfVertices()); + assertEquals(0, graph.size()); - shouldThrow(unsuppException, () -> vertex.addChild(null, null)); - shouldThrow(unsuppException, () -> vertex.mark(null)); - shouldThrow(unsuppException, () -> vertex.removeChild(null)); - shouldThrow(unsuppException, () -> vertex.visit(null, null)); - shouldThrow(unsuppException, vertex::unMark); - shouldThrow(unsuppException, vertex::getAncestors); - shouldThrow(unsuppException, vertex::getChildren); - shouldThrow(unsuppException, vertex::getEdgesOut); - shouldThrow(unsuppException, vertex::getEdgesIn); - shouldThrow(unsuppException, vertex::getChildrenAsVertex); - shouldThrow(unsuppException, vertex::getAncestorsAsVertex); - shouldThrow(unsuppException, vertex::getMarks); + shouldThrow(unSuppException, () -> vertex.addChild(null, null)); + shouldThrow(unSuppException, () -> vertex.mark(null)); + shouldThrow(unSuppException, () -> vertex.removeChild(null)); + shouldThrow(unSuppException, () -> vertex.visit(null, null)); + shouldThrow(unSuppException, vertex::unMark); + shouldThrow(unSuppException, vertex::getAncestors); + shouldThrow(unSuppException, vertex::getChildren); + shouldThrow(unSuppException, vertex::getEdgesOut); + shouldThrow(unSuppException, vertex::getEdgesIn); + shouldThrow(unSuppException, vertex::getChildrenAsVertex); + shouldThrow(unSuppException, vertex::getAncestorsAsVertex); + shouldThrow(unSuppException, vertex::getMarks); vertex.addIfAbsent(); - assertEquals(1, graph.numberOfVertices()); + assertEquals(1, graph.size()); vertex.addIfAbsent(); - assertEquals(1, graph.numberOfVertices()); + assertEquals(1, graph.size()); vertex.addIfAbsent(); - assertEquals(1, graph.numberOfVertices()); + assertEquals(1, graph.size()); - assertEquals(vertex, graph.getVertex("stronzo")); - shouldThrow(nullException, () -> graph.getVertex(null)); - shouldThrow(notException, () -> graph.getVertex("stronzo1")); + assertEquals(vertex, graph.get("stronzo")); + shouldThrow(nullException, () -> graph.get(null)); + shouldThrow(notException, () -> graph.get("stronzo1")); shouldThrow(nullException, () -> vertex.addChild(null, 3)); shouldThrow(nullException, () -> vertex.addChild(null, null)); @@ -1277,9 +1303,9 @@ public class TestGraph { shouldContain(vertex.getEdgesIn()); shouldContain(vertex.getEdgesOut()); - graph.addVertex("1"); - graph.addVertex("2"); - graph.addVertex("3"); + graph.add("1"); + graph.add("2"); + graph.add("3"); graph.addEdge("1", "2", 2); graph.addEdge("3", "stronzo", 6); @@ -1330,23 +1356,23 @@ public class TestGraph { vertex.remove(); assertFalse(vertex.isStillContained()); assertFalse(graph.contains(vertex.getValue())); - assertEquals(3, graph.numberOfVertices()); + assertEquals(3, graph.size()); - shouldThrow(unsuppException, () -> vertex.addChild(null, null)); - shouldThrow(unsuppException, () -> vertex.mark(null)); - shouldThrow(unsuppException, () -> vertex.removeChild(null)); - shouldThrow(unsuppException, () -> vertex.visit(null, null)); - shouldThrow(unsuppException, vertex::unMark); - shouldThrow(unsuppException, vertex::getAncestors); - shouldThrow(unsuppException, vertex::getChildren); - shouldThrow(unsuppException, vertex::getEdgesOut); - shouldThrow(unsuppException, vertex::getEdgesIn); - shouldThrow(unsuppException, vertex::getChildrenAsVertex); - shouldThrow(unsuppException, vertex::getAncestorsAsVertex); - shouldThrow(unsuppException, vertex::getMarks); + shouldThrow(unSuppException, () -> vertex.addChild(null, null)); + shouldThrow(unSuppException, () -> vertex.mark(null)); + shouldThrow(unSuppException, () -> vertex.removeChild(null)); + shouldThrow(unSuppException, () -> vertex.visit(null, null)); + shouldThrow(unSuppException, vertex::unMark); + shouldThrow(unSuppException, vertex::getAncestors); + shouldThrow(unSuppException, vertex::getChildren); + shouldThrow(unSuppException, vertex::getEdgesOut); + shouldThrow(unSuppException, vertex::getEdgesIn); + shouldThrow(unSuppException, vertex::getChildrenAsVertex); + shouldThrow(unSuppException, vertex::getAncestorsAsVertex); + shouldThrow(unSuppException, vertex::getMarks); vertex.addIfAbsent(); - assertEquals(4, graph.numberOfVertices()); + assertEquals(4, graph.size()); } @Test @@ -1401,33 +1427,35 @@ public class TestGraph { marks.put("6", new HashSet<>(temp)); temp.clear(); - graph.addAllVertices(vertices); + graph.addAll(vertices); graph.addAllEdges(edges); marks.forEach((v, m) -> m.forEach(mk -> graph.mark(v, mk))); + GraphSaveStructure struct = new GraphSaveStructure<>(); try { - Graph.save(graph, fileName); - Graph.load(graph, fileName, String.class, Integer.class); + 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.removeAllVertex(); - Graph.load(graph, fileName, String.class, Integer.class); + 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()); } try { - Graph.load(graph, "sadadafacensi", String.class, Integer.class); + struct.load(graph, "sadadafacensi", String.class, Integer.class); fail("Should have been thrown IOException"); - } catch (Exception ignore) { - if (!(ignore instanceof IOException)) - fail("Should have been thrown IOException " + ignore.getMessage()); + } catch (Exception e) { + if (!(e instanceof IOException)) + fail("Should have been thrown IOException " + e.getMessage()); } shouldContain(graph.vertices(), vertices.toArray()); @@ -1435,16 +1463,16 @@ public class TestGraph { //marks.forEach((v, m) -> shouldContain(graph.getMarks(v), m.toArray())); try { - Graph.load(graph, fileName + ".fail", String.class, Integer.class); + struct.load(graph, fileName + ".fail", String.class, Integer.class); fail("Should have been thrown JsonSyntaxException"); - } catch (Exception ignore) { - if (!(ignore instanceof JsonSyntaxException)) - fail("Should have been thrown JsonSyntaxException " + ignore.getMessage()); + } catch (Exception e) { + if (!(e instanceof JsonSyntaxException)) + fail("Should have been thrown JsonSyntaxException " + e.getMessage()); } graph = null; shouldThrow(new NullPointerException(), () -> { try { - Graph.load(graph, fileName, String.class, Integer.class); + struct.load(graph, fileName, String.class, Integer.class); } catch (IOException e) { fail(); e.printStackTrace(); @@ -1454,28 +1482,29 @@ public class TestGraph { private void shouldContain(Collection actual, Object... expected) { - assertTrue("You should pass me a collection!", actual != null); - assertEquals("They have not the same number of elements\nActual: " + actual, expected.length, actual.size()); + assertNotNull(actual, "You should pass me a collection!"); + assertEquals(expected.length, actual.size(), "They have not the same number of elements\nActual: " + actual); for (Object obj : expected) - assertTrue("Not containing: [" + obj + "]\nBut has: " + actual, actual.contains(obj)); + assertTrue(actual.contains(obj), "Not containing: [" + obj + "]\nBut has: " + actual); } private void shouldContainInOrder(List actual, Object... expected) { - assertTrue("You should pass me a list!", actual != null); - assertEquals("They have not the same number of elements\nActual: " + actual, expected.length, actual.size()); + assertNotNull(actual, "You should pass me a list!"); + assertEquals(expected.length, actual.size(), "They have not the same number of elements\nActual: " + actual); for (int i = 0; i < actual.size(); i++) - assertEquals("Index: " + i, expected[i], actual.get(i)); + assertEquals(expected[i], actual.get(i), "Index: " + i); } private void shouldThrow(Exception expected, Runnable runnable) { try { runnable.run(); - fail("It has't thrown: " + expected.getClass().getSimpleName()); + fail("It hasn't thrown: " + expected.getClass().getSimpleName()); } catch (Exception actual) { assertEquals(expected.getClass(), actual.getClass()); - assertEquals(expected.getMessage(), actual.getMessage()); + if(expected.getMessage()!=null) + assertEquals(expected.getMessage(), actual.getMessage()); } } } diff --git a/test/resources/test.json b/test/resources/test.json index adb59a0..3862700 100644 --- a/test/resources/test.json +++ b/test/resources/test.json @@ -1 +1 @@ -{"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"}],"other":""} \ No newline at end of file +{"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