/// <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);
            }