/// <summary>
        /// Entry point to the detection process. Provided with a <see cref="Match"/>
        /// configured with the information about the request.
        /// </summary>
        /// <remarks>
        /// The dataSet may be used by other threads in parallel and is not assumed to be used
        /// by only one detection process at a time.
        /// </remarks>
        /// <param name="match">
        /// The match object to be updated.
        /// </param>
        /// <remarks>
        /// The memory implementation of the data set will always perform fastest but does
        /// consume more memory. 
        /// </remarks>
        internal static void Match(
            Match match)
        {
            if (match.DataSet.Disposed)
                throw new InvalidOperationException(
                    "Data Set has been disposed and can't be used for match");

            // If the user agent is too short then don't try to match and 
            // return defaults.
            if (match.TargetUserAgentArray.Length == 0 ||
                match.TargetUserAgentArray.Length < match.DataSet.MinUserAgentLength)
            {
                // Set the default values.
                MatchDefault(match);
            }
            else
            {
                // Starting at the far right evaluate the nodes in the data
                // set recording matched nodes. Continue until all character
                // positions have been checked.
                Evaluate(match);

                // Can a precise match be found based on the nodes?
                var signatureIndex = match.GetExactSignatureIndex();

                if (signatureIndex >= 0)
                {
                    // Yes a precise match was found.
                    match._signature = match.DataSet.Signatures[signatureIndex];
                    match._method = MatchMethods.Exact;
                    match.LowestScore = 0;
                }
                else
                {
                    // No. So find any other nodes that match if numeric differences
                    // are considered.
                    EvaluateNumeric(match);

                    // Can a precise match be found based on the nodes?
                    signatureIndex = match.GetExactSignatureIndex();

                    if (signatureIndex >= 0)
                    {
                        // Yes a precise match was found.
                        match._signature = match.DataSet.Signatures[signatureIndex];
                        match._method = MatchMethods.Numeric;
                    }
                    else if (match.Nodes.Count > 0)
                    {
                        // Look for the closest signatures to the nodes found.
                        var closestSignatures = match.GetClosestSignatures();

#if DEBUG
                        // Validate the list is in ascending order of ranked signature index.
                        var enumerator = closestSignatures.GetEnumerator();
                        var lastIndex = -1;
                        while (enumerator.MoveNext())
                        {
                            Debug.Assert(lastIndex < enumerator.Current);
                            lastIndex = enumerator.Current;
                        }
#endif

                        // Try finding a signature with identical nodes just not in exactly the 
                        // same place.
                        _nearest.EvaluateSignatures(match, closestSignatures.Select(i =>
                            match.DataSet.Signatures[match.DataSet.RankedSignatureIndexes[i].Value]));

                        if (match._signature != null)
                        {
                            // All the sub strings matched, just in different character positions.
                            match._method = MatchMethods.Nearest;
                        }
                        else
                        {
                            // Find the closest signatures and compare them
                            // to the target looking at the smallest character
                            // difference.
                            _closest.EvaluateSignatures(match, closestSignatures.Select(i =>
                                match.DataSet.Signatures[match.DataSet.RankedSignatureIndexes[i].Value]));
                            match._method = MatchMethods.Closest;
                        }
                    }
                }

                // If there still isn't a signature then set the default.
                if (match._profiles == null &&
                    match._signature == null)
                    MatchDefault(match);
            }
        }