/// <summary> /// Compute the smallest possible use number that can be assigned to the expression /// </summary> /// <param name="eb"></param> /// <param name="loopVars">The set of all known loop variables</param> /// <returns></returns> public int GetUseNumber(ExpressionWithBinding eb, Set <IVariableDeclaration> loopVars) { if (containers.LoopCount > 0) { // exclude loop variables in the containers loopVars = (Set <IVariableDeclaration>)loopVars.Clone(); foreach (IStatement container in containers.inputs) { if (container is IForStatement ifs) { var loopVar = Recognizer.LoopVariable(ifs); loopVars.Remove(loopVar); } } } if (eb.Binding.Count > 0) { // collect the set of loop variables in the expression Set <IVariableDeclaration> loopVarsNotInExpression = Set <IVariableDeclaration> .FromEnumerable(loopVars); loopVarsNotInExpression.Remove(Recognizer.GetVariables(eb.Expression)); // eliminate any loop variables in the binding that are not in the expression eb.Binding = Recognizer.RemoveLoopVars(eb.Binding, loopVarsNotInExpression.Contains); if (eb.Binding == null) { return(0); // the conditions are contradictory so this use will never happen } } // if eb exactly matches an existing group key, then it cannot join any existing group Group newGroup = new Group(); newGroup.Key.Add(eb); bool mustMakeNewGroup = groups.ContainsKey(newGroup.Key); // this threshold was tuned on SpeedTests.MarkovChainUnrolledTest3 if (invertedIndex == null && !mustMakeNewGroup && groups.Count > 4) { BuildInvertedIndex(loopVars); } // collection of all GroupKeys that could be disjoint from eb ICollection <GroupKey> keysToSearch; Set <object> varsInBinding = null; if (invertedIndex == null) { if (mustMakeNewGroup) { keysToSearch = new Set <GroupKey>(); } else { // search all GroupKeys keysToSearch = groups.Keys; } } else { // construct a minimal set of GroupKeys to search keysToSearch = new Set <GroupKey>(); varsInBinding = GetVariablesAndParameters(eb.Binding, loopVars); if (!mustMakeNewGroup) { foreach (var entry in invertedIndex) { var dict = entry.Value; if (entry.Key.Equals(eb.Expression)) { // If the expression matches a previous expression, then the only way it could be disjoint // is if the condition context has some overlap with previous contexts. // Therefore we search only GroupKeys whose binding variables overlap with eb. foreach (var ivd in varsInBinding) { if (dict.TryGetValue(ivd, out List <GroupKey> keys)) { keysToSearch.AddRange(keys); } } } else { // if expression doesn't match, we conservatively search all GroupKeys foreach (var keys in dict.Values) { keysToSearch.AddRange(keys); } } } } } // find the first group in which eb is disjoint from all members of the group, and add eb to that group. // the use number becomes the index of that group. foreach (var key in keysToSearch) { var bucket = groups[key]; Group group = bucket.Peek(); bool disjointWithAll = true; foreach (ExpressionWithBinding eb2 in group.Key) { if (!Recognizer.AreDisjoint(eb.Expression, eb.Binding, eb2.Expression, eb2.Binding, loopVars.Contains)) { disjointWithAll = false; break; } } if (disjointWithAll) { group = bucket.Pop(); if (bucket.Count == 0) { groups.Remove(group.Key); } // This modifies the key of an existing group. // We must ensure that this is not a key in the groups dictionary. // This is ensured by using a Stack and setting the dictionary key to the key of the bottom group. group.Key.Add(eb); AddGroup(group, eb.Expression, varsInBinding); return(group.UseNumber); } } // no matching group found. create a new one. newGroup.UseNumber = NumberOfUses++; AddGroup(newGroup, eb.Expression, varsInBinding); return(newGroup.UseNumber); }