docs: public Clustering<CorrelationModel> run(Database database, Relation<V> relation) {
      final int dim = RelationUtil.dimensionality(relation);
  
      StepProgress stepprog = LOG.isVerbose() ? new StepProgress(3) : null;
  
      // Run Generalized DBSCAN
      LOG.beginStep(stepprog, 1, "Preprocessing local correlation dimensionalities and partitioning data");
      // FIXME: how to ensure we are running on the same relation?
      ERiCNeighborPredicate<V>.Instance npred = new ERiCNeighborPredicate<V>(settings).instantiate(database, relation);
      CorePredicate.Instance<DBIDs> cpred = new MinPtsCorePredicate(settings.minpts).instantiate(database);
      Clustering<Model> copacResult = new GeneralizedDBSCAN.Instance<>(npred, cpred, false).run();
  
      // extract correlation clusters
      LOG.beginStep(stepprog, 2, "Extract correlation clusters");
      List<List<Cluster<CorrelationModel>>> clusterMap = extractCorrelationClusters(copacResult, relation, dim, npred);
      if(LOG.isDebugging()) {
        StringBuilder msg = new StringBuilder("Step 2: Extract correlation clusters...");
        for(int corrDim = 0; corrDim < clusterMap.size(); corrDim++) {
          List<Cluster<CorrelationModel>> correlationClusters = clusterMap.get(corrDim);
          msg.append("\n\ncorrDim ").append(corrDim);
          for(Cluster<CorrelationModel> cluster : correlationClusters) {
            msg.append("\n  cluster ").append(cluster).append(", ids: ").append(cluster.getIDs().size());
            // .append(", level: ").append(cluster.getLevel()).append(", index:
            // ").append(cluster.getLevelIndex());
            // msg.append("\n basis " +
            // cluster.getPCA().getWeakEigenvectors().toString(" ", NF) +
            // " ids " + cluster.getIDs().size());
          }
        }
        LOG.debugFine(msg.toString());
      }
      if(LOG.isVerbose()) {
        int clusters = 0;
        for(List<Cluster<CorrelationModel>> correlationClusters : clusterMap) {
          clusters += correlationClusters.size();
        }
        LOG.verbose(clusters + " clusters extracted.");
      }
  
      // build hierarchy
      LOG.beginStep(stepprog, 3, "Building hierarchy");
      Clustering<CorrelationModel> clustering = new Clustering<>("ERiC clustering", "eric-clustering");
      buildHierarchy(clustering, clusterMap, npred);
      if(LOG.isDebugging()) {
        StringBuilder msg = new StringBuilder("Step 3: Build hierarchy");
        for(int corrDim = 0; corrDim < clusterMap.size(); corrDim++) {
          List<Cluster<CorrelationModel>> correlationClusters = clusterMap.get(corrDim);
          for(Cluster<CorrelationModel> cluster : correlationClusters) {
            msg.append("\n  cluster ").append(cluster).append(", ids: ").append(cluster.getIDs().size());
            // .append(", level: ").append(cluster.getLevel()).append(", index:
            // ").append(cluster.getLevelIndex());
            for(It<Cluster<CorrelationModel>> iter = clustering.getClusterHierarchy().iterParents(cluster); iter.valid(); iter.advance()) {
              msg.append("\n   parent ").append(iter.get());
            }
            for(It<Cluster<CorrelationModel>> iter = clustering.getClusterHierarchy().iterChildren(cluster); iter.valid(); iter.advance()) {
              msg.append("\n   child ").append(iter.get());
            }
          }
        }
        LOG.debugFine(msg.toString());
      }
      LOG.setCompleted(stepprog);
  
      for(Cluster<CorrelationModel> rc : clusterMap.get(clusterMap.size() - 1)) {
        clustering.addToplevelCluster(rc);
      }
      return clustering;
    }