public IEnumerable <RequestSequence> Generate(List <KnownEndpoint> endpoints, RequestSequence sequence, List <TokenCollection> sequenceResults)
        {
            // Only build off sequences that have an actual result.
            if (sequence.GetLastResponse() == null || (int)sequence.GetLastResponse().Status < 300)
            {
                // Search for an endpoint that we can append to the end of this request sequence.
                foreach (KnownEndpoint endpoint in endpoints)
                {
                    bool            foundMatch     = true;
                    TokenCollection requirements   = endpoint.InputTokens;
                    Stage           candidateStage = new Stage(endpoint.Request.Clone());

                    foreach (IToken token in requirements)
                    {
                        int    matchIndex = 0;
                        IToken?matchToken = null;

                        // Substituting a token with the same name is usually a good bet, but sometimes gets stuck if there is a false positive.
                        // Add some level of randomness to keep things working.
                        int skip = Rand.Next(0, 2);

                        if (skip >= 2 && GetRandomNameMatch(sequenceResults, token.Name, out matchToken, out matchIndex))
                        {
                            // Find a token that has the same name as this one.
                            candidateStage.Substitutions.Add(new SubstituteNamedToken(token, matchToken, matchIndex));
                        }
                        else if (skip >= 1 && GetRandomTypeMatch(sequenceResults, token.SupportedTypes, out matchToken, out matchIndex))
                        {
                            // Find a token that has the same type as this one.
                            candidateStage.Substitutions.Add(new SubstituteNamedToken(token, matchToken, matchIndex));
                        }
                        else
                        {
                            // Use the original value from the example request.
                            candidateStage.Substitutions.Add(new SubstituteConstant(token, token.Value));
                        }
                    }
                    if (foundMatch)
                    {
                        RequestSequence newSequence = sequence.Copy();
                        newSequence.Add(candidateStage);
                        yield return(newSequence);
                    }
                }
            }
        }
        public IEnumerable <RequestSequence> Generate(List <KnownEndpoint> endpoints, RequestSequence sequence, List <TokenCollection> sequenceResults)
        {
            if (dictionary.Count == 0)
            {
                yield break;
            }

            for (int i = 0; i < MaxSubstitutions; ++i)
            {
                if (sequence.StageCount() == 0)
                {
                    continue;
                }

                RequestSequence newSequence   = sequence.Copy();
                int             selectedStage = rand.Next(0, newSequence.StageCount());

                Stage stage = newSequence.Get(selectedStage);

                if (stage.Substitutions.Count == 0)
                {
                    continue;
                }

                int           subIndex = rand.Next(0, newSequence.Get(selectedStage).Substitutions.Count);
                ISubstitution sub      = newSequence.Get(selectedStage).Substitutions[subIndex];
                newSequence.Get(selectedStage).Substitutions.RemoveAt(subIndex);

                string             replacement        = dictionary[rand.Next(0, dictionary.Count)];
                SubstituteConstant substituteConstant = new SubstituteConstant(sub.GetTarget(), replacement);
                newSequence.Get(selectedStage).Substitutions.Add(substituteConstant);

                yield return(newSequence);
            }
            yield break;
        }