public static void NonMinimalDfa()
        {
            //
            // Non-minimal DFA (Exercise 4.4.1 in the book)
            //
            var nonMinDfa = new Dfa <string>('A', new [] { 'D' });

            nonMinDfa.AddTrans('A', "0", 'B');
            nonMinDfa.AddTrans('A', "1", 'A');
            nonMinDfa.AddTrans('B', "0", 'A');
            nonMinDfa.AddTrans('B', "1", 'C');
            nonMinDfa.AddTrans('C', "0", 'D');
            nonMinDfa.AddTrans('C', "1", 'B');
            nonMinDfa.AddTrans('D', "0", 'D');
            nonMinDfa.AddTrans('D', "1", 'A');
            nonMinDfa.AddTrans('E', "0", 'D');
            nonMinDfa.AddTrans('E', "1", 'F');
            nonMinDfa.AddTrans('F', "0", 'G');
            nonMinDfa.AddTrans('F', "1", 'E');
            nonMinDfa.AddTrans('G', "0", 'F');
            nonMinDfa.AddTrans('G', "1", 'G');
            nonMinDfa.AddTrans('H', "0", 'G');
            nonMinDfa.AddTrans('H', "1", 'D');

            SaveFile("dfaNonMin.dot", DotLanguagePrinter.ToDotLanguage(nonMinDfa));

            Console.WriteLine($"Eq state pairs: {nonMinDfa.DisplayEquivalentPairs()}");
            Console.WriteLine($"Eq state sets: {nonMinDfa.DisplayMergedEqSets()}");

            var minDfa = nonMinDfa.ToMinimumDfa();

            SaveFile("dfaMin.dot", DotLanguagePrinter.ToDotLanguage(minDfa));
        }
        public static void RegexParser()
        {
            //string re = "ab*";
            //string re = "(a+b)*";
            //string re = "bb";
            //string re = "(a+b)*ab";
            //string re = "((a+b)*ab)*";
            //string re = "((a+b)*ab)((a+b)*ab)";
            //string re = "(a+b)*abb";

            // L1: From slides on closure properties
            //string re = "((a+b)*a+ε)(bb)*b"; // ends in an odd number of b's

            // L2: From slides on closure properties
            //string re = "ε+(a+b)*b"; // ends in at least one 'b' and the empty string

            // TODO: Create L3 = L1 - L2 (via product DFA, where accepting state is any state where L1 accepts and L2 does not)

            // Are these equivalent
            //string re = "b*a(a+b)*";
            string re = "(a+b)*a(a+b)*";

            Regex regex = RegexTextbook.ParseRD(re);

            var dfa = regex.ToDfa(skipRenaming: true);

            SaveFile("regex.dot", DotLanguagePrinter.ToDotLanguage(dfa));
        }
        public static void EquivalenceOfTwoDfas()
        {
            //
            // Equivalence of two DFAs (Example 4.21 in book)
            //
            var eqDfas = new Dfa <string>('A', new [] { 'A', 'C', 'D' }); // start state is redundant for finding equivalent blocks

            // First DFA
            eqDfas.AddTrans('A', "0", 'A');
            eqDfas.AddTrans('A', "1", 'B');
            eqDfas.AddTrans('B', "0", 'A');
            eqDfas.AddTrans('B', "1", 'B');
            // Second DFA
            eqDfas.AddTrans('C', "0", 'D');
            eqDfas.AddTrans('C', "1", 'E');
            eqDfas.AddTrans('D', "0", 'D');
            eqDfas.AddTrans('D', "1", 'E');
            eqDfas.AddTrans('E', "0", 'C');
            eqDfas.AddTrans('E', "1", 'E');

            SaveFile("dfa_eq.dot", DotLanguagePrinter.ToDotLanguage(eqDfas));

            //System.Console.WriteLine();
            Console.WriteLine($"Eq state pairs: {eqDfas.DisplayEquivalentPairs()}");
            Console.WriteLine($"Eq state sets: {eqDfas.DisplayMergedEqSets()}");
        }
        //
        // Context-Free languages, CFG and LR Parsing
        //

        /// <summary>
        /// G3 in "A Survey of LR-Parsing Methods", Gallier.
        /// </summary>
        public static void GallierLookaheadLR_Example3()
        {
            var grammar = DragonBookExample4_48.GetGrammar();

            // characteristic automaton (LR(0) automaton)
            var dfaLr0 = grammar.GetLr0AutomatonDfa();

            SaveFile("GallierEx3_LR0Automaton.dot", DotLanguagePrinter.ToDotLanguage(dfaLr0, DotRankDirection.LeftRight, skipStateLabeling: true));

            var analyzer = Analyzers.CreateErasableSymbolsAnalyzer(grammar);

            //(ImmutableArray<IReadOnlySet<Terminal>> initfirstSets, IGraph graphFirst) = DigraphAlgorithm.GetFirstGraph(grammar, analyzer);
            (var initfirstSets, IGraph graphFirst) = DigraphAlgorithm.GetFirstGraph(grammar, analyzer);

            SaveFile("GallierEx3_FirstGraph.dot",
                     DigraphDotLanguagePrinter.PrintGraph("INITFIRST", initfirstSets, graphFirst, v => grammar.Nonterminals[v].Name));

            var firstSymbolsAnalyzer = Analyzers.CreateFirstSymbolsAnalyzer(grammar);

            (var initFollowSets, IGraph graphFollow) = DigraphAlgorithm.GetFollowGraph(grammar, firstSymbolsAnalyzer);

            SaveFile("GallierEx3_FollowGraph.dot",
                     DigraphDotLanguagePrinter.PrintGraph("INITFOLLOW", initFollowSets, graphFollow, v => grammar.Nonterminals[v].Name));

            var stringWriter = new StringWriter();

            grammar.PrintFirstAndFollowSets(stringWriter);
            SaveFile("GallierEx3_FirstAndFollowSets.txt", stringWriter.ToString());

            // Grammar is LR(0)
            var lr0Parser = grammar.ComputeLr0ParsingTable();
            var writer    = new StringWriter();

            lr0Parser.PrintParsingTable(writer);

            foreach (var conflict in lr0Parser.Conflicts)
            {
                writer.WriteLine(conflict);
                writer.WriteLine($"In state {conflict.State}: {lr0Parser.GetItems(conflict.State).KernelItems.ToVectorString()} (kernel items)");
            }
            writer.WriteLine();

            SaveFile("GallierEx3_Lr0ParsingTable.txt", writer.ToString());

            ////

            var vertices = LalrLookaheadSetsAlgorithm.GetGotoTransitionPairs(grammar, dfaLr0);

            // Read (INITFOLLOW) sets
            var(directReads, graphRead) = LalrLookaheadSetsAlgorithm.GetGraphReads(grammar, dfaLr0, vertices, analyzer);

            SaveFile("GallierEx3_ReadGraph.dot",
                     DigraphDotLanguagePrinter.PrintGraph("DR", directReads, graphRead, v => vertices[v].ToString()));

            var graphLaFollow = LalrLookaheadSetsAlgorithm.GetGraphLaFollow(grammar, dfaLr0, vertices, analyzer);

            SaveFile("GallierEx3_LaFollowGraph.dot",
                     DigraphDotLanguagePrinter.PrintGraph("INITFOLLOW", directReads, graphLaFollow, v => vertices[v].ToString()));
        }
        public static void DragonBookEx4_54()
        {
            var grammar = DragonBookExample4_54.GetGrammar();

            var dfaLr0 = grammar.GetLr0AutomatonDfa();

            SaveFile("DragonBookEx4_54_DCG0Lr.dot", DotLanguagePrinter.ToDotLanguage(dfaLr0, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfaLr1 = grammar.GetLr1AutomatonDfa();

            SaveFile("DragonBookEx4_54_DCG1Lr.dot", DotLanguagePrinter.ToDotLanguage(dfaLr1, DotRankDirection.LeftRight, skipStateLabeling: true));
        }
        public static void StanfordReduceReduceConflictGrammar()
        {
            var grammar = StanfordReduceReduceConflict.GetGrammar();

            var characteristicStringsNfa = grammar.GetLr0AutomatonNfa();

            SaveFile("StanReduceReduceConflictNCG.dot", DotLanguagePrinter.ToDotLanguage(characteristicStringsNfa, DotRankDirection.TopBottom));

            var dfa = characteristicStringsNfa.ToDfa();

            SaveFile("StanReduceReduceConflictDCG.dot", DotLanguagePrinter.ToDotLanguage(dfa, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfa2 = grammar.GetLr0AutomatonDfa();

            SaveFile("StanReduceReduceConflictDCGLr.dot", DotLanguagePrinter.ToDotLanguage(dfa2, DotRankDirection.LeftRight, skipStateLabeling: true));
        }
        public static void DanglingElseWhenParsing_iEtiEtSeS_ImpliesShiftReduceConflictAfterParsing_iEtiEtS_InState8()
        {
            var grammar = DanglingElse.GetGrammar();

            // Create NFA (digraph of items labeled by symbols)
            var characteristicStringsNfa = grammar.GetLr0AutomatonNfa();

            SaveFile("DanglingNCG.dot", DotLanguagePrinter.ToDotLanguage(characteristicStringsNfa, DotRankDirection.TopBottom));

            var dfa = characteristicStringsNfa.ToDfa();

            SaveFile("DanglingDCG.dot", DotLanguagePrinter.ToDotLanguage(dfa, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfa2 = grammar.GetLr0AutomatonDfa();

            SaveFile("DanglingDCGLr.dot", DotLanguagePrinter.ToDotLanguage(dfa2, DotRankDirection.LeftRight, skipStateLabeling: true));
        }
        /// <summary>
        /// G1 in "A Survey of LR-Parsing Methods", Gallier.
        /// </summary>
        public static void GallierToyExampleLr0()
        {
            var grammar = GallierG1.GetGrammar();

            // Create NFA (digraph of items labeled by symbols)
            var characteristicStringsNfa = grammar.GetLr0AutomatonNfa();

            SaveFile("GallierNCG0.dot", DotLanguagePrinter.ToDotLanguage(characteristicStringsNfa, DotRankDirection.TopBottom));

            var dfa = characteristicStringsNfa.ToDfa();

            SaveFile("GallierDCG0.dot", DotLanguagePrinter.ToDotLanguage(dfa, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfa2 = grammar.GetLr0AutomatonDfa();

            SaveFile("GallierDCG0Lr.dot", DotLanguagePrinter.ToDotLanguage(dfa2, DotRankDirection.LeftRight, skipStateLabeling: true));
        }
        public static void KeywordAutomata()
        {
            //
            // Keyword search: Build NFA directly
            //

            // TODO: Vi antager, at alfabetet er de mulige ord i 'web' og 'ebay', da grafen ellers bliver meget uoverskuelig
            // NOTE: Grafen er allerede uoverskuelig pga de mange pile, da hver vertex kun kan have et input

            // 9,1,0 is part of every state, so we remove them from the naming strategy
            var nfaKeywords = new Nfa <string>(9, new [] { 4, 8 }, s => new Set <int>(new[] { 0, 1, 9 }).Contains(s) == false);

            nfaKeywords.AddTrans(Transition.Move <string, int>(9, null, 1));
            nfaKeywords.AddTrans(Transition.Move <string, int>(9, null, 0));
            // guessing is smart in NFA
            nfaKeywords.AddTrans(Transition.Move(9, "w", 9));
            nfaKeywords.AddTrans(Transition.Move(9, "e", 9));
            nfaKeywords.AddTrans(Transition.Move(9, "b", 9));
            nfaKeywords.AddTrans(Transition.Move(9, "a", 9));
            nfaKeywords.AddTrans(Transition.Move(9, "y", 9));
            // web
            nfaKeywords.AddTrans(Transition.Move(1, "w", 2));
            nfaKeywords.AddTrans(Transition.Move(2, "e", 3));
            nfaKeywords.AddTrans(Transition.Move(3, "b", 4));
            // ebay
            nfaKeywords.AddTrans(Transition.Move(0, "e", 5));
            nfaKeywords.AddTrans(Transition.Move(5, "b", 6));
            nfaKeywords.AddTrans(Transition.Move(6, "a", 7));
            nfaKeywords.AddTrans(Transition.Move(7, "y", 8));

            var dfaKeywords = nfaKeywords.ToDfa();

            // Den virker, men grafen er uoverskuelig da vi ikke kan placere noderne
            SaveFile("dfa_keywords.dot", DotLanguagePrinter.ToDotLanguage(dfaKeywords));

            Console.WriteLine("");

            foreach (var word in new[] { "goto", "web", "ebay", "webay", "web1" })
            {
                var letters = Letterizer <string> .Default.GetLetters(word);

                // NFA is tail whatever, that is webay is a match because the suffix ebay is matched
                Console.WriteLine($"dfaKeywords.Match({word}) = {dfaKeywords.IsMatch(letters)}");
            }
        }
        /// <summary>
        /// G3 in "A Survey of LR-Parsing Methods", Gallier.
        /// </summary>
        public static void GallierToyExampleLr1()
        {
            var grammar = GallierG3.GetGrammar();

            var characteristicStringsNfa = grammar.GetLr1AutomatonNfa();

            SaveFile("GallierNCG1.dot", DotLanguagePrinter.ToDotLanguage(characteristicStringsNfa, DotRankDirection.TopBottom));

            var dfa = characteristicStringsNfa.ToDfa();

            SaveFile("GallierDCG1.dot", DotLanguagePrinter.ToDotLanguage(dfa, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfa2 = grammar.GetLr1AutomatonDfa();

            SaveFile("GallierDCG1Lr.dot", DotLanguagePrinter.ToDotLanguage(dfa2, DotRankDirection.LeftRight, skipStateLabeling: true));

            // TODO: Parsing table in unit test
        }
        /// <summary>
        /// Example 4.48 in Dragon book p. 255, 2nd ed
        /// </summary>
        public static void DragonBookEx4_48()
        {
            var grammar = DragonBookExample4_48.GetGrammar();

            //
            // LR(0) automaton
            //

            var nfa0 = grammar.GetLr0AutomatonNfa();

            SaveFile("DragonBookEx4_48_NCG0.dot", DotLanguagePrinter.ToDotLanguage(nfa0, DotRankDirection.TopBottom));

            var dfa0 = nfa0.ToDfa();

            SaveFile("DragonBookEx4_48_DCG0.dot", DotLanguagePrinter.ToDotLanguage(dfa0, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfaLr0 = grammar.GetLr0AutomatonDfa();

            SaveFile("DragonBookEx4_48_DCG0Lr.dot", DotLanguagePrinter.ToDotLanguage(dfaLr0, DotRankDirection.LeftRight, skipStateLabeling: true));

            // We will augment every LR(0) item with information about what portion of the follow set is appropriate given
            // the path we have taken to that state. We can be in state 2 {S → L•=R, R → L•} for one of two reasons
            //     (i)  We are trying to build from S => L=R    (shift =)
            //     (ii) We are trying to build from S => R => L (reduce by R → L•)

            //
            // LR(1) automaton
            //

            var nfa1 = grammar.GetLr1AutomatonNfa();

            SaveFile("DragonBookEx4_48_NCG1.dot", DotLanguagePrinter.ToDotLanguage(nfa1, DotRankDirection.TopBottom));

            var dfa1 = nfa1.ToDfa();

            SaveFile("DragonBookEx4_48_DCG1.dot", DotLanguagePrinter.ToDotLanguage(dfa1, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfaLr1 = grammar.GetLr1AutomatonDfa();

            SaveFile("DragonBookEx4_48_DCG1Lr.dot", DotLanguagePrinter.ToDotLanguage(dfaLr1, DotRankDirection.LeftRight, skipStateLabeling: true));

            // TODO: Parse 'a=a'
        }
        public static void ExprGrammarCh4DragonBook()
        {
            var grammar = DragonBook_ExprGrammarCh4.GetGrammar();

            // Create NFA (digraph of items labeled by symbols)
            var characteristicStringsNfa = grammar.GetLr0AutomatonNfa();

            SaveFile("DragonNCG.dot", DotLanguagePrinter.ToDotLanguage(characteristicStringsNfa, DotRankDirection.TopBottom));

            var dfa = characteristicStringsNfa.ToDfa();

            SaveFile("DragonDCG.dot", DotLanguagePrinter.ToDotLanguage(dfa, DotRankDirection.LeftRight, skipStateLabeling: true));

            var dfa2 = grammar.GetLr0AutomatonDfa();

            SaveFile("DragonDCGLr.dot", DotLanguagePrinter.ToDotLanguage(dfa2, DotRankDirection.LeftRight, skipStateLabeling: true));

            var lexer = DragonBook_ExprGrammarCh4.GetLexer("a*a+a");

            grammar.ComputeSlrParsingTable().Parse(lexer, Console.Out);
        }
        //
        // Regular languages, NFA, DFA
        //

        public static void CourseExercise()
        {
            var dfa = new Dfa <string>('A', new [] { 'B', 'E' });

            dfa.AddTrans('A', "0", 'E');
            dfa.AddTrans('A', "1", 'D');
            dfa.AddTrans('B', "0", 'A');
            dfa.AddTrans('B', "1", 'C');
            dfa.AddTrans('C', "0", 'G');
            dfa.AddTrans('C', "1", 'B');
            dfa.AddTrans('D', "0", 'E');
            dfa.AddTrans('D', "1", 'A');
            dfa.AddTrans('E', "0", 'H');
            dfa.AddTrans('E', "1", 'C');
            dfa.AddTrans('F', "0", 'C');
            dfa.AddTrans('F', "1", 'B');
            dfa.AddTrans('G', "0", 'F');
            dfa.AddTrans('G', "1", 'E');
            dfa.AddTrans('H', "0", 'B');
            dfa.AddTrans('H', "1", 'H');
            var minDfa = dfa.ToMinimumDfa();

            SaveFile("exercise.dot", DotLanguagePrinter.ToDotLanguage(minDfa));
        }
        public static void DecimalAutomata()
        {
            //
            // epsilon-NFA accepting accepting decimal numbers
            //

            var nfaDecimal = new Nfa <string>(0, 5);

            // TODO: Because we do not support ranges let d = [0-9]
            // TODO: Support characterRanges as spacial labels/inputs on transitions
            // TODO: Support putting single arc on every transition from p to q where label uses Sigma \ chars notation
            //            Sigma - {...}
            //            Sigma - d
            //            { .... }
            // TODO: Have the program calculate the label with fewest characters, and always use single arcs between any two nodes

            // sign
            nfaDecimal.AddTrans(Transition.Move <string, int>(0, null, 1));
            nfaDecimal.AddTrans(Transition.Move(0, "+", 1));
            nfaDecimal.AddTrans(Transition.Move(0, "-", 1));
            // optional digits [0-9] before decimal point
            nfaDecimal.AddTrans(Transition.Move(1, "d", 1));
            //nfa.AddTrans(1, "1", 1);
            //nfa.AddTrans(1, "2", 1);
            //nfa.AddTrans(1, "3", 1);
            //nfa.AddTrans(1, "4", 1);
            //nfa.AddTrans(1, "5", 1);
            //nfa.AddTrans(1, "6", 1);
            //nfa.AddTrans(1, "7", 1);
            //nfa.AddTrans(1, "8", 1);
            //nfa.AddTrans(1, "9", 1);
            // decimal point before mandatory digit(s)
            nfaDecimal.AddTrans(Transition.Move(1, ".", 2));
            // digit after state 2
            nfaDecimal.AddTrans(Transition.Move(2, "d", 3));
            //nfa.AddTrans(2, "1", 3);
            //nfa.AddTrans(2, "2", 3);
            //nfa.AddTrans(2, "3", 3);
            //nfa.AddTrans(2, "4", 3);
            //nfa.AddTrans(2, "5", 3);
            //nfa.AddTrans(2, "6", 3);
            //nfa.AddTrans(2, "7", 3);
            //nfa.AddTrans(2, "8", 3);
            //nfa.AddTrans(2, "9", 3);
            // digit before decimal point
            nfaDecimal.AddTrans(Transition.Move(1, "d", 4));
            //nfa.AddTrans(1, "1", 4);
            //nfa.AddTrans(1, "2", 4);
            //nfa.AddTrans(1, "3", 4);
            //nfa.AddTrans(1, "4", 4);
            //nfa.AddTrans(1, "5", 4);
            //nfa.AddTrans(1, "6", 4);
            //nfa.AddTrans(1, "7", 4);
            //nfa.AddTrans(1, "8", 4);
            //nfa.AddTrans(1, "9", 4);
            // decimal point after mandatory digit(s)
            nfaDecimal.AddTrans(Transition.Move(4, ".", 3));
            // optional digits [0-9] after decimal point
            nfaDecimal.AddTrans(Transition.Move(3, "d", 3));
            //nfa.AddTrans(3, "1", 3);
            //nfa.AddTrans(3, "2", 3);
            //nfa.AddTrans(3, "3", 3);
            //nfa.AddTrans(3, "4", 3);
            //nfa.AddTrans(3, "5", 3);
            //nfa.AddTrans(3, "6", 3);
            //nfa.AddTrans(3, "7", 3);
            //nfa.AddTrans(3, "8", 3);
            //nfa.AddTrans(3, "9", 3);
            // epsilon-transition to accepting/final state
            nfaDecimal.AddTrans(Transition.Move <string, int>(3, null, 5));

            var dfaDecimal = nfaDecimal.ToDfa();

            SaveFile("dfa_decimal.dot", DotLanguagePrinter.ToDotLanguage(dfaDecimal));

            foreach (var word in new[] { "+d.d", "-.", "-.d", ".", "d.", "d.d", ".d" })
            {
                var letters = Letterizer <string> .Default.GetLetters(word);

                Console.WriteLine($"dfaDecimal.Match({word}) = {dfaDecimal.IsMatch(letters)}");
            }
        }