public IPoints RunClusterAlgo(ClusterInfo clusterInfo)
        {
            // Skip points outside the grid
            IPoints filtered = clusterInfo.IsFilterData ? FilterDataset(Dataset, Grid) : Dataset;

            // Put points in buckets
            foreach (var p in filtered.Data)
            {
                var idxy = GetPointMappedIds(p, Grid, DeltaX, DeltaY);
                var idx = idxy[0];
                var idy = idxy[1];

                // Bucket id
                var id = GetId(idx, idy);

                // Bucket exists, add point
                if (BucketsLookup.ContainsKey(id))
                {
                    BucketsLookup[id].Points.Add(p);
                }
                // New bucket, create and add point
                else
                {
                    var bucket = new Bucket(idx, idy, id);
                    bucket.Points.Add(p);
                    BucketsLookup.Add(id, bucket);
                }
            }

            // Calculate centroid for all buckets
            SetCentroidForAllBuckets(BucketsLookup.Values);

            // Merge if gridpoint is to close
            if (AlgoConfig.DoMergeGridIfCentroidsAreCloseToEachOther) MergeClustersGrid();

            if (AlgoConfig.DoUpdateAllCentroidsToNearestContainingPoint) UpdateAllCentroidsToNearestContainingPoint();

            // Check again
            // Merge if gridpoint is to close
            if (AlgoConfig.DoMergeGridIfCentroidsAreCloseToEachOther
                && AlgoConfig.DoUpdateAllCentroidsToNearestContainingPoint)
            {
                MergeClustersGrid();
                // And again set centroid to closest point in bucket 
                UpdateAllCentroidsToNearestContainingPoint();
            }

            return GetClusterResult(Grid);
        }
        // Update centroid location to nearest point,
        // e.g. if you want to show cluster point on a real existing point area
        // O(n)
        public void UpdateCentroidToNearestContainingPoint(Bucket bucket)
        {
            if (bucket == null || bucket.Centroid == null ||
                bucket.Points == null || bucket.Points.Count == 0)
            {
                return;
            }

            var closest = GetClosestPoint(bucket.Centroid, bucket.Points);
            bucket.Centroid.X = closest.X; // no normalize, points are already normalized by default
            bucket.Centroid.Y = closest.Y;
        }