private void DiscardNotReachable(Partition blocks, List <Transition> transitions, Func <Transition, int> getFrom, Func <Transition, int> getTo)
        {
            var adjacentTransitions = new AdjacentTransitions(StateCount, transitions, getFrom);

            foreach (var state in blocks.MarkedInSet(0))
            {
                foreach (var transition in adjacentTransitions[state])
                {
                    blocks.Mark(getTo(transitions[transition]));
                }
            }

            blocks.DiscardUnmarked();

            transitions.RemoveAll(transition => blocks.SetOf(getFrom(transition)) == -1);
        }
        public DFA Minimize()
        {
            // We will be modifying this list of transitions and we don't want to mess up our own
            var transitions = new List <Transition>(this.transitions);

            var blocks = new Partition(StateCount);

            // Reachable from start
            blocks.Mark(StartState);

            DiscardNotReachable(blocks, transitions, t => t.From, t => t.To);

            // Reachable from final
            foreach (var finalState in finalStates)
            {
                blocks.Mark(finalState);
            }

            DiscardNotReachable(blocks, transitions, t => t.To, t => t.From);

            // Split final states from non-final
            foreach (var finalState in finalStates)
            {
                blocks.Mark(finalState);
            }

            blocks.SplitSets();

            // Cords partition to manage transitions
            var cords = new Partition(transitions.Count);

            // Split transitions by input
            cords.PartitionBy(transition => transitions[transition].OnInput);

            //Split blocks and cords
            var adjacentTransitions = new AdjacentTransitions(StateCount, transitions, t => t.To);
            var blockSet            = 1;

            for (var cordSet = 0; cordSet < cords.SetCount; cordSet++)
            {
                foreach (var transition in cords.Set(cordSet))
                {
                    blocks.Mark(transitions[transition].From);
                }

                blocks.SplitSets();

                for (; blockSet < blocks.SetCount; blockSet++)
                {
                    foreach (var state in blocks.Set(blockSet))
                    {
                        foreach (var transition in adjacentTransitions[state])
                        {
                            cords.Mark(transition);
                        }
                    }

                    cords.SplitSets();
                }
            }

            // Generate minimized DFA
            var minDFA = new DFA(blocks.SetCount, blocks.SetOf(StartState));

            // Set Final States
            foreach (var finalState in finalStates)
            {
                var set = blocks.SetOf(finalState);
                if (set != -1)                // not all final states may have been reachable
                {
                    minDFA.AddFinalState(set);
                }
            }

            // Create transitions
            for (var set = 0; set < cords.SetCount; set++)
            {
                var transition = transitions[cords.SomeElementOf(set)];
                var @from      = blocks.SetOf(transition.From);
                var to         = blocks.SetOf(transition.To);
                minDFA.AddTransition(@from, transition.OnInput, to);
            }

            return(minDFA);
        }