예제 #1
0
    /// <summary>
    /// Gets the lowest positive integer which when combined with a secret key has an MD5 hash whose
    /// hexadecimal representation starts with the specified number of zeroes.
    /// </summary>
    /// <param name="secretKey">The secret key to use.</param>
    /// <param name="zeroes">The number of zeroes to get the value for.</param>
    /// <returns>
    /// A <see cref="Task{TResult}"/> that represents the asynchronous operation to find the
    /// lowest positive integer that generates an MD5 hash with the number of zeroes specified.
    /// </returns>
    internal static async Task <int> GetLowestPositiveNumberWithStartingZeroesAsync(string secretKey, int zeroes)
    {
        var solutions      = new ConcurrentBag <int>();
        var searchedRanges = new ConcurrentBag <int>();

        int fromInclusive = 1;
        int rangeSize     = 50000;

        var chunks = Enumerable.Chunk(Enumerable.Range(fromInclusive, int.MaxValue - 1), rangeSize);

        using var cts = new CancellationTokenSource();

        try
        {
            await Parallel.ForEachAsync(
                chunks,
                cts.Token,
                (range, cancellationToken) =>
            {
                try
                {
                    // Does this range start at a value greater than an already found value?
                    if (!solutions.IsEmpty)
                    {
                        int bestSolution = solutions.Min();

                        if (range[0] > bestSolution)
                        {
                            var orderedRanges = searchedRanges.ToList();

                            if (orderedRanges.Count == 0)
                            {
                                return(ValueTask.CompletedTask);
                            }

                            orderedRanges.Sort();

                            // Have we searched the first possible range already?
                            if (orderedRanges[0] == fromInclusive)
                            {
                                for (int i = 1; i < orderedRanges.Count; i++)
                                {
                                    int lastRange = orderedRanges[i - 1];
                                    int thisRange = orderedRanges[i];

                                    // Is this range the next range?
                                    if (thisRange != lastRange + rangeSize)
                                    {
                                        // A range before the current best solution has not been searched yet
                                        break;
                                    }

                                    if (thisRange > bestSolution)
                                    {
                                        // We have found the best solution
                                        cts.Cancel();
                                    }
                                }
                            }
                        }

                        return(ValueTask.CompletedTask);
                    }

                    foreach (int i in range)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }

                        if (IsSolution(i, secretKey, zeroes))
                        {
                            solutions.Add(i);
                            break;
                        }
                    }
                }
                finally
                {
                    searchedRanges.Add(range[0]);
                }

                return(ValueTask.CompletedTask);
            });
        }
        catch (TaskCanceledException)
        {
            // Solution found
        }

        if (solutions.IsEmpty)
        {
            throw new PuzzleException("No answer was found for the specified secret key.");
        }

        return(solutions.Min());
    }