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 }