/// <summary>
        /// Finds exactly the cluster closest to the cluster whose label matches color1 and the points
        /// in each cluster that are closest, along with the square distance between them.
        /// </summary>
        /// <param name="color1">Label of the cluster whose nearest neighbor is being sought.</param>
        /// <returns>A point in the cluster corresponding to color1, the closest point to it
        /// from another cluster, the square distance between the points, and the label of the other cluster.
        /// NOTE: ClosestPair.Color1 will equal color1.
        /// </returns>
        public ClosestPair FindClusterExhaustively(TLabel color1)
        {
            var shortest = new ClosestPair();

            foreach (var p1 in Clusters.PointsInClass(color1))
            {
                foreach (var pc in Clusters.Points()
                         .Select(p => new { Point = p, Color = Clusters.GetClassLabel(p) })
                         .Where(pc => !color1.Equals(pc.Color)))
                {
                    var d = p1.Measure(pc.Point);
                    if (d < shortest.SquareDistance)
                    {
                        shortest.SquareDistance = d;
                        shortest.Color1         = color1;
                        shortest.Point1         = p1;
                        shortest.Color2         = pc.Color;
                        shortest.Point2         = pc.Point;
                    }
                }
            }
            //TODO: If there is only one cluster, the if statement above will not be triggered and shortest will
            //      be ill-defined and cause a Null Pointer exception in Swap.
            return(shortest.Swap(color1));
        }
        /// <summary>
        /// Finds an approximately closest pair of points (one of each color) by using the ordering found in SortedPoints.
        ///
        /// This compares points in two of the clusters, ignoring points in all other clusters.
        ///
        /// NOTE: This was a good idea, but yields results too poor to be used.
        /// </summary>
        /// <param name="color1">Label of the first cluster.</param>
        /// <param name="color2">Label of the second cluster.</param>
        /// <returns>The point with Color1, the point with Color2 and the square distance between them.
        /// </returns>
        public ClosestPair FindPairApproximately(TLabel color1, TLabel color2)
        {
            var           shortest  = new ClosestPair();
            UnsignedPoint prevP     = null;
            TLabel        prevColor = default(TLabel);

            foreach (var pc in SortedPoints
                     .Select(p => new { Point = p, Color = Clusters.GetClassLabel(p) })
                     .Where(pc => pc.Color.Equals(color1) || pc.Color.Equals(color2)))
            {
                if (prevP != null && !prevColor.Equals(pc.Color))
                {
                    var d = pc.Point.Measure(prevP);
                    if (d < shortest.SquareDistance)
                    {
                        shortest.SquareDistance = d;
                        shortest.Color1         = prevColor;
                        shortest.Point1         = prevP;
                        shortest.Color2         = pc.Color;
                        shortest.Point2         = pc.Point;
                    }
                }
                prevP     = pc.Point;
                prevColor = pc.Color;
            }
            return(shortest.Swap(color1));
        }
        /// <summary>
        /// Merge the clusters containing two points if the distance separating them does not exceed MergeSquareDistance.
        /// The points given here may be HilbertPoints frmo a HilbertIndex or UnsignedPoints already present in the Classification.
        /// In case of the former, a lookup is performed based on the id to find the proper UnsignedPoint corresponding to the HilbertPoint.
        /// </summary>
        /// <returns><c>true</c>, if a new merge performed, <c>false</c> if too far to merge or already merged.</returns>
        /// <param name="p1">First point to compare.</param>
        /// <param name="p2">Second point.</param>
        /// <param name="maxSquareDistance">If a positive value, use this as the maximum distance permitted between points.
        /// Otherwise, use MergeSquareDistance.</param>
        private bool MergeIfNear(UnsignedPoint p1, UnsignedPoint p2, long maxSquareDistance = 0)
        {
            var p1InClusters = IdsToPoints[p1.UniqueId];
            var p2InClusters = IdsToPoints[p2.UniqueId];

            maxSquareDistance = (maxSquareDistance <= 0) ? MergeSquareDistance : maxSquareDistance;
            if (p1InClusters.SquareDistanceCompare(p2InClusters, maxSquareDistance) <= 0)
            {
                var c1 = Clusters.GetClassLabel(p1InClusters);
                var c2 = Clusters.GetClassLabel(p2InClusters);
                return(Clusters.Merge(c1, c2));
            }
            else
            {
                return(false);
            }
        }
        /// <summary>
        /// Merge the clusters to which the two points belong, if their sizes permit.
        ///
        /// No more than one of the clusters may have a size greater than or equal to UnmergeableSize.
        /// </summary>
        /// <param name="p1">Point belonging to first cluster to merge.</param>
        /// <param name="p2">Point belonging to second cluster to merge.</param>
        /// <param name="forceMerge">If true and UnmergeableSize is the sole obstacle to the merge, perform the merge anyways.
        /// If false, honor UnmergeableSize.</param>
        /// <returns>True if the merge was performed successfully, false otherwise.</returns>
        private bool Merge(UnsignedPoint p1, UnsignedPoint p2, bool forceMerge = false)
        {
            var category1 = Clusters.GetClassLabel(p1);
            var category2 = Clusters.GetClassLabel(p2);

            if (category1.Equals(category2))
            {
                return(false);
            }
            var size1 = Clusters.PointsInClass(category1).Count;
            var size2 = Clusters.PointsInClass(category2).Count;

            if (size1 >= UnmergeableSize && size2 >= UnmergeableSize && !forceMerge)
            {
                return(false);
            }
            return(Clusters.Merge(category1, category2));
        }