|
|
|
@ -28,7 +28,7 @@ |
|
|
|
package explicit; |
|
|
|
|
|
|
|
import java.util.ArrayDeque; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.BitSet; |
|
|
|
import java.util.Deque; |
|
|
|
import java.util.function.IntPredicate; |
|
|
|
@ -50,10 +50,14 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
private int index = 0; |
|
|
|
/* Stack of nodes */ |
|
|
|
private Deque<Integer> stack = new ArrayDeque<Integer>(); |
|
|
|
/* List of nodes in the graph. Invariant: {@code nodeList.get(i).id == i} */ |
|
|
|
private ArrayList<Node> nodeList; |
|
|
|
/* Nodes currently on the stack. */ |
|
|
|
private BitSet onStack; |
|
|
|
|
|
|
|
/** The lowlink information for the nodes (states) */ |
|
|
|
private int[] nodeLowlink; |
|
|
|
/** The index information for the nodes (states) */ |
|
|
|
private int[] nodeIndex; |
|
|
|
|
|
|
|
/** Should we filter trivial SCCs? */ |
|
|
|
private boolean filterTrivialSCCs; |
|
|
|
private IntPredicate restrict; |
|
|
|
@ -66,10 +70,10 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
super(parent, consumer); |
|
|
|
this.model = model; |
|
|
|
this.numNodes = model.getNumStates(); |
|
|
|
this.nodeList = new ArrayList<Node>(numNodes); |
|
|
|
for (int i = 0; i < numNodes; i++) { |
|
|
|
nodeList.add(new Node(i)); |
|
|
|
} |
|
|
|
nodeLowlink = new int[numNodes]; |
|
|
|
Arrays.fill(nodeLowlink, -1); |
|
|
|
nodeIndex = new int[numNodes]; |
|
|
|
Arrays.fill(nodeIndex, -1); |
|
|
|
onStack = new BitSet(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -96,17 +100,16 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
for (int i = 0; i < numNodes; i++) { |
|
|
|
if (restrict != null && !restrict.test(i)) |
|
|
|
continue; // skip state if not one of the relevant states |
|
|
|
if (nodeList.get(i).lowlink == -1) |
|
|
|
if (nodeLowlink[i] == -1) |
|
|
|
tarjan(i); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private void tarjan(int i) throws PrismException |
|
|
|
private void tarjan(final int i) throws PrismException |
|
|
|
{ |
|
|
|
final Node v = nodeList.get(i); |
|
|
|
v.index = index; |
|
|
|
v.lowlink = index; |
|
|
|
nodeIndex[i] = index; |
|
|
|
nodeLowlink[i] = index; |
|
|
|
index++; |
|
|
|
stack.push(i); |
|
|
|
onStack.set(i); |
|
|
|
@ -114,7 +117,7 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
boolean hadSelfloop = false; |
|
|
|
SuccessorsIterator it = model.getSuccessors(i); |
|
|
|
while (it.hasNext()) { |
|
|
|
int e = it.nextInt(); |
|
|
|
final int e = it.nextInt(); |
|
|
|
|
|
|
|
if (e == i) { |
|
|
|
hadSelfloop = true; |
|
|
|
@ -125,15 +128,14 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
continue; // ignore edge to state that is not relevant |
|
|
|
} |
|
|
|
|
|
|
|
Node n = nodeList.get(e); |
|
|
|
if (n.index == -1) { |
|
|
|
if (nodeIndex[e] == -1) { |
|
|
|
tarjan(e); |
|
|
|
v.lowlink = Math.min(v.lowlink, n.lowlink); |
|
|
|
nodeLowlink[i] = Math.min(nodeLowlink[i], nodeLowlink[e]); |
|
|
|
} else if (onStack.get(e)) { |
|
|
|
v.lowlink = Math.min(v.lowlink, n.index); |
|
|
|
nodeLowlink[i] = Math.min(nodeLowlink[i], nodeIndex[e]); |
|
|
|
} |
|
|
|
} |
|
|
|
if (v.lowlink == v.index) { |
|
|
|
if (nodeLowlink[i] == nodeIndex[i]) { |
|
|
|
// this is a singleton SCC if the top of the stack equals i |
|
|
|
boolean singletonSCC = (stack.peek() == i); |
|
|
|
if (singletonSCC && filterTrivialSCCs) { |
|
|
|
@ -155,19 +157,4 @@ public class SCCComputerTarjan extends SCCComputer |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* A small class wrapping a node. |
|
|
|
* It carries extra information necessary for Tarjan's algorithm. |
|
|
|
*/ |
|
|
|
protected static class Node |
|
|
|
{ |
|
|
|
public int lowlink = -1; |
|
|
|
public int index = -1; |
|
|
|
public int id; |
|
|
|
|
|
|
|
public Node(int id) |
|
|
|
{ |
|
|
|
this.id = id; |
|
|
|
} |
|
|
|
} |
|
|
|
} |