public static IAutomaton Minimize(this IAutomaton @this) { //prepare NFA automaton to a DFA automaton var automaton = @this.Determinize(); //mark all distinguishable pairs (unacceptable <-> acceptable) var stateCount = automaton.StateCount; var distinguishablePairs = new bool?[stateCount, stateCount]; foreach (var acceptable in automaton.AcceptingStates) { foreach (var unacceptable in Enumerable.Range(0, stateCount) .Except(automaton.AcceptingStates)) { distinguishablePairs[acceptable, unacceptable] = true; distinguishablePairs[unacceptable, acceptable] = true; } } //try to mark all others // Example (#=4) // 1 X // 2 X X // 3 X X X // 0 1 2 var mergedByState = new Dictionary <int, HashSet <int> >(); var merged = new HashSet <HashSet <int> >(); for (var aIndex = 1; aIndex < stateCount; aIndex++) { for (var bIndex = 0; bIndex < aIndex; bIndex++) { if (!MarkDistinguishables(distinguishablePairs, automaton, aIndex, bIndex)) { var list = mergedByState.GetValueOrInsertedDefault(aIndex, mergedByState.GetValueOrInsertedLazyDefault(bIndex, () => new HashSet <int>())); list.Add(aIndex); list.Add(bIndex); merged.Add(list); } } } var mappings = new Dictionary <int, int>(); //old automaton to new automaton states var inverseMappings = new Dictionary <int, int>(); //NOW BUILD THE F*****G AUTOMATON!!! var builder = new AutomatonBuilder(); foreach (var mergedStates in merged) { var newState = builder.AddState(); inverseMappings[newState] = mergedStates.First(); foreach (var oldState in mergedStates) { mappings[oldState] = newState; } } for (var state = 0; state < automaton.StateCount; state++) { if (!mappings.ContainsKey(state)) { var newState = builder.AddState(); mappings[state] = newState; inverseMappings[newState] = state; } } //mark starts and ends builder.SetStartState(mappings[automaton.StartState]); foreach (var accept in automaton.AcceptingStates) { builder.AcceptState(mappings[accept]); } //add transitions foreach (var kv in inverseMappings) { var oldState = kv.Value; var newState = kv.Key; foreach (var transition in automaton.TransitionsBySource.GetOrDefault(oldState, AutomatonExtensions.EmptyTargets)) { builder.AddTransition(newState, transition.Key, mappings[transition.First()]); } } //build return(builder.Build()); }