/// <summary>
        /// Classifies the individual record against the input and score it based on similarity rules
        /// </summary>
        /// <typeparam name="T">The type of record being classified</typeparam>
        /// <param name="input">The input being classified</param>
        /// <param name="block">The block which is being classified</param>
        /// <param name="attributes">The match attributes to classify on</param>
        /// <returns>The match classification</returns>
        /// <param name="configurationName">The name of the configuration used</param>
        /// <param name="evaluationType">The evaluation type</param>
        /// <param name="matchThreshold">The matching threshold</param>
        /// <param name="collector">The diagnostics collector to use</param>
        private IRecordMatchResult <T> ClassifyInternal <T>(T input, T block, List <MatchAttribute> attributes, string configurationName, ThresholdEvaluationType evaluationType, double matchThreshold, double nonMatchThreshold, IRecordMatchingDiagnosticSession collector = null) where T : IdentifiedData
        {
            try
            {
                collector?.LogStartAction(block);
                var attributeResult = attributes.Select(v =>
                {
                    try
                    {
                        collector?.LogStartAction(v);
                        this.m_tracer.TraceVerbose("Initializing attribute {0}", v);
                        // Initialize the weights and such for the attribute
                        var attributeScores = v.GetPropertySelectors <T>().Select(selector =>
                        {
                            Func <T, dynamic> selectorExpression = (Func <T, dynamic>)selector.Value;
                            object aValue       = selectorExpression(input),
                            bValue              = selectorExpression(block);
                            var defaultInstance = selectorExpression.Method.ReturnType.GetConstructors().Any(c => c.GetParameters().Length == 0) ?
                                                  Activator.CreateInstance(selectorExpression.Method.ReturnType) :
                                                  null;
                            var result = AssertionUtil.ExecuteAssertion(selector.Key, v.Assertion, v, aValue, bValue);
                            return(result);
                        });
                        var bestScore = attributeScores.OrderByDescending(o => o.CalculatedScore).FirstOrDefault();

                        if (bestScore == null || !bestScore.CalculatedScore.HasValue)
                        {
                            return(null);
                        }
                        else
                        {
                            var result = new MatchVector(v, v.Id ?? bestScore.PropertyName, bestScore.CalculatedScore.Value, bestScore.Evaluated, bestScore.A, bestScore.B);
                            return(result);
                        }
                    }
                    finally
                    {
                        collector?.LogEndAction();
                    }
                }).OfType <MatchVector>().ToList();

                // Throw out attributes which are dependent however the dependent attribute was unsuccessful
                // So if for example: If the scoring for CITY is only counted when STATE is successful, but STATE was
                // unsuccessful, we want to exclude CITY.
                attributeResult.RemoveAll(o => !o.Attribute.When.All(w =>
                {
                    var attScore = attributeResult.First(r => r.Attribute.Id == w.AttributeRef);
                    // TODO: Allow cascaded operators to specify a value
                    //switch (w.Operator)
                    //{
                    //    case BinaryOperatorType.NotEqual:
                    //        return attScore != w.Value;

                    //    case BinaryOperatorType.Equal:
                    //        return attScore == w.Value;

                    //    case BinaryOperatorType.GreaterThan:
                    //        return attScore > w.Value;

                    //    case BinaryOperatorType.GreaterThanOrEqual:
                    //        return attScore >= w.Value;

                    //    case BinaryOperatorType.LessThan:
                    //        return attScore < w.Value;

                    //    case BinaryOperatorType.LessThanOrEqual:
                    //        return attScore <= w.Value;

                    //    default:
                    //        throw new InvalidOperationException($"Cannot use operator {w.Operator} on when");
                    //}
                    return(attScore?.Evaluated == true && attScore?.Score > 0);
                })); // Remove all failed attributes
                var score = attributeResult.Sum(v => v.Score);

                // The attribute scores which are produced will be from SUM(NonMatchWeight) .. SUM(MatchWeight)
                double maxScore = attributeResult.Sum(o => o.Attribute.MatchWeight),
                       minScore = attributeResult.Sum(o => o.Attribute.NonMatchWeight);
                // This forms a number line between -MIN .. MAX , our probability is the distance that our score
                // is on that line, for example: -30.392 .. 30.392
                // Then the strength is 0.5 of a score of 0 , and 1.0 for a score of 30.392
                var strength = (double)(score + -minScore) / (double)(maxScore + -minScore);
                if (Double.IsNaN(strength))
                {
                    strength = 0;
                }
                RecordMatchClassification classification = RecordMatchClassification.NonMatch;
                if (evaluationType == ThresholdEvaluationType.AbsoluteScore)
                {
                    classification = score > matchThreshold ? RecordMatchClassification.Match : score <= nonMatchThreshold ? RecordMatchClassification.NonMatch : RecordMatchClassification.Probable;
                }
                else
                {
                    classification = strength > matchThreshold ? RecordMatchClassification.Match : strength <= nonMatchThreshold ? RecordMatchClassification.NonMatch : RecordMatchClassification.Probable;
                }
                var retVal = new MatchResult <T>(block, score, strength, configurationName, classification, RecordMatchMethod.Weighted, attributeResult);

                return(retVal);
            }
            catch (Exception e)
            {
                this.m_tracer.TraceError("Error classifying result set (mt={0}, nmt={1}) - {2}", matchThreshold, nonMatchThreshold, e.Message);
                throw new MatchingException($"Error classifying result set", e);
            }
            finally
            {
                collector?.LogEndAction();
            }
        }