/// <summary> /// Implements <a href="http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm">Tarjan's algorithm</a> /// for finding the strongly connected components of the automaton graph. /// </summary> /// <param name="currentState">The state currently being traversed.</param> /// <param name="traversalIndex">The traversal index (as defined by the Tarjan's algorithm).</param> /// <param name="stateIdToStateInfo">A dictionary mapping state indices to info records maintained by the Tarjan's algorithm.</param> /// <param name="stateIdStack">The traversal stack (as defined by the Tarjan's algorithm).</param> private void FindStronglyConnectedComponents( State currentState, ref int traversalIndex, Dictionary <int, TarjanStateInfo> stateIdToStateInfo, Stack <State> stateIdStack) { Debug.Assert(!stateIdToStateInfo.ContainsKey(currentState.Index), "Visited states must not be revisited."); var stateInfo = new TarjanStateInfo(traversalIndex); stateIdToStateInfo.Add(currentState.Index, stateInfo); ++traversalIndex; stateIdStack.Push(currentState); stateInfo.InStack = true; for (int transitionIndex = 0; transitionIndex < currentState.TransitionCount; ++transitionIndex) { Transition transition = currentState.GetTransition(transitionIndex); if (!this.transitionFilter(transition)) { continue; } TarjanStateInfo destinationStateInfo; if (!stateIdToStateInfo.TryGetValue(transition.DestinationStateIndex, out destinationStateInfo)) { this.FindStronglyConnectedComponents( this.Root.Owner.states[transition.DestinationStateIndex], ref traversalIndex, stateIdToStateInfo, stateIdStack); stateInfo.Lowlink = Math.Min(stateInfo.Lowlink, stateIdToStateInfo[transition.DestinationStateIndex].Lowlink); } else if (destinationStateInfo.InStack) { stateInfo.Lowlink = Math.Min(stateInfo.Lowlink, destinationStateInfo.TraversalIndex); } } if (stateInfo.Lowlink == stateInfo.TraversalIndex) { var statesInComponent = new List <State>(); State state; do { state = stateIdStack.Pop(); stateIdToStateInfo[state.Index].InStack = false; statesInComponent.Add(state); }while (state.Index != currentState.Index); this.components.Add(new StronglyConnectedComponent(this.transitionFilter, statesInComponent, this.useApproximateClosure)); } }
/// <summary> /// Implements <a href="http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm">Tarjan's algorithm</a> /// for finding the strongly connected components of the automaton graph. /// </summary> private List <StronglyConnectedComponent> FindStronglyConnectedComponents() { var components = new List <StronglyConnectedComponent>(); var states = this.automaton.States; var stateIdStack = new Stack <int>(); var stateIdToStateInfo = new Dictionary <int, TarjanStateInfo>(); int traversalIndex = 0; var traversalStack = new Stack <(TarjanStateInfo, int stateIndex, int currentTransitionIndex)>(); traversalStack.Push((null, this.Root.Index, 0)); while (traversalStack.Count > 0) { var(stateInfo, currentStateIndex, currentTransitionIndex) = traversalStack.Pop(); var currentState = states[currentStateIndex]; var transitions = currentState.Transitions; if (stateInfo == null) { // Entered into processing of this state for the first time: create Tarjan info for it // and push the state onto Tarjan stack. stateInfo = new TarjanStateInfo(traversalIndex); stateIdToStateInfo.Add(currentStateIndex, stateInfo); ++traversalIndex; stateIdStack.Push(currentStateIndex); stateInfo.InStack = true; } else { // Just returned from recursion into 'currentTransitionIndex': update Lowlink // and advance to next transition. var lastDestination = transitions[currentTransitionIndex].DestinationStateIndex; stateInfo.Lowlink = Math.Min(stateInfo.Lowlink, stateIdToStateInfo[lastDestination].Lowlink); ++currentTransitionIndex; } for (; currentTransitionIndex < transitions.Count; ++currentTransitionIndex) { var transition = transitions[currentTransitionIndex]; if (!this.transitionFilter(transition)) { continue; } var destinationStateIndex = transition.DestinationStateIndex; if (!stateIdToStateInfo.TryGetValue(destinationStateIndex, out var destinationStateInfo)) { // Return to this (state/transition) after destinationState is processed. // Processing will resume from currentTransitionIndex. traversalStack.Push((stateInfo, currentStateIndex, currentTransitionIndex)); // Process destination state. traversalStack.Push((null, destinationStateIndex, 0)); // Do not process other transitions until destination state is processed. break; } if (destinationStateInfo.InStack) { stateInfo.Lowlink = Math.Min(stateInfo.Lowlink, destinationStateInfo.TraversalIndex); } } if (currentTransitionIndex < transitions.Count) { // Not all states processed: continue processing according to stack. Current state // is already pushed onto it -> will be processed again. continue; } if (stateInfo.Lowlink == stateInfo.TraversalIndex) { var statesInComponent = new List <State>(); int stateIndex; do { stateIndex = stateIdStack.Pop(); stateIdToStateInfo[stateIndex].InStack = false; statesInComponent.Add(states[stateIndex]); } while (stateIndex != currentStateIndex); components.Add(new StronglyConnectedComponent( this.automaton, this.transitionFilter, statesInComponent, this.useApproximateClosure)); this.TotalStatesCount += statesInComponent.Count; } } return(components); }