private static void CompareInstructionDetail(
     int streamNum,
     int numInstruction,
     LookupInstructionPlanForge expected,
     LookupInstructionPlanForge actual,
     IDictionary<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping)
 {
     Assert.AreEqual(expected.LookupPlans.Length, actual.LookupPlans.Length);
     for (var i = 0; i < expected.LookupPlans.Length; i++) {
         CompareTableLookupPlan(
             streamNum,
             numInstruction,
             expected.LookupPlans[i],
             actual.LookupPlans[i],
             indexNameMapping);
     }
 }
        private static LookupInstructionPlanDesc BuildLookupInstructions(
            int rootStreamNum,
            IDictionary<int, int[]> substreamsPerStream,
            bool[] requiredPerStream,
            string[] streamNames,
            QueryGraphForge queryGraph,
            QueryPlanIndexForge[] indexSpecs,
            EventType[] typesPerStream,
            OuterJoinDesc[] outerJoinDescList,
            bool[] isHistorical,
            HistoricalStreamIndexListForge[] historicalStreamIndexLists,
            TableMetaData[] tablesPerStream,
            StreamJoinAnalysisResultCompileTime streamJoinAnalysisResult,
            StatementRawInfo statementRawInfo,
            StatementCompileTimeServices services)
        {
            var result = new List<LookupInstructionPlanForge>();
            var additionalForgeables = new List<StmtClassForgeableFactory>();
            
            foreach (var fromStream in substreamsPerStream.Keys) {
                var substreams = substreamsPerStream.Get(fromStream);

                // for streams with no substreams we don't need to look up
                if (substreams.Length == 0) {
                    continue;
                }

                var plans = new TableLookupPlanForge[substreams.Length];
                var historicalPlans = new HistoricalDataPlanNodeForge[substreams.Length];

                for (var i = 0; i < substreams.Length; i++) {
                    var toStream = substreams[i];

                    if (isHistorical[toStream]) {
                        // There may not be an outer-join descriptor, use if provided to build the associated expression
                        ExprNode outerJoinExpr = null;
                        if (outerJoinDescList.Length > 0) {
                            OuterJoinDesc outerJoinDesc;
                            if (toStream == 0) {
                                outerJoinDesc = outerJoinDescList[0];
                            }
                            else {
                                outerJoinDesc = outerJoinDescList[toStream - 1];
                            }

                            outerJoinExpr = outerJoinDesc.MakeExprNode(statementRawInfo, services);
                        }

                        if (historicalStreamIndexLists[toStream] == null) {
                            historicalStreamIndexLists[toStream] = new HistoricalStreamIndexListForge(
                                toStream,
                                typesPerStream,
                                queryGraph);
                        }

                        historicalStreamIndexLists[toStream].AddIndex(fromStream);
                        historicalPlans[i] = new HistoricalDataPlanNodeForge(
                            toStream,
                            rootStreamNum,
                            fromStream,
                            typesPerStream.Length,
                            outerJoinExpr == null ? null : outerJoinExpr.Forge);
                    }
                    else {
                        TableLookupPlanDesc planDesc = NStreamQueryPlanBuilder.CreateLookupPlan(
                            queryGraph,
                            fromStream,
                            toStream,
                            streamJoinAnalysisResult.IsVirtualDW(toStream),
                            indexSpecs[toStream],
                            typesPerStream,
                            tablesPerStream[toStream],
                            statementRawInfo,
                            SerdeCompileTimeResolverNonHA.INSTANCE);
                        plans[i] = planDesc.Forge;
                        additionalForgeables.AddAll(planDesc.AdditionalForgeables);
                    }
                }

                var fromStreamName = streamNames[fromStream];
                var instruction = new LookupInstructionPlanForge(
                    fromStream,
                    fromStreamName,
                    substreams,
                    plans,
                    historicalPlans,
                    requiredPerStream);
                result.Add(instruction);
            }

            return new LookupInstructionPlanDesc(result, additionalForgeables);
        }