package nl.tudelft.lifetiles.graph.model; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Set; /** * Iterate the graph from source to sink using true vertical * breadth-first search. * * @author Joren Hammudoglu * * @param <V> * the vertex type */ public class BreadthFirstIterator<V> implements Iterator<V> { /** * The graph to iterate over. */ private final Graph<V> graph; /** * The queued vertices. */ private final Queue<V> queue; /** * The waiting vertices with their remaining count. */ private final Map<V, Integer> waiting; /** * Whether it should iterate backwards. */ private boolean reverse; /** * Create a new iterator. * * @param graph * the graph to iterate over. */ public BreadthFirstIterator(final Graph<V> graph) { this(graph, false); } /** * Create a new iterator. * * @param graph * the graph to iterate over. * @param reverse * whether it should iterate backwards */ public BreadthFirstIterator(final Graph<V> graph, final boolean reverse) { this.graph = graph; this.queue = new LinkedList<>(); this.waiting = new HashMap<>(); this.reverse = reverse; if (reverse) { initializeBackwards(); } else { initializeForwards(); } } /** * Initialize for forwards iteration. */ private void initializeForwards() { for (V source : graph.getSources()) { queue.add(source); } } /** * Initialize for backwards iteration. */ private void initializeBackwards() { for (V sink : graph.getSinks()) { queue.add(sink); } } /** * {@inheritDoc} */ @Override public boolean hasNext() { return !queue.isEmpty(); } /** * {@inheritDoc} */ @Override public V next() { V vertex = queue.poll(); if (vertex == null) { return null; } Set<Edge<V>> outgoingVertices; if (reverse) { outgoingVertices = graph.getIncoming(vertex); } else { outgoingVertices = graph.getOutgoing(vertex); } if (waitForVertex(vertex)) { return next(); } for (Edge<V> edge : outgoingVertices) { V destination; if (reverse) { destination = graph.getSource(edge); } else { destination = graph.getDestination(edge); } queue.add(destination); } return vertex; } /** * Check if the iterator has to wait for a vertex. * * @param vertex * the vertex to wait for * @return whether to wait */ private boolean waitForVertex(final V vertex) { int inLinks; if (reverse) { inLinks = graph.getOutgoing(vertex).size(); } else { inLinks = graph.getIncoming(vertex).size(); } if (waiting.containsKey(vertex)) { int newVal = waiting.get(vertex) - 1; if (newVal < 1) { // not waiting for the vertex anymore, continue traversal waiting.remove(vertex); } else { waiting.put(vertex, newVal); return true; } } else if (inLinks > 1) { waiting.put(vertex, inLinks - 1); return true; } return false; } }