//initialize memoization with 2-Component (i.e., arc) subassemblies so heuristic works
        protected static void InitializeMemo()
        {
            foreach (Connection arc in Graph.arcs.Where(a => a is Connection))
            {
                var         Asm  = new HashSet <Component>(new Component[] { (Component)arc.From, (Component)arc.To });
                var         Fr   = new List <Component>(new Component[] { (Component)arc.From });
                var         dirs = new HashSet <int>(arc.InfiniteDirections);
                SubAssembly sa;
                if (AssemblyEvaluator.EvaluateSub(Graph, Asm, Fr, dirs, out sa) > 0)
                {
                    HashSet <Component> A = new HashSet <Component>(Asm);
                    MemoData            D = new MemoData(sa.Install.Time, sa);
                    Memo.Add(A, D);
                }
            }

            foreach (var node in Graph.nodes)
            {
                var component = (Component)node;
                var N         = new HashSet <Component>(new[] { component });
                var sa        = new SubAssembly(N, EvaluationForBinaryTree.ConvexHullsForParts[component.name], component.Mass,
                                                component.Volume, new Vertex(component.CenterOfMass));
                MemoData D = new MemoData(0, sa);
                Memo.Add(N, D);
            }
        }
        protected static double F(out SubAssembly tree, HashSet <Component> A)
        {
            Count[A.Count]++;
            TimeEstmCounter++;
            TimeEstm++;

            if (Memo.ContainsKey(A) /*||
                                     * Memo.Keys.Any(k => k.Count == A.Count && k.All(kk => A.Any(a => a.name == kk.name)))*/)
            {
                tree = Memo[A].sa;
                return(Memo[A].Value);
            }

            var candidates = GetCandidates(A);

            candidates.Sort();

            var         best = double.PositiveInfinity;
            SubAssembly bestsa = null, bestReference = null, bestMoving = null;

            foreach (var tc in candidates)
            {
                if (tc.H >= best)
                {
                    break;
                }
                SubAssembly reference, moving;
                var         refTime    = F(out reference, tc.RefNodes);
                var         movTime    = F(out moving, tc.MovNodes);
                var         maxT       = Math.Max(refTime, movTime);
                var         evaluation = tc.sa.Install.Time + maxT;
                if (evaluation < best)
                {
                    best          = evaluation;
                    bestsa        = tc.sa;
                    bestReference = reference;
                    bestMoving    = moving;
                }
                if (Estimate)
                {
                    break;
                }
            }

            tree = bestsa;
            tree.Install.Reference = bestReference;
            tree.Install.Moving    = bestMoving;

            if (!Estimate)
            {
                var d = new MemoData(best, bestsa);
                Memo.Add(A, d);
            }
            return(best);
        }
        protected static void InitializeMemoBoosted()
        {
            var newMemo = new List <HashSet <HashSet <Component> > >();

            for (var i = 1; i <= Graph.nodes.Count; i++)
            {
                var combinations = CombinationFinder(i);
                var column       = new HashSet <HashSet <Component> >();
                if (!combinations.Any())
                {
                    foreach (var node in Graph.nodes)
                    {
                        var component = (Component)node;
                        var N         = new HashSet <Component>(new[] { component });
                        var sa        = new SubAssembly(N, EvaluationForBinaryTree.ConvexHullsForParts[component.name],
                                                        component.Mass,
                                                        component.Volume, new Vertex(component.CenterOfMass));
                        var D = new MemoData(0, sa);
                        column.Add(N);
                        Memo.Add(N, D);
                    }
                    newMemo.Add(column);
                }
                else
                {
                    Parallel.ForEach(combinations, comb =>
                    {
                        foreach (var subAssem1 in newMemo[comb[0] - 1])
                        {
                            foreach (var subAssem2 in newMemo[comb[1] - 1])
                            {
                                if (subAssem1.Any(subAssem2.Contains))
                                {
                                    continue;
                                }
                                var connections =
                                    new HashSet <Connection>(
                                        subAssem1.SelectMany(
                                            n => n.arcs.Where(c => c is Connection).Cast <Connection>().Where(c =>
                                                                                                              (subAssem1.Contains(c.From) && subAssem2.Contains(c.To)) ||
                                                                                                              (subAssem1.Contains(c.To) && subAssem2.Contains(c.From)))));
                                var secConnections =
                                    new HashSet <SecondaryConnection>(
                                        subAssem1.SelectMany(
                                            n =>
                                            n.arcs.Where(a => a is SecondaryConnection)
                                            .Cast <SecondaryConnection>()
                                            .Where(c =>
                                                   (subAssem1.Contains(c.From) && subAssem2.Contains(c.To)) ||
                                                   (subAssem1.Contains(c.To) && subAssem2.Contains(c.From)))));

                                if (!connections.Any())
                                {
                                    continue;
                                }
                                var dirs = ValidDirectionsFinder(subAssem1, subAssem2, connections);
                                ApplySecondaryConnections(dirs, subAssem1, subAssem2, secConnections);
                                if (!dirs.Any())
                                {
                                    continue;
                                }
                                var asm = new HashSet <Component>(subAssem1);
                                asm.UnionWith(subAssem2);
                                SubAssembly sa;
                                if (AssemblyEvaluator.EvaluateSub(Graph, asm, subAssem1.ToList(), dirs, out sa) > 0)
                                {
                                    if (!Memo.ContainsKey(asm))
                                    {
                                        var D = new MemoData(sa.Install.Time, sa);
                                        lock (Memo)
                                            Memo.Add(asm, D);
                                        lock (column)
                                            column.Add(asm);
                                        continue;
                                    }
                                    if (sa.Install.Time < Memo[asm].Value)
                                    {
                                        lock (Memo)
                                            Memo[asm] = new MemoData(sa.Install.Time, sa);
                                    }
                                }
                            }
                        }
                    });
                    newMemo.Add(column);
                    if (newMemo.Count == 3)
                    {
                        break;
                    }
                }
            }
        }