//
        // Generate ALL maximal problems from the given start node (note, each incoming basic edge results in a maximal problem).
        // This gives soundness of generating problems from given nodes (current def of interesting problems), but lacks
        // completeness by not generating all of the subproblems along the way
        //
        //
        // From this node, generate all problems backward.
        // Do so by generating simple problems first (in a depth-first manner)
        //
        private List<Problem<Hypergraph.EdgeAnnotation>> GenerateAllMaximalProblemsFrom(ProblemHashMap<Hypergraph.EdgeAnnotation> memoizedProblems,
                                                                                        HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase, int node)
        {
            if (node == 118)
            {
                //Debug.WriteLine("NO-OP");
            }

            // If we have already generated problems, no need to regenerate
            if (memoizedProblems.HasNodeBeenGenerated(node)) return memoizedProblems.Get(node);

            // For all edges, create base problems and add them all to the database
            List<PebblerHyperEdge<Hypergraph.EdgeAnnotation>> edges = edgeDatabase.GetBasedOnGoal(node);

            // If there are no edges, this is a 'root' node (has no predecessors)
            if (edges == null || !edges.Any())
            {
                // We've successfully generated all the problems for this node: zero problems
                memoizedProblems.SetGenerated(node);
                return new List<Problem<Hypergraph.EdgeAnnotation>>();
            }

            //
            // Create all the base problems consisting of only one edge and add to the database
            List<Problem<Hypergraph.EdgeAnnotation>> baseProblems = new List<Problem<Hypergraph.EdgeAnnotation>>();
            foreach (PebblerHyperEdge<Hypergraph.EdgeAnnotation> edge in edges)
            {
                baseProblems.Add(new Problem<Hypergraph.EdgeAnnotation>(edge));
            }
            // memoizedProblems.PutUnchecked(baseProblems);

            // For all of the base problems, generate backward
            foreach (Problem<Hypergraph.EdgeAnnotation> baseProblem in baseProblems)
            {
                GenerateMaximalProblemFromSingleEdge(memoizedProblems, edgeDatabase, baseProblem);
            }

            // We've successfully generated all the problems for this node
            memoizedProblems.SetGenerated(node);

            // Return all the generated problems
            return memoizedProblems.Get(node);
        }
        //
        // Generates all forward directional problems using the template-based clauses from the pre-computation
        //
        private List<Problem<Hypergraph.EdgeAnnotation>> GenerateForwardProblems(List<GroundedClause> goalClauses,
                                                                                 Pebbler.HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase,
                                                                                 int numGivens)
        {
            //            System.Diagnostics.Debug.WriteLine("Forward");

            List<int> clauseIndices = AcquireGoalIndices(goalClauses);

            // The resultant structure of problems; graph size dictates associated array size in hashMap; number of givens is a limiting factor the size of problems
            ProblemHashMap<Hypergraph.EdgeAnnotation> problems = new ProblemHashMap<Hypergraph.EdgeAnnotation>(edgeDatabase, graph.vertices.Count, numGivens);

            // Generate all the problems based on the node indices
            foreach (int goalNode in clauseIndices)
            {
                if (Utilities.PROBLEM_GEN_DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Template node; will generate problems (" + goalNode + "): " + graph.vertices[goalNode].data.ToString());
                }

                pathGenerator.GenerateProblemsUsingBackwardPathToLeaves(problems, edgeDatabase, goalNode);
            }

            return FilterForMinimalAndRedundantProblems(problems.GetAll());
        }
 //
 // Given a start node, construct a path using reachability analysis to construct a path from start template node back to leaves.
 // Forward problems are generated by following forward edges in reverse
 //
 //                                      A multimap of all pebbled edges (from pebbling)
 public void GenerateProblemsUsingBackwardPathToLeaves(ProblemHashMap<Hypergraph.EdgeAnnotation> memoizedProblems,
                                                       HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase, int goalNode)
 {
     // Although this returns the set of problems, we can still acquire them through the ProblemHashMap
     GenerateAllMaximalProblemsFrom(memoizedProblems, edgeDatabase, goalNode);
 }
        //
        // For all given nodes in this problem, generate the single maximal problem
        //
        // This is done is a convluted way to speed up execution since we need to combine in the size of the powerset of cardinality of givens
        //
        private void GenerateMaximalProblemFromSingleEdge(ProblemHashMap<Hypergraph.EdgeAnnotation> memoizedProblems,
                                                          HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase,
                                                          Problem<Hypergraph.EdgeAnnotation> baseProblem)
        {
            // Create all simple problems by pursuing only a single path backward in the givens: append
            // all problems from a given to the base problem
            //
            //           |
            //           v
            //         given    __
            //           |        |
            //           v        |  baseProblem
            //          goal    __|
            //
            // Acquire all of the problems for the singleton sets of the powerset for all of the givens of this problem
            List<Problem<Hypergraph.EdgeAnnotation>>[] singletonMapToNewProblems = new List<Problem<Hypergraph.EdgeAnnotation>>[baseProblem.givens.Count];
            List<Problem<Hypergraph.EdgeAnnotation>>[] singletonMapToOriginalProblems = new List<Problem<Hypergraph.EdgeAnnotation>>[baseProblem.givens.Count];
            bool generatedNewProblems = false;
            for (int g = 0; g < baseProblem.givens.Count; g++)
            {
                // Acquire the original problems and save them
                singletonMapToOriginalProblems[g] = GenerateAllMaximalProblemsFrom(memoizedProblems, edgeDatabase, baseProblem.givens[g]);
                if (singletonMapToOriginalProblems[g] == null) singletonMapToOriginalProblems[g] = new List<Problem<Hypergraph.EdgeAnnotation>>();

                // Using the original problems, append them to the base problem
                singletonMapToNewProblems[g] = new List<Problem<Hypergraph.EdgeAnnotation>>();

                // Append all of these given problems to the base problem
                foreach (Problem<Hypergraph.EdgeAnnotation> problem in singletonMapToOriginalProblems[g])
                {
                    Problem<Hypergraph.EdgeAnnotation> baseProblemCopy = new Problem<Hypergraph.EdgeAnnotation>(baseProblem);
                    baseProblemCopy.Append(graph, edgeDatabase, problem);
                    //memoizedProblems.Put(baseProblemCopy);
                    singletonMapToNewProblems[g].Add(baseProblemCopy);
                    generatedNewProblems = true;
                }
            }

            if (baseProblem.goal == 98)
            {
                //Debug.WriteLine("98NO-OP");
            }

            // If we did not perform any appending, we have reached a maximal situation
            // Add the maximal problem to the database
            if (!generatedNewProblems)
            {
                // Determine suppression now to mitigate number of problems added
                baseProblem.DetermineSuppressedGivens(graph);
                memoizedProblems.Put(baseProblem);
                return;
            }

            //
            // Stitch together all of the possible combinations of maximal problems
            //
            //    |  |        |
            //    |  |        |
            //    v  v        v
            //   g_1 g_2 ... g_n   __
            //           |           |  baseProblem
            //           v           |
            //         target      __|
            //
            //
            // We are looking for the maximal set of problems; therefore, we don't need all combinations.
            // What we do require is the combining of all generated problems with the base problem.
            //
            // Find all the sets of populated new problems
            List<int> populatedIndices = new List<int>();
            for (int index = 0; index < singletonMapToNewProblems.Length; index++)
            {
                if (singletonMapToNewProblems[index].Any()) populatedIndices.Add(index);
            }

            List<Problem<Hypergraph.EdgeAnnotation>> maximalProblems = new List<Problem<Hypergraph.EdgeAnnotation>>(singletonMapToNewProblems[populatedIndices[0]]);
            populatedIndices.RemoveAt(0);
            foreach (int index in populatedIndices)
            {
                //int count = 0;
                List<Problem<Hypergraph.EdgeAnnotation>> tmpMaximalProbs = new List<Problem<Hypergraph.EdgeAnnotation>>();
                foreach (Problem<Hypergraph.EdgeAnnotation> singleton in singletonMapToOriginalProblems[index])
                {
                    foreach (Problem<Hypergraph.EdgeAnnotation> problem in maximalProblems)
                    {
                        //if (baseProblem.goal == 98)
                        //{
                        //    Debug.WriteLine(count++);
                        //}

                        Problem<Hypergraph.EdgeAnnotation> problemCopy = new Problem<Hypergraph.EdgeAnnotation>(problem);
                        // It is possible for a problem to have been created which deduced further information with an additional edge;
                        // that is, a given node was pushed into the path of the problem. Hence, no need to append in this situation
                        if (problem.givens.Contains(singleton.goal))
                        {
                            problemCopy.Append(graph, edgeDatabase, singleton);
                        }
                        tmpMaximalProbs.Add(problemCopy);
                    }
                }
                maximalProblems = tmpMaximalProbs;
            }

            // Add all the maximal problems to the database
            foreach (Problem<Hypergraph.EdgeAnnotation> problem in maximalProblems)
            {
                // Determine suppression now to mitigate number of problems added
                problem.DetermineSuppressedGivens(graph);
                memoizedProblems.Put(problem);
            }
        }
        //
        // For all given nodes in this problem, generate all problems
        //
        // This is done is a convluted way to speed up execution since we need to combine in the size of the powerset of cardinality of givens
        //
        private void GenerateAllProblemsFromSingleEdge(ProblemHashMap<Hypergraph.EdgeAnnotation> memoizedProblems,
                                                       HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase,
                                                       Problem<Hypergraph.EdgeAnnotation> baseProblem)
        {
            // Create all simple problems by pursuing only a single path backward in the givens: append
            // all problems from a given to the base problem
            //
            //           |
            //           v
            //         given    __
            //           |        |
            //           v        |  baseProblem
            //          goal    __|
            //
            // Acquire all of the problems for the singleton sets of the powerset for all of the givens of this problem
            List<Problem<Hypergraph.EdgeAnnotation>>[] singletonMapToNewProblems = new List<Problem<Hypergraph.EdgeAnnotation>>[baseProblem.givens.Count];
            List<Problem<Hypergraph.EdgeAnnotation>>[] singletonMapToOriginalProblems = new List<Problem<Hypergraph.EdgeAnnotation>>[baseProblem.givens.Count];
            for (int g = 0; g < baseProblem.givens.Count; g++)
            {
                // Acquire the original problems and save them
                singletonMapToOriginalProblems[g] = GenerateAllProblemsFrom(memoizedProblems, edgeDatabase, baseProblem.givens[g]);
                if (singletonMapToOriginalProblems[g] == null) singletonMapToOriginalProblems[g] = new List<Problem<Hypergraph.EdgeAnnotation>>();

                // Using the original problems, append them to the base problem
                singletonMapToNewProblems[g] = new List<Problem<Hypergraph.EdgeAnnotation>>();

                // Append all of these given problems to the base problem
                foreach (Problem<Hypergraph.EdgeAnnotation> problem in singletonMapToOriginalProblems[g])
                {
                    Problem<Hypergraph.EdgeAnnotation> baseProblemCopy = new Problem<Hypergraph.EdgeAnnotation>(baseProblem);
                    baseProblemCopy.Append(graph, edgeDatabase, problem);
                    memoizedProblems.Put(baseProblemCopy);
                    singletonMapToNewProblems[g].Add(baseProblemCopy);
                }
            }

            // If there was only 1 given, we need to perform a powerset combining
            if (baseProblem.givens.Count == 1) return;

            //
            // Stitch together all of the possible combinations of problems that will result;
            // note this is a POWERSET construction of all possible problems; hence, note exponential explosion in number of problems
            //
            //    |   |
            //    |   |
            //    v   v
            //   g_1 g_2 ... g_n   __
            //           |           |  baseProblem
            //           v           |
            //         target      __|
            //
            //    |           |
            //    |           |
            //    v           v
            //   g_1 g_2 ... g_n   __
            //           |           |  baseProblem
            //           v           |
            //         target      __|
            //
            //    |  |        |
            //    |  |        |
            //    v  v        v
            //   g_1 g_2 ... g_n   __
            //           |           |  baseProblem
            //           v           |
            //         target      __|
            //
            // Acquire an encoded list of subsets: the powerset for the number of givens in this problem
            List<string> powerset = Utilities.ConstructPowerSetStringsWithNoEmpty(baseProblem.givens.Count, baseProblem.givens.Count); // <-- We avoid generating singleton sets

            // Maps a subset string representation (from the powerset) to the list of problems that it results in.
            Dictionary<string, List<Problem<Hypergraph.EdgeAnnotation>>> powersetMapToProblems = new Dictionary<string, List<Problem<Hypergraph.EdgeAnnotation>>>(powerset.Count);

            //
            // For every combination of possible problems (a subset of the powerset), we combine problems
            //
            // Note, the Utilities generated the powerset in the specific sorted ordering of lower cardinality sets first (also values in subset are increasing)
            // So a typical subset string is given by 012 or 023
            foreach (string subset in powerset)
            {
                // Break the subset string into its constituent elements: <Arbitrary subset, singleton>
                KeyValuePair<string, int> splitSubset = Utilities.SplitStringIntoKnownToProcess(subset);

                // Subset Problems; given the subset {012}: {01}
                List<Problem<Hypergraph.EdgeAnnotation>> subsetProblems;
                if (!powersetMapToProblems.TryGetValue(splitSubset.Key, out subsetProblems))
                {
                    // Use the singleton list; ASCII conversion of char to int
                    subsetProblems = singletonMapToNewProblems[Convert.ToInt32(splitSubset.Key)];
                }

                // Singleton tails; given the subset {012}: {2}
                List<Problem<Hypergraph.EdgeAnnotation>> singletonProblems = singletonMapToOriginalProblems[splitSubset.Value];

                // Combine the precomputed powerset subset (.Key) guiding the appending of the singleton (.Value) problems
                List<Problem<Hypergraph.EdgeAnnotation>> combinedSubsetProblems = new List<Problem<Hypergraph.EdgeAnnotation>>();
                foreach (Problem<Hypergraph.EdgeAnnotation> subsetProblem in subsetProblems)
                {
                    foreach (Problem<Hypergraph.EdgeAnnotation> singletonProb in singletonProblems)
                    {
                        // It is possible for a problem to have been created which deduced further information with an additional edge;
                        // that is, a given node was pushed into the path of the problem. Hence, no need to append in this situation
                        if (subsetProblem.givens.Contains(singletonProb.goal))
                        {
                            // Combine the two problems and record
                            Problem<Hypergraph.EdgeAnnotation> subsetProblemCopy = new Problem<Hypergraph.EdgeAnnotation>(subsetProblem);
                            subsetProblemCopy.Append(graph, edgeDatabase, singletonProb);
                            memoizedProblems.Put(subsetProblemCopy);
                            combinedSubsetProblems.Add(subsetProblemCopy);
                        }
                    }
                }

                // We have calculated the problems for this subset, keep track of them
                powersetMapToProblems.Add(subset, combinedSubsetProblems);
            }
        }
        private List<Problem<Hypergraph.EdgeAnnotation>> GenerateBackwardProblems(List<GroundedClause> goalClauses,
                                                                                  Pebbler.HyperEdgeMultiMap<Hypergraph.EdgeAnnotation> edgeDatabase)
        {
            System.Diagnostics.Debug.WriteLine("Backward");

            List<int> clauseIndices = AcquireGoalIndices(goalClauses);

            // The resultant structure of problems; graph size dictates associated array size in hashMap; number of givens is a limiting factor the size of problems
            // Problem generation limits the number of givens in BACKWARD problems to 4 (or the constant in the HashMap structure)
            ProblemHashMap<Hypergraph.EdgeAnnotation> problems = new ProblemHashMap<Hypergraph.EdgeAnnotation>(edgeDatabase, graph.vertices.Count);

            // Generate all the problems based on the node indices
            foreach (int goalNode in clauseIndices)
            {
                if (Utilities.PROBLEM_GEN_DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Template node; will generate problems (" + goalNode + "): " + graph.vertices[goalNode].data.ToString());
                }

                pathGenerator.GenerateProblemsUsingBackwardPathToLeaves(problems, edgeDatabase, goalNode);
            }

            // Filter backward problems so that only problems with goal being the original figure givens persist.
            return FilterBackwardProblems(clauseIndices, FilterForMinimalAndRedundantProblems(problems.GetAll()));
        }