package berack96.lib.graph.visit.impl;
import java.util.*;
import java.util.function.Consumer;
import berack96.lib.graph.visit.VisitStrategy;
/**
* The class used for getting the info of the visit.
* It could be used with the algorithm of the visit for set some useful data.
*
* @param the vertex of the visit
* @author Berack96
*/
public class VisitInfo {
private final Map discovered;
private final Map visited;
private final Map parent;
private final V source;
private long time;
/**
* Need a source for initialize the basic values
*
* @param source the source of the visit
* @throws NullPointerException if the source is null
*/
public VisitInfo(V source) {
if (source == null)
throw new NullPointerException();
discovered = new Hashtable<>();
visited = new Hashtable<>();
parent = new Hashtable<>();
this.time = 0;
this.source = source;
setDiscovered(source);
}
/**
* The time of the vertex when it is discovered in the visit.
* For "discovered" i mean when the node is first found by the visit algorithm. It may depends form {@link VisitStrategy}
* The time starts at 0 and for each vertex discovered it is increased by one. If a vertex is visited it also increase the time
*
* @param vertex the vertex needed
* @return the time of it's discovery
* @throws IllegalArgumentException if the vertex is not discovered
* @throws NullPointerException if the vertex is null
*/
public long getTimeDiscover(V vertex) throws IllegalArgumentException, NullPointerException {
Long time = discovered.get(vertex);
if (time == null)
throw new IllegalArgumentException();
return time;
}
/**
* The time when the vertex is visited by the algorithm
* For "visited" i mean when the node is finally visited by the visit algorithm. It may depends form {@link VisitStrategy}
* The time starts at 0 and for each vertex discovered or visited is increased by one
*
* @param vertex the vertex needed
* @return the time of it's visit
* @throws IllegalArgumentException if the vertex is not visited
* @throws NullPointerException if the vertex is null
*/
public long getTimeVisit(V vertex) throws IllegalArgumentException, NullPointerException {
Long time = visited.get(vertex);
if (time == null)
throw new IllegalArgumentException();
return time;
}
/**
* Tells if a vertex is discovered or not
*
* @param vertex the vertex chosen
* @return true if is discovered
*/
public boolean isDiscovered(V vertex) throws NullPointerException {
try {
return discovered.containsKey(vertex);
} catch (NullPointerException e) {
return false;
}
}
/**
* Tells if the vertex is visited or not
*
* @param vertex the vertex chosen
* @return true if is visited
*/
public boolean isVisited(V vertex) throws NullPointerException {
try {
return visited.containsKey(vertex);
} catch (NullPointerException e) {
return false;
}
}
/**
* Set a vertex as "visited". After this call the vertex is set as discovered (if not already) and visited.
* Next this call it will be possible to get the time of visit of that vertex
* Does nothing if the vertex is already been visited.
*
* @param vertex the vertex that has been visited
*/
synchronized void setVisited(V vertex) {
setDiscovered(vertex);
if (!visited.containsKey(vertex))
visited.put(vertex, time++);
}
/**
* Set a vertex as "discovered". After this call the vertex is set as discovered and it will be possible to get the time of it's discovery
* Does nothing if the vertex is already been discovered.
*
* @param vertex the vertex that has been discovered
*/
synchronized void setDiscovered(V vertex) {
if (!discovered.containsKey(vertex))
discovered.put(vertex, time++);
}
/**
* Set the parent of a particular vertex
* The parent of a vertex is the one that has discovered it
* If the target vertex is not already discovered, then {@link #setDiscovered(Object)} is called
*
* @param parent the vertex that is the parent
* @param child the vertex discovered
* @throws IllegalArgumentException if the parent is not already discovered
*/
synchronized void setParent(V parent, V child) throws IllegalArgumentException {
if (!isDiscovered(parent))
throw new IllegalArgumentException(parent.toString());
setDiscovered(child);
this.parent.putIfAbsent(child, parent);
}
/**
* Get the source of the visit.
*
* @return the source vertex where it's started the visit
*/
public V getSource() {
return source;
}
/**
* Get the parent of a particular vertex.
* The parent of a vertex is the one that has discovered it
* If the vertex has no parent (it has not been set by the visit algorithm or it's the source) then null is returned.
*
* @param vertex the child vertex
* @return the parent of the child
* @throws IllegalArgumentException if the vertex has not been discovered yet
*/
public V getParentOf(V vertex) throws IllegalArgumentException {
if (isDiscovered(vertex))
return parent.get(vertex);
throw new IllegalArgumentException();
}
/**
* Get all the visited vertices so far.
*
* @return the visited vertices
*/
public Set getVisited() {
return visited.keySet();
}
/**
* Get all the discovered vertices so far.
*
* @return the discovered vertices
*/
public Set getDiscovered() {
return discovered.keySet();
}
/**
* Iterate through all the vertices that are discovered.
* The vertices will be ordered by the time of their discover.
*
* @param consumer the function to apply to each
*/
public void forEachDiscovered(Consumer consumer) {
Set vertices = getDiscovered();
Queue queue = new PriorityQueue<>();
vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), isVisited(vertex) ? getTimeVisit(vertex) : -1, false)));
while (!queue.isEmpty())
consumer.accept(queue.poll());
}
/**
* Iterate through all the vertices that are visited.
* The vertices will be ordered by the time of their visit.
*
* @param consumer the function to apply to each
*/
public void forEachVisited(Consumer consumer) {
Set vertices = getVisited();
Queue queue = new PriorityQueue<>();
vertices.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true)));
while (!queue.isEmpty())
consumer.accept(queue.poll());
}
/**
* Iterate through all the vertices discovered and visited with the correct timeline.
* The vertices will be visited in the order that they are discovered and visited, so a vertex can appear two times (one for the discovery, anc the other for the visit)
*
* @param consumer the function to apply at each vertex
*/
public void forEach(Consumer consumer) {
Set discovered = getDiscovered();
Set visited = getVisited();
Queue queue = new PriorityQueue<>();
discovered.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), false)));
visited.forEach((vertex) -> queue.offer(new VertexInfo(vertex, getParentOf(vertex), getTimeDiscover(vertex), getTimeVisit(vertex), true)));
while (!queue.isEmpty())
consumer.accept(queue.remove());
}
/**
* Class used mainly for storing the data of the visit
*/
public class VertexInfo implements Comparable {
public final V vertex;
public final V parent;
public final long timeDiscovered;
public final long timeVisited;
private final boolean compareVisited;
private VertexInfo(V vertex, V parent, long timeDiscovered, long timeVisited, boolean compareVisited) {
this.vertex = vertex;
this.parent = parent;
this.timeDiscovered = timeDiscovered;
this.timeVisited = timeVisited;
this.compareVisited = compareVisited;
}
@Override
public int compareTo(VertexInfo other) {
long compareThis = compareVisited ? timeVisited : timeDiscovered;
long compareOther = other.compareVisited ? other.timeVisited : other.timeDiscovered;
return (int) (compareThis - compareOther);
}
@Override
public String toString() {
return String.format("%s -> %s [D:%3d, V:%3d]", parent, vertex, timeDiscovered, timeVisited);
}
}
}