/// <summary>
        /// Creates a PropNet for the game with the given description.
        /// </summary>
        public static PropNet Create(IList<Expression> description, IComponentFactory componentFactory)
        {
            Console.WriteLine("Building propnet...");

            _componentFactory = componentFactory;
            DateTime startTime = DateTime.UtcNow;

            description = GdlCleaner.Run(description);
            description = DeORer.Run(description);
            description = VariableConstrainer.ReplaceFunctionValuedVariables(description);
            description = Relationizer.Run(description);
            description = CondensationIsolator.Run(description);

            if (Logger.IsDebugEnabled)
                foreach (var gdl in description)
                    Logger.Debug(gdl);

            //We want to start with a rule graph and follow the rule graph. Start by finding general information about the game
            ISentenceDomainModel model = SentenceDomainModelFactory.CreateWithCartesianDomains(description);
            //Restrict domains to values that could actually come up in rules.
            //See chinesecheckers4's "count" relation for an example of why this could be useful.
            model = SentenceDomainModelOptimizer.RestrictDomainsToUsefulValues(model);

            Logger.Debug("Setting constants...");

            //TODO: ConstantChecker constantChecker = ConstantCheckerFactory.createWithForwardChaining(model);
            IConstantChecker constantChecker = ConstantCheckerFactory.CreateWithProver(model);
            Logger.Debug("Done setting constants");

            HashSet<String> sentenceFormNames = SentenceForms.GetNames(model.SentenceForms);
            bool usingBase = sentenceFormNames.Contains("base");
            bool usingInput = sentenceFormNames.Contains("input");

            //For now, we're going to build this to work on those with a particular restriction on the dependency graph:
            //Recursive loops may only contain one sentence form. This describes most games, but not all legal games.
            IDictionary<ISentenceForm, ICollection<ISentenceForm>> dependencyGraph = model.DependencyGraph;
            Logger.Debug("Computing topological ordering... ");

            //ConcurrencyUtils.checkForInterruption();
            IEnumerable<ISentenceForm> topologicalOrdering = GetTopologicalOrdering(model.SentenceForms, dependencyGraph, usingBase, usingInput);
            Logger.Debug("done");

            List<TermObject> roles = GameContainer.GameInformation.GetRoles();
            var components = new Dictionary<Fact, IComponent>();
            var negations = new Dictionary<Fact, IComponent>();
            IConstant trueComponent = _componentFactory.CreateConstant(true);
            IConstant falseComponent = _componentFactory.CreateConstant(false);
            var functionInfoMap = new Dictionary<ISentenceForm, FunctionInfo>();
            var completedSentenceFormValues = new Dictionary<ISentenceForm, ICollection<Fact>>();

            var sentenceFormAdder = new SentenceFormAdder(_componentFactory, DoesProcessor, TrueProcessor, TempFact);

            foreach (ISentenceForm form in topologicalOrdering)
            {
                //ConcurrencyUtils.checkForInterruption();
                Logger.Debug("Adding sentence form " + form);

                if (constantChecker.IsConstantForm(form))
                {
                    Logger.Debug(" (constant)");
                    //Only add it if it's important
                    if (form.Name.Equals(GameContainer.Parser.TokLegal) || form.Name.Equals(GameContainer.Parser.TokGoal)
                                                                            || form.Name.Equals(GameContainer.Parser.TokInit))
                    {
                        //Add it
                        foreach (Fact trueSentence in constantChecker.GetTrueSentences(form))
                        {
                            var trueProp = _componentFactory.CreateProposition(trueSentence);
                            trueProp.AddInput(trueComponent);
                            trueComponent.AddOutput(trueProp);
                            components[trueSentence] = trueComponent;
                        }
                    }

                    Logger.Debug("Checking whether {0} is a functional constant...", form);
                    AddConstantsToFunctionInfo(form, constantChecker, functionInfoMap);
                    AddFormToCompletedValues(form, completedSentenceFormValues, constantChecker);

                    continue;
                }
                Logger.Debug(string.Empty);
                //TODO: Adjust "recursive forms" appropriately
                //Add a temporary sentence form thingy? ...
                var temporaryComponents = new Dictionary<Fact, IComponent>();
                var temporaryNegations = new Dictionary<Fact, IComponent>();

                sentenceFormAdder.AddSentenceForm(form, model, components, negations, trueComponent, falseComponent, usingBase, usingInput,
                    ImmutableHashSet.Create(form), temporaryComponents, temporaryNegations, functionInfoMap, constantChecker,
                    completedSentenceFormValues);

                //TODO: Pass these over groups of multiple sentence forms
                if (temporaryComponents.Any())
                    Logger.Debug("Processing temporary components...");
                ProcessTemporaryComponents(temporaryComponents, temporaryNegations, components, negations, trueComponent, falseComponent);
                AddFormToCompletedValues(form, completedSentenceFormValues, components);

                //TODO: Add this, but with the correct total number of components (not just Propositions)
                Console.WriteLine("  {0} components added", completedSentenceFormValues[form].Count);
            }
            //Connect "next" to "true"
            Logger.Debug("Adding transitions...");
            AddTransitions(components);
            //Set up "init" proposition
            Logger.Debug("Setting up 'init' proposition...");
            SetUpInit(components, trueComponent, falseComponent);
            //Now we can safely...
            RemoveUselessBasePropositions(components, negations, trueComponent, falseComponent);
            Logger.Debug("Creating component set...");
            var componentSet = new HashSet<IComponent>(components.Values);
            CompleteComponentSet(componentSet);
            //ConcurrencyUtils.checkForInterruption();
            Logger.Debug("Initializing propnet object...");
            //Make it look the same as the PropNetFactory results, until we decide how we want it to look
            NormalizePropositions(componentSet);
            var propnet = new PropNet(roles, componentSet);

            Logger.Debug("Done setting up propnet; took {0}ms, has {1} components and {2} links", (DateTime.UtcNow - startTime).TotalMilliseconds, componentSet.Count, propnet.GetNumLinks());
            Logger.Debug("Propnet has {0} ands; {1} ors; {2} nots", propnet.GetNumAnds(), propnet.GetNumOrs(), propnet.GetNumNots());

            if (ConfigurationManager.AppSettings["OutputPropNet"] == "true")
                propnet.RenderToFile("propnet.dot");

            return propnet;
        }
        //TODO: Create a version with just a set of components that we can share with post-optimizations
        private static void OptimizeAwayFalse(IDictionary<Fact, IComponent> components, Dictionary<Fact, IComponent> negations,
            PropNet pn, IComponent trueComponent, IComponent falseComponent)
        {
            Debug.Assert((components != null && negations != null) || pn != null);
            Debug.Assert((components == null && negations == null) || pn == null);
            foreach (IComponent output in falseComponent.Outputs.ToList())
            {
                if (IsEssentialProposition(output) || output is ITransition)
                {
                    //Since this is the false constant, there are a few "essential" types we don't actually want to keep around.
                    if (!IsLegalOrGoalProposition(output))
                        continue;
                }
                var prop = output as IProposition;
                if (prop != null)
                {
                    //Move its outputs to be outputs of false
                    foreach (IComponent child in prop.Outputs)
                    {
                        //Disconnect
                        child.RemoveInput(prop);
                        //output.removeOutput(child); //do at end
                        //Reconnect; will get children before returning, if nonessential
                        falseComponent.AddOutput(child);
                        child.AddInput(falseComponent);
                    }
                    prop.RemoveAllOutputs();

                    if (!IsEssentialProposition(prop))
                    {
                        //Remove the proposition entirely
                        falseComponent.RemoveOutput(prop);
                        output.RemoveInput(falseComponent);
                        //Update its location to the trueComponent in our map
                        if (components != null)
                        {
                            components[prop.Name] = falseComponent;
                            negations[prop.Name] = trueComponent;
                        }
                        else
                            pn.RemoveComponent(prop);
                    }
                }
                else
                {
                    var and = output as IAnd;
                    if (and != null)
                    {
                        //Attach children of and to falseComponent
                        foreach (IComponent child in and.Outputs)
                        {
                            child.AddInput(falseComponent);
                            falseComponent.AddOutput(child);
                            child.RemoveInput(and);
                        }
                        //Disconnect and completely
                        and.RemoveAllOutputs();
                        foreach (IComponent parent in and.Inputs)
                            parent.RemoveOutput(and);
                        and.RemoveAllInputs();
                        if (pn != null)
                            pn.RemoveComponent(and);
                    }
                    else
                    {
                        var or = output as IOr;
                        if (or != null)
                        {
                            //Remove as input from or
                            or.RemoveInput(falseComponent);
                            falseComponent.RemoveOutput(or);
                            //If or has only one input, remove it
                            if (or.Inputs.Count == 1)
                            {
                                IComponent input = or.GetSingleInput();
                                or.RemoveInput(input);
                                input.RemoveOutput(or);
                                foreach (IComponent output1 in or.Outputs)
                                {
                                    //Disconnect from and
                                    output1.RemoveInput(or);
                                    //or.removeOutput(out); //do at end
                                    //Connect directly to the new input
                                    output1.AddInput(input);
                                    input.AddOutput(output1);
                                }
                                or.RemoveAllOutputs();
                                if (pn != null)
                                {
                                    pn.RemoveComponent(or);
                                }
                            }
                            else if (!or.Inputs.Any())
                            {
                                if (pn != null)
                                {
                                    pn.RemoveComponent(or);
                                }
                            }
                        }
                        else
                        {
                            var not = output as INot;
                            if (not != null)
                            {
                                //Disconnect from falseComponent
                                not.RemoveInput(falseComponent);
                                falseComponent.RemoveOutput(not);
                                //Connect all children of the not to trueComponent
                                foreach (IComponent child in not.Outputs)
                                {
                                    //Disconnect
                                    child.RemoveInput(not);
                                    //not.removeOutput(child); //Do at end
                                    //Connect to trueComponent
                                    child.AddInput(trueComponent);
                                    trueComponent.AddOutput(child);
                                }
                                not.RemoveAllOutputs();
                                if (pn != null)
                                    pn.RemoveComponent(not);
                            }
                            else if (output is ITransition)
                            {
                                //???
                                throw new Exception("Fix optimizeAwayFalse's case for Transitions");
                            }
                        }
                    }
                }
            }
        }
        private static void OptimizeAwayTrue(IDictionary<Fact, IComponent> components, IDictionary<Fact, IComponent> negations,
            PropNet pn, IComponent trueComponent, IComponent falseComponent)
        {
            Debug.Assert((components != null && negations != null) || pn != null);
            foreach (IComponent output in trueComponent.Outputs.ToList())
            {
                if (IsEssentialProposition(output) || output is ITransition)
                    continue;
                var prop = output as IProposition;
                if (prop != null)
                {
                    //Move its outputs to be outputs of true
                    foreach (IComponent child in prop.Outputs)
                    {
                        //Disconnect
                        child.RemoveInput(prop);
                        //output.removeOutput(child); //do at end
                        //Reconnect; will get children before returning, if nonessential
                        trueComponent.AddOutput(child);
                        child.AddInput(trueComponent);
                    }
                    prop.RemoveAllOutputs();

                    if (!IsEssentialProposition(prop))
                    {
                        //Remove the proposition entirely
                        trueComponent.RemoveOutput(prop);
                        output.RemoveInput(trueComponent);
                        //Update its location to the trueComponent in our map
                        if (components != null)
                        {
                            components[prop.Name] = trueComponent;
                            Debug.Assert(negations != null, "negations != null");
                            negations[prop.Name] = falseComponent;
                        }
                        else
                            pn.RemoveComponent(prop);
                    }
                }
                else
                {
                    var or = output as IOr;
                    if (or != null)
                    {
                        //Attach children of or to trueComponent
                        foreach (IComponent child in or.Outputs)
                        {
                            child.AddInput(trueComponent);
                            trueComponent.AddOutput(child);
                            child.RemoveInput(or);
                        }
                        //Disconnect or completely
                        or.RemoveAllOutputs();
                        foreach (IComponent parent in or.Inputs)
                            parent.RemoveOutput(or);
                        or.RemoveAllInputs();
                        if (pn != null)
                            pn.RemoveComponent(or);
                    }
                    else
                    {
                        var and = output as IAnd;
                        if (and != null)
                        {
                            //Remove as input from and
                            and.RemoveInput(trueComponent);
                            trueComponent.RemoveOutput(and);
                            //If and has only one input, remove it
                            if (and.Inputs.Count == 1)
                            {
                                IComponent input = and.GetSingleInput();
                                and.RemoveInput(input);
                                input.RemoveOutput(and);
                                foreach (IComponent output1 in and.Outputs)
                                {
                                    //Disconnect from and
                                    output1.RemoveInput(and);
                                    //and.removeOutput(out); //do at end
                                    //Connect directly to the new input
                                    output1.AddInput(input);
                                    input.AddOutput(output1);
                                }
                                and.RemoveAllOutputs();
                                if (pn != null)
                                    pn.RemoveComponent(and);
                            }
                            else if (and.Inputs.Any())
                                if (pn != null)
                                    pn.RemoveComponent(and);
                        }
                        else
                        {
                            var not = output as INot;
                            if (not != null)
                            {
                                //Disconnect from trueComponent
                                not.RemoveInput(trueComponent);
                                trueComponent.RemoveOutput(not);
                                //Connect all children of the not to falseComponent
                                foreach (IComponent child in not.Outputs)
                                {
                                    //Disconnect
                                    child.RemoveInput(not);
                                    //not.removeOutput(child); //Do at end
                                    //Connect to falseComponent
                                    child.AddInput(falseComponent);
                                    falseComponent.AddOutput(child);
                                }
                                not.RemoveAllOutputs();
                                if (pn != null)
                                    pn.RemoveComponent(not);
                            }
                        }
                    }
                }
            }
        }
 public static void OptimizeAwayTrueAndFalse(PropNet pn, IComponent trueComponent, IComponent falseComponent)
 {
     while (HasNonEssentialChildren(trueComponent) || HasNonEssentialChildren(falseComponent))
     {
         OptimizeAwayTrue(null, null, pn, trueComponent, falseComponent);
         OptimizeAwayFalse(null, null, pn, trueComponent, falseComponent);
     }
 }