private void PrecomputeAllDistances()
 {
     Parallel.For(0, centroidDistances.Length, i =>
     {
         for (int j = 0; j < i; j++)
         {
             // precompute distance
             centroidDistances[i][j] = WeightedCentroid.GetDistance(weightedCentroids[i], weightedCentroids[j]);
             // precompute minimal distance in a row
             if (centroidDistances[i][j] < rowMinimalDistances[i])
             {
                 rowMinimalDistances[i]   = centroidDistances[i][j];
                 rowMinimalDistanceIds[i] = j;
             }
         }
     });
 }
        private void ComputeNewDistances(int mergedCentroidId, int droppedCentroidId)
        {
            // invalidate dropped data
            rowMinimalDistances[droppedCentroidId]   = double.NaN;
            rowMinimalDistanceIds[droppedCentroidId] = -1;
            Parallel.For(0, droppedCentroidId, j =>
            {
                centroidDistances[droppedCentroidId][j] = double.NaN;
            });
            Parallel.For(droppedCentroidId + 1, centroidDistances.Length, i =>
            {
                centroidDistances[i][droppedCentroidId] = double.NaN;
            });


            // compute new distances and minimal value in mergedId row
            rowMinimalDistances[mergedCentroidId] = double.MaxValue;
            Parallel.For(0, mergedCentroidId, j =>
            {
                if (!isDropped[j])
                {
                    centroidDistances[mergedCentroidId][j]
                        = WeightedCentroid.GetDistance(weightedCentroids[j], weightedCentroids[mergedCentroidId]);

                    // update minimal distances
                    if (centroidDistances[mergedCentroidId][j] < rowMinimalDistances[mergedCentroidId])
                    {
                        rowMinimalDistances[mergedCentroidId]   = centroidDistances[mergedCentroidId][j];
                        rowMinimalDistanceIds[mergedCentroidId] = j;
                    }
                }
            });
            if (rowMinimalDistances[mergedCentroidId] == double.MaxValue)
            {
                rowMinimalDistances[mergedCentroidId] = double.NaN;
            }

            // compute new distances in mergedId column
            Parallel.For(mergedCentroidId + 1, centroidDistances.Length, i =>
            {
                if (!isDropped[i])
                {
                    centroidDistances[i][mergedCentroidId]
                        = WeightedCentroid.GetDistance(weightedCentroids[i], weightedCentroids[mergedCentroidId]);

                    // update minimal distances
                    if (centroidDistances[i][mergedCentroidId] < rowMinimalDistances[i])
                    {
                        rowMinimalDistances[i]   = centroidDistances[i][mergedCentroidId];
                        rowMinimalDistanceIds[i] = mergedCentroidId;
                    }
                }
            });


            // find new minimal value in the row if the value from dropped column was minimal
            for (int i = droppedCentroidId + 1; i < centroidDistances.Length; i++)
            {
                if (!isDropped[i] && rowMinimalDistanceIds[i] == droppedCentroidId)
                {
                    rowMinimalDistances[i]   = double.MaxValue;
                    rowMinimalDistanceIds[i] = -1;
                    for (int j = 0; j < i; j++)
                    {
                        if (!isDropped[j] && centroidDistances[i][j] < rowMinimalDistances[i])
                        {
                            rowMinimalDistances[i]   = centroidDistances[i][j];
                            rowMinimalDistanceIds[i] = j;
                        }
                    }
                    if (rowMinimalDistances[i] == double.MaxValue)
                    {
                        rowMinimalDistances[i] = double.NaN;
                    }
                }
            }
        }