/// <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>
        /// 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);
        }
        private static HashSet <string> generateWordsWithLength(ContextFreeGrammar cnf, int length, Dictionary <Nonterminal, Dictionary <int, HashSet <string> > > dp)
        {
            HashSet <string> res = new HashSet <string>();

            if (cnf == null)
            {
                return(res); //empty grammar -> can't generate any words
            }
            if (length == 0) //case: length = 0
            {
                if (cnf.acceptsEmptyString())
                {
                    res.Add("");
                }
            }
            else if (length == 1) //case: length = 1
            {
                foreach (Nonterminal nt in cnf.Variables)
                {
                    //init dp[nt]
                    Dictionary <int, HashSet <string> > curDP = new Dictionary <int, HashSet <string> >();
                    dp.Add(nt, curDP);

                    //find words of length 1
                    HashSet <string> l = new HashSet <string>();
                    foreach (Production p in cnf.GetProductions(nt))
                    {
                        if (p.IsSingleExprinal)
                        {
                            l.Add(p.Rhs[0].ToString());
                        }
                    }
                    curDP.Add(1, l);
                    if (nt.Equals(cnf.StartSymbol))
                    {
                        res = l;
                    }
                }
            }
            else //case: length > 1
            {
                foreach (KeyValuePair <Nonterminal, Dictionary <int, HashSet <string> > > entry in dp)
                {
                    Nonterminal cur = entry.Key;
                    Dictionary <int, HashSet <string> > curDP = entry.Value;
                    HashSet <string> curSet = new HashSet <string>();
                    curDP.Add(length, curSet);
                    if (cur.Equals(cnf.StartSymbol))
                    {
                        res = curSet;
                    }

                    foreach (Production p in cnf.GetProductions(entry.Key))
                    {
                        if (p.Rhs.Length != 2)
                        {
                            continue;                    //ignore productions that don't have form X->AB
                        }
                        Nonterminal left = (Nonterminal)p.Rhs[0];
                        Dictionary <int, HashSet <string> > leftDP = null;
                        dp.TryGetValue(left, out leftDP);

                        Nonterminal right = (Nonterminal)p.Rhs[1];
                        Dictionary <int, HashSet <string> > rightDP = null;
                        dp.TryGetValue(right, out rightDP);

                        for (int leftPart = 1; leftPart < length; leftPart++)
                        {
                            int rightPart = length - leftPart;

                            HashSet <string> leftPossibilities = null;
                            leftDP.TryGetValue(leftPart, out leftPossibilities);
                            HashSet <string> rightPossibilities = null;
                            rightDP.TryGetValue(rightPart, out rightPossibilities);

                            foreach (string leftString in leftPossibilities)
                            {
                                foreach (string rightString in rightPossibilities)
                                {
                                    curSet.Add(leftString + rightString);
                                }
                            }
                        }
                    }
                }
            }

            return(res);
        }