private Automaton <S> MakeKleeneClosure(Automaton <S> sfa)
        {
            if (sfa.IsEmpty || sfa.IsEpsilon)
            {
                return(this.epsilon);
            }

            if (sfa.IsKleeneClosure())
            {
                return(sfa);
            }


            if (sfa.DoesNotContainWordBoundaries && sfa.InitialStateIsSource && sfa.HasSingleFinalSink)
            {
                //common case, avoid epsilons in this case
                //just destructively make the final state to be the initial state
                sfa.RenameInitialState(sfa.FinalState);
                return(sfa);
            }

            int origInitState = sfa.InitialState;

            if (!sfa.IsFinalState(sfa.InitialState))//the initial state is not final
            {
                if (sfa.InitialStateIsSource)
                {
                    //make the current initial state final
                    sfa.MakeInitialStateFinal();
                }
                else
                {
                    //add a new initial state that is also final
                    sfa.AddNewInitialStateThatIsFinal(this.MkStateId());
                }
            }

            //add epsilon transitions from final states to the original initial state
            foreach (int state in sfa.GetFinalStates())
            {
                if (state != sfa.InitialState && state != origInitState)
                {
                    sfa.AddMove(Move <S> .Epsilon(state, origInitState));
                }
            }

            //epsilon loops might have been created, remove them
            var sfa1 = sfa.RemoveEpsilonLoops();

            if (!sfa.DoesNotContainWordBoundaries)
            {
                sfa1.AddWordBoundaries(sfa.EnumerateWordBoundaries());
            }
            return(sfa1);
        }
        /// <summary>
        /// Builds an automaton equivalent to the regex r{m,n}, or r{m,} when n is int.MaxValue;
        /// r{0,int.MaxValue} is the same as r*;
        /// r{1,int.MaxValue} is the same as r+.
        /// </summary>
        /// <param name="r">regular expression node</param>
        /// <param name="m">lower loop bound</param>
        /// <param name="n">upper loop bound</param>
        /// <returns></returns>
        public Automaton <S> MkLoop(T r, int m, int n)
        {
            bool start = isBeg;
            bool end   = isEnd;

            isBeg = false;
            isEnd = false;
            Automaton <S> sfa = Callback(r);

            isBeg = start;
            isEnd = end;


            Automaton <S> loop;

            if (m == 0 && sfa.IsEmpty)
            {
                loop = this.epsilon;
            }
            else if (m == 0 && n == int.MaxValue) //case: *
            {
                loop = MakeKleeneClosure(sfa);
            }
            else if (m == 0 && n == 1) //case: ?
            {
                ;
                if (sfa.IsFinalState(sfa.InitialState))
                {
                    return(sfa);
                }
                else if (sfa.InitialStateIsSource)
                {
                    sfa.MakeInitialStateFinal();
                }
                else
                {
                    sfa.AddNewInitialStateThatIsFinal(this.MkStateId());
                }
                loop = sfa;
            }
            else if (m == 1 && n == 1) //trivial case: r{1,1} = r
            {
                if (sfa.IsEmpty)
                {
                    return(this.empty);
                }
                loop = sfa;
            }
            else if (n == int.MaxValue) //case: + or generally {m,} for m >= 1
            {
                if (sfa.IsEmpty)
                {
                    return(this.empty);
                }

                if (sfa.IsFinalState(sfa.InitialState))
                {
                    loop = MakeKleeneClosure(sfa); //the repetition is a loop
                }
                else
                {
                    List <Automaton <S> > sfas = new List <Automaton <S> >();
                    for (int i = 0; i < m; i++)
                    {
                        //make m fresh copies
                        sfas.Add(sfa);
                        sfa = sfa.MakeCopy(this.MkStateId);
                    }
                    //the last one is made into a Kleene closure
                    sfas.Add(MakeKleeneClosure(sfa));
                    //concatenate them all
                    loop = ConcatenateSFAs(sfas);
                }
            }
            else //general case {m,n}
            {
                List <Automaton <S> > sfas = new List <Automaton <S> >();
                //List<int> newFinals = new List<int>();
                for (int i = 0; i < n; i++)
                {
                    sfas.Add(sfa);
                    if (i < n - 1)
                    {
                        sfa = sfa.MakeCopy(this.MkStateId);
                        if (i >= m - 1)
                        {
                            if (sfa.DoesNotContainWordBoundaries && sfa.InitialStateIsSource && !sfa.IsFinalState(sfa.InitialState))
                            {
                                sfa.MakeInitialStateFinal();
                            }
                            else
                            {
                                sfa.AddNewInitialStateThatIsFinal(this.MkStateId());
                            }
                        }
                    }
                }
                loop = ConcatenateSFAs(sfas);
                if (m == 0)
                {
                    loop.MakeInitialStateFinal();
                }
                //loop.SetFinalStates(newFinals);
            }
            loop = ExtendLoop(start, end, loop);
            return(loop);
        }