docs: public double evaluateClustering(Database db, Relation<? extends NumberVector> rel, Clustering<?> c) {
      List<? extends Cluster<?>> clusters = c.getAllClusters();
      NumberVector[] centroids = new NumberVector[clusters.size()];
      int ignorednoise = EvaluateSimplifiedSilhouette.centroids(rel, clusters, centroids, noiseHandling);
  
      // Build global centroid and cluster count:
      final int dim = RelationUtil.dimensionality(rel);
      Centroid overallCentroid = new Centroid(dim);
      EvaluateVarianceRatioCriteria.globalCentroid(overallCentroid, rel, clusters, centroids, noiseHandling);
  
      // Maximum distance between centroids:
      double max = 0;
      for(int i = 0; i < centroids.length; i++) {
        if(centroids[i] == null && noiseHandling != NoiseHandling.TREAT_NOISE_AS_SINGLETONS) {
          continue;
        }
        for(int j = i + 1; j < centroids.length; j++) {
          if(centroids[j] == null && noiseHandling != NoiseHandling.TREAT_NOISE_AS_SINGLETONS) {
            continue;
          }
          if(centroids[i] == null && centroids[j] == null) {
            // Need to compute pairwise distances of noise clusters.
            for(DBIDIter iti = clusters.get(i).getIDs().iter(); iti.valid(); iti.advance()) {
              for(DBIDIter itj = clusters.get(j).getIDs().iter(); itj.valid(); itj.advance()) {
                double dist = distanceFunction.distance(rel.get(iti), rel.get(itj));
                max = dist > max ? dist : max;
              }
            }
          }
          else if(centroids[i] == null) {
            for(DBIDIter iti = clusters.get(i).getIDs().iter(); iti.valid(); iti.advance()) {
              double dist = distanceFunction.distance(rel.get(iti), centroids[j]);
              max = dist > max ? dist : max;
            }
          }
          else if(centroids[j] == null) {
            for(DBIDIter itj = clusters.get(j).getIDs().iter(); itj.valid(); itj.advance()) {
              double dist = distanceFunction.distance(centroids[i], rel.get(itj));
              max = dist > max ? dist : max;
            }
          }
          else {
            double dist = distanceFunction.distance(centroids[i], centroids[j]);
            max = dist > max ? dist : max;
          }
        }
      }
  
      // a: Distance to own centroid
      // b: Distance to overall centroid
      double a = 0, b = 0;
      Iterator<? extends Cluster<?>> ci = clusters.iterator();
      for(int i = 0; ci.hasNext(); i++) {
        Cluster<?> cluster = ci.next();
        if(cluster.size() <= 1 || cluster.isNoise()) {
          switch(noiseHandling){
          case IGNORE_NOISE:
            continue; // Ignored
          case TREAT_NOISE_AS_SINGLETONS:
            // Singletons: a = 0 by definition.
            for(DBIDIter it = cluster.getIDs().iter(); it.valid(); it.advance()) {
              b += SquaredEuclideanDistanceFunction.STATIC.distance(overallCentroid, rel.get(it));
            }
            continue; // with NEXT cluster.
          case MERGE_NOISE:
            break; // Treat like a cluster below:
          }
        }
  
        for(DBIDIter it = cluster.getIDs().iter(); it.valid(); it.advance()) {
          NumberVector obj = rel.get(it);
          a += distanceFunction.distance(centroids[i], obj);
          b += distanceFunction.distance(overallCentroid, obj);
        }
      }
  
      final double pbm = FastMath.pow((1. / centroids.length) * (b / a) * max, 2.);
  
      if(LOG.isStatistics()) {
        LOG.statistics(new StringStatistic(key + ".pbm.noise-handling", noiseHandling.toString()));
        if(ignorednoise > 0) {
          LOG.statistics(new LongStatistic(key + ".pbm.ignored", ignorednoise));
        }
        LOG.statistics(new DoubleStatistic(key + ".pbm", pbm));
      }
  
      EvaluationResult ev = EvaluationResult.findOrCreate(db.getHierarchy(), c, "Internal Clustering Evaluation", "internal evaluation");
      MeasurementGroup g = ev.findOrCreateGroup("Distance-based Evaluation");
      g.addMeasure("PBM-Index", pbm, 0., Double.POSITIVE_INFINITY, 0., false);
      db.getHierarchy().resultChanged(ev);
      return pbm;
    }