/// <summary>
        /// Counts up the ForegroundColor from each entry in _rowResults, then
        /// combines similar colors to allow for things like antialiasing.
        /// Candidate colors are evaluated, beginning at the highest contrast
        /// from backgroundColor, until we find a candidate that seems to occur
        /// frequently enough that we accept it as the foreground color.
        /// </summary>
        private ColorVoteInfo GetForegroundInfo(Color backgroundColor)
        {
            CountMap <Color> simpleForegroundColorBallots = new CountMap <Color>();

            _rowResults.ForEach(r =>
            {
                Color foregroundColor     = r.ForegroundColor;
                Color testBackgroundColor = r.BackgroundColor;
                if (foregroundColor != null && backgroundColor.Equals(testBackgroundColor))
                {
                    simpleForegroundColorBallots.Increment(r.ForegroundColor, r.TransitionCount);
                }
            });

            if (!simpleForegroundColorBallots.Any())
            {
                return(null);
            }

            var simpleForegroundBallotsSortedByContrast = simpleForegroundColorBallots.OrderByDescending(x =>
            {
                ColorPair cp = new ColorPair(backgroundColor, x.Key);
                return(cp.ColorContrast());
            });

            CountMap <Color> aggregatedForegroundColorBallots = new CountMap <Color>();

            foreach (var pair in simpleForegroundBallotsSortedByContrast)
            {
                // Remember counts so we can add them after we iterate the entire list
                CountMap <Color> rememberedCounts = new CountMap <Color>();

                foreach (var aggregateForegroundColorBallot in aggregatedForegroundColorBallots)
                {
                    if (aggregateForegroundColorBallot.Key.IsSimilarColor(pair.Key))
                    {
                        rememberedCounts
                        .Increment(aggregateForegroundColorBallot.Key, pair.Value);
                    }
                }

                foreach (var countToAdd in rememberedCounts)
                {
                    aggregatedForegroundColorBallots
                    .Increment(countToAdd.Key, countToAdd.Value);
                }

                aggregatedForegroundColorBallots.Increment(pair.Key, pair.Value);
            }

            var sortedForegroundColorBallots = aggregatedForegroundColorBallots
                                               .OrderByDescending(x => x.Value);

            return(BuildColorVoteInfo(sortedForegroundColorBallots, GetTotalVoteCountFromAllBallots));
        }
        /// <summary>
        /// Counts up the BackgroundColor from each entry in _rowResults, then
        /// selects the most frequent BackgroundColor as the overall BackgroundColor.
        /// </summary>
        private ColorVoteInfo GetBackgroundInfo()
        {
            CountMap <Color> backgroundColorBallots = new CountMap <Color>();

            _rowResults.ForEach(r =>
            {
                Color backgroundColor = r.BackgroundColor;
                if (backgroundColor != null)
                {
                    backgroundColorBallots.Increment(r.BackgroundColor);
                }
            });

            var sortedBackgroundColorBallots = backgroundColorBallots.OrderByDescending(x => x.Value);

            return(BuildColorVoteInfo(sortedBackgroundColorBallots, GetTotalVoteCountFromAllBallots));
        }
        // Returns true when entries have lead to a confident conclusion about Text and Background color.

        internal ColorContrastResult OnRowEnd()
        {
            ColorContrastResult result = new ColorContrastResult();

            CountMap <ColorPair> pairsWithSimilarTextColor = new CountMap <ColorPair>();

            foreach (var exactPairOuter in countExactPairs)
            {
                foreach (var exactPairInner in countExactPairs)
                {
                    if (exactPairOuter.Key.backgroundColor.Equals(exactPairInner.Key.backgroundColor))
                    {
                        if (exactPairOuter.Key.foregroundColor.IsSimilarColor(exactPairInner.Key.foregroundColor))
                        {
                            pairsWithSimilarTextColor.Increment(exactPairOuter.Key, exactPairInner.Value);
                        }
                    }
                }
            }

            var sortedByValueAndContrast = pairsWithSimilarTextColor.OrderByDescending(x => x.Value)
                                           .ThenByDescending(x => x.Key.ColorContrast());

            if (!sortedByValueAndContrast.Any())
            {
                return(result);
            }

            var resultPairs = new HashSet <ColorPair>();

            var firstEntryCount = sortedByValueAndContrast.First().Value;

            if (firstEntryCount < ColorContrastConfig.MinNumberColorTransitions)
            {
                return(result);
            }

            var firstEntryCountAdjusted = firstEntryCount / ColorContrastConfig.TransitionCountDominanceFactor;

            foreach (var entry in sortedByValueAndContrast)
            {
                // Only Collect Pairs that have a reasonable occurence count.
                if (entry.Value < firstEntryCountAdjusted)
                {
                    break;
                }

                resultPairs.Add(entry.Key);
            }

            foreach (var colorPair in resultPairs)
            {
                result.Add(colorPair);
            }

            countExactColors.Clear();

            openTransitions.Clear();

            return(result);
        }