blob: f49c640dde0c5899067c8481102f62d1df63bbbb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.draw2d.graph;
import java.util.Iterator;
import java.util.Stack;
/**
* Assigns the final rank assignment for a DirectedGraph with an initial
* feasible spanning tree.
*
* @author Randy Hudson
* @since 2.1.2
*/
class RankAssignmentSolver extends SpanningTreeVisitor {
DirectedGraph graph;
EdgeList spanningTree;
boolean searchDirection;
int depthFirstCutValue(Edge edge, int count) {
Node n = getTreeTail(edge);
setTreeMin(n, count);
int cutvalue = 0;
int multiplier = (edge.target == n) ? 1 : -1;
EdgeList list;
list = n.outgoing;
Edge e;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (e.tree && e != edge) {
count = depthFirstCutValue(e, count);
cutvalue += (e.cut - e.weight) * multiplier;
} else {
cutvalue -= e.weight * multiplier;
}
}
list = n.incoming;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (e.tree && e != edge) {
count = depthFirstCutValue(e, count);
cutvalue -= (e.cut - e.weight) * multiplier;
} else {
cutvalue += e.weight * multiplier;
}
}
edge.cut = cutvalue;
if (cutvalue < 0)
spanningTree.add(edge);
setTreeMax(n, count);
return count + 1;
}
/**
* returns the Edge which should be entered.
*
* @param branch
* @return Edge
*/
Edge enter(Node branch) {
Node n;
Edge result = null;
int minSlack = Integer.MAX_VALUE;
boolean incoming = getParentEdge(branch).target != branch;
// searchDirection = !searchDirection;
for (int i = 0; i < graph.nodes.size(); i++) {
if (searchDirection)
n = graph.nodes.getNode(i);
else
n = graph.nodes.getNode(graph.nodes.size() - 1 - i);
if (subtreeContains(branch, n)) {
EdgeList edges;
if (incoming)
edges = n.incoming;
else
edges = n.outgoing;
for (int j = 0; j < edges.size(); j++) {
Edge e = edges.getEdge(j);
if (!subtreeContains(branch, e.opposite(n)) && !e.tree
&& e.getSlack() < minSlack) {
result = e;
minSlack = e.getSlack();
}
}
}
}
return result;
}
int getTreeMax(Node n) {
return n.workingInts[1];
}
int getTreeMin(Node n) {
return n.workingInts[0];
}
void initCutValues() {
Node root = graph.nodes.getNode(0);
spanningTree = new EdgeList();
Edge e;
setTreeMin(root, 1);
setTreeMax(root, 1);
for (int i = 0; i < root.outgoing.size(); i++) {
e = root.outgoing.getEdge(i);
if (!getSpanningTreeChildren(root).contains(e))
continue;
setTreeMax(root, depthFirstCutValue(e, getTreeMax(root)));
}
for (int i = 0; i < root.incoming.size(); i++) {
e = root.incoming.getEdge(i);
if (!getSpanningTreeChildren(root).contains(e))
continue;
setTreeMax(root, depthFirstCutValue(e, getTreeMax(root)));
}
}
Edge leave() {
Edge result = null;
Edge e;
int minCut = 0;
int weight = -1;
for (int i = 0; i < spanningTree.size(); i++) {
e = spanningTree.getEdge(i);
if (e.cut < minCut) {
result = e;
minCut = result.cut;
weight = result.weight;
} else if (e.cut == minCut && e.weight > weight) {
result = e;
weight = result.weight;
}
}
return result;
}
void networkSimplexLoop() {
Edge leave, enter;
int count = 0;
while ((leave = leave()) != null && count < 900) {
count++;
Node leaveTail = getTreeTail(leave);
Node leaveHead = getTreeHead(leave);
enter = enter(leaveTail);
if (enter == null)
break;
// Break the "leave" edge from the spanning tree
getSpanningTreeChildren(leaveHead).remove(leave);
setParentEdge(leaveTail, null);
leave.tree = false;
spanningTree.remove(leave);
Node enterTail = enter.source;
if (!subtreeContains(leaveTail, enterTail))
// Oops, wrong end of the edge
enterTail = enter.target;
Node enterHead = enter.opposite(enterTail);
// Prepare enterTail by making it the root of its sub-tree
updateSubgraph(enterTail);
// Add "enter" edge to the spanning tree
getSpanningTreeChildren(enterHead).add(enter);
setParentEdge(enterTail, enter);
enter.tree = true;
repairCutValues(enter);
Node commonAncestor = enterHead;
while (!subtreeContains(commonAncestor, leaveHead)) {
repairCutValues(getParentEdge(commonAncestor));
commonAncestor = getTreeParent(commonAncestor);
}
while (leaveHead != commonAncestor) {
repairCutValues(getParentEdge(leaveHead));
leaveHead = getTreeParent(leaveHead);
}
updateMinMax(commonAncestor, getTreeMin(commonAncestor));
tightenEdge(enter);
}
}
void repairCutValues(Edge edge) {
spanningTree.remove(edge);
Node n = getTreeTail(edge);
int cutvalue = 0;
int multiplier = (edge.target == n) ? 1 : -1;
EdgeList list;
list = n.outgoing;
Edge e;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (e.tree && e != edge)
cutvalue += (e.cut - e.weight) * multiplier;
else
cutvalue -= e.weight * multiplier;
}
list = n.incoming;
for (int i = 0; i < list.size(); i++) {
e = list.getEdge(i);
if (e.tree && e != edge)
cutvalue -= (e.cut - e.weight) * multiplier;
else
cutvalue += e.weight * multiplier;
}
edge.cut = cutvalue;
if (cutvalue < 0)
spanningTree.add(edge);
}
void setTreeMax(Node n, int value) {
n.workingInts[1] = value;
}
void setTreeMin(Node n, int value) {
n.workingInts[0] = value;
}
boolean subtreeContains(Node parent, Node child) {
return parent.workingInts[0] <= child.workingInts[1]
&& child.workingInts[1] <= parent.workingInts[1];
}
void tightenEdge(Edge edge) {
Node tail = getTreeTail(edge);
int delta = edge.getSlack();
if (tail == edge.target)
delta = -delta;
Node n;
for (int i = 0; i < graph.nodes.size(); i++) {
n = graph.nodes.getNode(i);
if (subtreeContains(tail, n))
n.rank += delta;
}
}
int updateMinMax(Node root, int count) {
setTreeMin(root, count);
EdgeList edges = getSpanningTreeChildren(root);
for (int i = 0; i < edges.size(); i++)
count = updateMinMax(getTreeTail(edges.getEdge(i)), count);
setTreeMax(root, count);
return count + 1;
}
void updateSubgraph(Node root) {
Edge flip = getParentEdge(root);
if (flip != null) {
Node rootParent = getTreeParent(root);
getSpanningTreeChildren(rootParent).remove(flip);
updateSubgraph(rootParent);
setParentEdge(root, null);
setParentEdge(rootParent, flip);
repairCutValues(flip);
getSpanningTreeChildren(root).add(flip);
}
}
public void visit(DirectedGraph graph) {
this.graph = graph;
initCutValues();
networkSimplexLoop();
if (graph.forestRoot == null)
graph.nodes.normalizeRanks();
else
normalizeForest();
}
private void normalizeForest() {
NodeList tree = new NodeList();
graph.nodes.resetFlags();
graph.forestRoot.flag = true;
EdgeList rootEdges = graph.forestRoot.outgoing;
Stack stack = new Stack();
for (int i = 0; i < rootEdges.size(); i++) {
Node node = rootEdges.getEdge(i).target;
node.flag = true;
stack.push(node);
while (!stack.isEmpty()) {
node = (Node) stack.pop();
tree.add(node);
Iterator neighbors = node.iteratorNeighbors();
while (neighbors.hasNext()) {
Node neighbor = (Node) neighbors.next();
if (!neighbor.flag) {
neighbor.flag = true;
stack.push(neighbor);
}
}
}
tree.normalizeRanks();
tree.clear();
}
}
}