1 module gamma.parsgen.lalr1.SCCSetComputation; 2 3 import gamma.util.Indexed; 4 5 /** 6 * Computation of the set-valued function F on the nodes V of a digraph 7 * G=(V,E), such that, for all x,y in V, F(x) is the smallest set 8 * satisfying (1) F(x) >= F'(x), and (2) F(x) >= F(y) if (x,y) in E. 9 * The computation effectively computes the strongly connected components 10 * of G using Tarjan's algorithm (as streamlined by Eve and Kurko-Suonio). 11 * <p> 12 * The digraph nodes are passed to the algorithm as an 13 * <code>Indexed [] nodes</code> (which will imply the node traversal order). 14 * For <code>x, y</code> from <code>nodes[]</code> it is required that 15 * <code>x <> y</code> implies <code>x.index <> y.index</code>. 16 * 17 * @author SöKa 18 */ 19 public class SCCSetComputation 20 { 21 /** 22 * "Walks" edges in the digraph. 23 */ 24 public interface TransitionWalker 25 { 26 /** 27 * "Walks down" an edge <code>(x,y)</code> in the digraph. 28 */ 29 void walk(Indexed x, Indexed y); 30 } 31 32 /** 33 * Finds out the outgoing edges of a given node in the digraph. 34 */ 35 public interface TransitionEnumerator 36 { 37 /** 38 * Given the digraph node <code>x</code>, call 39 * <code>transitionWalker(x,y)</code> for each edge <code>(x,y)</code> 40 * in the digraph. 41 */ 42 void visitNeighborsOf(Indexed x, TransitionWalker transitionWalker); 43 } 44 45 public interface SetOperator 46 { 47 /** Initialize F(x), i.e. compute F(x) := F'(x). */ 48 void initSet(Indexed x); 49 50 /** Include F(y) in F(x), i.e. perform the assignment F(x) := F(x) + F(y). */ 51 void includeSet(Indexed x, Indexed y); 52 } 53 54 /** 55 * Runs the algorithm to compute all sets F(x) as described in the class 56 * documentation. Caller provides a <code>transitionEnumerator</code> to 57 * realize the digraph's edge relation E, and a 58 * <code>setOperator</code> to realize the necessary set computations. 59 * 60 * @param nodes The nodes of the digraph 61 * @param transitionEnumerator provides digraph edges 62 * @param setOperator realizes set computations 63 */ 64 static public void computeSets(Indexed [] nodes, 65 TransitionEnumerator transitionEnumerator, 66 SetOperator setOperator) 67 { 68 auto instance = new SCCSetComputation(nodes, transitionEnumerator, setOperator); 69 70 instance.run; 71 } 72 73 private Indexed[] nodes; 74 75 private Indexed[] stack; 76 77 private int top; 78 79 private int[] low; 80 81 private TransitionEnumerator transitionEnumerator; 82 83 private SetOperator setOperator; 84 85 private enum UNVISITED = -1; 86 87 private enum DONE = int.max; 88 89 private TransitionWalker minimizeLowxOp; 90 91 private this(Indexed[] nodes, 92 TransitionEnumerator transitionEnumerator, 93 SetOperator setOperator) 94 { 95 this.nodes = nodes; 96 this.transitionEnumerator = transitionEnumerator; 97 this.setOperator = setOperator; 98 this.minimizeLowxOp = new class TransitionWalker 99 { 100 public void walk(Indexed x, Indexed y) 101 { 102 traverse(y); 103 setOperator.includeSet(x, y); 104 if (low[x.index] < low[y.index]) 105 low[x.index] = low[y.index]; 106 } 107 }; 108 } 109 110 private void traverse(Indexed x) 111 { 112 if (low[x.index] == UNVISITED) 113 { 114 stack[++top] = x; 115 116 const depth = top; 117 118 low[x.index] = top; 119 setOperator.initSet(x); 120 transitionEnumerator.visitNeighborsOf(x, minimizeLowxOp); 121 if (low[x.index] == depth) 122 { 123 while (top > depth) 124 { 125 // Set F(x) to F(y), i.e. perform the assignment F(x) := F(y). 126 // Can be realized by including F(x) into F(y). 127 setOperator.includeSet(stack[top], x); 128 low[stack[top].index] = DONE; 129 --top; 130 } 131 low[stack[top].index] = DONE; 132 --top; 133 } 134 } 135 } 136 137 private void run() 138 { 139 import std.conv : to; 140 141 int maxIndex = -1; 142 143 for (int i = 0; i < nodes.length; ++i) 144 if (nodes[i].index.to!int > maxIndex) 145 maxIndex = nodes[i].index.to!int; 146 147 this.stack = new Indexed[nodes.length]; 148 this.low = new int[maxIndex + 1]; 149 this.top = -1; 150 151 for (int i = 0; i < nodes.length; ++i) 152 low[nodes[i].index] = UNVISITED; 153 for (int i = 0; i < nodes.length; ++i) 154 traverse(nodes[i]); 155 } 156 }