package com.google.code.joto.util.graph;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.code.joto.util.ToStringFormatter;
/**
* helper class for computing the topological sort of a graph
*/
public class TopologicalSort<Vertex> {
private boolean debug = false;
private static Logger log = LoggerFactory.getLogger(TopologicalSort.class);
public static class Counter {
int count;
public Counter() {
}
}
private static final Object CounterAttrKey = Counter.class;
private IGraph<Vertex> graph;
private ToStringFormatter<Vertex> debugVertexFormat;
private Map<Vertex,Integer> debugVertex2id = new IdentityHashMap<Vertex,Integer>();
// private Comparator<Vertex> vertexCountComparator = new Comparator<Vertex>() {
// public int compare(Vertex o1, Vertex o2) {
// int c1 = vertexToCounter(o1).count;
// int c2 = vertexToCounter(o2).count;
// int res = (c1 < c2)? -1 : (c1 == c2)? 0 : +1;
// return res;
// }
// };
// -------------------------------------------------------------------------
public TopologicalSort(IGraph<Vertex> graph) {
this.graph = graph;
}
// -------------------------------------------------------------------------
public List<Vertex> topologicalSort() {
List<Vertex> res = new ArrayList<Vertex>();
if (debug) {
log.info("topological sort...");
}
List<Vertex> vertexes = graph.getVertexes();
// init attr count = counter of from vertex list
int i = 0;
for(Vertex v : vertexes) {
List<Vertex> vertexToList = graph.getVertexToList(v);
if (vertexToList != null && !vertexToList.isEmpty()) {
for(Vertex vertexTo : vertexToList) {
vertexToCounter(vertexTo).count++;
}
}
debugVertex2id.put(v, Integer.valueOf(i));
i++;
}
// init working queue
StablePriorityQueue<Vertex> queue = new StablePriorityQueue<Vertex>();
if (debug) {
queue.setDebugObj2id(debugVertex2id);
}
for(Vertex v : vertexes) {
int count = vertexToCounter(v).count;
queue.add(count, v);
}
if (debug) {
log.info("queue: " + queue.toString());
}
// consume elt from queue, decrement counter for each elt polled
while(!queue.isEmpty()) {
Vertex polledVertex = queue.poll();
if (polledVertex == null) {
throw new IllegalStateException("should not occur");
}
Counter c = vertexToCounter(polledVertex);
if (c.count != 0) {
log.info("circular dependency found...");
// throw new IllegalStateException("topological sort failed... circular dependency found");
}
res.add(polledVertex);
if (debug) {
log.info("found next: " + debugVertexFormat.objectToString(polledVertex));
log.info("queue size:" + queue.size()+ " elt(s)");
}
// decrement (and re-index in queue) all vertex pointed "to"
List<Vertex> vertexToList = graph.getVertexToList(polledVertex);
if (vertexToList != null && !vertexToList.isEmpty()) {
for(Vertex vertexTo : vertexToList) {
Counter vertexToCounter = vertexToCounter(vertexTo);
// int checkLen = queue.size();
// boolean removed = queue.remove(vertexTo);
// if (!removed) {
// throw new IllegalStateException();
// }
// vertexToCounter.count--;
// queue.add(vertexTo);
// if (checkLen != queue.size()) {
// throw new IllegalStateException();
// }
queue.moveUp(vertexToCounter.count, vertexTo);
vertexToCounter.count--;
if (debug) {
log.info("decremented count: " + vertexToCounter(vertexTo).count + " for: " + debugVertexFormat.objectToString(vertexTo));
}
}
}
}
if (debug) {
log.info("... done topological sort");
}
return res;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public void setDebugVertexFormat(ToStringFormatter<Vertex> debugVertexFormat) {
this.debugVertexFormat = debugVertexFormat;
}
private Counter vertexToCounter(Vertex p) {
return (Counter) graph.getVertexAttributeSupport(p).getAttrOrPutNewInstance(CounterAttrKey, Counter.class);
}
// private String debugQueueContent(PriorityQueue<Vertex> queue) {
// StringBuilder sb = new StringBuilder();
// if (!queue.isEmpty()) {
// for(Vertex v : queue) {
// Counter c = vertexToCounter(v);
// Integer vId = debugVertex2id.get(v);
// sb.append(vId + "(" + c.count + "), ");
// }
// sb.delete(sb.length() - 2, sb.length());
// }
// return sb.toString();
// }
// (re-)implementation of a stable priority queue ???!!!
// using [count]->List<>
// -------------------------------------------------------------------------
private static class StablePriorityQueue<T> {
// Map<Integer,List<T>> subQueues = new TreeMap<Integer,List<T>>();
private List<List<T>> subQueues = new ArrayList<List<T>>();
private int size;
Map<T,Integer> debugObj2id;
//-------------------------------------------------------------------------
public StablePriorityQueue() {
}
//-------------------------------------------------------------------------
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public T poll() {
List<T> subQueue = getSubQueue(0);
if (subQueue.isEmpty()) {
return null;
}
size--;
T res = subQueue.remove(0);
return res;
}
public void remove(int priority, T obj) {
List<T> subQueue = getSubQueue(priority);
subQueue.remove(obj);
size--;
}
public void add(int priority, T obj) {
List<T> subQueue = getSubQueue(priority);
subQueue.add(obj);
size++;
}
public void moveUp(int priority, T obj) {
remove(priority, obj);
add(priority - 1, obj);
}
public List<T> getSubQueue(int priority) {
// Integer key = Integer.valueOf(priority);
// List<T> res = subQueues.get(key);
// if (res == null) {
// res = new ArrayList();
// subQueues.put(key, res);
// }
int len = subQueues.size();
if (priority >= len) {
// alloc!
for(int i = len; i <= priority; i++) {
subQueues.add(new ArrayList<T>());
}
}
List<T> res = subQueues.get(priority);
return res;
}
public void setDebugObj2id(Map<T, Integer> debugObj2id) {
this.debugObj2id = debugObj2id;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PriorityQueue[" + size + " elt(s)\n");
for(int i = 0; i < subQueues.size(); i++) {
List<T> subQueue = subQueues.get(i);
if (subQueue != null && !subQueue.isEmpty()) {
sb.append("[" + i + "] " + subQueue.size() + " elt(s) : ");
for(T elt : subQueue) {
Integer id = debugObj2id.get(elt);
sb.append(id + ", ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append("\n");
}
}
sb.append("\n]");
return sb.toString();
}
}
}