public void TestComputeBestPath()
        {
            NStreamQueryPlanBuilder.BestChainResult bestChain = NStreamQueryPlanBuilder.ComputeBestPath(0, queryGraph, dependencyGraph);
            Assert.AreEqual(3, bestChain.Depth);
            Assert.IsTrue(Arrays.AreEqual(bestChain.Chain, new int[] { 2, 4, 3, 1 }));

            bestChain = NStreamQueryPlanBuilder.ComputeBestPath(3, queryGraph, dependencyGraph);
            Assert.AreEqual(4, bestChain.Depth);
            Assert.IsTrue(Arrays.AreEqual(bestChain.Chain, new int[] { 4, 2, 0, 1 }));

            // try a stream that is not connected in any way
            queryGraph = new QueryGraphForge(6, null, false);
            bestChain  = NStreamQueryPlanBuilder.ComputeBestPath(5, queryGraph, dependencyGraph);
            log.Debug(".testComputeBestPath bestChain=" + bestChain);
            Assert.AreEqual(0, bestChain.Depth);
            Assert.IsTrue(Arrays.AreEqual(bestChain.Chain, new int[] { 0, 1, 2, 3, 4 }));
        }
        private static QueryPlanNodeForgeDesc BuildPlanNode(
            int numStreams,
            int streamNo,
            string[] streamNames,
            QueryGraphForge queryGraph,
            OuterInnerDirectionalGraph outerInnerGraph,
            OuterJoinDesc[] outerJoinDescList,
            InnerJoinGraph innerJoinGraph,
            QueryPlanIndexForge[] indexSpecs,
            EventType[] typesPerStream,
            bool[] isHistorical,
            DependencyGraph dependencyGraph,
            HistoricalStreamIndexListForge[] historicalStreamIndexLists,
            TableMetaData[] tablesPerStream,
            StreamJoinAnalysisResultCompileTime streamJoinAnalysisResult,
            StatementRawInfo statementRawInfo,
            StatementCompileTimeServices services)
        {
            // For each stream build an array of substreams, considering required streams (inner joins) first
            // The order is relevant therefore preserving order via a LinkedHashMap.
            var substreamsPerStream = new LinkedHashMap<int, int[]>();
            var requiredPerStream = new bool[numStreams];
            var additionalForgeables = new List<StmtClassForgeableFactory>();

            // Recursive populating the required (outer) and optional (inner) relationships
            // of this stream and the substream
            ISet<int> completedStreams = new HashSet<int>();
            // keep track of tree path as only those stream events are always available to historical streams
            var streamCallStack = new Stack<int>();
            streamCallStack.Push(streamNo);

            // For all inner-joins, the algorithm is slightly different
            if (innerJoinGraph.IsAllInnerJoin) {
                requiredPerStream.Fill(true);
                RecursiveBuildInnerJoin(
                    streamNo,
                    streamCallStack,
                    queryGraph,
                    completedStreams,
                    substreamsPerStream,
                    dependencyGraph);

                // compute a best chain to see if all streams are handled and add the remaining
                var bestChain = NStreamQueryPlanBuilder.ComputeBestPath(streamNo, queryGraph, dependencyGraph);
                AddNotYetNavigated(streamNo, numStreams, substreamsPerStream, bestChain);
            }
            else {
                RecursiveBuild(
                    streamNo,
                    streamCallStack,
                    queryGraph,
                    outerInnerGraph,
                    innerJoinGraph,
                    completedStreams,
                    substreamsPerStream,
                    requiredPerStream,
                    dependencyGraph);
            }

            // verify the substreamsPerStream, all streams must exists and be linked
            VerifyJoinedPerStream(streamNo, substreamsPerStream);

            // build list of instructions for lookup
            LookupInstructionPlanDesc lookupDesc = BuildLookupInstructions(
                streamNo,
                substreamsPerStream,
                requiredPerStream,
                streamNames,
                queryGraph,
                indexSpecs,
                typesPerStream,
                outerJoinDescList,
                isHistorical,
                historicalStreamIndexLists,
                tablesPerStream,
                streamJoinAnalysisResult,
                statementRawInfo,
                services);
            var lookupInstructions = lookupDesc.Forges;
            additionalForgeables.AddAll(lookupDesc.AdditionalForgeables);

            // build historical index and lookup strategies
            foreach (var lookups in lookupInstructions) {
                foreach (var historical in lookups.HistoricalPlans) {
                    if (historical == null) {
                        continue;
                    }

                    JoinSetComposerPrototypeHistoricalDesc desc = historicalStreamIndexLists[historical.StreamNum]
                        .GetStrategy(historical.LookupStreamNum, statementRawInfo, services.SerdeResolver);
                    historical.HistoricalIndexLookupStrategy = desc.LookupForge;
                    historical.PollResultIndexingStrategy = desc.IndexingForge;
                    additionalForgeables.AddAll(desc.AdditionalForgeables);
                }
            }

            // build strategy tree for putting the result back together
            var assemblyTopNodeFactory = AssemblyStrategyTreeBuilder.Build(
                streamNo,
                substreamsPerStream,
                requiredPerStream);
            var assemblyInstructionFactories =
                BaseAssemblyNodeFactory.GetDescendentNodesBottomUp(assemblyTopNodeFactory);

            var forge = new LookupInstructionQueryPlanNodeForge(
                streamNo,
                streamNames[streamNo],
                numStreams,
                requiredPerStream,
                lookupInstructions,
                assemblyInstructionFactories);
            
            return new QueryPlanNodeForgeDesc(forge, additionalForgeables);
        }