// NOTE: Maybe should REMOVE this method
        // because the formulas that are And
        // will eventually be expanded by ExpandToAnd(Symbol).
        // It will also make the Tableau more easy to look.
        /// <summary>
        /// Expand all the Ands formula
        /// </summary>
        /// <param name="formulas"></param>
        /// <returns>A set of formulas with all the And expanded</returns>
        public static HashSet <Symbol> ExpandAllAnds(
            TableauNode curNode, bool alphaRules, bool deltaRules)
        {
            List <Symbol> delList = new List <Symbol>();
            List <Symbol> addList = new List <Symbol>();

            HashSet <Symbol> formulas = curNode.SetFormulas;

            // Subformula that is expanded to And
            foreach (Symbol subformula in formulas)
            {
                List <Symbol> expansion = new List <Symbol>();

                if (alphaRules)
                {
                    expansion = ExpandToAnd(subformula);
                }

                // Delta rules - Expand !x and ~@x
                if (deltaRules)
                {
                    expansion = DeltaRules(subformula, curNode);
                }

                if (!(expansion is null))
                {
                    foreach (Symbol term in expansion)
                    {
                        addList.Add(term);
                    }

                    delList.Add(subformula);
                }
            }

            foreach (Symbol subformula in delList)
            {
                formulas.Remove(subformula);
            }
            foreach (Symbol subformula in addList)
            {
                formulas.Add(subformula);
            }

            delList.Clear();
            addList.Clear();


            return(formulas);
        }
        public void Build()
        {
            // Root of Tableau
            Root = new TableauNode(new HashSet <Symbol>(new SymbolComparer())
            {
                new Not(Formula)
            });
            Root.RuleApplied = TableauRule.NONE;

            bool unsatis = BuildUtil(Root);

            State = 0;
            if (unsatis)
            {
                State = 1;
            }
        }
        public static List <Symbol> DeltaRules(Symbol formula, TableauNode currentNode)
        {
            char newVar = '#';

            for (char c = 'a'; c <= 'z'; c++)
            {
                if (!currentNode.CurrentVariables[c])
                {
                    newVar = c;

                    break;
                }
            }

            if (formula is Existential)
            {
                char boundedVar = ((Existential)formula).BoundVariables[0]; // extend later

                formula = formula.ChangeVariableName(boundedVar, newVar, true).Childs[0];
                currentNode.CurrentVariables[newVar] = true;
            }
            else if (formula is Not && formula.Childs[0] is Universal)
            {
                formula = formula.Childs[0];
                char boundedVar = ((Universal)formula).BoundVariables[0]; // extend later

                formula = new Not(formula.ChangeVariableName(boundedVar, newVar, true).Childs[0]);
                currentNode.CurrentVariables[newVar] = true;
            }
            else
            {
                return(null);
            }

            return(new List <Symbol>()
            {
                formula
            });
        }
        public static List <Symbol> GammaRules(Symbol formula, TableauNode currentNode)
        {
            List <Symbol> returnList = new List <Symbol>();

            if (formula is Universal)
            {
                foreach (char c in currentNode.ListVariables)
                {
                    if (formula.GammaApplied[c])
                    {
                        continue;
                    }
                    char boundVariable = ((Universal)formula).BoundVariables[0];

                    returnList.Add(formula.ChangeVariableName(boundVariable, c, true).Childs[0]);

                    formula.GammaApplied[c] = true;
                }
            }
            else if (formula is Not && formula.Childs[0] is Existential)
            {
                char boundVariable = ((Existential)formula.Childs[0]).BoundVariables[0];

                foreach (char c in currentNode.ListVariables)
                {
                    if (formula.GammaApplied[c])
                    {
                        continue;
                    }
                    returnList.Add(new Not(formula.ChangeVariableName(boundVariable, c, true).Childs[0].Childs[0]));
                    formula.GammaApplied[c] = true;
                }
            }

            return(returnList.Count == 0 ? null : returnList);
        }
        /*
         *  1) Check contradiction
         *  2) Alpha rules
         *  3) Delta rules
         *  4) Beta rules
         *  5) Gamma rules
         */
        /// <summary>
        /// Build the Semantic Tableau
        /// </summary>
        /// <returns>True if the formula is Unsatifiable (has contradiction)</returns>
        public bool BuildUtil(TableauNode curNode) // return true if contradiction
        {
            TableauNode newNode = new TableauNode(curNode.SetFormulas, curNode.CurrentVariables);

            if (CheckContradiction(newNode.SetFormulas))
            {
                newNode.SetFormulas = new HashSet <Symbol>()
                {
                    new Constant('0')
                };
                curNode.Childs.Add(newNode);
                return(true);
            }

            HashSet <Symbol> formulas = newNode.SetFormulas;

            //////////////////////////////////////////////////////////////
            // Alpha Rules
            //////////////////////////////////////////////////////////////

            formulas = ExpandAllAnds(newNode, true, false);

            if (!TableauNode.EqualSet(formulas, curNode.SetFormulas)) // successful
            {
                newNode.RuleApplied = TableauRule.ALPHA;
                curNode.Childs.Add(newNode);

                return(BuildUtil(newNode));
            }

            // Haven't applied Alpha rules
            // Continue with

            //////////////////////////////////////////////////////////////
            // Delta rules
            //////////////////////////////////////////////////////////////

            formulas = ExpandAllAnds(newNode, false, true);

            if (!TableauNode.EqualSet(formulas, curNode.SetFormulas)) // successful
            {
                newNode.RuleApplied = TableauRule.DELTA;
                curNode.Childs.Add(newNode);

                return(BuildUtil(newNode));
            }


            //////////////////////////////////////////////////////////////
            // Beta Rules
            //////////////////////////////////////////////////////////////
            bool CurrentlyContradicted = true;

            Symbol        chosenFormula = null;                // The formula that can be applied beta rules
            List <Symbol> expansion     = new List <Symbol>(); // expanded version of the chosen formula

            // Find a subformula that can be applied beta rules
            foreach (Symbol subformula in formulas)
            {
                expansion = ExpandToOr(subformula);

                if (expansion is null)
                {
                    continue;
                }

                chosenFormula = subformula;
                break;
            }

            // There exist a formula expanded.
            if (!(chosenFormula == null))
            {
                formulas.Remove(chosenFormula); // remove the formula

                foreach (Symbol term in expansion)
                {
                    formulas.Add(term); // Add an expanded subformula

                    var newSet = new HashSet <Symbol>(new SymbolComparer());
                    foreach (var item in formulas)
                    {
                        newSet.Add(ObjectExtensions.Copy(item));
                    }

                    TableauNode tmpNode = new TableauNode(newSet, curNode.CurrentVariables);
                    tmpNode.RuleApplied = TableauRule.BETA;

                    curNode.Childs.Add(tmpNode);

                    if (!BuildUtil(tmpNode))
                    {
                        CurrentlyContradicted = false;
                    }

                    formulas.Remove(term);
                }

                return(CurrentlyContradicted); // both childs must be contradition
            }

            //////////////////////////////////////////////////////////////
            // Gamma rules
            //////////////////////////////////////////////////////////////


            var expansionStorage = new HashSet <Symbol>(new SymbolComparer());

            expansion = new List <Symbol>();

            foreach (var subformula in formulas)
            {
                expansion = GammaRules(subformula, newNode);

                if (expansion is null)
                {
                    continue;
                }

                foreach (var expansionTerm in expansion)
                {
                    expansionStorage.Add(expansionTerm);
                }
            }

            if (expansionStorage.Count > 0)
            {
                foreach (var expansionTerm in expansionStorage)
                {
                    formulas.Add(expansionTerm);
                }

                curNode.Childs.Add(newNode);
                newNode.RuleApplied = TableauRule.GAMMA;

                return(BuildUtil(newNode));
            }

            return(false);
        }
        public int GenerateGraphUtil(StreamWriter sw, TableauNode u)
        {
            string content = ""; // text of the Tableau Node (the set of formulas at the current node)

            // Add all formulas to the content
            foreach (var formula in u.SetFormulas)
            {
                content += formula.ToString() + "\n";
            }
            //content = content.Remove(content.Length - 1); // remove last ','
            //content = content.Remove(1, 1); // remove first space
            //content += "]";

            // List active variables
            content += " [";

            foreach (char c in u.ListVariables)
            {
                content += c + ",";
            }

            if (content[content.Length - 1] == ',')
            {
                content = content.Remove(content.Length - 1); // remove last ','
            }
            content += "]";

            int uNum = Counter; // ID of current node

            string fillColor = (u.RuleApplied == TableauRule.ALPHA ? "yellow" :
                                u.RuleApplied == TableauRule.BETA ? "palegreen" :
                                u.RuleApplied == TableauRule.DELTA ? "skyblue" :
                                u.RuleApplied == TableauRule.GAMMA ? "brown1" :
                                "gray88");
            // content of the node
            string graphvizContent = $"\tnode{uNum} [shape=rectangle; style = filled;" +
                                     $"label = \"{content}\"; color = black; fillcolor={fillColor}];";

            // if this node is a contradiction (a node containing only false)
            // then color it red
            if (TableauNode.EqualSet(u.SetFormulas, new HashSet <Symbol>()
            {
                new Constant('0')
            }))
            {
                graphvizContent = $"\tnode{uNum} [shape=rectangle; style=filled;" +
                                  $"label = \"{content}\"; color=crimson fillcolor={fillColor}];";
            }

            // draw current node
            sw.WriteLine(graphvizContent);

            foreach (TableauNode v in u.Childs)
            {
                Counter++;
                int vNum = GenerateGraphUtil(sw, v);
                sw.WriteLine("\tnode{0} -- node{1};", uNum, vNum); // draw edge to child node
            }

            return(uNum); // return ID of current node
        }