/// <summary>
        /// Finds the closest pair of clusters.
        /// </summary>
        /// <param name="a">The first cluster.</param>
        /// <param name="b">The second cluster.</param>
        /// <returns>Returns false if no more clusters can be merged.</returns>
        private bool FindClosestPair(out LinkedCluster a, out LinkedCluster b)
        {
            // initialize the minimal distance
            double minDist = double.PositiveInfinity;

            a = b = null;

            // look at all clusters
            foreach (LinkedCluster f in clusters)
            {
                if (f.distanceList.Count == 0)
                {
                    continue;
                }

                // take the minimal distance
                if (f.distanceList.Keys[0] < minDist)
                {
                    // store the minimal distance
                    minDist = f.distanceList.Keys[0];
                    // store the indices of the closest pair
                    a = f;
                    b = f.distanceList.Values[0];
                }
            }

            if (a != null && b != null)
            {
                // closest pair found
                return(true);
            }
            else
            {
                // no more pairs to merge
                return(false);
            }
        }
        /// <summary>
        /// Computes the complete link clustering.
        /// </summary>
        /// <param name="vertices">The array of vertices to cluster.</param>
        /// <param name="maxClusterDiameter">The maximal allowed distance between any points in a cluster.</param>
        public int ComputeClustering(Vertex[] vertices)
        {
            this.vertices = vertices;

            // initialize every vertex as a cluster
            clusters = new List <LinkedCluster>(vertices.Length);
            for (int i = 0; i < vertices.Length; i++)
            {
                LinkedCluster f = new LinkedCluster(i);
                f.AddVertex(i);
                clusters.Add(f);
            }

            // initialize mutual distances
            for (int i = 0; i < vertices.Length - 1; i++)
            {
                for (int j = i + 1; j < vertices.Length; j++)
                {
                    double distance = vertices[i].WeightedDistance(vertices[j]);
                    if (distance <= maxClusterDiameter)
                    {
                        clusters[i].distanceList.Add(distance, clusters[j]);
                        clusters[j].distanceList.Add(distance, clusters[i]);
                    }
                }
            }

            double[,] distanceMatrix = new double[vertices.Length, vertices.Length];
            int half = vertices.Length / 2;

            // create distance matrix
            for (int i = 0; i < vertices.Length; i++)
            {
                for (int jPartial = 0; jPartial < half; jPartial++)
                {
                    // make the index start after i and rotate modulo Length
                    int j = (jPartial + i + 1) % vertices.Length;

                    // compute the distance
                    double distance = vertices[i].WeightedDistance(vertices[j]);
                    // store it to the matrix
                    if (distance <= maxClusterDiameter)
                    {
                        distanceMatrix[i, j] = distance;
                    }
                }
            }

            LinkedCluster a, b;

            // do the clustering
            while (FindClosestPair(out a, out b))
            {
                // merge b into a
                foreach (int vertexIndex in b.MemberIndices)
                {
                    a.AddVertex(vertexIndex);
                }

                // keep only a, discard b
                clusters.Remove(b);

                // remove the distance between the merged pair
                a.distanceList.RemoveAt(0);
                // update distances
                foreach (LinkedCluster f in clusters)
                {
                    // skip a and b
                    if (f == a) // || f == b	b is already removed from facilities
                    {
                        continue;
                    }

                    // find distance to both clusters of the pair
                    int indexOfA = f.distanceList.IndexOfValue(a);
                    int indexOfB = f.distanceList.IndexOfValue(b);
                    int indexOfF = a.distanceList.IndexOfValue(f);

                    if (indexOfA < 0 || indexOfB < 0)
                    {
                        // farther than the maximal allowed diameter
                        if (indexOfA >= 0)
                        {
                            f.distanceList.RemoveAt(indexOfA);
                        }
                        if (indexOfB >= 0)
                        {
                            f.distanceList.RemoveAt(indexOfB);
                        }
                        if (indexOfF >= 0)
                        {
                            a.distanceList.RemoveAt(indexOfF);
                        }
                    }
                    else
                    {
                        double newDistance = f.distanceList.Keys[indexOfA];
                        // keep the greater distance

                        // compare only indices, the array is sorted

                        if (f.distanceList.Keys[indexOfA] < f.distanceList.Keys[indexOfB])
                        {
                            // replace the key, keep the value
                            LinkedCluster value = f.distanceList.Values[indexOfA];
                            double        key   = f.distanceList.Keys[indexOfB];
                            f.distanceList.RemoveAt(indexOfB);
                            f.distanceList.Add(key, value);
                            newDistance = key;
                        }
                        // discard the other distance
                        f.distanceList.RemoveAt(indexOfB);

                        if (indexOfF >= 0)
                        {
                            // do this only if the above if passes

                            // update distance in a
                            LinkedCluster value2 = a.distanceList.Values[indexOfF];
                            a.distanceList.RemoveAt(indexOfF);
                            a.distanceList.Add(newDistance, value2);
                        }
                    }
                }
            }

            return(-1);
        }