docs: private Clustering<Model> doRun(Relation<ParameterizationFunction> relation, FiniteProgress progress) {
      Clustering<Model> res = new Clustering<>("CASH clustering", "cash-clustering");
      final int dim = dimensionality(relation);
  
      // init heap
      ObjectHeap<CASHInterval> heap = new ComparatorMaxHeap<>(new Comparator<CASHInterval>() {
        @Override
        public int compare(CASHInterval o1, CASHInterval o2) {
          return Integer.compare(o1.priority(), o2.priority());
        }
      });
      ModifiableDBIDs noiseIDs = DBIDUtil.newHashSet(relation.getDBIDs());
      initHeap(heap, relation, dim, noiseIDs);
  
      if(LOG.isVerbose()) {
        LOG.verbose(new StringBuilder().append("dim ").append(dim).append(" database.size ").append(relation.size()).toString());
      }
  
      // get the ''best'' d-dimensional intervals at max level
      while(!heap.isEmpty()) {
        CASHInterval interval = determineNextIntervalAtMaxLevel(heap);
        if(LOG.isVerbose()) {
          LOG.verbose("next interval in dim " + dim + ": " + interval);
        }
  
        // only noise left
        if(interval == null) {
          break;
        }
  
        // do a dim-1 dimensional run
        ModifiableDBIDs clusterIDs = DBIDUtil.newHashSet();
        if(dim > minDim + 1) {
          ModifiableDBIDs ids;
          double[][] basis_dim_minus_1;
          if(adjust) {
            ids = DBIDUtil.newHashSet();
            basis_dim_minus_1 = runDerivator(relation, dim, interval, ids);
          }
          else {
            ids = interval.getIDs();
            basis_dim_minus_1 = determineBasis(SpatialUtil.centroid(interval));
          }
  
          if(ids.size() != 0) {
            MaterializedRelation<ParameterizationFunction> db = buildDB(dim, basis_dim_minus_1, ids, relation);
            // add result of dim-1 to this result
            Clustering<Model> res_dim_minus_1 = doRun(db, progress);
            for(Cluster<Model> cluster : res_dim_minus_1.getAllClusters()) {
              res.addToplevelCluster(cluster);
              noiseIDs.removeDBIDs(cluster.getIDs());
              clusterIDs.addDBIDs(cluster.getIDs());
              processedIDs.addDBIDs(cluster.getIDs());
            }
          }
        }
        // dim == minDim
        else {
          LinearEquationSystem les = runDerivator(relation, dim - 1, interval.getIDs());
          Cluster<Model> c = new Cluster<Model>(interval.getIDs(), new LinearEquationModel(les));
          res.addToplevelCluster(c);
          noiseIDs.removeDBIDs(interval.getIDs());
          clusterIDs.addDBIDs(interval.getIDs());
          processedIDs.addDBIDs(interval.getIDs());
        }
  
        // Rebuild heap
        ArrayList<CASHInterval> heapVector = new ArrayList<>(heap.size());
        for(ObjectHeap.UnsortedIter<CASHInterval> iter = heap.unsortedIter(); iter.valid(); iter.advance()) {
          heapVector.add(iter.get());
        }
        heap.clear();
        for(CASHInterval currentInterval : heapVector) {
          currentInterval.removeIDs(clusterIDs);
          if(currentInterval.getIDs().size() >= minPts) {
            heap.add(currentInterval);
          }
        }
  
        if(progress != null) {
          progress.setProcessed(processedIDs.size(), LOG);
        }
      }
  
      // put noise to clusters
      if(!noiseIDs.isEmpty()) {
        if(dim == noiseDim) {
          res.addToplevelCluster(new Cluster<Model>(noiseIDs, true, ClusterModel.CLUSTER));
          processedIDs.addDBIDs(noiseIDs);
        }
        else if(noiseIDs.size() >= minPts) {
          LinearEquationSystem les = runDerivator(fulldatabase, dim - 1, noiseIDs);
          res.addToplevelCluster(new Cluster<Model>(noiseIDs, true, new LinearEquationModel(les)));
          processedIDs.addDBIDs(noiseIDs);
        }
      }
  
      if(LOG.isDebugging()) {
        StringBuilder msg = new StringBuilder();
        msg.append("noise fuer dim ").append(dim).append(": ").append(noiseIDs.size());
  
        for(Cluster<Model> c : res.getAllClusters()) {
          if(c.getModel() instanceof LinearEquationModel) {
            msg.append("\n Cluster: Dim: ").append(((LinearEquationModel) c.getModel()).getLes().subspacedim());
          }
          else {
            msg.append("\n Cluster: ").append(c.getModel().getClass().getName());
          }
          msg.append(" size: ").append(c.size());
        }
        LOG.debugFine(msg.toString());
      }
  
      if(progress != null) {
        progress.setProcessed(processedIDs.size(), LOG);
      }
      return res;
    }