/// <summary>
        /// Builds an automaton equivalent to the regex s[0]s[1] ... s[r.Length-1],
        /// returns the automaton accepting only the empty word when s is empty.
        /// </summary>
        public Automaton <S> MkSeq(params S[] s)
        {
            if (s.Length == 0)
            {
                return(MkEmptyWord());
            }

            bool start = isBeg;
            bool end   = isEnd;

            //sequence of characters
            //string sequence = node._str;
            int count = s.Length;

            //bool ignoreCase = ((node._options & RegexOptions.IgnoreCase) != 0);

            int initialstate = nodeId;

            nodeId = nodeId + count + 1;
            int finalstate = initialstate + count;

            int[] finalstates = new int[] { finalstate };

            var moves = new List <Move <S> >();

            for (int i = 0; i < count; i++)
            {
                moves.Add(Move <S> .Create(initialstate + i, initialstate + i + 1, s[i]));
            }

            Automaton <S> res = Automaton <S> .Create(this.solver, initialstate, finalstates, moves);

            res.isDeterministic = true;
            if (start) //may start with any characters
            {
                res.AddMove(Move <S> .Create(initialstate, initialstate, solver.True));
                res.isDeterministic = false;
            }
            if (end) //may end with any characters
            {
                res.AddMove(Move <S> .Create(finalstate, finalstate, solver.True));
            }
            res.isEpsilonFree = true;
            return(res);
        }
        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);
        }