/// <summary>
        /// Produces the CNF (Chomsky Normal Form) for the grammar g.
        /// It first eliminates epsilons, useless symbols, and unit productions.
        /// If Assumes that there are no epsilons, useless symbols or unit productions
        /// </summary>
        public static ContextFreeGrammar MkCNF(ContextFreeGrammar g, bool removeEpsilonsUselessSymbolsUnitsProductions)
        {
            if (removeEpsilonsUselessSymbolsUnitsProductions)
            {
                g = g.RemoveEpsilonsAndUselessSymbols();
                g = g.RemoveUnitProductions();
            }
            var productions = new Dictionary <Nonterminal, List <Production> >();
            List <Nonterminal> variables = new List <Nonterminal>(g.variables);

            foreach (Nonterminal v in g.variables)
            {
                productions[v] = new List <Production>();
            }

            int nonterminalID = 0;

            //Implements algo in Theorem 4.5, page 92-93, in Hopcroft-Ullman

            #region make productions of the form V --> V0...Vn or V --> a
            var freshVarMap = new Dictionary <GrammarSymbol, Nonterminal>();
            foreach (Nonterminal v in g.variables)
            {
                foreach (Production p in g.productionMap[v])
                {
                    if (p.ContainsNoExprinals || p.IsCNF)
                    {
                        productions[v].Add(p);
                    }
                    else
                    {
                        GrammarSymbol[] rhs = new GrammarSymbol[p.Rhs.Length];
                        for (int i = 0; i < rhs.Length; i++)
                        {
                            if (p.Rhs[i] is Nonterminal)
                            {
                                rhs[i] = p.Rhs[i];
                            }
                            else
                            {
                                Nonterminal u;
                                if (!freshVarMap.TryGetValue(p.Rhs[i], out u))
                                {
                                    u = new Nonterminal(nonterminalID++);
                                    freshVarMap[p.Rhs[i]] = u;
                                    variables.Add(u);
                                    var prods = new List <Production>();
                                    prods.Add(new Production(u, p.Rhs[i]));
                                    productions[u] = prods;
                                }
                                rhs[i] = u;
                            }
                        }
                        productions[v].Add(new Production(v, rhs));
                    }
                }
            }
            #endregion


            var productionsCNF = new Dictionary <Nonterminal, List <Production> >();
            List <Nonterminal> variablesCNF = new List <Nonterminal>(variables);
            foreach (Nonterminal v in variablesCNF)
            {
                productionsCNF[v] = new List <Production>();
            }

            #region replace V --> V0V1...Vn (n > 2), by V --> V0U0, U0 --> V1U1, ..., Un-2 --> Vn-1Vn
            foreach (Nonterminal v in variables)
            {
                foreach (Production p in productions[v])
                {
                    if (p.IsCNF)
                    {
                        productionsCNF[v].Add(p);
                    }
                    else
                    {
                        Nonterminal x = v;
                        Nonterminal y = new Nonterminal(nonterminalID++);
                        variablesCNF.Add(y);
                        productionsCNF[y] = new List <Production>();
                        for (int i = 0; i < p.Rhs.Length - 2; i++)
                        {
                            productionsCNF[x].Add(new Production(x, p.Rhs[i], y));
                            if (i < p.Rhs.Length - 3)
                            {
                                x = y;
                                y = new Nonterminal(nonterminalID++);
                                variablesCNF.Add(y);
                                productionsCNF[y] = new List <Production>();
                            }
                        }
                        productionsCNF[y].Add(new Production(y, p.Rhs[p.Rhs.Length - 2], p.Rhs[p.Rhs.Length - 1]));
                    }
                }
            }
            #endregion

            ContextFreeGrammar cnf = new ContextFreeGrammar(variablesCNF, g.startSymbol, productionsCNF);
            return(cnf);
        }