/// <summary>
        /// Generates a CNF for a given grammar or returns null if the Grammar doesn't produce any words.
        /// </summary>
        /// <param name="g">the original grammar</param>
        /// <returns>the CNF or null</returns>
        public static ContextFreeGrammar getEquivalentCNF(ContextFreeGrammar g)
        {
            if (g == null)
            {
                return(null);
            }
            if (g.IsInCNF())
            {
                return(g);
            }

            try
            {
                ContextFreeGrammar res = ContextFreeGrammar.MkCNF(g);

                //handle empty string
                res.setAcceptanceForEmptyString(g.acceptsEmptyString());

                return(res);
            }
            catch (AutomataException e)
            {
                if (g.acceptsEmptyString())
                {
                    var res = new ContextFreeGrammar(new Nonterminal("S"), new Production[] { new Production(new Nonterminal("S"), new GrammarSymbol[] { new Nonterminal("S"), new Nonterminal("S") }) });
                    res.setAcceptanceForEmptyString(true);
                    return(res);
                }
                return(null);
            }
        }
        /// <summary>
        /// Checks if a word is recognized by the given grammar. (CYK-algorithm)
        /// </summary>
        /// <param name="grammar">the grammar</param>
        /// <param name="word">the word</param>
        /// <returns>true, if there exists a dereviation from the startsymbol to the word</returns>
        public static bool isWordInGrammar(ContextFreeGrammar grammar, string word)
        {
            if (word == null || grammar == null)
            {
                return(false);
            }
            if (!grammar.IsInCNF())
            {
                grammar = getEquivalentCNF(grammar);
            }
            if (grammar == null)
            {
                return(false);
            }

            //empty word
            if (word.Length == 0)
            {
                return(grammar.acceptsEmptyString());
            }

            //CYK
            var cyk_table = cyk(grammar, word);

            return(cyk_table[word.Length - 1][0].Item1.Contains(grammar.StartSymbol));
        }
        /// <summary>
        /// Generates a CNF that accepts the prefix closure of a given grammar.
        /// </summary>
        /// <param name="g">the original grammar</param>
        /// <returns>the prefix closure</returns>
        public static ContextFreeGrammar getCNFPrefixClosure(ContextFreeGrammar g)
        {
            if (g == null)
            {
                return(g);
            }
            if (!g.IsInCNF())
            {
                g = getEquivalentCNF(g);
            }
            if (g == null)
            {
                return(g);
            }

            var prefixClosure = getPrefixClosure(g);

            prefixClosure = getEquivalentCNF(prefixClosure); // !!ATTENTION!! this may remove old productions

            var productions = g.GetProductions();

            productions = productions.Concat(prefixClosure.GetProductions());

            return(new ContextFreeGrammar(prefixClosure.StartSymbol, productions));
        }
        /// <summary>
        /// Finds the longest prefix of a given word that is still recognized by a given grammar. (CYK algorithm with prefix closure)
        /// </summary>
        /// <param name="grammar">the grammar</param>
        /// <param name="word">the word</param>
        /// <returns>-1 if the grammar is empty; -2 if the word is in the grammar; n (if the substring up to index n is the longest prefix)</returns>
        public static int longestPrefixLength(ContextFreeGrammar grammar, string word)
        {
            if (word == null || grammar == null)
            {
                return(-1);
            }
            if (!grammar.IsInCNF())
            {
                grammar = getEquivalentCNF(grammar);
            }
            if (grammar == null)
            {
                return(-1);
            }

            //empty word
            if (word.Length == 0)
            {
                if (grammar.acceptsEmptyString())
                {
                    return(-2);
                }
                return(0);
            }

            //prefix closure
            var prefixGrammar = getCNFPrefixClosure(grammar);

            //CYK
            var cyk_table = cyk(prefixGrammar, word);

            //check if word was in original grammar
            if (cyk_table[word.Length - 1][0].Item1.Contains(grammar.StartSymbol))
            {
                return(-2);
            }

            //check for startsymbol in first row
            for (int i = word.Length - 1; i >= 0; i--)
            {
                if (cyk_table[i][0].Item1.Contains(prefixGrammar.StartSymbol))
                {
                    return(i + 1);
                }
            }
            return(0);
        }
        /// <summary>
        /// Generates a CFG that accepts the prefix closure of a given grammar.
        /// </summary>
        /// <param name="g">the original grammar</param>
        /// <returns>the prefix closure</returns>
        public static ContextFreeGrammar getPrefixClosure(ContextFreeGrammar g)
        {
            Func <Nonterminal, Nonterminal> prefixFor = delegate(Nonterminal x)
            {
                return(new Nonterminal(x.Name + "PREFIX"));
            };

            if (g == null)
            {
                return(g);
            }
            if (!g.IsInCNF())
            {
                g = getEquivalentCNF(g);
            }
            if (g == null)
            {
                return(g);
            }
            Nonterminal prefixStart       = prefixFor(g.StartSymbol);
            var         prefixProductions = new List <Production>();

            foreach (Production p in g.GetProductions())
            {
                //add original
                prefixProductions.Add(p);

                Nonterminal prefixNT = prefixFor(p.Lhs);
                if (p.Rhs.Length == 2) // case:  X->AB      ==>     X' ->A' | AB'
                {
                    prefixProductions.Add(new Production(prefixNT, new GrammarSymbol[] { p.Rhs[0], prefixFor((Nonterminal)p.Rhs[1]) }));
                    prefixProductions.Add(new Production(prefixNT, new GrammarSymbol[] { prefixFor((Nonterminal)p.Rhs[0]) }));
                }
                else // case:  X->a   ==>    X'->a
                {
                    prefixProductions.Add(new Production(prefixNT, new GrammarSymbol[] { p.Rhs[0] }));
                }
            }

            var res = new ContextFreeGrammar(prefixStart, prefixProductions);

            res.setAcceptanceForEmptyString(true);

            return(res);
        }