Path refactoring net.berack is now the corect pakage, update java to 23
This commit is contained in:
677
src/main/java/net/berack/upo/Graph.java
Normal file
677
src/main/java/net/berack/upo/Graph.java
Normal file
@@ -0,0 +1,677 @@
|
||||
package net.berack.upo;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.Vertex;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
import net.berack.upo.graph.visit.BFS;
|
||||
import net.berack.upo.graph.visit.Dijkstra;
|
||||
import net.berack.upo.graph.visit.VisitInfo;
|
||||
|
||||
/**
|
||||
* An abstract class for the graphs.<br>
|
||||
* This class is used for the graphs in general.<br>
|
||||
* There are more specific {@link GraphDirected} and {@link GraphUndirected} edges graph interfaces.<br>
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public abstract class Graph<V> implements Iterable<V> {
|
||||
|
||||
//------------------- STAIC -----------------
|
||||
|
||||
public static final int NO_EDGE = 0;
|
||||
public final static String NOT_CONNECTED = "The source vertex doesn't have a path that reach the destination";
|
||||
public final static String PARAM_NULL = "The parameter must not be null";
|
||||
public final static String VERTEX_NOT_CONTAINED = "The vertex must be contained in the graph";
|
||||
public final static Comparator<Object> OBJECT_COMPARATOR = new Comparator<Object>() {
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
return o1.hashCode() - o2.hashCode();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the default map. All operations are O(log(n))<br>
|
||||
* It returns a TreeMap with a ObjectComparator as comparator.<br>
|
||||
* This way all the graphs will use the same maps.<br>
|
||||
* It is not required to use this method, but it is highly recommended.<br>
|
||||
*
|
||||
* @return A newly created TreeMap instance with ObjectsComparator as comparator
|
||||
*/
|
||||
public final static <X, Y> Map<X, Y> getDefaultMap() {
|
||||
return new TreeMap<X, Y>(OBJECT_COMPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the default set. All operations are O(log(n))<br>
|
||||
* It returns a TreeSet with a ObjectComparator as comparator.<br>
|
||||
* This way all the graphs will use the same sets.<br>
|
||||
* It is not required to use this method, but it is highly recommended.<br>
|
||||
*
|
||||
* @return A newly created TreeSet instance with ObjectsComparator as comparator
|
||||
*/
|
||||
public final static <X> Set<X> getDefaultSet() {
|
||||
return new TreeSet<X>(OBJECT_COMPARATOR);
|
||||
}
|
||||
|
||||
//------------------- INSTANCE -----------------
|
||||
|
||||
/**
|
||||
* Map that contains the vertex as key and a set of all the marker associated with it.
|
||||
*/
|
||||
private final Map<V, Set<Object>> markers = getDefaultMap();
|
||||
|
||||
/**
|
||||
* Get a new instance of this graph.
|
||||
*
|
||||
* @return A new instance of the graph
|
||||
*/
|
||||
protected abstract Graph<V> getNewInstance();
|
||||
|
||||
/**
|
||||
* Check if the vertex passed is contained in the graph or not.<br>
|
||||
* The vertex V1 is contained in the graph G, if and only if:<br>
|
||||
* exist V2 in G such that V2 == V1
|
||||
*
|
||||
* @param vertex the vertex to check
|
||||
* @return true if the vertex is contained, false otherwise
|
||||
*/
|
||||
public abstract boolean contains(V vertex) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Get an instance of the vertex linked with this graph.<br>
|
||||
* For more info see {@link Vertex}
|
||||
*
|
||||
* @param vertex the vertex
|
||||
* @return a vertex
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public final Vertex<V> get(V vertex) throws IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
return new Vertex<>(this, vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vertex to the graph. If it's already in the graph it will be replaced and all its edges will be reset.<br>
|
||||
* Of course the vertex added will have no marks nor edge to any other vertex nor form any other vertex.
|
||||
*
|
||||
* @param vertex the vertex to add
|
||||
* @throws NullPointerException if the vertex is null
|
||||
*/
|
||||
public abstract void add(V vertex) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Add the specified vertex to the graph only if the graph doesn't contains it.<br>
|
||||
* The graph contains a vertex only if the method {@link #contains(Object)} returns true.
|
||||
*
|
||||
* @param vertex the vertex to add
|
||||
* @return true if it adds a vertex, false if it was already in the graph
|
||||
* @throws NullPointerException if the vertex is null
|
||||
*/
|
||||
public final boolean addIfAbsent(V vertex) throws NullPointerException {
|
||||
if (contains(vertex))
|
||||
return false;
|
||||
add(vertex);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the vertices contained in the collection to the graph.<br>
|
||||
* If a vertex is contained in the collection and in the graph is ignored and it will not be replaced.<br>
|
||||
* Null vertices will be ignored and they will not be added to the graph.
|
||||
*
|
||||
* @param vertices a collection of the vertices to add
|
||||
* @throws NullPointerException if the set is null
|
||||
*/
|
||||
public void addAll(Collection<V> vertices) throws NullPointerException {
|
||||
check(vertices);
|
||||
for (V vertex : vertices)
|
||||
addIfAbsent(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the selected vertex from the graph.<br>
|
||||
* After this method's call the vertex will be no longer present in the graph, and nether all his edges and marks.
|
||||
*
|
||||
* @param vertex the vertex to remove
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained
|
||||
*/
|
||||
public abstract void remove(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Remove all the vertex contained in the graph.<br>
|
||||
* After this method's call the graph will be empty; no vertices nor edges.
|
||||
*/
|
||||
public void removeAll() {
|
||||
unMarkAll();
|
||||
for (V vertex : vertices())
|
||||
remove(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the edge between the two vertex passed is contained in the graph or not.<br>
|
||||
* If one of the two vertices is not contained in the graph, then even the edge isn't
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @return true if the edge is contained, false otherwise
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
*/
|
||||
public boolean containsEdge(V vertex1, V vertex2) throws NullPointerException {
|
||||
try {
|
||||
return getWeight(vertex1, vertex2) != NO_EDGE;
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weight of the selected edge.<br>
|
||||
* If the edge doesn't exist, then 0 is returned
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @return the weight previously set, or 0 if the edge doesn't exist
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract int getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Add an edge between the two vertex.<br>
|
||||
* This method will overwrite any existing edge between the two vertex.<br>
|
||||
* If there was a previous edge then it is returned
|
||||
*
|
||||
* @param edge the edge to add
|
||||
* @return 0 or the previous weight of the edge if there was already one
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public final int addEdge(Edge<V> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdge(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an edge between the two vertex.<br>
|
||||
* This method will overwrite any existing edge between the two vertices.<br>
|
||||
* By default using this method will set the edge to the value 1.
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public final void addEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
addEdge(vertex1, vertex2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an edge between the two vertex.<br>
|
||||
* This method will overwrite any existing edge between the two vertex.<br>
|
||||
* If there was a previous edge then it's value is returned.<br>
|
||||
* If the weight passed is equals to 0 or {@link Graph#NO_EDGE}, then
|
||||
* the edge will be removed.
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @param weight the weight of the edge
|
||||
* @return 0 or the previous weight of the edge if there was already one
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract int addEdge(V vertex1, V vertex2, int weight) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* This particular function add an edge to the graph.<br>
|
||||
* If one of the two, or both vertices of the edge aren't contained in the graph, then the vertices will be added.<br>
|
||||
* This method will overwrite any existing edge between the two vertices.<br>
|
||||
* If there was a previous edge then it is returned
|
||||
*
|
||||
* @param edge the edge to add
|
||||
* @return 0 or the previous weight of the edge if there was already one
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
*/
|
||||
public final int addEdgeAndVertices(Edge<V> edge) throws NullPointerException, IllegalArgumentException {
|
||||
return addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* This particular function add an edge to the graph.<br>
|
||||
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.<br>
|
||||
* This method will overwrite any existing edge between the two vertices.<br>
|
||||
* By default using this method will set the edge to the value 1.
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
*/
|
||||
public final int addEdgeAndVertices(V vertex1, V vertex2) throws NullPointerException {
|
||||
return addEdgeAndVertices(vertex1, vertex2, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This particular function add an edge to the graph.<br>
|
||||
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.<br>
|
||||
* This method will overwrite any existing edge between the two vertices.<br>
|
||||
* If there was a previous edge then it is returned
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @param weight the weight of the edge
|
||||
* @return 0 or the previous weight of the edge if there was already one
|
||||
* @throws NullPointerException if one of the parameter is null
|
||||
*/
|
||||
public final int addEdgeAndVertices(V vertex1, V vertex2, int weight) throws NullPointerException {
|
||||
addIfAbsent(vertex1);
|
||||
addIfAbsent(vertex2);
|
||||
return addEdge(vertex1, vertex2, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the edges of the collection to the graph.<br>
|
||||
* If one of the two, or both vertices aren't contained in the graph, then the vertices will be added.<br>
|
||||
* Any null edges will be ignored.<br>
|
||||
* This method will overwrite any existing edge between the two vertices.
|
||||
*
|
||||
* @param edges the edges to add
|
||||
* @throws NullPointerException if the set is null
|
||||
*/
|
||||
public void addAllEdges(Collection<Edge<V>> edges) throws NullPointerException {
|
||||
edges.forEach(edge -> addEdgeAndVertices(edge.getSource(), edge.getDestination(), edge.getWeight()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the edge between the two vertex by setting it's value to 0.<br>
|
||||
* If the edge doesn't exist, then this call does nothing.<br>
|
||||
* This method is equivalent to calling {@link Graph#addEdge(Object, Object, int)} )}
|
||||
* with the weight set to {@link Graph#NO_EDGE}
|
||||
*
|
||||
* @param vertex1 a vertex of the graph
|
||||
* @param vertex2 a vertex of the graph
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public void removeEdge(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
addEdge(vertex1, vertex2, NO_EDGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all edges form a particular vertex of the graph.<br>
|
||||
* After this method's call the selected vertex will have 0 edges.<br>
|
||||
* It will be no longer possible to reach this vertex from any other vertex, and vice versa.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
unMark(vertex);
|
||||
remove(vertex);
|
||||
add(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the edges of the graph.<br>
|
||||
* After this method's call the graph will have only vertices, and no edge.
|
||||
*/
|
||||
public void removeAllEdge() {
|
||||
Set<V> vertices = vertices();
|
||||
removeAll();
|
||||
addAll(vertices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the edges of a particular vertex.<br>
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return a set of edges
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract Set<Edge<V>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Get all the vertices that are children of the vertex passed as parameter.<br>
|
||||
* The vertices V(0-N) that are 'children' of a vertex V1, are all the vertices that have an edge
|
||||
* where V1 is the source of that edge.
|
||||
*
|
||||
* @param vertex the source vertex
|
||||
* @return a set of vertices
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Get all the vertices that have the vertex passed as their child.<br>
|
||||
* Basically is the opposite of {@link #getChildren(Object)}
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return a set of ancestors of the vertex
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Get all the marks of this graph.<br>
|
||||
* Specifically it will return a Set of marks where every mark<br>
|
||||
* as associated at least one vertex of the graph.<br>
|
||||
* If the graph doesn't have vertex marked then it is returned an empty Set.
|
||||
*
|
||||
* @return a set of marks
|
||||
*/
|
||||
public final Set<Object> marks() {
|
||||
Set<Object> ret = getDefaultSet();
|
||||
markers.forEach((_, set) -> ret.addAll(set));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the specified vertex the mark passed.<br>
|
||||
* A vertex can have multiple marker.<br>
|
||||
* The null marker cannot be used.
|
||||
*
|
||||
* @param vertex the vertex to mark
|
||||
* @param mark the mark to add
|
||||
* @throws NullPointerException if one of the param is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public final void mark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
check(mark);
|
||||
checkVert(vertex);
|
||||
Set<Object> marks = markers.computeIfAbsent(vertex, _ -> getDefaultSet());
|
||||
marks.add(mark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the selected mark from the vertex.<br>
|
||||
*
|
||||
* @param vertex the vertex where remove the mark
|
||||
* @param mark the mark to remove
|
||||
* @throws NullPointerException if a param is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public final void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
|
||||
check(mark);
|
||||
checkVert(vertex);
|
||||
markers.computeIfPresent(vertex, (_, set) -> {
|
||||
set.remove(mark);
|
||||
if (set.size() > 0)
|
||||
return set;
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmark the vertex selected.<br>
|
||||
* After this call the vertex will not have any marked object to himself.
|
||||
*
|
||||
* @param vertex the vertex
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public final void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
try {
|
||||
markers.remove(vertex).clear();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the vertices that are marked with the specific mark passed.<br>
|
||||
* If there aren't vertices with that mark then it is returned an empty set.<br>
|
||||
*
|
||||
* @param mark the mark
|
||||
* @return a set of all the vertices that are marked with that specific mark
|
||||
* @throws NullPointerException if the mark is null
|
||||
*/
|
||||
public final Set<V> getMarkedWith(Object mark) throws NullPointerException {
|
||||
check(mark);
|
||||
Set<V> vertices = getDefaultSet();
|
||||
markers.forEach((v, set) -> {
|
||||
if (set.contains(mark))
|
||||
vertices.add(v);
|
||||
});
|
||||
return vertices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the marker of this vertex.<br>
|
||||
* If the vertex doesn't have any mark, then it will return an empty set.<br>
|
||||
* Note: modifying the returned Set affect the marker of the vertex.
|
||||
*
|
||||
* @param vertex the vertex
|
||||
* @return a set of all the mark to the vertex or an empty Set if none
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public final Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
return markers.getOrDefault(vertex, getDefaultSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the selected mark from all the vertices
|
||||
*
|
||||
* @param mark the mark to remove
|
||||
* @throws NullPointerException if the mark is null
|
||||
*/
|
||||
public final void unMarkAll(Object mark) throws NullPointerException {
|
||||
check(mark);
|
||||
Set<V> toRemove = getDefaultSet();
|
||||
markers.forEach((v, set) -> {
|
||||
set.remove(mark);
|
||||
if (set.size() == 0)
|
||||
toRemove.add(v);
|
||||
});
|
||||
markers.keySet().removeAll(toRemove);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the marker to all the vertex.<br>
|
||||
* After this call the {@link #getMarks(Object)} applied to any vertex will return an empty set
|
||||
*/
|
||||
public final void unMarkAll() {
|
||||
markers.values().forEach(Set::clear);
|
||||
markers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the vertices in the graph.<br>
|
||||
* If the graph doesn't contains vertices, it'll return an empty Set.<br>
|
||||
*
|
||||
* @return a set that include all the vertices
|
||||
*/
|
||||
public Set<V> vertices() {
|
||||
Set<V> vertices = getDefaultSet();
|
||||
forEach(vertices::add);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the edges in the graph.<br>
|
||||
* If the graph doesn't contains edges, it'll return an empty Set.<br>
|
||||
*
|
||||
* @return a Set that include all the edges
|
||||
*/
|
||||
public abstract Set<Edge<V>> edges();
|
||||
|
||||
/**
|
||||
* Tells the degree of a vertex.<br>
|
||||
* The degree of a vertex is the quantity of edges that have.<br>
|
||||
* Basically, it'll count how many edge it have.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return the degree of the vertex
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public abstract int degree(V vertex) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tells how many vertices are in the graph.
|
||||
*
|
||||
* @return the number of vertices
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
/**
|
||||
* Tells how many edges are in the graph.
|
||||
*
|
||||
* @return the number of edges
|
||||
*/
|
||||
public abstract int numberOfEdges();
|
||||
|
||||
/**
|
||||
* Visit the graph accordingly to the strategy that is passed.<br>
|
||||
* Some strategy can accept a source vertex null, because they visit all the graph anyway.
|
||||
*
|
||||
* @param source the starting vertex for the visit
|
||||
* @param strategy the algorithm for visiting the graph
|
||||
* @param visit the function to apply at each vertex visited
|
||||
* @return an info of the visit if provided by the strategy
|
||||
* @throws NullPointerException if one of the parameter is null (except the consumer)
|
||||
* @throws UnsupportedOperationException in the case the visit cannot be applied to the graph
|
||||
*/
|
||||
public final VisitInfo<V> visit(V source, VisitStrategy<V> strategy, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
return strategy.visit(this, source, visit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sub-graph of the current one based on the maximum depth that is given.<br>
|
||||
* If the depth is 1 then only the source and it's children will be in the sub-graph.<br>
|
||||
* If the depth is 2 then only the source, it's children and it's children of it's children will be in the sub-graph.<br>
|
||||
* And so on.<br>
|
||||
* 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)
|
||||
* @return a sub-graph of the original
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained
|
||||
*/
|
||||
public final Graph<V> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(source);
|
||||
Graph<V> sub = getNewInstance();
|
||||
Set<V> vertices = getDefaultSet();
|
||||
new BFS<V>().setMaxDepth(Math.max(depth, 0)).visit(this, source, vertices::add);
|
||||
|
||||
sub.addAll(vertices);
|
||||
for (V src : vertices)
|
||||
for (V dest : getChildren(src))
|
||||
if (sub.contains(dest))
|
||||
sub.addEdge(src, dest, this.getWeight(src, dest));
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sub-graph of the current one with only the vertex marked with the selected markers. (OR set operation)<br>
|
||||
* Each vertex will have all his markers and his edges, but only the ones with the destination marked with the same marker.<br>
|
||||
* If the marker is not specified or is null then the returning graph will have all the vertices that are not marked by any marker.<br>
|
||||
* If in the list of markers there is a null marker it will be skipped.<br>
|
||||
* If the graph doesn't contain any vertex with any marker passed then an empty graph is returned.
|
||||
*
|
||||
* @param marker one or more markers
|
||||
* @return a sub-graph of the current graph
|
||||
*/
|
||||
public final Graph<V> subGraph(Object... marker) {
|
||||
final Graph<V> sub = getNewInstance();
|
||||
final Set<V> allVertices = getDefaultSet();
|
||||
final Set<Object> allMarkers = getDefaultSet();
|
||||
|
||||
if (marker != null && marker.length > 0)
|
||||
for(int i=0; i<marker.length; i++)
|
||||
if(marker[i] != null)
|
||||
allMarkers.add(marker[i]);
|
||||
|
||||
if(allMarkers.size() > 0)
|
||||
markers.forEach((v, set) -> {
|
||||
if (!Collections.disjoint(allMarkers, set))
|
||||
allVertices.add(v);
|
||||
});
|
||||
else {
|
||||
Set<V> toAdd = vertices();
|
||||
toAdd.removeAll(markers.keySet());
|
||||
allVertices.addAll(toAdd);
|
||||
}
|
||||
|
||||
sub.addAll(allVertices);
|
||||
for (V src : sub.vertices()) {
|
||||
for (Object mark : getMarks(src))
|
||||
sub.mark(src, mark);
|
||||
for (V dest : getChildren(src))
|
||||
if (sub.contains(dest))
|
||||
sub.addEdge(src, dest, getWeight(src, dest));
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum path from the source vertex to the destination vertex.<br>
|
||||
* If the source vertex can't reach the destination, then an exception is thrown.
|
||||
*
|
||||
* @param source the vertex where to start
|
||||
* @param destination the destination chosen
|
||||
* @return an ordered list of edges from source to destination that represent the minimum path between the two vertices
|
||||
* @throws NullPointerException if one of the parameter is null (except the consumer)
|
||||
* @throws IllegalArgumentException if the vertex is not in the graph
|
||||
* @throws UnsupportedOperationException if from the source it's not possible to reach the destination
|
||||
*/
|
||||
public final List<Edge<V>> distance(V source, V destination) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
checkVert(source, destination);
|
||||
List<Edge<V>> path = distance(source).get(destination);
|
||||
if (path == null)
|
||||
throw new UnsupportedOperationException(NOT_CONNECTED);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum path from the source vertex to all the possible reachable vertices.
|
||||
*
|
||||
* @param source the vertex where to start
|
||||
* @return a map containing all the possible reachable vertices from the source and the minimum path to reach them
|
||||
* @throws NullPointerException if one of the parameter is null (except the consumer)
|
||||
* @throws IllegalArgumentException if the vertex is not in the graph
|
||||
*/
|
||||
public final Map<V, List<Edge<V>>> distance(V source) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(source);
|
||||
Dijkstra<V> dijkstra = new Dijkstra<>();
|
||||
dijkstra.visit(this, source, null);
|
||||
return dijkstra.getLastDistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the object passed is not null.
|
||||
* If it's null then throws eventual exception
|
||||
*
|
||||
* @param objects the objects to test
|
||||
*/
|
||||
protected final void check(Object... objects) {
|
||||
for (Object obj : objects)
|
||||
if (obj == null)
|
||||
throw new NullPointerException(PARAM_NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the vertex passed is not null and if exist in the graph.
|
||||
* If not then throws eventual exception
|
||||
*
|
||||
* @param vertices the vertices to test
|
||||
*/
|
||||
@SafeVarargs
|
||||
protected final void checkVert(V... vertices) {
|
||||
check((Object[]) vertices);
|
||||
for (V vert : vertices)
|
||||
try {
|
||||
if (!contains(vert))
|
||||
throw new IllegalArgumentException(VERTEX_NOT_CONTAINED);
|
||||
} catch (ClassCastException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
206
src/main/java/net/berack/upo/GraphDirected.java
Normal file
206
src/main/java/net/berack/upo/GraphDirected.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package net.berack.upo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitSCC;
|
||||
import net.berack.upo.graph.VisitTopological;
|
||||
import net.berack.upo.graph.visit.Tarjan;
|
||||
|
||||
/**
|
||||
* This is a more specific interface for an implementation of a Undirected Graph.<br>
|
||||
* An Undirected Graph is a Graph where an arc or edge can be traversed in both ways.<br>
|
||||
* For example an arc between A and B can be traversed even from B to A.<br>
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public abstract class GraphDirected<V> extends Graph<V> {
|
||||
String NOT_DAG = "The graph is not a DAG";
|
||||
|
||||
/**
|
||||
* Tells if the graph has some cycle.<br>
|
||||
* A cycle is detected if visiting the graph G starting from V1 (that is any of the vertex of G),
|
||||
* the visit can return to V1 in any point.
|
||||
*
|
||||
* @return true if has cycle, false otherwise
|
||||
*/
|
||||
public final boolean isCyclic() {
|
||||
return stronglyConnectedComponents().size() != size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the graph has the property of DAG (Directed Acyclic Graph).<br>
|
||||
* A graph is a DAG only if absent of any cycle. ( see {@link #isCyclic()} )
|
||||
*
|
||||
* @return true if is a DAG, false otherwise
|
||||
*/
|
||||
public final boolean isDAG() {
|
||||
return !isCyclic();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the edges that goes in the vertex.<br>
|
||||
* After this method's call it will be no longer possible travel to this vertex.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
for (V ancestor : getAncestors(vertex))
|
||||
removeEdge(ancestor, vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the edges that start from this vertex.<br>
|
||||
* After this method's call it will be no longer possible travel to any vertex from this one.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @throws NullPointerException if one of the parameters is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
for (V child : getChildren(vertex))
|
||||
removeEdge(vertex, child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the edges of a particular vertex.<br>
|
||||
* Note: the edges that are returned are the one that have this vertex as destination and another as source.<br>
|
||||
* Note2: depending on the implementation, modifying the returned Set<br>
|
||||
* could affect the graph behavior and the changes could be reflected to the graph.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return a Set of edges
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public Set<Edge<V>> getEdgesIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
Set<V> ancestors = getAncestors(vertex);
|
||||
Set<Edge<V>> edgesIn = getDefaultSet();
|
||||
|
||||
for (V ancestor : ancestors) {
|
||||
int weight = getWeight(ancestor, vertex);
|
||||
if (weight != NO_EDGE)
|
||||
edgesIn.add(new Edge<>(ancestor, vertex, weight));
|
||||
}
|
||||
return edgesIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the edges that goes OUT of a particular vertex.<br>
|
||||
* Note: the edges that are returned are the one that have this vertex as source and another one as destination.<br>
|
||||
* Note2: depending on the implementation, modifying the returned Set<br>
|
||||
* could affect the graph behavior and the changes could be reflected to the graph.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return a Set of edges
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public Set<Edge<V>> getEdgesOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
Set<V> children = getChildren(vertex);
|
||||
Set<Edge<V>> edgesOut = getDefaultSet();
|
||||
|
||||
for (V child : children) {
|
||||
int weight = getWeight(vertex, child);
|
||||
if (weight != NO_EDGE)
|
||||
edgesOut.add(new Edge<>(vertex, child, weight));
|
||||
}
|
||||
return edgesOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the degree of all the edges that goes to this vertex.<br>
|
||||
* Basically, it'll count how many edge towards himself it have.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return the in degree of the vertex
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return getAncestors(vertex).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the degree of all the edges that goes form this vertex to others.<br>
|
||||
* Basically, it'll count how many edge towards any other vertex it have.
|
||||
*
|
||||
* @param vertex a vertex of the graph
|
||||
* @return the out degree of the vertex
|
||||
* @throws NullPointerException if the vertex is null
|
||||
* @throws IllegalArgumentException if the vertex is not contained in the graph
|
||||
*/
|
||||
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return getChildren(vertex).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will create a new Graph that is the transposed version of the original.<br>
|
||||
* At the end of this method the new graph will have all the edges inverted in orientation.<br>
|
||||
* Example: if the graph G contains (V1, V2, V3) as vertex, and (V1->V2, V3->V2) as edges,
|
||||
* the transpose graph G' will contain (V1, V2, V3) as vertex, and (V2->V1, V2->V3) as edges.
|
||||
*
|
||||
* @return a transposed graph of this instance
|
||||
*/
|
||||
public final GraphDirected<V> transpose() {
|
||||
GraphDirected<V> transposed = (GraphDirected<V>) getNewInstance();
|
||||
transposed.addAll(vertices());
|
||||
|
||||
for (V vertex : transposed)
|
||||
for (V child : getChildren(vertex))
|
||||
transposed.addEdge(child, vertex, getWeight(vertex, child));
|
||||
return transposed;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current graph is a DAG, it returns a topological sort of this graph.<br>
|
||||
* A topological ordering of a graph is a linear ordering of its vertices such that for
|
||||
* every directed edge (V1, V2) from vertex V1 to vertex V2, V2 comes before V1 in the ordering.
|
||||
*
|
||||
* @return a list containing the topological order of the vertices
|
||||
* @throws UnsupportedOperationException if the graph is not a DAG (see {@link #isDAG()})
|
||||
*/
|
||||
public final List<V> topologicalSort() throws UnsupportedOperationException {
|
||||
VisitTopological<V> visit = new Tarjan<>();
|
||||
visit.visit(this, null, null);
|
||||
|
||||
if (visit.getTopologicalSort() == null)
|
||||
throw new UnsupportedOperationException(NOT_DAG);
|
||||
return visit.getTopologicalSort();
|
||||
}
|
||||
|
||||
/**
|
||||
* The strongly connected components or disconnected components of an arbitrary directed graph
|
||||
* form a partition into subgraphs that are themselves strongly connected.
|
||||
*
|
||||
* @return a Set containing the strongly connected components
|
||||
*/
|
||||
public final Set<Set<V>> stronglyConnectedComponents() {
|
||||
VisitSCC<V> visit = new Tarjan<>();
|
||||
visit.visit(this, null, null);
|
||||
return visit.getSCC();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
Set<Edge<V>> edges = getEdgesIn(vertex);
|
||||
edges.addAll(getEdgesOut(vertex));
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> edges() {
|
||||
Set<Edge<V>> set = getDefaultSet();
|
||||
forEach(v -> set.addAll(getEdgesIn(v)));
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int degree(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return degreeIn(vertex) + degreeOut(vertex);
|
||||
}
|
||||
}
|
||||
46
src/main/java/net/berack/upo/GraphUndirected.java
Normal file
46
src/main/java/net/berack/upo/GraphUndirected.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package net.berack.upo;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitMST;
|
||||
import net.berack.upo.graph.visit.Prim;
|
||||
|
||||
/**
|
||||
* This is a more specific interface for an implementation of a Directed Graph.<br>
|
||||
* A Directed Graph is a Graph where an arc or edge can be traversed in only one way.<br>
|
||||
* A directed edge between V1 and V2 is an edge that has V1 as source and V2 as destination.<br>
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public abstract class GraphUndirected<V> extends Graph<V> {
|
||||
|
||||
/**
|
||||
* The connected components of an arbitrary undirected graph form a partition into subgraphs that are themselves connected.
|
||||
*
|
||||
* @return a Set containing the strongly connected components
|
||||
*/
|
||||
public Set<Set<V>> connectedComponents() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* minimum spanning forest or minimum spamming tree of the graph
|
||||
*
|
||||
* @return A Set of edges representing the M.S.F.
|
||||
*/
|
||||
public Set<Edge<V>> minimumSpanningForest() {
|
||||
VisitMST<V> visit = new Prim<>();
|
||||
visit.visit(this, iterator().next(), null);
|
||||
return visit.getMST();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
Set<Edge<V>> edges = getDefaultSet();
|
||||
getChildren(vertex).forEach(v -> edges.add(new Edge<>(vertex, v, getWeight(vertex, v))));
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
113
src/main/java/net/berack/upo/graph/Edge.java
Normal file
113
src/main/java/net/berack/upo/graph/Edge.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class used for retrieving the edges of the graph.
|
||||
*
|
||||
* @param <V> the vertices
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Edge<V> implements Comparable<Edge<V>> {
|
||||
|
||||
/**
|
||||
* The source vertex
|
||||
*/
|
||||
private final V source;
|
||||
/**
|
||||
* The destination vertex
|
||||
*/
|
||||
private final V destination;
|
||||
/**
|
||||
* The weight of this edge
|
||||
*/
|
||||
private final int weight;
|
||||
|
||||
/**
|
||||
* Create a final version of this object with weight 1
|
||||
*
|
||||
* @param source the source of the edge
|
||||
* @param destination the destination of the edge
|
||||
*/
|
||||
public Edge(V source, V destination) {
|
||||
this(source, destination, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a final version of this object
|
||||
*
|
||||
* @param source the source of the edge
|
||||
* @param destination the destination of the edge
|
||||
* @param weight the weight of the edge
|
||||
*/
|
||||
public Edge(V source, V destination, int weight) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public Collection<V> getVertices() {
|
||||
if (source == null && destination == null)
|
||||
return List.of();
|
||||
if (source == null)
|
||||
return List.of(destination);
|
||||
if (destination == null)
|
||||
return List.of(source);
|
||||
return List.of(source, destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertex where the edge goes
|
||||
*
|
||||
* @return the vertex
|
||||
*/
|
||||
public V getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* The vertex where the edge starts from
|
||||
*
|
||||
* @return the vertex
|
||||
*/
|
||||
public V getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* The weight of the edge
|
||||
*
|
||||
* @return the weight
|
||||
*/
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + source + " -> " + destination + ", " + weight + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return obj.getClass().equals(getClass()) && obj.toString().equals(toString());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Edge<V> edge) {
|
||||
return weight - edge.weight;
|
||||
}
|
||||
}
|
||||
161
src/main/java/net/berack/upo/graph/ListGraph.java
Normal file
161
src/main/java/net/berack/upo/graph/ListGraph.java
Normal file
@@ -0,0 +1,161 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphDirected;
|
||||
|
||||
/**
|
||||
* An implementation of the graph using an adjacent list for representing the edges
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class ListGraph<V> extends GraphDirected<V> {
|
||||
|
||||
// in case of thread safety use -> Collections.synchronizedSortedMap(TreeMap)
|
||||
final private Map<V, List<Adj>> adj = getDefaultMap();
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return adj.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Graph<V> getNewInstance() {
|
||||
return new ListGraph<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(V vertex) {
|
||||
check(vertex);
|
||||
if (adj.containsKey(vertex))
|
||||
removeAllEdge(vertex);
|
||||
else
|
||||
adj.put(vertex, new LinkedList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) {
|
||||
check(vertex);
|
||||
return adj.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(V vertex) {
|
||||
checkVert(vertex);
|
||||
adj.remove(vertex);
|
||||
adj.forEach((_, list) -> list.remove(getAdj(list, vertex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEdge(V vertex1, V vertex2, int weight) {
|
||||
checkVert(vertex1, vertex2);
|
||||
|
||||
List<Adj> list = adj.get(vertex1);
|
||||
Adj a = getAdj(list, vertex2);
|
||||
int old = a == null ? NO_EDGE : a.weight;
|
||||
|
||||
if (weight == NO_EDGE)
|
||||
list.remove(a);
|
||||
else if (old == NO_EDGE)
|
||||
list.add(new Adj(vertex2, weight));
|
||||
else
|
||||
a.weight = weight;
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight(V vertex1, V vertex2) {
|
||||
checkVert(vertex1, vertex2);
|
||||
Adj a = getAdj(adj.get(vertex1), vertex2);
|
||||
return a == null ? NO_EDGE : a.weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
Set<V> children = getDefaultSet();
|
||||
for (Adj adj : adj.get(vertex))
|
||||
children.add(adj.vertex);
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
Set<V> ancestors = getDefaultSet();
|
||||
adj.forEach((v, list) -> {
|
||||
if (getAdj(list, vertex) != null)
|
||||
ancestors.add(v);
|
||||
});
|
||||
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* From here on there are some optimization for the methods of the generic DirectedGraph
|
||||
**/
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return adj.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
AtomicInteger size = new AtomicInteger(0);
|
||||
adj.values().forEach(list -> size.addAndGet(list.size()));
|
||||
return size.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
AtomicInteger degree = new AtomicInteger(0);
|
||||
adj.values().forEach(list -> degree.addAndGet(getAdj(list, vertex) != null ? 1 : 0));
|
||||
return degree.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
return adj.get(vertex).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
adj.get(vertex).clear();
|
||||
adj.forEach((_, list) -> list.remove(getAdj(list, vertex)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge() {
|
||||
adj.forEach((_, list) -> list.clear());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
adj.clear();
|
||||
}
|
||||
|
||||
private Adj getAdj(List<Adj> list, V vertex) {
|
||||
for (Adj adj : list)
|
||||
if (Objects.equals(adj.vertex, vertex))
|
||||
return adj;
|
||||
return null;
|
||||
}
|
||||
|
||||
private class Adj {
|
||||
private final V vertex;
|
||||
private int weight;
|
||||
|
||||
private Adj(V vertex, int weight) {
|
||||
this.vertex = vertex;
|
||||
this.weight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/main/java/net/berack/upo/graph/MapGraph.java
Normal file
116
src/main/java/net/berack/upo/graph/MapGraph.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphDirected;
|
||||
|
||||
/**
|
||||
* Graph that uses TreeMap for vertices and edges<br>
|
||||
* More specifically it utilizes a Map containing all the vertices mapped to all their edges<br>
|
||||
* Technically this version of the graph combine the fast adding/removing of the edges of the Matrix implementation,
|
||||
* with the low memory and fast adding/removing of vertices of the Linked List implementation.<br>
|
||||
* "Fast" in this case means O(log(n)) since it is a tree, so not tecnically as fast as O(1),
|
||||
* but better than O(n).<br>
|
||||
*
|
||||
* @param <V> the vertices
|
||||
* @author Berack96
|
||||
*/
|
||||
public class MapGraph<V> extends GraphDirected<V> {
|
||||
|
||||
/**
|
||||
* Map that contains the edges from a vertex to another<br>
|
||||
* The first vertex is the vertex where start the edge, the second one is where the edge goes<br>
|
||||
* If an edge exist, then it's weight is returned
|
||||
*/
|
||||
private final Map<V, Map<V, Integer>> edges = getDefaultMap();
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return edges.keySet().iterator();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Graph<V> getNewInstance() {
|
||||
return new MapGraph<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(V vertex) {
|
||||
check(vertex);
|
||||
edges.computeIfAbsent(vertex, _ -> new TreeMap<>());
|
||||
edges.forEach((_, adj) -> adj.remove(vertex));
|
||||
edges.get(vertex).clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) {
|
||||
check(vertex);
|
||||
return edges.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(V vertex) {
|
||||
checkVert(vertex);
|
||||
edges.remove(vertex);
|
||||
edges.forEach((_, map) -> map.remove(vertex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEdge(V vertex1, V vertex2, int weight) {
|
||||
checkVert(vertex1, vertex2);
|
||||
Map<V, Integer> edge = edges.get(vertex1);
|
||||
Integer old = edge.get(vertex2);
|
||||
old = old == null ? NO_EDGE : old;
|
||||
|
||||
if (weight == NO_EDGE)
|
||||
edge.remove(vertex2);
|
||||
else
|
||||
edge.put(vertex2, weight);
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight(V vertex1, V vertex2) {
|
||||
checkVert(vertex1, vertex2);
|
||||
Integer weight = edges.get(vertex1).get(vertex2);
|
||||
return weight == null ? NO_EDGE : weight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
return new HashSet<>(edges.get(vertex).keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
Set<V> ancestors = getDefaultSet();
|
||||
edges.forEach((v, adj) -> {
|
||||
if (adj.containsKey(vertex))
|
||||
ancestors.add(v);
|
||||
});
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
edges.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return edges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
AtomicInteger sum = new AtomicInteger(0);
|
||||
edges.forEach((_, map) -> sum.getAndAdd(map.size()));
|
||||
|
||||
return sum.get();
|
||||
}
|
||||
}
|
||||
196
src/main/java/net/berack/upo/graph/MatrixGraph.java
Normal file
196
src/main/java/net/berack/upo/graph/MatrixGraph.java
Normal file
@@ -0,0 +1,196 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphDirected;
|
||||
|
||||
/**
|
||||
* An implementation of the graph using a matrix for representing the edges
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class MatrixGraph<V> extends GraphDirected<V> {
|
||||
|
||||
private final Map<V, Integer> map = getDefaultMap();
|
||||
private int[][] matrix = new int[0][0];
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Graph<V> getNewInstance() {
|
||||
return new MatrixGraph<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(V vertex) {
|
||||
check(vertex);
|
||||
if (map.containsKey(vertex))
|
||||
removeAllEdge(vertex);
|
||||
else {
|
||||
map.put(vertex, map.size());
|
||||
matrix = modifyMatrix(map.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) {
|
||||
check(vertex);
|
||||
return map.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(V vertex) {
|
||||
checkVert(vertex);
|
||||
int x = map.remove(vertex);
|
||||
int newSize = map.size();
|
||||
|
||||
int[][] newMatrix = new int[newSize][newSize];
|
||||
for (int i = 0; i < newSize; i++)
|
||||
for (int j = 0; j < newSize; j++) {
|
||||
int indexI = i + (i < x ? 0 : 1);
|
||||
int indexJ = j + (j < x ? 0 : 1);
|
||||
|
||||
newMatrix[i][j] = matrix[indexI][indexJ];
|
||||
}
|
||||
|
||||
matrix = newMatrix;
|
||||
map.replaceAll((_, index) -> index > x ? index - 1 : index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEdge(V vertex1, V vertex2, int weight) {
|
||||
checkVert(vertex1, vertex2);
|
||||
int i = map.get(vertex1);
|
||||
int j = map.get(vertex2);
|
||||
|
||||
int old = matrix[i][j];
|
||||
matrix[i][j] = weight;
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight(V vertex1, V vertex2) {
|
||||
checkVert(vertex1, vertex2);
|
||||
return matrix[map.get(vertex1)][map.get(vertex2)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.get(vertex);
|
||||
Set<V> children = getDefaultSet();
|
||||
Map<Integer, V> invert = getInverted();
|
||||
|
||||
for (int i = 0; i < matrix.length; i++)
|
||||
if (matrix[x][i] != NO_EDGE)
|
||||
children.add(invert.get(i));
|
||||
return children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.get(vertex);
|
||||
Set<V> ancestors = getDefaultSet();
|
||||
Map<Integer, V> invert = getInverted();
|
||||
|
||||
for (int i = 0; i < matrix.length; i++)
|
||||
if (matrix[i][x] != NO_EDGE)
|
||||
ancestors.add(invert.get(i));
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
/**
|
||||
* From here on there are some optimization for the methods of the generic DirectedGraph
|
||||
**/
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
int sum = 0;
|
||||
for (int[] adj : matrix)
|
||||
for (int edge : adj)
|
||||
if (edge != NO_EDGE)
|
||||
sum++;
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeIn(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int degree = 0, x = map.get(vertex);
|
||||
for (int[] ints : matrix) degree += ints[x] == NO_EDGE ? 0 : 1;
|
||||
return degree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degreeOut(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int degree = 0, x = map.get(vertex);
|
||||
for (int ints : matrix[x]) degree += ints == NO_EDGE ? 0 : 1;
|
||||
return degree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.get(vertex);
|
||||
Arrays.fill(matrix[x], NO_EDGE);
|
||||
for (int[] ints : matrix) ints[x] = NO_EDGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge() {
|
||||
for (int[] ints : matrix)
|
||||
Arrays.fill(ints, NO_EDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
map.clear();
|
||||
matrix = new int[0][0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(Collection<V> vertices) throws NullPointerException {
|
||||
check(vertices);
|
||||
for (V vert : vertices)
|
||||
if (vert != null)
|
||||
map.compute(vert, (_, i) -> {
|
||||
if (i == null)
|
||||
return map.size();
|
||||
removeAllEdge(vert);
|
||||
return i;
|
||||
});
|
||||
matrix = modifyMatrix(map.size());
|
||||
}
|
||||
|
||||
private int[][] modifyMatrix(int newSize) {
|
||||
int oldSize = matrix.length;
|
||||
if (newSize <= oldSize)
|
||||
return matrix;
|
||||
|
||||
int[][] newMatrix = new int[newSize][newSize];
|
||||
for (int[] ints : newMatrix)
|
||||
Arrays.fill(ints, NO_EDGE);
|
||||
for (int i = 0; i < oldSize; i++)
|
||||
System.arraycopy(matrix[i], 0, newMatrix[i], 0, oldSize);
|
||||
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
private Map<Integer, V> getInverted() {
|
||||
Map<Integer, V> invert = getDefaultMap();
|
||||
map.forEach((v, i) -> invert.put(i, v));
|
||||
return invert;
|
||||
}
|
||||
}
|
||||
176
src/main/java/net/berack/upo/graph/MatrixUndGraph.java
Normal file
176
src/main/java/net/berack/upo/graph/MatrixUndGraph.java
Normal file
@@ -0,0 +1,176 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphUndirected;
|
||||
|
||||
public class MatrixUndGraph<V> extends GraphUndirected<V> {
|
||||
|
||||
Map<V, Integer> map = getDefaultMap();
|
||||
private int[][] matrix = new int[0][0];
|
||||
|
||||
@Override
|
||||
protected Graph<V> getNewInstance() {
|
||||
return new MatrixUndGraph<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(V vertex) throws NullPointerException {
|
||||
check(vertex);
|
||||
return map.containsKey(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(V vertex) throws NullPointerException {
|
||||
check(vertex);
|
||||
if (map.containsKey(vertex))
|
||||
removeAllEdge(vertex);
|
||||
else {
|
||||
map.put(vertex, map.size());
|
||||
matrix = modifyMatrix(map.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.remove(vertex);
|
||||
int newSize = map.size();
|
||||
|
||||
int[][] newMatrix = new int[newSize][];
|
||||
for (int i = 0; i < newSize; i++)
|
||||
newMatrix[i] = i < x ? matrix[i] : new int[i];
|
||||
|
||||
for (int i = x; i < newSize; i++)
|
||||
for (int j = 0; j < newMatrix[i].length; j++)
|
||||
newMatrix[i][j] = matrix[i + 1][j + (j < x ? 0 : 1)];
|
||||
|
||||
matrix = newMatrix;
|
||||
map.replaceAll((_, index) -> index > x ? index - 1 : index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeight(V vertex1, V vertex2) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex1, vertex2);
|
||||
int x = map.get(vertex1);
|
||||
int y = map.get(vertex2);
|
||||
return x == y ? 0 : matrix[Math.max(x, y)][Math.min(x, y)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEdge(V vertex1, V vertex2, int weight) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex1, vertex2);
|
||||
int x = map.get(vertex1);
|
||||
int y = map.get(vertex2);
|
||||
int max = Math.max(x, y);
|
||||
int min = Math.min(x, y);
|
||||
|
||||
int old = matrix[max][min];
|
||||
matrix[max][min] = weight;
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
V[] inverted = getInverted();
|
||||
Set<V> set = getDefaultSet();
|
||||
int x = map.get(vertex);
|
||||
for (int i = 0; i < matrix.length; i++)
|
||||
if (i < x && matrix[x][i] != 0)
|
||||
set.add(inverted[i]);
|
||||
else if (i > x && matrix[i][x] != 0)
|
||||
set.add(inverted[i]);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
return getChildren(vertex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> edges() {
|
||||
V[] inverted = getInverted();
|
||||
Set<Edge<V>> edges = getDefaultSet();
|
||||
|
||||
for (int i = 0; i < matrix.length; i++)
|
||||
for (int j = 0; j < matrix[i].length; j++)
|
||||
if (matrix[i][j] != NO_EDGE)
|
||||
edges.add(new Edge<>(inverted[i], inverted[j], matrix[i][j]));
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int degree(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.get(vertex);
|
||||
int degree = 0;
|
||||
for (int i = 0; i < x; i++)
|
||||
if (matrix[x][i] != NO_EDGE)
|
||||
degree++;
|
||||
for (int i = x; i < matrix.length; i++)
|
||||
if (matrix[i][x] != NO_EDGE)
|
||||
degree++;
|
||||
return degree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int numberOfEdges() {
|
||||
int num = 0;
|
||||
for (int[] ints : matrix)
|
||||
for (int edge : ints)
|
||||
if (edge != NO_EDGE)
|
||||
num++;
|
||||
return num;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return map.keySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge(V vertex) throws NullPointerException, IllegalArgumentException {
|
||||
checkVert(vertex);
|
||||
int x = map.get(vertex);
|
||||
|
||||
Arrays.fill(matrix[x], NO_EDGE);
|
||||
for (int i = x + 1; i < matrix.length; i++)
|
||||
matrix[i][x] = NO_EDGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllEdge() {
|
||||
for (int[] adj : matrix)
|
||||
Arrays.fill(adj, NO_EDGE);
|
||||
}
|
||||
|
||||
private int[][] modifyMatrix(int newSize) {
|
||||
int oldSize = matrix.length;
|
||||
if (newSize <= oldSize)
|
||||
return matrix;
|
||||
int[][] newMatrix = new int[newSize][];
|
||||
System.arraycopy(matrix, 0, newMatrix, 0, oldSize);
|
||||
|
||||
for (int i = oldSize; i < newSize; i++) {
|
||||
newMatrix[i] = new int[i];
|
||||
Arrays.fill(newMatrix[i], NO_EDGE);
|
||||
}
|
||||
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private V[] getInverted() {
|
||||
V[] invert = (V[]) new Object[map.size()];
|
||||
map.forEach((v, i) -> invert[i] = v);
|
||||
return invert;
|
||||
}
|
||||
}
|
||||
283
src/main/java/net/berack/upo/graph/Vertex.java
Normal file
283
src/main/java/net/berack/upo/graph/Vertex.java
Normal file
@@ -0,0 +1,283 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.visit.VisitInfo;
|
||||
|
||||
/**
|
||||
* Class used for represent a vertex of the graph.<br>
|
||||
* The vertex contained is linked with the graph, so if any changes are made to
|
||||
* it, then they will be reflected here.
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Vertex<V> {
|
||||
|
||||
public static final String REMOVED = "The vertex is no longer in the graph";
|
||||
|
||||
/**
|
||||
* The vertex associated
|
||||
*/
|
||||
private final V vertex;
|
||||
/**
|
||||
* The graph associated
|
||||
*/
|
||||
private final Graph<V> graph;
|
||||
|
||||
/**
|
||||
* Get a Vertex linked with the graph
|
||||
*
|
||||
* @param graph the graph of the vertex
|
||||
* @param vertex the vertex
|
||||
* @throws NullPointerException if one of the param is null
|
||||
*/
|
||||
public Vertex(Graph<V> graph, V vertex) throws NullPointerException {
|
||||
if (graph == null || vertex == null)
|
||||
throw new NullPointerException();
|
||||
this.graph = graph;
|
||||
this.vertex = vertex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the vertex
|
||||
*
|
||||
* @return the vertex
|
||||
*/
|
||||
public V get() {
|
||||
return vertex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the vertex with the associated string
|
||||
*
|
||||
* @param mark the marker
|
||||
* @throws NullPointerException if the marker is null
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void mark(Object mark) throws NullPointerException, UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.mark(vertex, mark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified mark from this vertex
|
||||
*
|
||||
* @param mark the marker
|
||||
* @throws NullPointerException if the mark is null
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void unMark(Object mark) throws UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.unMark(vertex, mark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the marker from the vertex
|
||||
*
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void unMark() throws UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.unMark(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the marks that are associated with this vertex
|
||||
*
|
||||
* @return a set of marks
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public Collection<Object> getMarks() throws UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
return graph.getMarks(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the vertex children of the current vertex
|
||||
*
|
||||
* @return all the children
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public Collection<V> getChildren() throws UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
return graph.getChildren(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the children of this vertex like {@link #getChildren()}, but as {@link Vertex}.<br>
|
||||
* In this way they are linked to the graph as this one.<br>
|
||||
* This method allocate a new object for each vertex, so it is more heavy.
|
||||
*
|
||||
* @return a collection of vertices that are children of the current one
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public Collection<Vertex<V>> getChildrenAsVertex() throws UnsupportedOperationException {
|
||||
Collection<V> children = getChildren();
|
||||
Collection<Vertex<V>> toReturn = new ArrayList<>(children.size());
|
||||
for (V vertex : children)
|
||||
toReturn.add(graph.get(vertex));
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the vertex ancestor of this vertex.<br>
|
||||
* The ancestors are all the vertices that have as destination this vertex.
|
||||
*
|
||||
* @return a collection of vertices
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public Collection<V> getAncestors() throws UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
return graph.getAncestors(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the ancestors of this vertex like {@link #getAncestors()}, but as {@link Vertex}.<br>
|
||||
* In this way they are linked to the graph as this one.<br>
|
||||
* This method allocate a new object for each vertex, so it is more heavy.
|
||||
*
|
||||
* @return a collection of vertices that are children of the current one
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public Collection<Vertex<V>> getAncestorsAsVertex() throws UnsupportedOperationException {
|
||||
Collection<V> ancestors = getAncestors();
|
||||
Collection<Vertex<V>> toReturn = new ArrayList<>(ancestors.size());
|
||||
for (V vertex : ancestors)
|
||||
toReturn.add(graph.get(vertex));
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the weight of the edge that connects<br>
|
||||
* this vertex to the vertex inserted.<br>
|
||||
* In the case that the two vertices aren't connected this method will return 0.
|
||||
*
|
||||
* @param child a child of this vertex
|
||||
* @return the weight to the child or 0 if the edge doesn't exist.
|
||||
*/
|
||||
public int getChildWeight(V child) {
|
||||
throwIfNotContained();
|
||||
return graph.getWeight(vertex, child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child to this vertex.<br>
|
||||
* The added child must be in the graph or it will return an exception.
|
||||
*
|
||||
* @param child the destination vertex of this edge
|
||||
* @param weight the weight of the edge
|
||||
* @throws NullPointerException if the param is null
|
||||
* @throws IllegalArgumentException if the child vertex is not contained in the graph
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void addChild(V child, int weight) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.addEdge(vertex, child, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child to this vertex.<br>
|
||||
* The added child must be in the graph or it will return an exception.<br>
|
||||
* This method will add the basic value for the weight provided by the graph. {@link Graph#addEdge(V, V)}
|
||||
*
|
||||
* @param child the destination vertex of this edge
|
||||
* @throws NullPointerException if the param is null
|
||||
* @throws IllegalArgumentException if the child vertex is not contained in the graph
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void addChild(V child) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.addEdge(vertex, child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a child of this vertex.
|
||||
* If the vertex passed as param is not a child, then this call does nothing.
|
||||
*
|
||||
* @param child the child of the current vertex
|
||||
* @throws NullPointerException if the param is null
|
||||
* @throws IllegalArgumentException if the child vertex is not contained in the graph
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
public void removeChild(V child) throws NullPointerException, IllegalArgumentException, UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
graph.removeEdge(vertex, child);
|
||||
}
|
||||
|
||||
/**
|
||||
* This call tell if the current vertex is still contained in the graph linked.<br>
|
||||
* While this function return false all the other methods will throw an exception.
|
||||
*
|
||||
* @return true if it is, false otherwise
|
||||
*/
|
||||
public boolean isStillContained() {
|
||||
return graph.contains(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vertex to the graph only if it's not already in the graph.
|
||||
*/
|
||||
public void addIfAbsent() {
|
||||
graph.addIfAbsent(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the vertex from the graph.<br>
|
||||
* After this call all the other methods will throw an exception
|
||||
*/
|
||||
public void remove() {
|
||||
if (graph.contains(vertex))
|
||||
graph.remove(vertex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the graph from this current vertex with the strategy assigned
|
||||
*
|
||||
* @param strategy the strategy of the visit
|
||||
* @param visit the function to apply at each vertex (can be null)
|
||||
* @return an info of the visit if supported by the strategy
|
||||
* @throws NullPointerException if the strategy is null
|
||||
* @throws UnsupportedOperationException if the vertex is not in the graph anymore
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public VisitInfo<V> visit(final VisitStrategy<V> strategy, final Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
throwIfNotContained();
|
||||
return graph.visit(vertex, strategy, visit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return vertex.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return vertex.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return obj instanceof Vertex && (Objects.equals(vertex, obj) || Objects.equals(vertex, ((Vertex<?>) obj).vertex));
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for throwing the UnsupportedOperationException if the vertex is not contained anymore
|
||||
*
|
||||
* @throws UnsupportedOperationException if IllegalArgumentException is thrown by the runnable
|
||||
*/
|
||||
private void throwIfNotContained() throws UnsupportedOperationException {
|
||||
if (!graph.contains(vertex))
|
||||
throw new UnsupportedOperationException(REMOVED);
|
||||
}
|
||||
}
|
||||
27
src/main/java/net/berack/upo/graph/VisitDistSourceDest.java
Normal file
27
src/main/java/net/berack/upo/graph/VisitDistSourceDest.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
|
||||
/**
|
||||
* Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitDistSourceDest<V> extends VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* Get the distance from the source to the destination<br>
|
||||
* The list contains the minimum path from the vertex marked as source to the destination vertex
|
||||
*
|
||||
* @param graph the graph were to find the min path
|
||||
* @param source the source vertex
|
||||
* @param destination the destination vertex
|
||||
* @return the distance
|
||||
* @throws NullPointerException if one of the vertex is null
|
||||
* @throws IllegalArgumentException if one of the vertex is not contained in the graph
|
||||
*/
|
||||
List<Edge<V>> distance(Graph<V> graph, V source, V destination) throws NullPointerException, IllegalArgumentException;
|
||||
}
|
||||
32
src/main/java/net/berack/upo/graph/VisitDistance.java
Normal file
32
src/main/java/net/berack/upo/graph/VisitDistance.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface that is helpful for implements visit that needs to retrieve the distance between a vertex to all the others
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitDistance<V> extends VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* Get the last calculated distance to all the possible destinations<br>
|
||||
* The map contains all the possible vertices that are reachable from the source set in the visit<br>
|
||||
* If there is no path between the destination and the source, then null is returned as accordingly to the map interface<br>
|
||||
* If the visit is not already been done, then the map is null.
|
||||
*
|
||||
* @return the last distance
|
||||
* @throws NullPointerException if the visit is not already been done
|
||||
*/
|
||||
Map<V, List<Edge<V>>> getLastDistance() throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Get the last source vertex of the visit for calculating the destinations.<br>
|
||||
* Returns null if the visit is not already been done
|
||||
*
|
||||
* @return the last vertex
|
||||
*/
|
||||
V getLastSource();
|
||||
}
|
||||
18
src/main/java/net/berack/upo/graph/VisitMST.java
Normal file
18
src/main/java/net/berack/upo/graph/VisitMST.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @param <V>
|
||||
*/
|
||||
public interface VisitMST<V> extends VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* Return the latest calculated MST.<br>
|
||||
* https://en.wikipedia.org/wiki/Minimum_spanning_tree
|
||||
*
|
||||
* @return the latest MST
|
||||
* @throws NullPointerException if there is no last calculated MST
|
||||
*/
|
||||
Set<Edge<V>> getMST();
|
||||
}
|
||||
20
src/main/java/net/berack/upo/graph/VisitSCC.java
Normal file
20
src/main/java/net/berack/upo/graph/VisitSCC.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Interface that is helpful for implements visit that needs to retrieve the SCC
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitSCC<V> extends VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* Return the latest calculated strongly connected components of the graph.
|
||||
*
|
||||
* @return the latest SCC
|
||||
* @throws NullPointerException if there is no last calculated SCC
|
||||
*/
|
||||
Set<Set<V>> getSCC();
|
||||
}
|
||||
44
src/main/java/net/berack/upo/graph/VisitStrategy.java
Normal file
44
src/main/java/net/berack/upo/graph/VisitStrategy.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphDirected;
|
||||
import net.berack.upo.graph.visit.VisitInfo;
|
||||
|
||||
/**
|
||||
* This class is used for define some strategy for the visit of a graph.
|
||||
*
|
||||
* @param <V> The Object that represent a vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* With this the graph will be visited accordingly to the strategy of the visit.<br>
|
||||
* Some strategy can accept a source vertex null, because they visit all the graph anyway.<br>
|
||||
* If you want to stop the visit of the graph, you just have to throw any exception in the visit function, but be sure to catch it
|
||||
*
|
||||
* @param graph the graph to visit
|
||||
* @param source the vertex where the visit starts
|
||||
* @param visit the function to apply at each vertex when they are visited
|
||||
* @return an info of the view
|
||||
* @throws NullPointerException if the graph is null
|
||||
* @throws UnsupportedOperationException in the case that the visit algorithm cannot be applied to the graph
|
||||
*/
|
||||
VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException;
|
||||
|
||||
/**
|
||||
* Method used for checking if the graph is Directed.<br>
|
||||
* It's useful when the algorithm can only be applied to Directed graph.
|
||||
*
|
||||
* @param graph the instance of the graph to check
|
||||
* @return the instance of the graph casted to a {@link GraphDirected}
|
||||
* @throws UnsupportedOperationException in the case it's not a directed graph
|
||||
*/
|
||||
default GraphDirected<V> checkDirected(Graph<V> graph) {
|
||||
if (graph instanceof GraphDirected)
|
||||
return (GraphDirected<V>) graph;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
21
src/main/java/net/berack/upo/graph/VisitTopological.java
Normal file
21
src/main/java/net/berack/upo/graph/VisitTopological.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package net.berack.upo.graph;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface that is helpful for implements visit that needs to retrieve the topological sort
|
||||
*
|
||||
* @param <V> the vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface VisitTopological<V> extends VisitStrategy<V> {
|
||||
|
||||
/**
|
||||
* Return the latest calculated Topological sort of the graph.<br>
|
||||
* If the latest visited graph is not a DAG, it will return null.
|
||||
*
|
||||
* @return the topological order of the DAG
|
||||
* @throws NullPointerException if there is no last calculated topological sort
|
||||
*/
|
||||
List<V> getTopologicalSort();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.berack.upo.graph.savemodels;
|
||||
|
||||
/**
|
||||
* Support class used for saving a Graph in a file.
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public class EdgeSaveStructure {
|
||||
protected EdgeSaveStructure(String s, String d, int w) {
|
||||
this.src = s;
|
||||
this.dest = d;
|
||||
this.weight = w;
|
||||
}
|
||||
|
||||
public String src;
|
||||
public String dest;
|
||||
public int weight;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package net.berack.upo.graph.savemodels;
|
||||
|
||||
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 net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.Edge;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Support class used for saving a Graph in a file.
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public class GraphSaveStructure<V> {
|
||||
final static public Gson gson = new Gson();
|
||||
public String[] vertices;
|
||||
public EdgeSaveStructure[] edges;
|
||||
//public MarkSaveStructure[] marks;
|
||||
|
||||
/**
|
||||
* Load the graph saved in this class in an instance of a graph passed.
|
||||
* Before loading the graph, it is emptied.
|
||||
*
|
||||
* @param graph the graph where insert the data
|
||||
* @param fileName the file path and name
|
||||
* @param classV the class of the vertices
|
||||
* @throws FileNotFoundException in the case the file is not found
|
||||
* @throws NullPointerException in the case the graph is null
|
||||
*/
|
||||
public final void load(Graph<V> graph, String fileName, Class<V> classV) throws FileNotFoundException, NullPointerException {
|
||||
//this way i use this class for the load
|
||||
InstanceCreator<GraphSaveStructure<V>> creator = _ -> this;
|
||||
Gson gson = new GsonBuilder().registerTypeAdapter(this.getClass(), creator).create();
|
||||
JsonReader reader = new JsonReader(new FileReader(fileName));
|
||||
gson.fromJson(reader, GraphSaveStructure.class);
|
||||
loadGraph(graph, classV);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used by sub-classes for saving other stuff from the graph
|
||||
*
|
||||
* @param graph the graph to load with
|
||||
* @param classV the class used for the Vertex
|
||||
* @throws NullPointerException if the graph is null
|
||||
* @throws JsonSyntaxException if the file is malformed or corrupted
|
||||
*/
|
||||
protected void loadGraph(Graph<V> graph, Class<V> classV) 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), edge.weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the Graph passed as input to a file inserted as parameter.<br>
|
||||
* The resulting file is a Json string representing all the graph.<br>
|
||||
* If the directory for getting through the file do not exist,<br>
|
||||
* then it is created.<br>
|
||||
* Marks are not included.<br>
|
||||
* 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<V> 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<V> 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<V> edge : graph.edges())
|
||||
this.edges[i++] = new EdgeSaveStructure(gson.toJson(edge.getSource()), gson.toJson(edge.getDestination()), edge.getWeight());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.berack.upo.graph.savemodels;
|
||||
|
||||
/**
|
||||
* Support class used for saving a Graph in a file.
|
||||
*
|
||||
* @author Berack96
|
||||
*
|
||||
*/
|
||||
public class MarkSaveStructure {
|
||||
protected MarkSaveStructure(String v, Object m) {
|
||||
this.vert = v;
|
||||
this.mark = m;
|
||||
}
|
||||
|
||||
public String vert;
|
||||
public Object mark;
|
||||
}
|
||||
204
src/main/java/net/berack/upo/graph/view/GraphInfo.java
Normal file
204
src/main/java/net/berack/upo/graph/view/GraphInfo.java
Normal file
@@ -0,0 +1,204 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.BevelBorder;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
import net.berack.upo.graph.view.edge.EdgeListener;
|
||||
import net.berack.upo.graph.view.vertex.VertexListener;
|
||||
|
||||
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<V> extends JPanel {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Map<String, VisitListener<V>> visits;
|
||||
|
||||
public GraphInfo(GraphPanel<V> graphPanel, VertexListener<V> vListener, EdgeListener<V> eListener, Set<VisitStrategy<V>> visits) {
|
||||
this.visits = new HashMap<>();
|
||||
|
||||
/* ZERO (DESCRIPTION) */
|
||||
JLabel listenerDescription = new JLabel();
|
||||
|
||||
JPanel panelDescription = new JPanel();
|
||||
panelDescription.setOpaque(false);
|
||||
panelDescription.add(listenerDescription);
|
||||
|
||||
/* FIRST (GRAPH INFO) */
|
||||
|
||||
JLabel vNumber = new JLabel(String.valueOf(graphPanel.getGraph().size()));
|
||||
JLabel eNumber = new JLabel(String.valueOf(graphPanel.getGraph().numberOfEdges()));
|
||||
//JLabel gCyclic = new JLabel(String.valueOf(graphPanel.getGraph().isCyclic()));
|
||||
|
||||
List<Component> components = new LinkedList<>();
|
||||
JLabel selected = new JLabel();
|
||||
JComboBox<String> comboBox = new JComboBox<>();
|
||||
comboBox.addItemListener(e -> {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
try {
|
||||
String clazz = (String) e.getItem();
|
||||
VisitListener<V> listener = this.visits.get(clazz);
|
||||
|
||||
selected.setText(listener != null? "visit":"nothing");
|
||||
listenerDescription.setText(listener != null? listener.getDescription():"");
|
||||
|
||||
graphPanel.getGraph().unMarkAll();
|
||||
graphPanel.repaint();
|
||||
graphPanel.setGraphListener(listener);
|
||||
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
comboBox.addItem("None");
|
||||
for (VisitStrategy<V> strategy : visits) {
|
||||
String clazz = strategy.getClass().getSimpleName();
|
||||
VisitListener<V> visit = new VisitListener<>(graphPanel, strategy);
|
||||
comboBox.addItem(clazz);
|
||||
this.visits.put(clazz, visit);
|
||||
}
|
||||
|
||||
components.add(new JLabel("Visit Strategy: "));
|
||||
components.add(comboBox);
|
||||
components.add(new JLabel("Selected modality: "));
|
||||
components.add(selected);
|
||||
components.add(new JLabel("Vertex Number: "));
|
||||
components.add(vNumber);
|
||||
components.add(new JLabel("Edge Number: "));
|
||||
components.add(eNumber);
|
||||
//components.add(new JLabel("Is Cyclic: "));
|
||||
//components.add(gCyclic);
|
||||
|
||||
JPanel panelInfo = new JPanel();
|
||||
panelInfo.setOpaque(false);
|
||||
panelInfo.setBorder(BorderFactory.createLineBorder(Color.RED));
|
||||
panelInfo.setLayout(new GridLayout(components.size()/2, 2));
|
||||
components.forEach(panelInfo::add);
|
||||
components.clear();
|
||||
|
||||
/* SECOND (VERTEX) */
|
||||
JLabel vVertex = new JLabel();
|
||||
JLabel vEdgesNumber = new JLabel();
|
||||
JLabel vEdgesInNumber = new JLabel();
|
||||
JLabel vEdgesOutNumber = new JLabel();
|
||||
|
||||
JButton modVertex = new JButton("Modify Vertices");
|
||||
modVertex.addActionListener(_ -> {
|
||||
comboBox.setSelectedIndex(0);
|
||||
listenerDescription.setText(vListener.getDescription());
|
||||
graphPanel.setGraphListener(vListener);
|
||||
graphPanel.getGraph().unMarkAll();
|
||||
graphPanel.repaint();
|
||||
selected.setText("vertices");
|
||||
});
|
||||
|
||||
JButton modEdge = new JButton("Modify Edges");
|
||||
modEdge.addActionListener(_ -> {
|
||||
comboBox.setSelectedIndex(0);
|
||||
listenerDescription.setText(eListener.getDescription());
|
||||
graphPanel.setGraphListener(eListener);
|
||||
graphPanel.getGraph().unMarkAll();
|
||||
graphPanel.repaint();
|
||||
selected.setText("edges");
|
||||
});
|
||||
|
||||
components.add(modVertex);
|
||||
components.add(modEdge);
|
||||
components.add(new JLabel("Vertex name: "));
|
||||
components.add(vVertex);
|
||||
components.add(new JLabel("Edges: "));
|
||||
components.add(vEdgesNumber);
|
||||
components.add(new JLabel("Edges IN: "));
|
||||
components.add(vEdgesInNumber);
|
||||
components.add(new JLabel("Edges OUT: "));
|
||||
components.add(vEdgesOutNumber);
|
||||
|
||||
JPanel panelVertex = new JPanel();
|
||||
panelVertex.setOpaque(false);
|
||||
panelVertex.setLayout(new GridLayout(components.size()/2, 2));
|
||||
components.forEach(panelVertex::add);
|
||||
components.clear();
|
||||
|
||||
/* SAVE/LOAD errors */
|
||||
JLabel textResult = new JLabel();
|
||||
textResult.setForeground(Color.RED);
|
||||
|
||||
JPanel panelErrors = new JPanel();
|
||||
panelErrors.setOpaque(false);
|
||||
panelErrors.add(textResult);
|
||||
|
||||
/* SAVE/LOAD */
|
||||
JTextField fileText = new JTextField();
|
||||
JButton saveB = new JButton("Save");
|
||||
saveB.addActionListener(_ -> {
|
||||
try {
|
||||
graphPanel.save(fileText.getText());
|
||||
textResult.setText("");
|
||||
} catch (IOException e1) {
|
||||
textResult.setText(e1.getMessage());
|
||||
}
|
||||
});
|
||||
JButton loadB = new JButton("Load");
|
||||
loadB.addActionListener(_ -> {
|
||||
try {
|
||||
graphPanel.load(fileText.getText());
|
||||
textResult.setText("");
|
||||
} catch (IOException e1) {
|
||||
textResult.setText(e1.getMessage());
|
||||
}
|
||||
});
|
||||
components.add(new JLabel("File to save/load: "));
|
||||
components.add(fileText);
|
||||
components.add(saveB);
|
||||
components.add(loadB);
|
||||
|
||||
JPanel panelSave = new JPanel();
|
||||
panelSave.setOpaque(false);
|
||||
panelSave.setBorder(BorderFactory.createLineBorder(Color.RED));
|
||||
panelSave.setLayout(new GridLayout(components.size()/2, 2));
|
||||
components.forEach(panelSave::add);
|
||||
components.clear();
|
||||
|
||||
/* ADDING COMPONENTS */
|
||||
this.setBackground(Color.LIGHT_GRAY);
|
||||
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
this.setOpaque(true);
|
||||
this.setBorder(BorderFactory.createSoftBevelBorder(BevelBorder.RAISED, Color.GRAY, Color.DARK_GRAY));
|
||||
this.add(panelDescription);
|
||||
this.add(panelInfo);
|
||||
this.add(panelVertex);
|
||||
this.add(panelErrors);
|
||||
this.add(panelSave);
|
||||
|
||||
modVertex.doClick();
|
||||
|
||||
graphPanel.addObserver((_, arg) -> {
|
||||
Graph<V> graph = graphPanel.getGraph();
|
||||
if(arg.equals(graph)) {
|
||||
vNumber.setText(String.valueOf(graph.size()));
|
||||
eNumber.setText(String.valueOf(graph.numberOfEdges()));
|
||||
//gCyclic.setText(String.valueOf(graph.isCyclic()));
|
||||
|
||||
/* There should be only one */
|
||||
for(V v : graph.getMarkedWith("selected")) {
|
||||
int inE = graph.getAncestors(v).size();
|
||||
int outE = graph.getChildren(v).size();
|
||||
|
||||
vEdgesInNumber.setText(String.valueOf(inE));
|
||||
vEdgesOutNumber.setText(String.valueOf(outE));
|
||||
vEdgesNumber.setText(String.valueOf(inE + outE));
|
||||
vVertex.setText(v.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
27
src/main/java/net/berack/upo/graph/view/GraphListener.java
Normal file
27
src/main/java/net/berack/upo/graph/view/GraphListener.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
|
||||
/**
|
||||
* An interface for creating a listener of the Graph.
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface GraphListener extends MouseListener, MouseMotionListener, KeyListener {
|
||||
|
||||
/**
|
||||
* Remove the listener to the graph.
|
||||
* This function is called when the listener is removed to the graph.
|
||||
* Here you could remove any other thing that you have done.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
String getDescription();
|
||||
}
|
||||
211
src/main/java/net/berack/upo/graph/view/GraphPanel.java
Normal file
211
src/main/java/net/berack/upo/graph/view/GraphPanel.java
Normal file
@@ -0,0 +1,211 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
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;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.MapGraph;
|
||||
import net.berack.upo.graph.Vertex;
|
||||
import net.berack.upo.graph.view.edge.EdgeComponent;
|
||||
import net.berack.upo.graph.view.vertex.VertexComponent;
|
||||
|
||||
@SuppressWarnings({"unchecked", "deprecation"})
|
||||
public class GraphPanel<V> extends Component {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final GraphicalView<VertexComponent<V>> vertexRender;
|
||||
private final GraphicalView<EdgeComponent<V>> edgeRender;
|
||||
private final Class<V> classV;
|
||||
|
||||
final Container vertices = new Container();
|
||||
final Container edges = new Container();
|
||||
|
||||
private final Graph<V> graph = new MapGraph<>();
|
||||
private final Set<Observer> observers = new HashSet<>();
|
||||
|
||||
private GraphListener old = null;
|
||||
|
||||
public GraphPanel(GraphicalView<VertexComponent<V>> vertexRender, GraphicalView<EdgeComponent<V>> edgeRender, Class<V> classV) {
|
||||
this.vertexRender = vertexRender;
|
||||
this.edgeRender = edgeRender;
|
||||
this.classV = classV;
|
||||
}
|
||||
|
||||
public Graph<V> getGraph() {
|
||||
return graph;
|
||||
}
|
||||
|
||||
public void setGraphListener(GraphListener listener) {
|
||||
if(old != null)
|
||||
old.remove();
|
||||
for (MouseListener l : getMouseListeners())
|
||||
removeMouseListener(l);
|
||||
for (MouseMotionListener l : getMouseMotionListeners())
|
||||
removeMouseMotionListener(l);
|
||||
for (KeyListener l : getKeyListeners())
|
||||
removeKeyListener(l);
|
||||
|
||||
old = listener;
|
||||
addMouseListener(listener);
|
||||
addMouseMotionListener(listener);
|
||||
addKeyListener(listener);
|
||||
}
|
||||
|
||||
public void addVertex(Point center, V vertex) {
|
||||
VertexComponent<V> component = getVertexAt(center);
|
||||
|
||||
if (component == null) {
|
||||
VertexComponent<V> v = new VertexComponent<>(new Vertex<>(graph, vertex));
|
||||
v.vertex.addIfAbsent();
|
||||
boolean isContained = false;
|
||||
|
||||
for(Component comp : vertices.getComponents())
|
||||
if (comp.equals(v)) {
|
||||
isContained = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isContained) {
|
||||
v.setBounds(vertexRender.getBox(v, center));
|
||||
vertices.add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeVertex(Point center) {
|
||||
try {
|
||||
VertexComponent<V> component = getVertexAt(center);
|
||||
component.vertex.remove();
|
||||
vertices.remove(component);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
public void moveVertex(VertexComponent<V> vertex, Point destination) {
|
||||
Rectangle rectangle = vertexRender.getBox(vertex, destination);
|
||||
vertex.setLocation(rectangle.x, rectangle.y);
|
||||
}
|
||||
|
||||
public void addEdge(V source, V destination, int weight) {
|
||||
VertexComponent<V> vSource = null;
|
||||
VertexComponent<V> vDest = null;
|
||||
for (Component comp : vertices.getComponents()) {
|
||||
VertexComponent<V> temp = (VertexComponent<V>) comp;
|
||||
V vTemp = temp.vertex.get();
|
||||
if (vSource == null && vTemp.equals(source))
|
||||
vSource = temp;
|
||||
if (vDest == null && vTemp.equals(destination))
|
||||
vDest = temp;
|
||||
}
|
||||
addEdge(vSource, vDest, weight);
|
||||
}
|
||||
|
||||
public void addEdge(VertexComponent<V> source, VertexComponent<V> dest, int weight) {
|
||||
try {
|
||||
Point center = new Point(Math.abs(source.getX() - dest.getY()), Math.abs(source.getY() - dest.getY()));
|
||||
EdgeComponent<V> edgeComponent = new EdgeComponent<>(source, dest, weight);
|
||||
edgeComponent.setBounds(edgeRender.getBox(edgeComponent, center));
|
||||
edges.add(edgeComponent);
|
||||
graph.addEdge(edgeComponent.edge);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeEdge(VertexComponent<V> source, VertexComponent<V> dest) {
|
||||
try {
|
||||
graph.removeEdge(source.vertex.get(), dest.vertex.get());
|
||||
EdgeComponent<V> toRemove = null;
|
||||
for (Component c : edges.getComponents()) {
|
||||
EdgeComponent<V> edge = (EdgeComponent<V>) c;
|
||||
if (edge.source.equals(source) && edge.destination.equals(dest))
|
||||
toRemove = edge;
|
||||
}
|
||||
edges.remove(toRemove);
|
||||
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
public void modEdge(VertexComponent<V> source, VertexComponent<V> dest, int weight) {
|
||||
removeEdge(source, dest);
|
||||
addEdge(source, dest, weight);
|
||||
}
|
||||
|
||||
public VertexComponent<V> getVertexAt(Point point) {
|
||||
Component component = vertices.getComponentAt(point);
|
||||
return component instanceof VertexComponent ? (VertexComponent<V>) component : null;
|
||||
}
|
||||
|
||||
public EdgeComponent<V> getEdgeAt(Point point) {
|
||||
Component component = edges.getComponentAt(point);
|
||||
return component instanceof EdgeComponent ? (EdgeComponent<V>) component : null;
|
||||
}
|
||||
|
||||
public void addObserver(Observer observer) {
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
public void removeObserver(Observer observer) {
|
||||
observers.remove(observer);
|
||||
}
|
||||
|
||||
public void save(String fileName) throws IOException {
|
||||
new GraphPointsSave<>(this).save(graph, fileName);
|
||||
}
|
||||
|
||||
public void load(String fileName) throws IOException {
|
||||
new GraphPointsSave<>(this).load(graph, fileName, classV);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int x, int y, int width, int height) {
|
||||
super.setBounds(x, y, width, height);
|
||||
vertices.setBounds(x, y, width, height);
|
||||
edges.setBounds(x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
Collection<EdgeComponent<V>> toRemove = new HashSet<>();
|
||||
for (Component component : edges.getComponents()) {
|
||||
EdgeComponent<V> edge = (EdgeComponent<V>) component;
|
||||
Vertex<V> source = edge.source.vertex;
|
||||
Vertex<V> dest = edge.destination.vertex;
|
||||
if (source.isStillContained() && dest.isStillContained() && graph.containsEdge(source.get(), dest.get())) {
|
||||
Point center = new Point(edge.getX() + edge.getWidth() / 2, edge.getY() + edge.getHeight() / 2);
|
||||
edge.setBounds(edgeRender.getBox(edge, center));
|
||||
edgeRender.paint((Graphics2D) g2.create(), edge, center);
|
||||
} else
|
||||
toRemove.add(edge);
|
||||
}
|
||||
toRemove.forEach(edges::remove);
|
||||
|
||||
for (Component component : vertices.getComponents()) {
|
||||
VertexComponent<V> vertex = (VertexComponent<V>) component;
|
||||
if (graph.contains(vertex.vertex.get())) {
|
||||
Point center = new Point(vertex.getX() + vertex.getWidth() / 2, vertex.getY() + vertex.getHeight() / 2);
|
||||
vertexRender.paint((Graphics2D) g2.create(), vertex, center);
|
||||
}
|
||||
}
|
||||
|
||||
updateObservers();
|
||||
}
|
||||
|
||||
private void updateObservers() {
|
||||
observers.forEach(observer -> observer.update(null, this.graph));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
55
src/main/java/net/berack/upo/graph/view/GraphPointsSave.java
Normal file
55
src/main/java/net/berack/upo/graph/view/GraphPointsSave.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.savemodels.GraphSaveStructure;
|
||||
|
||||
public class GraphPointsSave<V> extends GraphSaveStructure<V> {
|
||||
|
||||
final private GraphPanel<V> panel;
|
||||
public Point[] points;
|
||||
|
||||
public GraphPointsSave(GraphPanel<V> panel) {
|
||||
this.panel = panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveGraph(Graph<V> graph) {
|
||||
super.saveGraph(graph);
|
||||
List<Point> 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<V> graph, Class<V> classV) {
|
||||
super.loadGraph(graph, classV);
|
||||
panel.vertices.removeAll();
|
||||
panel.edges.removeAll();
|
||||
|
||||
for (int i = 0; i < vertices.length; i++) {
|
||||
V v = gson.fromJson(vertices[i], classV);
|
||||
Point p = points[i];
|
||||
panel.addVertex(p, v);
|
||||
}
|
||||
|
||||
for (String v : vertices) {
|
||||
V src = gson.fromJson(v, classV);
|
||||
graph.getChildren(src).forEach(child -> panel.addEdge(src, child, graph.getWeight(src, child)));
|
||||
}
|
||||
panel.repaint();
|
||||
}
|
||||
}
|
||||
54
src/main/java/net/berack/upo/graph/view/GraphWindow.java
Normal file
54
src/main/java/net/berack/upo/graph/view/GraphWindow.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
import net.berack.upo.graph.view.edge.EdgeListener;
|
||||
import net.berack.upo.graph.view.vertex.VertexListener;
|
||||
import net.berack.upo.graph.visit.BFS;
|
||||
import net.berack.upo.graph.visit.DFS;
|
||||
import net.berack.upo.graph.visit.Dijkstra;
|
||||
import net.berack.upo.graph.visit.Tarjan;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class is the Window that appear for building the graph and playing around with it
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public class GraphWindow<V> extends JFrame {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final GraphPanel<V> graphPanel;
|
||||
|
||||
public GraphWindow(GraphPanel<V> graphPanel, VertexListener<V> vListener, EdgeListener<V> eListener) {
|
||||
this.setTitle("Grafo");
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
this.setLayout(new BorderLayout());
|
||||
|
||||
Set<VisitStrategy<V>> strats = new LinkedHashSet<>();
|
||||
strats.add(new DFS<>());
|
||||
strats.add(new BFS<>());
|
||||
strats.add(new Dijkstra<>());
|
||||
strats.add(new Tarjan<>());
|
||||
|
||||
GraphInfo<V> infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats);
|
||||
this.graphPanel = graphPanel;
|
||||
this.add(infoPanel, BorderLayout.EAST);
|
||||
this.add(graphPanel);
|
||||
}
|
||||
|
||||
public void visitRefresh(int millis) {
|
||||
VisitListener.changeRefresh(millis);
|
||||
}
|
||||
|
||||
public GraphPanel<V> getGraphPanel() {
|
||||
return graphPanel;
|
||||
}
|
||||
}
|
||||
30
src/main/java/net/berack/upo/graph/view/GraphicalView.java
Normal file
30
src/main/java/net/berack/upo/graph/view/GraphicalView.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* An interface for divide the "hitbox" and the "paint" of the various items
|
||||
*
|
||||
* @param <O> the object to paint
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface GraphicalView<O> {
|
||||
|
||||
/**
|
||||
* Box where the object is sensible at listeners (like Hitbox)
|
||||
*
|
||||
* @param obj the object to draw
|
||||
* @param center the center point of the object
|
||||
* @return a rectangle where the object is sensible to the listeners
|
||||
*/
|
||||
Rectangle getBox(O obj, Point center);
|
||||
|
||||
/**
|
||||
* The paint function, aka the part where you can draw things (like Mesh)
|
||||
*
|
||||
* @param g2 the graphics object used for painting
|
||||
* @param obj the object to paint
|
||||
* @param center the center point of the object
|
||||
*/
|
||||
void paint(Graphics2D g2, O obj, Point center);
|
||||
}
|
||||
23
src/main/java/net/berack/upo/graph/view/Main.java
Normal file
23
src/main/java/net/berack/upo/graph/view/Main.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import net.berack.upo.graph.view.edge.EdgeIntListener;
|
||||
import net.berack.upo.graph.view.edge.EdgeView;
|
||||
import net.berack.upo.graph.view.vertex.VertexIntListener;
|
||||
import net.berack.upo.graph.view.vertex.VertexView;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
GraphPanel<Integer> panel = new GraphPanel<>(new VertexView<>(), new EdgeView<>(), Integer.class);
|
||||
GraphWindow<Integer> win = new GraphWindow<>(panel, new VertexIntListener(panel), new EdgeIntListener<>(panel));
|
||||
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); // full screen
|
||||
dim.setSize(dim.width / 2, dim.height / 2);
|
||||
win.setSize(dim);
|
||||
win.setLocationRelativeTo(null); //centered
|
||||
win.visitRefresh(500);
|
||||
|
||||
win.setVisible(true);
|
||||
}
|
||||
}
|
||||
113
src/main/java/net/berack/upo/graph/view/VisitListener.java
Normal file
113
src/main/java/net/berack/upo/graph/view/VisitListener.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package net.berack.upo.graph.view;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
import net.berack.upo.graph.view.vertex.VertexComponent;
|
||||
import net.berack.upo.graph.visit.VisitInfo;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class VisitListener<V> implements GraphListener {
|
||||
|
||||
private final GraphPanel<V> panel;
|
||||
private final VisitStrategy<V> strategy;
|
||||
private final Set<Timer> timers = new HashSet<>();
|
||||
|
||||
private static int refreshTime = 1000;
|
||||
|
||||
public VisitListener(GraphPanel<V> panel, VisitStrategy<V> strategy) {
|
||||
this.panel = panel;
|
||||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
public static void changeRefresh(int mills) {
|
||||
refreshTime = mills;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
timers.forEach(Timer::stop);
|
||||
timers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<html>"
|
||||
+ "Start a visit by pressing<br />"
|
||||
+ "with the mouse SX on the root vertex<br />"
|
||||
+ "</html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
this.remove();
|
||||
|
||||
Graph<V> graph = panel.getGraph();
|
||||
graph.unMarkAll();
|
||||
panel.repaint();
|
||||
|
||||
if (e.getButton() == MouseEvent.BUTTON1)
|
||||
try {
|
||||
VertexComponent<V> vertex = panel.getVertexAt(e.getPoint());
|
||||
AtomicInteger count = new AtomicInteger(0);
|
||||
VisitInfo<V> info = vertex.vertex.visit(strategy, null);
|
||||
|
||||
info.forEach(v -> {
|
||||
final boolean visited = v.timeVisited == count.get();
|
||||
Timer timer = new Timer(count.getAndIncrement() * refreshTime, _ -> {
|
||||
if (visited && v.parent !=null)
|
||||
graph.mark(v.vertex, v.parent);
|
||||
graph.mark(v.vertex, visited ? "visited" : "discovered");
|
||||
panel.repaint();
|
||||
});
|
||||
|
||||
timers.add(timer);
|
||||
timer.setRepeats(false);
|
||||
timer.start();
|
||||
});
|
||||
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.berack.upo.graph.view.edge;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.view.vertex.VertexComponent;
|
||||
|
||||
public class EdgeComponent<V> extends Component {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final VertexComponent<V> source;
|
||||
public final VertexComponent<V> destination;
|
||||
public final int weight;
|
||||
public final Edge<V> edge;
|
||||
|
||||
public EdgeComponent(VertexComponent<V> source, VertexComponent<V> destination, int weight) {
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.weight = weight;
|
||||
this.edge = new Edge<>(source.vertex.get(), destination.vertex.get(), weight);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.berack.upo.graph.view.edge;
|
||||
|
||||
import net.berack.upo.graph.Vertex;
|
||||
import net.berack.upo.graph.view.GraphPanel;
|
||||
|
||||
public class EdgeIntListener<V> extends EdgeListener<V> {
|
||||
|
||||
public EdgeIntListener(GraphPanel<V> graphPanel) {
|
||||
super(graphPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1) {
|
||||
return (int) (Math.random() * 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int buildEdgeFrom(String string) {
|
||||
return Integer.parseInt(string.replaceAll("[^0-9]+", ""));
|
||||
}
|
||||
}
|
||||
128
src/main/java/net/berack/upo/graph/view/edge/EdgeListener.java
Normal file
128
src/main/java/net/berack/upo/graph/view/edge/EdgeListener.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package net.berack.upo.graph.view.edge;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import net.berack.upo.graph.Vertex;
|
||||
import net.berack.upo.graph.view.GraphListener;
|
||||
import net.berack.upo.graph.view.GraphPanel;
|
||||
import net.berack.upo.graph.view.vertex.VertexComponent;
|
||||
|
||||
public abstract class EdgeListener<V> implements GraphListener {
|
||||
|
||||
private final GraphPanel<V> graphPanel;
|
||||
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
|
||||
private final AtomicReference<Integer> buttonPressed = new AtomicReference<>();
|
||||
private final AtomicReference<EdgeComponent<V>> edge = new AtomicReference<>();
|
||||
private final StringBuilder string = new StringBuilder();
|
||||
|
||||
public EdgeListener(GraphPanel<V> graphPanel) {
|
||||
this.graphPanel = graphPanel;
|
||||
}
|
||||
|
||||
protected abstract int buildNewEdge(Vertex<V> vertex, Vertex<V> vertex1);
|
||||
|
||||
protected abstract int buildEdgeFrom(String string);
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<html>"
|
||||
+ "Modify edges with:<br />"
|
||||
+ "mouse SX on vertex to another ==> add<br />"
|
||||
+ "mouse DX on edge ==> change weigth<br />"
|
||||
+ "(only numbers allowed)"
|
||||
+ "</html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
buttonPressed.set(e.getButton());
|
||||
componentPressed.set(graphPanel.getVertexAt(e.getPoint()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
try {
|
||||
edge.get().source.vertex.unMark("modS");
|
||||
edge.get().destination.vertex.unMark("modD");
|
||||
edge.set(null);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (buttonPressed.get() == MouseEvent.BUTTON1) {
|
||||
try {
|
||||
VertexComponent<V> source = componentPressed.get();
|
||||
VertexComponent<V> destination = graphPanel.getVertexAt(e.getPoint());
|
||||
|
||||
if (!graphPanel.getGraph().containsEdge(source.vertex.get(), destination.vertex.get())
|
||||
&& !source.vertex.equals(destination.vertex))
|
||||
graphPanel.addEdge(source, destination, buildNewEdge(source.vertex, destination.vertex));
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
} else {
|
||||
edge.set(graphPanel.getEdgeAt(e.getPoint()));
|
||||
|
||||
try {
|
||||
edge.get().source.vertex.mark("modS");
|
||||
edge.get().destination.vertex.mark("modD");
|
||||
graphPanel.setFocusTraversalKeysEnabled(false);
|
||||
graphPanel.requestFocusInWindow();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
string.delete(0, string.length());
|
||||
componentPressed.set(null);
|
||||
graphPanel.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (edge.get() != null && Character.isDigit(e.getKeyChar())) {
|
||||
string.append(e.getKeyChar());
|
||||
try {
|
||||
graphPanel.modEdge(edge.get().source, edge.get().destination, buildEdgeFrom(string.toString()));
|
||||
graphPanel.repaint();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
} else {
|
||||
|
||||
try {
|
||||
edge.get().source.vertex.unMark("modS");
|
||||
edge.get().destination.vertex.unMark("modD");
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
edge.set(null);
|
||||
string.delete(0, string.length());
|
||||
}
|
||||
|
||||
graphPanel.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
109
src/main/java/net/berack/upo/graph/view/edge/EdgeView.java
Normal file
109
src/main/java/net/berack/upo/graph/view/edge/EdgeView.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package net.berack.upo.graph.view.edge;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.berack.upo.graph.view.GraphicalView;
|
||||
import net.berack.upo.graph.view.polygons.Arrow;
|
||||
|
||||
public class EdgeView<V> implements GraphicalView<EdgeComponent<V>> {
|
||||
|
||||
private static final Font FONT = new Font("Papyrus", Font.BOLD, 14);
|
||||
|
||||
@Override
|
||||
public Rectangle getBox(EdgeComponent<V> edge, Point center) {
|
||||
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
|
||||
Point srcLoc = edge.source.getLocation();
|
||||
Point desLoc = edge.destination.getLocation();
|
||||
/* getting the radius */
|
||||
int srcRadius = edge.source.getHeight() / 2;
|
||||
int desRadius = edge.destination.getHeight() / 2;
|
||||
/* centering the location */
|
||||
srcLoc.translate(srcRadius, srcRadius);
|
||||
desLoc.translate(desRadius, desRadius);
|
||||
|
||||
/* using vector for moving to the edge of the circumference and finding location of int box */
|
||||
final Point.Double vector = getVector(srcLoc, desLoc);
|
||||
|
||||
/* CALCULATING THE NUMBER SPACE */
|
||||
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
|
||||
FontMetrics metrics = edge.getFontMetrics(FONT);
|
||||
int dimString = metrics.stringWidth(String.valueOf(edge.weight));
|
||||
int dimRect = Math.max(dimString, metrics.getHeight());
|
||||
return new Rectangle(
|
||||
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
|
||||
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
|
||||
dimRect, dimRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics2D g2, EdgeComponent<V> edge, Point center) {
|
||||
/* CALCULATING BOUNDS AND ARROW STARTING AND ENDING POINTS */
|
||||
Point srcLoc = edge.source.getLocation();
|
||||
Point desLoc = edge.destination.getLocation();
|
||||
/* getting the radius */
|
||||
int srcRadius = edge.source.getHeight() / 2;
|
||||
int desRadius = edge.destination.getHeight() / 2;
|
||||
/* centering the location */
|
||||
srcLoc.translate(srcRadius, srcRadius);
|
||||
desLoc.translate(desRadius, desRadius);
|
||||
|
||||
/* using vector for moving to the edge of the circumference and finding location of int box */
|
||||
final Point.Double vector = getVector(srcLoc, desLoc);
|
||||
|
||||
/* CALCULATING THE NUMBER SPACE */
|
||||
int boxDistance = (int) (srcLoc.distance(desLoc) / 2.7);
|
||||
FontMetrics metrics = edge.getFontMetrics(FONT);
|
||||
int dimString = metrics.stringWidth(String.valueOf(edge.weight));
|
||||
int dimRect = Math.max(dimString, metrics.getHeight());
|
||||
Rectangle integerRect = new Rectangle(
|
||||
(int) ((desLoc.x - (vector.x * boxDistance)) - (dimRect / 2)),
|
||||
(int) ((desLoc.y - (vector.y * boxDistance)) - (dimRect / 2)),
|
||||
dimRect, dimRect);
|
||||
|
||||
/* moving to a distance R to the center */
|
||||
srcLoc.translate((int) (vector.x * srcRadius), (int) (vector.y * srcRadius));
|
||||
desLoc.translate((int) (-vector.x * desRadius), (int) (-vector.y * desRadius));
|
||||
|
||||
/* THE COLOR OF THE ARROW */
|
||||
Collection<Object> marksD = edge.destination.vertex.getMarks();
|
||||
Collection<Object> marksS = edge.source.vertex.getMarks();
|
||||
|
||||
boolean isChild = marksD.contains(edge.source.vertex.get());
|
||||
boolean selected = marksS.contains("selected");
|
||||
boolean isMod = marksD.contains("modD") && marksS.contains("modS");
|
||||
|
||||
Color arrowColor = isChild || selected ? Color.RED : Color.BLACK;
|
||||
Color boxColor = isChild || selected || isMod ? Color.ORANGE : Color.BLUE;
|
||||
Color stringColor = isChild || selected || isMod ? Color.BLACK : Color.CYAN;
|
||||
|
||||
g2.setFont(FONT);
|
||||
/* draw all */
|
||||
g2.setColor(arrowColor);
|
||||
//g2d.drawLine(arrowStart.x, arrowStart.y, arrowEnd.x, arrowEnd.y);
|
||||
Polygon arrow = new Arrow(srcLoc, desLoc, 1, 10);
|
||||
//g2d.draw(arrow);
|
||||
g2.fillPolygon(arrow);
|
||||
|
||||
/* draw the integer space */
|
||||
g2.setColor(boxColor);
|
||||
g2.fill(integerRect);
|
||||
g2.setColor(arrowColor);
|
||||
g2.draw(integerRect);
|
||||
g2.setColor(stringColor);
|
||||
g2.drawString(String.valueOf(edge.weight), (float) (integerRect.x + (dimRect - dimString) / 2), (float) (integerRect.y + (dimRect + metrics.getHeight() / 2) / 2));
|
||||
}
|
||||
|
||||
private Point.Double getVector(Point a, Point b) {
|
||||
final Point.Double vector = new Point2D.Double(b.x - a.x, b.y - a.y);
|
||||
/* normalizing vector */
|
||||
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
if(length != 0) {
|
||||
vector.x = vector.x / length;
|
||||
vector.y = vector.y / length;
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
61
src/main/java/net/berack/upo/graph/view/polygons/Arrow.java
Normal file
61
src/main/java/net/berack/upo/graph/view/polygons/Arrow.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package net.berack.upo.graph.view.polygons;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* Class that create a Polygon that has a shape of an arrow
|
||||
*
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Arrow extends Polygon {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create an arrow
|
||||
*
|
||||
* @param start the starting point of your arrow (the base)
|
||||
* @param end the ending point of your arrow (the head)
|
||||
* @param size the size of the arrow base
|
||||
* @param headSize the size of the arrow's head
|
||||
*/
|
||||
public Arrow(Point start, Point end, final int size, final int headSize) {
|
||||
final Point.Double vector = new Point.Double(end.x - start.x, end.y - start.y);
|
||||
/* vectors normalization */
|
||||
double length = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
vector.x = vector.x / length;
|
||||
vector.y = vector.y / length;
|
||||
|
||||
final Point headStart = new Point((int) (end.x - vector.x * headSize), (int) (end.y - vector.y * headSize));
|
||||
|
||||
/* rotating vector for the parallels */
|
||||
double cs = Math.cos(Math.PI / 2);
|
||||
double sn = Math.sin(Math.PI / 2);
|
||||
double x = vector.x * cs - vector.y * sn;
|
||||
double y = vector.x * sn + vector.y * cs;
|
||||
vector.setLocation(x, y);
|
||||
|
||||
/* TODO here use some magic for create a curve arrow if vector.x == vector.y && vector.x == 0 */
|
||||
/* building arrow starting from A to G */
|
||||
/*
|
||||
C
|
||||
|\
|
||||
| \
|
||||
A--------B \
|
||||
| D
|
||||
G--------F /
|
||||
| /
|
||||
|/
|
||||
E
|
||||
*/
|
||||
addPoint((int) (start.x - vector.x * size), (int) (start.y - vector.y * size));
|
||||
addPoint((int) (start.x + vector.x * size), (int) (start.y + vector.y * size));
|
||||
|
||||
addPoint((int) (headStart.x + vector.x * size), (int) (headStart.y + vector.y * size));
|
||||
addPoint((int) (headStart.x + vector.x * headSize), (int) (headStart.y + vector.y * headSize));
|
||||
addPoint(end.x, end.y);
|
||||
addPoint((int) (headStart.x - vector.x * headSize), (int) (headStart.y - vector.y * headSize));
|
||||
addPoint((int) (headStart.x - vector.x * size), (int) (headStart.y - vector.y * size));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.berack.upo.graph.view.vertex;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.Serial;
|
||||
|
||||
import net.berack.upo.graph.Vertex;
|
||||
|
||||
public class VertexComponent<V> extends Component {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final Vertex<V> vertex;
|
||||
|
||||
public VertexComponent(Vertex<V> vertex) {
|
||||
this.vertex = vertex;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + vertex + " {" + getX() + "," + getY() + "}]";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return obj.getClass().equals(getClass()) && ((VertexComponent<V>)obj).vertex.equals(vertex);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.berack.upo.graph.view.vertex;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.view.GraphPanel;
|
||||
|
||||
public class VertexIntListener extends VertexListener<Integer> {
|
||||
|
||||
public VertexIntListener(GraphPanel<Integer> panel) {
|
||||
super(panel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {}
|
||||
|
||||
@Override
|
||||
protected Integer buildNewVertex(Graph<Integer> graph) {
|
||||
int counter = 0;
|
||||
Integer[] vertices = graph.vertices().toArray(new Integer[graph.size()]);
|
||||
Arrays.sort(vertices);
|
||||
|
||||
for (Integer vertex : vertices) {
|
||||
if (!vertex.equals(counter))
|
||||
return counter;
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package net.berack.upo.graph.view.vertex;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.view.GraphListener;
|
||||
import net.berack.upo.graph.view.GraphPanel;
|
||||
|
||||
public abstract class VertexListener<V> implements GraphListener {
|
||||
|
||||
protected final GraphPanel<V> panel;
|
||||
private final AtomicReference<VertexComponent<V>> componentPressed = new AtomicReference<>();
|
||||
|
||||
public VertexListener(GraphPanel<V> panel) {
|
||||
this.panel = panel;
|
||||
}
|
||||
|
||||
protected abstract V buildNewVertex(Graph<V> graph);
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "<html>"
|
||||
+ "Modify vertex with:<br />"
|
||||
+ "mouse SX ==> add<br />"
|
||||
+ "mouse SX on vertex ==> move<br />"
|
||||
+ "mouse DX ==> remove<br />"
|
||||
+ "</html>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
try {
|
||||
if (e.getButton() == MouseEvent.BUTTON1)
|
||||
componentPressed.set(panel.getVertexAt(e.getPoint()));
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1 && componentPressed.get() == null)
|
||||
panel.addVertex(e.getPoint(), buildNewVertex(panel.getGraph()));
|
||||
else if (e.getButton() == MouseEvent.BUTTON3)
|
||||
panel.removeVertex(e.getPoint());
|
||||
|
||||
panel.getGraph().unMarkAll("selected");
|
||||
|
||||
if(componentPressed.get() != null)
|
||||
componentPressed.get().vertex.mark("selected");
|
||||
|
||||
panel.repaint();
|
||||
componentPressed.set(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (componentPressed.get() != null) {
|
||||
panel.moveVertex(componentPressed.get(), e.getPoint());
|
||||
panel.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.berack.upo.graph.view.vertex;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import net.berack.upo.graph.view.GraphicalView;
|
||||
|
||||
public class VertexView<V> implements GraphicalView<VertexComponent<V>> {
|
||||
|
||||
private static final Font FONT = new Font("Comic Sans MS", Font.BOLD, 17);
|
||||
private static final int PADDING = 6;
|
||||
|
||||
@Override
|
||||
public Rectangle getBox(VertexComponent<V> obj, Point center) {
|
||||
FontMetrics metrics = obj.getFontMetrics(FONT);
|
||||
int stringPixels = metrics.stringWidth(obj.vertex.get().toString());
|
||||
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
|
||||
|
||||
return new Rectangle(center.x - size / 2, center.y - size / 2, size, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics2D g2, VertexComponent<V> obj, Point center) {
|
||||
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.get().toString());
|
||||
int size = Math.max(stringPixels, metrics.getHeight()) + 2 * PADDING;
|
||||
|
||||
center.x = center.x - size / 2;
|
||||
center.y = center.y - size / 2;
|
||||
|
||||
g2.setFont(FONT);
|
||||
g2.setColor(visited || selected ? Color.RED : Color.ORANGE);
|
||||
g2.fillOval(center.x, center.y, size, size);
|
||||
g2.setColor(visited || discovered || selected ? Color.ORANGE : Color.YELLOW);
|
||||
g2.fillOval(center.x + PADDING / 2, center.y + PADDING / 2, size - PADDING, size - PADDING);
|
||||
g2.setColor(Color.BLACK);
|
||||
g2.drawString(obj.vertex.get().toString(), center.x + PADDING + (size - 2 * PADDING - stringPixels) / 2, center.y + (size) / 2 + PADDING);
|
||||
}
|
||||
}
|
||||
52
src/main/java/net/berack/upo/graph/visit/BFS.java
Normal file
52
src/main/java/net/berack/upo/graph/visit/BFS.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* Breadth-first search<br>
|
||||
* The algorithm starts at the root node and explores all of the neighbor nodes at the present depth prior to moving on to the nodes at the next depth level.
|
||||
*
|
||||
* @param <V> the vertex of the graph
|
||||
* @author Berack96
|
||||
*/
|
||||
public class BFS<V> implements VisitStrategy<V> {
|
||||
|
||||
private int maxDepth = -1;
|
||||
|
||||
public BFS<V> setMaxDepth(int depth) {
|
||||
this.maxDepth = depth;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
final LinkedList<V> toVisitChildren = new LinkedList<>();
|
||||
|
||||
toVisitChildren.push(source);
|
||||
if (visit != null)
|
||||
visit.accept(source);
|
||||
info.setVisited(source);
|
||||
|
||||
while (!toVisitChildren.isEmpty()) {
|
||||
V current = toVisitChildren.removeFirst();
|
||||
if (maxDepth > -1 && info.getDepth(current) >= maxDepth)
|
||||
break;
|
||||
|
||||
for (V child : graph.getChildren(current))
|
||||
if (!info.isDiscovered(child)) {
|
||||
toVisitChildren.addLast(child);
|
||||
|
||||
info.setVisited(child);
|
||||
info.setParent(current, child);
|
||||
if (visit != null)
|
||||
visit.accept(child);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
49
src/main/java/net/berack/upo/graph/visit/DFS.java
Normal file
49
src/main/java/net/berack/upo/graph/visit/DFS.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* Depth-first search<br>
|
||||
* The algorithm starts at the root node and explores as far as possible along each branch before backtracking.
|
||||
*
|
||||
* @param <V> the vertex of the graph
|
||||
* @author Berack96
|
||||
*/
|
||||
public class DFS<V> implements VisitStrategy<V> {
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
final Stack<V> toVisit = new Stack<>();
|
||||
|
||||
toVisit.push(source);
|
||||
|
||||
while (!toVisit.isEmpty()) {
|
||||
V current = toVisit.peek();
|
||||
boolean hasChildToVisit = false;
|
||||
Iterator<V> iter = graph.getChildren(current).iterator();
|
||||
|
||||
while (iter.hasNext() && !hasChildToVisit) {
|
||||
V child = iter.next();
|
||||
if (!info.isDiscovered(child)) {
|
||||
hasChildToVisit = true;
|
||||
toVisit.push(child);
|
||||
info.setParent(current, child);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChildToVisit) {
|
||||
toVisit.pop();
|
||||
info.setVisited(current);
|
||||
if (visit != null)
|
||||
visit.accept(current);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
109
src/main/java/net/berack/upo/graph/visit/Dijkstra.java
Normal file
109
src/main/java/net/berack/upo/graph/visit/Dijkstra.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitDistance;
|
||||
|
||||
/**
|
||||
* Class that implements the Dijkstra algorithm and uses it for getting all the distance from a source
|
||||
*
|
||||
* @param <V> vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Dijkstra<V> implements VisitDistance<V> {
|
||||
|
||||
private Map<V, List<Edge<V>>> distance = null;
|
||||
private V source = null;
|
||||
|
||||
@Override
|
||||
public Map<V, List<Edge<V>>> getLastDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getLastSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
Queue<QueueEntry> queue = new PriorityQueue<>();
|
||||
Map<V, Integer> dist = Graph.getDefaultMap();
|
||||
Map<V, V> prev = Graph.getDefaultMap();
|
||||
|
||||
this.source = source;
|
||||
dist.put(source, 0); // Initialization
|
||||
queue.add(new QueueEntry(source, 0));
|
||||
|
||||
while (!queue.isEmpty()) { // The main loop
|
||||
QueueEntry u = queue.poll(); // Remove and return best vertex
|
||||
|
||||
info.setVisited(u.entry);
|
||||
if (visit != null)
|
||||
visit.accept(u.entry);
|
||||
|
||||
for (V child : graph.getChildren(u.entry)) {
|
||||
info.setDiscovered(child);
|
||||
int alt = dist.get(u.entry) + graph.getWeight(u.entry, child);
|
||||
Integer distCurrent = dist.get(child);
|
||||
|
||||
if (distCurrent == null || alt < distCurrent) {
|
||||
dist.put(child, alt);
|
||||
prev.put(child, u.entry);
|
||||
|
||||
QueueEntry current = new QueueEntry(child, alt);
|
||||
queue.remove(current);
|
||||
queue.add(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleaning up the results */
|
||||
distance = Graph.getDefaultMap();
|
||||
for (V vertex : prev.keySet()) {
|
||||
List<Edge<V>> path = new LinkedList<>();
|
||||
V child = vertex;
|
||||
V father = prev.get(child);
|
||||
do {
|
||||
Edge<V> edge = new Edge<>(father, child, graph.getWeight(father, child));
|
||||
path.add(0, edge);
|
||||
info.setParent(father, child);
|
||||
child = father;
|
||||
father = prev.get(child);
|
||||
} while (father != null);
|
||||
|
||||
distance.put(vertex, new ArrayList<>(path));
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
private class QueueEntry implements Comparable<QueueEntry> {
|
||||
final V entry;
|
||||
final int weight;
|
||||
|
||||
QueueEntry(V entry, int weight) {
|
||||
this.entry = entry;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
try {
|
||||
return ((QueueEntry) obj).entry.equals(entry);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(QueueEntry queueEntry) {
|
||||
double ret = this.weight - queueEntry.weight;
|
||||
return ret==0? 0: ret<0? -1:1;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/main/java/net/berack/upo/graph/visit/Kruskal.java
Normal file
44
src/main/java/net/berack/upo/graph/visit/Kruskal.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphUndirected;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitMST;
|
||||
import net.berack.upo.graph.visit.struct.QuickFind;
|
||||
import net.berack.upo.graph.visit.struct.UnionFind;
|
||||
|
||||
/**
|
||||
* Class that implement the algorithm discovered by Kruskal for the minimum spanning forest
|
||||
* for a given {@link GraphUndirected}
|
||||
*
|
||||
* @param <V> The vertex of the graph
|
||||
*/
|
||||
public class Kruskal<V> implements VisitMST<V> {
|
||||
private Set<Edge<V>> mst;
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> getMST() {
|
||||
return mst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
UnionFind<V> sets = new QuickFind<>();
|
||||
sets.makeSetAll(graph.vertices());
|
||||
|
||||
List<Edge<V>> edges = new ArrayList<>(graph.edges());
|
||||
Collections.sort(edges);
|
||||
|
||||
mst = Graph.getDefaultSet();
|
||||
Iterator<Edge<V>> iter = edges.iterator();
|
||||
while (iter.hasNext() && sets.size() > 1) {
|
||||
Edge<V> edge = iter.next();
|
||||
if (sets.union(edge.getSource(), edge.getDestination()))
|
||||
mst.add(edge);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
62
src/main/java/net/berack/upo/graph/visit/Prim.java
Normal file
62
src/main/java/net/berack/upo/graph/visit/Prim.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.GraphUndirected;
|
||||
import net.berack.upo.graph.Edge;
|
||||
import net.berack.upo.graph.VisitMST;
|
||||
|
||||
/**
|
||||
* Class that implement the algorithm discovered by Prim for the minimum spanning forest
|
||||
* for a given {@link GraphUndirected}
|
||||
*
|
||||
* @param <V> The vertex of the graph
|
||||
*/
|
||||
public class Prim<V> implements VisitMST<V> {
|
||||
|
||||
private Set<Edge<V>> mst;
|
||||
|
||||
@Override
|
||||
public Set<Edge<V>> getMST() {
|
||||
return mst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, UnsupportedOperationException {
|
||||
mst = Graph.getDefaultSet();
|
||||
Set<V> vertices = graph.vertices();
|
||||
|
||||
if (source == null)
|
||||
source = vertices.iterator().next();
|
||||
VisitInfo<V> info = new VisitInfo<>(source);
|
||||
V current = source;
|
||||
|
||||
do {
|
||||
if (current == null)
|
||||
current = vertices.iterator().next();
|
||||
|
||||
Edge<V> min = null;
|
||||
for (Edge<V> edge : graph.edgesOf(current))
|
||||
if (vertices.contains(edge.getDestination()))
|
||||
min = (min == null || edge.getWeight() < min.getWeight() ? edge : min);
|
||||
|
||||
info.setParent(source, current);
|
||||
info.setVisited(current);
|
||||
if (visit != null)
|
||||
visit.accept(current);
|
||||
|
||||
if (min == null)
|
||||
current = null;
|
||||
else {
|
||||
vertices.remove(current);
|
||||
source = min.getSource();
|
||||
current = min.getDestination();
|
||||
mst.add(min);
|
||||
}
|
||||
} while (vertices.size() != 0);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
108
src/main/java/net/berack/upo/graph/visit/Tarjan.java
Normal file
108
src/main/java/net/berack/upo/graph/visit/Tarjan.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
import net.berack.upo.graph.VisitSCC;
|
||||
import net.berack.upo.graph.VisitTopological;
|
||||
|
||||
/**
|
||||
* Class that implements the Tarjan algorithm and uses it for getting the SCC and the topological sort
|
||||
*
|
||||
* @param <V> vertex
|
||||
* @author Berack96
|
||||
*/
|
||||
public class Tarjan<V> implements VisitSCC<V>, VisitTopological<V> {
|
||||
|
||||
private Set<Set<V>> SCC = null;
|
||||
private List<V> topologicalSort = null;
|
||||
|
||||
private Map<V, Integer> indices = null;
|
||||
private Map<V, Integer> lowLink = null;
|
||||
private Stack<V> stack = null;
|
||||
private VisitInfo<V> info = null;
|
||||
|
||||
@Override
|
||||
public Set<Set<V>> getSCC() {
|
||||
return SCC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> getTopologicalSort() {
|
||||
return topologicalSort;
|
||||
}
|
||||
|
||||
/**
|
||||
* This particular visit strategy use only the graph and the visit, so the source param is not needed.
|
||||
*
|
||||
* @param graph the graph to visit
|
||||
* @param source not needed
|
||||
* @param visit the function to apply at each vertex when they are visited
|
||||
* @throws NullPointerException if the graph is null
|
||||
* @throws IllegalArgumentException doesn't throw this
|
||||
*/
|
||||
@Override
|
||||
public VisitInfo<V> visit(Graph<V> graph, V source, Consumer<V> visit) throws NullPointerException, IllegalArgumentException {
|
||||
SCC = Graph.getDefaultSet();
|
||||
topologicalSort = new ArrayList<>(graph.size());
|
||||
info = null;
|
||||
|
||||
indices = Graph.getDefaultMap();
|
||||
lowLink = Graph.getDefaultMap();
|
||||
stack = new Stack<>();
|
||||
int index = 0;
|
||||
|
||||
for (V vertex : graph) {
|
||||
if (info == null)
|
||||
info = new VisitInfo<>(vertex);
|
||||
if (!indices.containsKey(vertex))
|
||||
strongConnect(graph, vertex, index, visit);
|
||||
}
|
||||
|
||||
topologicalSort = (graph.size() == SCC.size()) ? topologicalSort : null;
|
||||
return info;
|
||||
}
|
||||
|
||||
private void strongConnect(Graph<V> graph, V vertex, Integer index, Consumer<V> visit) {
|
||||
// Set the depth index for v to the smallest unused index
|
||||
indices.put(vertex, index);
|
||||
lowLink.put(vertex, index);
|
||||
index++;
|
||||
stack.push(vertex);
|
||||
info.setDiscovered(vertex);
|
||||
|
||||
// Consider successors of v
|
||||
for (V child : graph.getChildren(vertex)) {
|
||||
if (!indices.containsKey(child)) {
|
||||
info.setParent(vertex, child);
|
||||
strongConnect(graph, child, index, visit);
|
||||
lowLink.put(vertex, Math.min(lowLink.get(vertex), lowLink.get(child)));
|
||||
} else if (stack.contains(child)) {
|
||||
// Successor w is in stack S and hence in the current SCC
|
||||
// If w is not on stack, then (v, w) is a cross-edge in the DFS tree and must be ignored
|
||||
// Note: The next line may look odd - but is correct.
|
||||
// It says w.index not w.lowlink; that is deliberate and from the original paper
|
||||
lowLink.put(vertex, Math.min(lowLink.get(vertex), indices.get(child)));
|
||||
}
|
||||
}
|
||||
|
||||
// If v is a root node, pop the stack and generate an SCC
|
||||
if (lowLink.get(vertex).equals(indices.get(vertex))) {
|
||||
Set<V> newComponent = Graph.getDefaultSet();
|
||||
V temp;
|
||||
do {
|
||||
temp = stack.pop();
|
||||
topologicalSort.add(0, temp);
|
||||
newComponent.add(temp);
|
||||
|
||||
info.setVisited(temp);
|
||||
if (visit != null)
|
||||
visit.accept(temp);
|
||||
|
||||
} while (!temp.equals(vertex));
|
||||
|
||||
SCC.add(newComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
327
src/main/java/net/berack/upo/graph/visit/VisitInfo.java
Normal file
327
src/main/java/net/berack/upo/graph/visit/VisitInfo.java
Normal file
@@ -0,0 +1,327 @@
|
||||
package net.berack.upo.graph.visit;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.berack.upo.graph.VisitStrategy;
|
||||
|
||||
/**
|
||||
* The class used for getting the info of the visit.<br>
|
||||
* It could be used with the algorithm of the visit for set some useful data.
|
||||
*
|
||||
* @param <V> the vertex of the visit
|
||||
* @author Berack96
|
||||
*/
|
||||
public class VisitInfo<V> implements Iterable<VisitInfo<V>.VertexInfo> {
|
||||
private static final int NOT_SET = -1;
|
||||
|
||||
private final Map<V, VertexInfo> vertices;
|
||||
private final V source;
|
||||
private long time;
|
||||
|
||||
/**
|
||||
* Need a source for initialize the basic values
|
||||
*
|
||||
* @param source the source of the visit
|
||||
* @throws NullPointerException if the source is null
|
||||
*/
|
||||
public VisitInfo(V source) {
|
||||
if (source == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
this.vertices = new Hashtable<>();
|
||||
this.time = 0;
|
||||
this.source = source;
|
||||
setDiscovered(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source of the visit.
|
||||
*
|
||||
* @return the source vertex where it's started the visit
|
||||
*/
|
||||
public V getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent of a particular vertex.<br>
|
||||
* The parent of a vertex is the one that has discovered it<br>
|
||||
* If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned.
|
||||
*
|
||||
* @param vertex the child vertex
|
||||
* @return the parent of the child
|
||||
* @throws IllegalArgumentException if the vertex has not been discovered yet or is null
|
||||
*/
|
||||
public V getParentOf(V vertex) throws IllegalArgumentException {
|
||||
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.<br>
|
||||
* For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}<br>
|
||||
* 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<br>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The time when the vertex is visited by the algorithm<br>
|
||||
* For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}<br>
|
||||
* The time starts at 0 and for each vertex discovered or visited is increased by one<br>
|
||||
*
|
||||
* @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.<br>
|
||||
* Next this call it will be possible to get the time of visit of that vertex<br>
|
||||
* 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<br>
|
||||
* 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, _ -> new VertexInfo(vertex));
|
||||
if(info.timeDiscovered != NOT_SET)
|
||||
return;
|
||||
|
||||
info.timeDiscovered = time;
|
||||
info.depth = 0;
|
||||
time++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent of a particular vertex<br>
|
||||
* The parent of a vertex is the one that has discovered it<br>
|
||||
* If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called<br>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the visited vertices so far.
|
||||
*
|
||||
* @return the visited vertices
|
||||
*/
|
||||
public Set<V> getVisited() {
|
||||
Set<V> visited = new HashSet<>(vertices.size());
|
||||
vertices.forEach((vert, info) -> {
|
||||
if(info.timeVisited != NOT_SET)
|
||||
visited.add(vert);
|
||||
});
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the discovered vertices so far.
|
||||
*
|
||||
* @return the discovered vertices
|
||||
*/
|
||||
public Set<V> getDiscovered() {
|
||||
Set<V> discovered = new HashSet<>(vertices.size());
|
||||
vertices.forEach((vert, info) -> {
|
||||
if(info.timeDiscovered != NOT_SET)
|
||||
discovered.add(vert);
|
||||
});
|
||||
return discovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through all the vertices that are discovered.<br>
|
||||
* The vertices will be ordered by the time of their discover.
|
||||
*
|
||||
* @param consumer the function to apply to each
|
||||
*/
|
||||
public void forEachDiscovered(Consumer<? super VertexInfo> consumer) {
|
||||
Queue<VertexInfo> queue = new PriorityQueue<>();
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeDiscovered != NOT_SET)
|
||||
queue.offer(new VertexInfo(info, false));
|
||||
});
|
||||
|
||||
while (!queue.isEmpty())
|
||||
consumer.accept(queue.poll());
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through all the vertices that are visited.<br>
|
||||
* The vertices will be ordered by the time of their visit.
|
||||
*
|
||||
* @param consumer the function to apply to each
|
||||
*/
|
||||
public void forEachVisited(Consumer<? super VertexInfo> consumer) {
|
||||
Queue<VertexInfo> queue = new PriorityQueue<>();
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeVisited != NOT_SET)
|
||||
queue.offer(new VertexInfo(info, true));
|
||||
});
|
||||
|
||||
while (!queue.isEmpty())
|
||||
consumer.accept(queue.poll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<VertexInfo> iterator() {
|
||||
List<VertexInfo> list = new ArrayList<>(vertices.size() * 2);
|
||||
vertices.forEach((_, info) -> {
|
||||
if (info.timeDiscovered != NOT_SET)
|
||||
list.add(new VertexInfo(info, false));
|
||||
if (info.timeVisited != NOT_SET)
|
||||
list.add(new VertexInfo(info, true));
|
||||
});
|
||||
Collections.sort(list);
|
||||
return list.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used mainly for storing the data of the visit
|
||||
*/
|
||||
public class VertexInfo implements Comparable<VertexInfo> {
|
||||
public V vertex;
|
||||
public V parent;
|
||||
public long timeDiscovered;
|
||||
public long timeVisited;
|
||||
public long depth;
|
||||
private final boolean 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 (%3d) [D:%3d, V:%3d]", parent, vertex, depth, timeDiscovered, timeVisited);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.berack.upo.graph.visit.struct;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.berack.upo.Graph;
|
||||
|
||||
/**
|
||||
* Simple implementation of the {@link UnionFind} interface with priority to the find function.
|
||||
*
|
||||
* @param <X> the elements to search and merge
|
||||
*/
|
||||
public class QuickFind<X> implements UnionFind<X> {
|
||||
Map<X, Collection<X>> struct = Graph.getDefaultMap();
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return struct.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeSetAll(Collection<X> elements) throws NullPointerException {
|
||||
Map<X, Collection<X>> temp = Graph.getDefaultMap();
|
||||
for (X elem : elements)
|
||||
temp.computeIfAbsent(elem, new AddElement());
|
||||
struct.putAll(temp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeSet(X element) throws NullPointerException {
|
||||
if (element == null)
|
||||
throw new NullPointerException();
|
||||
struct.computeIfAbsent(element, new AddElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException {
|
||||
element1 = find(element1);
|
||||
element2 = find(element2);
|
||||
if (element1 == null || element2 == null)
|
||||
throw new IllegalArgumentException();
|
||||
if (element1 == element2)
|
||||
return false;
|
||||
|
||||
return struct.get(element1).addAll(struct.remove(element2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public X find(X element) throws NullPointerException {
|
||||
if (element == null)
|
||||
throw new NullPointerException();
|
||||
if (struct.containsKey(element))
|
||||
return element;
|
||||
|
||||
AtomicReference<X> toReturn = new AtomicReference<>(null);
|
||||
struct.forEach((key, collection) -> {
|
||||
if (collection.contains(element))
|
||||
toReturn.set(key);
|
||||
});
|
||||
return toReturn.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stupid class for implementing the adding of a new element
|
||||
*/
|
||||
private class AddElement implements Function<X, Set<X>> {
|
||||
@Override
|
||||
public Set<X> apply(X x) {
|
||||
Set<X> coll = Graph.getDefaultSet();
|
||||
coll.add(x);
|
||||
return coll;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package net.berack.upo.graph.visit.struct;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Basic interface for the UnionFind tree sets
|
||||
*
|
||||
* @param <X> the object
|
||||
* @author Berack96
|
||||
*/
|
||||
public interface UnionFind<X> {
|
||||
|
||||
/**
|
||||
* Indicate how many different sets there are.
|
||||
*
|
||||
* @return the number of sets
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* It creates the single element set for every element in the collection
|
||||
*
|
||||
* @param elements the collection of the elements
|
||||
* @throws NullPointerException in the case of the set being null
|
||||
*/
|
||||
void makeSetAll(Collection<X> elements) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Creates the single element set for the element
|
||||
*
|
||||
* @param element the element to insert
|
||||
* @throws NullPointerException in the case of a null element
|
||||
*/
|
||||
void makeSet(X element) throws NullPointerException;
|
||||
|
||||
/**
|
||||
* Merge the tho elements into a single set.<br>
|
||||
* In the case that the two elements are in the same set it returns false.
|
||||
*
|
||||
* @param element1 an element of a set
|
||||
* @param element2 an element of another set
|
||||
* @return true in the case of a successful merge, false otherwise
|
||||
* @throws NullPointerException in the case of a null element
|
||||
* @throws IllegalArgumentException in the case of an element not in the sets
|
||||
*/
|
||||
boolean union(X element1, X element2) throws NullPointerException, IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Returns the element representing the set in which the element passed resides.<br>
|
||||
* In case of an element not found then it's returned null
|
||||
*
|
||||
* @param element the element in the set
|
||||
* @return the representing element of the set found
|
||||
* @throws NullPointerException in the case of a null element
|
||||
*/
|
||||
X find(X element) throws NullPointerException;
|
||||
}
|
||||
Reference in New Issue
Block a user