/// <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;
        }