/// <summary>
        /// Generates an LGSPAction object for the given scheduled search plan.
        /// </summary>
        /// <param name="action">Needed for the rule pattern and the name</param>
        /// <param name="sourceOutputFilename">null if no output file needed</param>
        public LGSPAction GenerateAction(ScheduledSearchPlan scheduledSearchPlan, LGSPAction action,
            String modelAssemblyLocation, String actionAssemblyLocation, String sourceOutputFilename)
        {
            SourceBuilder sourceCode = new SourceBuilder(CommentSourceCode);
            GenerateFileHeaderForActionsFile(sourceCode, model.GetType().Namespace, action.rulePattern.GetType().Namespace);

            // can't generate new subpattern matchers due to missing scheduled search plans for them / missing graph analyze data
            Debug.Assert(action.rulePattern.patternGraph.embeddedGraphsPlusInlined.Length == 0);

            GenerateActionAndMatcher(sourceCode, action.rulePattern, false);

            // close namespace
            sourceCode.Append("}");

            // write generated source to file if requested
            if(sourceOutputFilename != null)
            {
                StreamWriter writer = new StreamWriter(sourceOutputFilename);
                writer.Write(sourceCode.ToString());
                writer.Close();
            }

            // set up compiler
            CSharpCodeProvider compiler = new CSharpCodeProvider();
            CompilerParameters compParams = GetDynCompilerSetup(modelAssemblyLocation, actionAssemblyLocation);

//            Stopwatch compilerWatch = Stopwatch.StartNew();

            // compile generated code
            CompilerResults compResults = compiler.CompileAssemblyFromSource(compParams, sourceCode.ToString());
            if(compResults.Errors.HasErrors)
            {
                String errorMsg = compResults.Errors.Count + " Errors:";
                foreach(CompilerError error in compResults.Errors)
                    errorMsg += Environment.NewLine + "Line: " + error.Line + " - " + error.ErrorText;
                throw new ArgumentException("Illegal dynamic C# source code produced for actions \"" + action.Name + "\": " + errorMsg);
            }

            // create action instance
            Object obj = compResults.CompiledAssembly.CreateInstance("de.unika.ipd.grGen.lgspActions.DynAction_" + action.Name);

//            long compSourceTicks = compilerWatch.ElapsedTicks;
//            Console.WriteLine("GenMatcher: Compile source: {0} us", compSourceTicks / (Stopwatch.Frequency / 1000000));
            return (LGSPAction) obj;
        }
        /// <summary>
        /// Generates scheduled search plans needed for matcher code generation for action compilation
        /// out of graph with analyze information, 
        /// The scheduled search plans are added to the main and the nested pattern graphs.
        /// </summary>
        public void GenerateScheduledSearchPlans(PatternGraph patternGraph, LGSPGraph graph,
            bool isSubpatternLike, bool isNegativeOrIndependent, 
            ScheduledSearchPlan nestingScheduledSearchPlan)
        {
            for(int i=0; i<patternGraph.schedules.Length; ++i)
            {
                patternGraph.AdaptToMaybeNull(i);
                if(Profile)
                    SetNeedForProfiling(patternGraph);
                PlanGraph planGraph = GeneratePlanGraph(graph.statistics, patternGraph,
                    isNegativeOrIndependent, isSubpatternLike,
                    ExtractOwnElements(nestingScheduledSearchPlan, patternGraph));
                MarkMinimumSpanningArborescence(planGraph, patternGraph.name);
                SearchPlanGraph searchPlanGraph = GenerateSearchPlanGraph(planGraph);
                ScheduledSearchPlan scheduledSearchPlan = ScheduleSearchPlan(
                    searchPlanGraph, patternGraph, isNegativeOrIndependent);
                AppendHomomorphyInformation(scheduledSearchPlan);
                patternGraph.schedules[i] = scheduledSearchPlan;
                patternGraph.RevertMaybeNullAdaption(i);

                foreach(PatternGraph neg in patternGraph.negativePatternGraphsPlusInlined)
                {
                    GenerateScheduledSearchPlans(neg, graph,
                        isSubpatternLike, true, null);
                }

                foreach(PatternGraph idpt in patternGraph.independentPatternGraphsPlusInlined)
                {
                    GenerateScheduledSearchPlans(idpt, graph,
                        isSubpatternLike, true, patternGraph.schedules[i]);
                }

                foreach(Alternative alt in patternGraph.alternativesPlusInlined)
                {
                    foreach (PatternGraph altCase in alt.alternativeCases)
                    {
                        GenerateScheduledSearchPlans(altCase, graph,
                            true, false, null);
                    }
                }

                foreach(Iterated iter in patternGraph.iteratedsPlusInlined)
                {
                    GenerateScheduledSearchPlans(iter.iteratedPattern, graph,
                        true, false, null);
                }
            }
        }
        public static IDictionary<PatternElement, SetValueType> ExtractOwnElements(ScheduledSearchPlan nestingScheduledSearchPlan, PatternGraph patternGraph)
        {
            Dictionary<PatternElement, SetValueType> ownElements = new Dictionary<PatternElement,SetValueType>();

            // elements contained in the schedule of the nesting pattern, that are declared in the current pattern graph
            // stem from inlining an indepent, extract them to treat them specially in search plan building (preset from nesting pattern)
            if(nestingScheduledSearchPlan != null)
            {
                for(int i = 0; i < nestingScheduledSearchPlan.Operations.Length; ++i)
                {
                    if(nestingScheduledSearchPlan.Operations[i].Type == SearchOperationType.Condition
                        || nestingScheduledSearchPlan.Operations[i].Type == SearchOperationType.AssignVar
                        || nestingScheduledSearchPlan.Operations[i].Type == SearchOperationType.DefToBeYieldedTo)
                    {
                        continue;
                    }

                    SearchPlanNode spn = (SearchPlanNode)nestingScheduledSearchPlan.Operations[i].Element;
                    if(spn.PatternElement.pointOfDefinition == patternGraph)
                    {
                        ownElements.Add(spn.PatternElement.OriginalIndependentElement, null);
                    }
                }
            }

            return ownElements;
        }
        /// <summary>
        /// Non- is-matched-flag-based isomorphy checking for nested alternative cases/iterateds
        /// </summary>
        public void ParallelizeAlternativeIterated(PatternGraph patternGraph)
        {
            foreach(Alternative alt in patternGraph.alternativesPlusInlined)
            {
                foreach(PatternGraph altCase in alt.alternativeCases)
                {
                    ScheduledSearchPlan ssp = altCase.schedulesIncludingNegativesAndIndependents[0];
                    altCase.parallelizedSchedule = new ScheduledSearchPlan[1];
                    List<SearchOperation> operations = new List<SearchOperation>(ssp.Operations.Length);
                    for(int i = 0; i < ssp.Operations.Length; ++i)
                    {
                        SearchOperation so = ssp.Operations[i];
                        SearchOperation clone = (SearchOperation)so.Clone();
                        clone.Isomorphy.Parallel = true;
                        operations.Add(clone);
                        if(clone.Element is PatternCondition)
                            SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression);
                    }
                    ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan(
                        altCase, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0);
                    altCase.parallelizedSchedule[0] = clonedSsp;

                    ParallelizeNegativeIndependent(clonedSsp);
                    ParallelizeAlternativeIterated(altCase);
                    ParallelizeYielding(altCase);
                }
            }
            foreach(Iterated iter in patternGraph.iteratedsPlusInlined)
            {
                ScheduledSearchPlan ssp = iter.iteratedPattern.schedulesIncludingNegativesAndIndependents[0];
                iter.iteratedPattern.parallelizedSchedule = new ScheduledSearchPlan[1];
                List<SearchOperation> operations = new List<SearchOperation>(ssp.Operations.Length);
                for(int i = 0; i < ssp.Operations.Length; ++i)
                {
                    SearchOperation so = ssp.Operations[i];
                    SearchOperation clone = (SearchOperation)so.Clone();
                    clone.Isomorphy.Parallel = true;
                    operations.Add(clone);
                    if(clone.Element is PatternCondition)
                        SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression);
                }
                ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan(
                        iter.iteratedPattern, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0);
                iter.iteratedPattern.parallelizedSchedule[0] = clonedSsp;

                ParallelizeNegativeIndependent(clonedSsp);
                ParallelizeAlternativeIterated(iter.iteratedPattern);
                ParallelizeYielding(iter.iteratedPattern);
            }
        }
        /// <summary>
        /// Non- is-matched-flag-based isomorphy checking for nested negatives/independents, 
        /// patch into already cloned parallel ssp
        /// </summary>
        public void ParallelizeNegativeIndependent(ScheduledSearchPlan ssp)
        {
            foreach(SearchOperation so in ssp.Operations)
            {
                so.Isomorphy.Parallel = true;

                if(so.Type == SearchOperationType.NegativePattern
                    || so.Type == SearchOperationType.IndependentPattern)
                {
                    ScheduledSearchPlan nestedSsp = (ScheduledSearchPlan)so.Element;
                    List<SearchOperation> operations = new List<SearchOperation>(nestedSsp.Operations.Length);
                    for(int i = 0; i < nestedSsp.Operations.Length; ++i)
                    {
                        SearchOperation nestedSo = nestedSsp.Operations[i];
                        SearchOperation clone = (SearchOperation)nestedSo.Clone();
                        operations.Add(clone);
                        if(clone.Element is PatternCondition)
                            SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression);
                    }
                    Debug.Assert(nestedSsp.PatternGraph.parallelizedSchedule == null); // may fire in case explain was used before, ignore it then
                    nestedSsp.PatternGraph.parallelizedSchedule = new ScheduledSearchPlan[1];
                    ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan(
                        nestedSsp.PatternGraph, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0);
                    nestedSsp.PatternGraph.parallelizedSchedule[0] = clonedSsp;
                    so.Element = clonedSsp;

                    ParallelizeNegativeIndependent(clonedSsp);
                    ParallelizeAlternativeIterated(nestedSsp.PatternGraph);
                    if(so.Type == SearchOperationType.IndependentPattern)
                        ParallelizeYielding(nestedSsp.PatternGraph);
                }
            }
        }
        /// <summary>
        /// Parallelize the scheduled search plan to the branching factor,
        /// splitting it at the first loop into a header part and a body part
        /// </summary>
        public void ParallelizeHeadBody(LGSPRulePattern rulePattern)
        {
            Debug.Assert(rulePattern.patternGraph.schedulesIncludingNegativesAndIndependents.Length == 1);
            ScheduledSearchPlan ssp = rulePattern.patternGraph.schedulesIncludingNegativesAndIndependents[0];
            
            int indexToSplitAt = 0;
            for(int i = 0; i < ssp.Operations.Length; ++i)
            {
                SearchOperation so = ssp.Operations[i];
                if(so.Type == SearchOperationType.Lookup || so.Type == SearchOperationType.Incident
                    || so.Type == SearchOperationType.Incoming || so.Type == SearchOperationType.Outgoing
                    || so.Type == SearchOperationType.PickFromStorage || so.Type == SearchOperationType.PickFromStorageDependent
                    || so.Type == SearchOperationType.PickFromIndex || so.Type == SearchOperationType.PickFromIndexDependent)
                {
                    indexToSplitAt = i;
                    break;
                }
            }

            rulePattern.patternGraph.parallelizedSchedule = new ScheduledSearchPlan[2];
            List<SearchOperation> headOperations = new List<SearchOperation>();
            List<SearchOperation> bodyOperations = new List<SearchOperation>();
            for(int i = 0; i < rulePattern.Inputs.Length; ++i)
            {
                if(rulePattern.Inputs[i] is VarType) // those don't appear in the schedule, they are only extracted into the search program
                {
                    VarType varType = (VarType)rulePattern.Inputs[i];
                    String varName = rulePattern.InputNames[i];
                    PatternVariable dummy = new PatternVariable(varType, varName, varName, i, false, null);
                    headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPresetVar,
                        dummy, null, 0));
                    bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPresetVar,
                        dummy, null, 0));
                }
            }
            for(int i = 0; i < ssp.Operations.Length; ++i)
            {
                SearchOperation so = ssp.Operations[i];
                if(i < indexToSplitAt)
                {
                    SearchOperation clone = (SearchOperation)so.Clone();
                    clone.Isomorphy.Parallel = true;
                    clone.Isomorphy.LockForAllThreads = true;
                    headOperations.Add(clone);
                    switch(so.Type)
                    {
                            // the target binding looping operations can't appear in the header, so we don't treat them here
                            // the non-target binding operations are completely handled by just adding them, happended already above
                            // the target binding non-looping operations are handled below,
                            // by parallel preset writing in the header and reading in the body
                            // with exception of def, its declaration and initializion is just re-executed in the body
                            // some presets can't appear in an action header, they are thus not taken care of
                        case SearchOperationType.ActionPreset:
                        case SearchOperationType.MapWithStorage:
                        case SearchOperationType.MapWithStorageDependent:
                        case SearchOperationType.Cast:
                        case SearchOperationType.Assign:
                        case SearchOperationType.Identity:
                        case SearchOperationType.ImplicitSource:
                        case SearchOperationType.ImplicitTarget:
                        case SearchOperationType.Implicit:
                            headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPreset,
                                (SearchPlanNode)so.Element, so.SourceSPNode, 0));
                            bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPreset, 
                                (SearchPlanNode)so.Element, so.SourceSPNode, 0));
                            break;
                        case SearchOperationType.AssignVar:
                            headOperations.Add(new SearchOperation(SearchOperationType.WriteParallelPresetVar,
                                (PatternVariable)so.Element, so.SourceSPNode, 0));
                            bodyOperations.Add(new SearchOperation(SearchOperationType.ParallelPresetVar,
                                (PatternVariable)so.Element, so.SourceSPNode, 0));
                            break;
                        case SearchOperationType.DefToBeYieldedTo:
                            bodyOperations.Add((SearchOperation)so.Clone());
                            break;
                    }                    
                }
                else if(i == indexToSplitAt)
                {
                    SearchOperation cloneHead = (SearchOperation)so.Clone();
                    headOperations.Add(cloneHead);
                    SearchOperation cloneBody = (SearchOperation)so.Clone();
                    cloneBody.Isomorphy.Parallel = true;
                    bodyOperations.Add(cloneBody);
                    switch(so.Type)
                    {
                        case SearchOperationType.Lookup:
                            cloneHead.Type = SearchOperationType.SetupParallelLookup;
                            cloneBody.Type = SearchOperationType.ParallelLookup;
                            break;
                        case SearchOperationType.Incident:
                            cloneHead.Type = SearchOperationType.SetupParallelIncident;
                            cloneBody.Type = SearchOperationType.ParallelIncident;
                            break;
                        case SearchOperationType.Incoming:
                            cloneHead.Type = SearchOperationType.SetupParallelIncoming;
                            cloneBody.Type = SearchOperationType.ParallelIncoming;
                            break;
                        case SearchOperationType.Outgoing:
                            cloneHead.Type = SearchOperationType.SetupParallelOutgoing;
                            cloneBody.Type = SearchOperationType.ParallelOutgoing;
                            break;
                        case SearchOperationType.PickFromStorage:
                            cloneHead.Type = SearchOperationType.SetupParallelPickFromStorage;
                            cloneBody.Type = SearchOperationType.ParallelPickFromStorage;
                            break;
                        case SearchOperationType.PickFromStorageDependent:
                            cloneHead.Type = SearchOperationType.SetupParallelPickFromStorageDependent;
                            cloneBody.Type = SearchOperationType.ParallelPickFromStorageDependent;
                            break;
                        case SearchOperationType.PickFromIndex:
                            cloneHead.Type = SearchOperationType.SetupParallelPickFromIndex;
                            cloneBody.Type = SearchOperationType.ParallelPickFromIndex;
                            break;
                        case SearchOperationType.PickFromIndexDependent:
                            cloneHead.Type = SearchOperationType.SetupParallelPickFromIndexDependent;
                            cloneBody.Type = SearchOperationType.ParallelPickFromIndexDependent;
                            break;
                    }
                }
                else
                {
                    SearchOperation clone = (SearchOperation)so.Clone();
                    clone.Isomorphy.Parallel = true;
                    bodyOperations.Add(clone);
                    if(clone.Element is PatternCondition)
                        SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression);
                }
            }
            ScheduledSearchPlan headSsp = new ScheduledSearchPlan(
                rulePattern.patternGraph, headOperations.ToArray(), headOperations.Count > 0 ? headOperations[0].CostToEnd : 0);
            rulePattern.patternGraph.parallelizedSchedule[0] = headSsp;
            ScheduledSearchPlan bodySsp = new ScheduledSearchPlan(
                rulePattern.patternGraph, bodyOperations.ToArray(), bodyOperations.Count > 0 ? bodyOperations[0].CostToEnd : 0);
            rulePattern.patternGraph.parallelizedSchedule[1] = bodySsp;
            ParallelizeNegativeIndependent(bodySsp);
            ParallelizeAlternativeIterated(rulePattern.patternGraph);
            ParallelizeYielding(rulePattern.patternGraph);
        }
 /// <summary>
 /// Parallelize the scheduled search plan for usage from a parallelized matcher
 /// (non- is-matched-flag-based isomorphy checking)
 /// </summary>
 public void Parallelize(LGSPMatchingPattern matchingPattern)
 {
     Debug.Assert(matchingPattern.patternGraph.schedulesIncludingNegativesAndIndependents.Length == 1);
     ScheduledSearchPlan ssp = matchingPattern.patternGraph.schedulesIncludingNegativesAndIndependents[0];
     matchingPattern.patternGraph.parallelizedSchedule = new ScheduledSearchPlan[1];
     List<SearchOperation> operations = new List<SearchOperation>(ssp.Operations.Length);
     for(int i = 0; i < ssp.Operations.Length; ++i)
     {
         SearchOperation so = ssp.Operations[i];
         SearchOperation clone = (SearchOperation)so.Clone();
         clone.Isomorphy.Parallel = true;
         operations.Add(clone);
         if(clone.Element is PatternCondition)
             SetNeedForParallelizedVersion((clone.Element as PatternCondition).ConditionExpression);
     }
     ScheduledSearchPlan clonedSsp = new ScheduledSearchPlan(
         matchingPattern.patternGraph, operations.ToArray(), operations.Count > 0 ? operations[0].CostToEnd : 0);
     matchingPattern.patternGraph.parallelizedSchedule[0] = clonedSsp;
     ParallelizeNegativeIndependent(clonedSsp);
     ParallelizeAlternativeIterated(matchingPattern.patternGraph);
     ParallelizeYielding(matchingPattern.patternGraph);
 }
        /// <summary>
        /// fill in globally homomorphic elements as exception to global isomorphy check
        /// </summary>
        public void FillInGlobalHomomorphyPatternElements(ScheduledSearchPlan ssp, int j)
        {
            SearchPlanNode spn_j = (SearchPlanNode)ssp.Operations[j].Element;

            if(spn_j.NodeType == PlanNodeType.Node)
            {
                if(spn_j.ElementID == -1 // inlined from independent for better matching, independent from the rest
                    || ssp.PatternGraph.totallyHomomorphicNodes[spn_j.ElementID - 1])
                {
                    ssp.Operations[j].Isomorphy.TotallyHomomorph = true;
                    return; // iso-exceptions to totally hom are handled with non-global iso checks
                }
            }
            else
            {
                if(spn_j.ElementID == -1 // inlined from independent for better matching, independent from the rest
                    || ssp.PatternGraph.totallyHomomorphicEdges[spn_j.ElementID - 1])
                {
                    ssp.Operations[j].Isomorphy.TotallyHomomorph = true;
                    return; // iso-exceptions to totally hom are handled with non-global iso checks
                }
            }

            bool[,] homGlobal;
            if(spn_j.NodeType == PlanNodeType.Node) {
                homGlobal = ssp.PatternGraph.homomorphicNodesGlobal;
            }
            else { // (spn_j.NodeType == PlanNodeType.Edge)
                homGlobal = ssp.PatternGraph.homomorphicEdgesGlobal;
            }

            // iterate through the operations before our position
            for(int i = 0; i < j; ++i)
            {
                // only check operations computing nodes or edges
                if(ssp.Operations[i].Type == SearchOperationType.Condition
                    || ssp.Operations[i].Type == SearchOperationType.NegativePattern
                    || ssp.Operations[i].Type == SearchOperationType.IndependentPattern
                    || ssp.Operations[i].Type == SearchOperationType.Assign 
                    || ssp.Operations[i].Type == SearchOperationType.AssignVar
                    || ssp.Operations[i].Type == SearchOperationType.DefToBeYieldedTo
                    || ssp.Operations[i].Type == SearchOperationType.InlinedIndependentCheckForDuplicateMatch)
                {
                    continue;
                }

                SearchPlanNode spn_i = (SearchPlanNode)ssp.Operations[i].Element;

                // don't compare nodes with edges
                if(spn_i.NodeType != spn_j.NodeType)
                {
                    continue;
                }

                if(spn_i.ElementID == -1)
                {
                    // inlined from independent for better matching, independent from the rest
                    continue;
                }
           
                // in global isomorphy check at current position 
                // allow globally homomorphic elements as exception
                // if they were already defined(preset)
                if(homGlobal[spn_j.ElementID - 1, spn_i.ElementID - 1])
                {
                    if(ssp.Operations[j].Isomorphy.GloballyHomomorphPatternElements == null)
                    {
                        ssp.Operations[j].Isomorphy.GloballyHomomorphPatternElements = new List<SearchPlanNode>();
                    }
                    ssp.Operations[j].Isomorphy.GloballyHomomorphPatternElements.Add(spn_i);
                }
            }
        }
        /// <summary>
        /// Determines which homomorphy check operations are necessary 
        /// at the operation of the given position within the scheduled search plan
        /// and appends them.
        /// </summary>
        public void DetermineAndAppendHomomorphyChecks(ScheduledSearchPlan ssp, int j)
        {
            // take care of global homomorphy
            FillInGlobalHomomorphyPatternElements(ssp, j);
        
            ///////////////////////////////////////////////////////////////////////////
            // first handle special case pure homomorphy

            SearchPlanNode spn_j = (SearchPlanNode)ssp.Operations[j].Element;

            if(spn_j.ElementID == -1)
            {
                // inlined from independent for better matching, independent from the rest
                return;
            }

            bool homToAll = true;
            if(spn_j.NodeType == PlanNodeType.Node)
            {
                for(int i = 0; i < ssp.PatternGraph.nodesPlusInlined.Length; ++i)
                {
                    if(!ssp.PatternGraph.homomorphicNodes[spn_j.ElementID - 1, i])
                    {
                        homToAll = false;
                        break;
                    }
                }
            }
            else //(spn_j.NodeType == PlanNodeType.Edge)
            {
                for(int i = 0; i < ssp.PatternGraph.edgesPlusInlined.Length; ++i)
                {
                    if(!ssp.PatternGraph.homomorphicEdges[spn_j.ElementID - 1, i])
                    {
                        homToAll = false;
                        break;
                    }
                }
            }

            if(homToAll)
            {
                // operation is allowed to be homomorph with everything
                // no checks for isomorphy or restricted homomorphy needed at all
                return;
            }

            ///////////////////////////////////////////////////////////////////////////
            // no pure homomorphy, so we have restricted homomorphy or isomorphy
            // and need to inspect the operations before, together with the homomorphy matrix 
            // for determining the necessary homomorphy checks

            GrGenType[] types;
            bool[,] hom;

            if(spn_j.NodeType == PlanNodeType.Node) {
                types = model.NodeModel.Types;
                hom = ssp.PatternGraph.homomorphicNodes;
            }
            else { // (spn_j.NodeType == PlanNodeType.Edge)
                types = model.EdgeModel.Types;
                hom = ssp.PatternGraph.homomorphicEdges;
            }

            // order operation to check against all elements it's not allowed to be homomorph to

            // iterate through the operations before our position
            bool homomorphyPossibleAndAllowed = false;
            for(int i = 0; i < j; ++i)
            {
                // only check operations computing nodes or edges
                if(ssp.Operations[i].Type == SearchOperationType.Condition
                    || ssp.Operations[i].Type == SearchOperationType.NegativePattern
                    || ssp.Operations[i].Type == SearchOperationType.IndependentPattern
                    || ssp.Operations[i].Type == SearchOperationType.Assign
                    || ssp.Operations[i].Type == SearchOperationType.AssignVar
                    || ssp.Operations[i].Type == SearchOperationType.DefToBeYieldedTo
                    || ssp.Operations[i].Type == SearchOperationType.InlinedIndependentCheckForDuplicateMatch)
                {
                    continue;
                }

                SearchPlanNode spn_i = (SearchPlanNode)ssp.Operations[i].Element;

                // don't compare nodes with edges
                if(spn_i.NodeType != spn_j.NodeType)
                {
                    continue;
                }

                if(spn_i.ElementID == -1)
                {
                    // inlined from independent for better matching, independent from the rest
                    continue;
                }
                
                // find out whether element types are disjoint
                GrGenType type_i = types[spn_i.PatternElement.TypeID];
                GrGenType type_j = types[spn_j.PatternElement.TypeID];
                bool disjoint = true;
                foreach(GrGenType subtype_i in type_i.SubOrSameTypes)
                {
                    if(type_j.IsA(subtype_i) || subtype_i.IsA(type_j)) // IsA==IsSuperTypeOrSameType
                    {
                        disjoint = false;
                        break;
                    }
                }

                // don't check elements if their types are disjoint
                if(disjoint)
                {
                    continue;
                }

                // at this position we found out that spn_i and spn_j 
                // might get matched to the same host graph element, i.e. homomorphy is possible
                
                // if that's ok we don't need to insert checks to prevent this from happening
                if(hom[spn_i.ElementID - 1, spn_j.ElementID - 1])
                {
                    homomorphyPossibleAndAllowed = true;
                    continue;
                }

                // otherwise the generated matcher code has to check 
                // that pattern element j doesn't get bound to the same graph element
                // the pattern element i is already bound to 
                if(ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst == null) {
                    ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst = new List<SearchPlanNode>();
                }
                ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst.Add(spn_i);

                // if spn_j might get matched to the same host graph element as spn_i and this is not allowed
                // make spn_i set the is-matched-bit so that spn_j can detect this situation
                ssp.Operations[i].Isomorphy.SetIsMatchedBit = true;
            }

            // only if elements, the operation must be isomorph to, were matched before
            // (otherwise there were only elements, the operation is allowed to be homomorph to,
            //  matched before, so no check needed here)
            if(ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst != null
                && ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst.Count > 0)
            {
                // order operation to check whether the is-matched-bit is set
                ssp.Operations[j].Isomorphy.CheckIsMatchedBit = true;
            }

            // if no check for isomorphy was skipped due to homomorphy being allowed
            // pure isomorphy is to be guaranteed - simply check the is-matched-bit and be done
            // the pattern elements to check against are only needed 
            // if spn_j is allowed to be homomorph to some elements but must be isomorph to some others
            if(ssp.Operations[j].Isomorphy.CheckIsMatchedBit && !homomorphyPossibleAndAllowed)
            {
                ssp.Operations[j].Isomorphy.PatternElementsToCheckAgainst = null;
            }
        }
        /// <summary>
        /// Appends homomorphy information to each operation of the scheduled search plan
        /// </summary>
        public void AppendHomomorphyInformation(ScheduledSearchPlan ssp)
        {
            // no operation -> nothing which could be homomorph
            if(ssp.Operations.Length == 0)
            {
                return;
            }

            // iterate operations of the search plan to append homomorphy checks
            for(int i = 0; i < ssp.Operations.Length; ++i)
            {
                if(ssp.Operations[i].Type == SearchOperationType.Condition
                    || ssp.Operations[i].Type == SearchOperationType.AssignVar
                    || ssp.Operations[i].Type == SearchOperationType.DefToBeYieldedTo
                    || ssp.Operations[i].Type == SearchOperationType.InlinedIndependentCheckForDuplicateMatch)
                {
                    continue;
                }

                if(ssp.Operations[i].Type == SearchOperationType.NegativePattern)
                {
                    AppendHomomorphyInformation((ScheduledSearchPlan)ssp.Operations[i].Element);
                    continue;
                }

                if(ssp.Operations[i].Type == SearchOperationType.IndependentPattern)
                {
                    AppendHomomorphyInformation((ScheduledSearchPlan)ssp.Operations[i].Element);
                    continue;
                }

                DetermineAndAppendHomomorphyChecks(ssp, i);
            }
        }
        private void DumpScheduledSearchPlan(ScheduledSearchPlan ssp, String dumpname)
        {
            StreamWriter sw = new StreamWriter(dumpname + "-scheduledsp.txt", false);
            SourceBuilder sb = new SourceBuilder();
            ssp.Explain(sb, model);
            sb.Append("\n");
            sw.WriteLine(sb.ToString());
            sw.Close();

/*            StreamWriter sw = new StreamWriter(dumpname + "-scheduledsp.vcg", false);

            sw.WriteLine("graph:{\ninfoname 1: \"Attributes\"\ndisplay_edge_labels: no\nport_sharing: no\nsplines: no\n"
                + "\nmanhattan_edges: no\nsmanhattan_edges: no\norientation: bottom_to_top\nedges: yes\nnodes: yes\nclassname 1: \"normal\"");
            sw.WriteLine("node:{title:\"root\" label:\"ROOT\"}\n");
            SearchPlanNode root = new SearchPlanNode("root");

            sw.WriteLine("graph:{{title:\"pattern\" label:\"{0}\" status:clustered color:lightgrey", dumpname);

            foreach(SearchOperation op in ssp.Operations)
            {
                switch(op.Type)
                {
                    case SearchOperationType.Lookup:
                    case SearchOperationType.Incoming:
                    case SearchOperationType.Outgoing:
                    case SearchOperationType.ImplicitSource:
                    case SearchOperationType.ImplicitTarget:
                    {
                        SearchPlanNode spnode = (SearchPlanNode) op.Element;
                        DumpNode(sw, spnode);
                        SearchPlanNode src;
                        switch(op.Type)
                        {
                            case SearchOperationType.Lookup:
                            case SearchOperationType.ActionPreset:
                            case SearchOperationType.NegPreset:
                                src = root;
                                break;
                            default:
                                src = op.SourceSPNode;
                                break;
                        }
                        DumpEdge(sw, op.Type, src, spnode, op.CostToEnd, false);
                        break;
                    }
                    case SearchOperationType.Condition:
                        sw.WriteLine("node:{title:\"Condition\" label:\"CONDITION\"}\n");
                        break;
                    case SearchOperationType.NegativePattern:
                        sw.WriteLine("node:{title:\"NAC\" label:\"NAC\"}\n");
                        break;
                }
            }*/
        }
 /// <summary>
 /// Creates an interpretation plan builder for the given scheduled search plan.
 /// Only a limited amount of search operations is supported, the ones needed for isomorphy checking.
 /// </summary>
 /// <param name="ssp">the scheduled search plan to build an interpretation plan for</param>
 /// <param name="spg">the search plan graph for determining the pattern element needed for attribute checking</param>
 /// <param name="model">the model over which the patterns are to be searched</param>
 public InterpretationPlanBuilder(ScheduledSearchPlan ssp, SearchPlanGraph spg, IGraphModel model)
 {
     this.ssp = ssp;
     this.spg = spg;
     this.model = model;
 }