docs: public RetrievalPerformanceResult run(Database database, Relation<O> relation, Relation<?> lrelation) {
      final DistanceQuery<O> distQuery = database.getDistanceQuery(relation, getDistanceFunction());
      final DBIDs ids = DBIDUtil.randomSample(relation.getDBIDs(), sampling, random);
  
      // For storing the positive neighbors.
      ModifiableDBIDs posn = DBIDUtil.newHashSet();
      // Distance storage.
      ModifiableDoubleDBIDList nlist = DBIDUtil.newDistanceDBIDList(relation.size());
  
      // For counting labels seen in kNN
      Object2IntOpenHashMap<Object> counters = new Object2IntOpenHashMap<>();
  
      // Statistics tracking
      double map = 0., mroc = 0.;
      double[] knnperf = new double[maxk];
      int samples = 0;
  
      FiniteProgress objloop = LOG.isVerbose() ? new FiniteProgress("Processing query objects", ids.size(), LOG) : null;
      for(DBIDIter iter = ids.iter(); iter.valid(); iter.advance()) {
        Object label = lrelation.get(iter);
        findMatches(posn, lrelation, label);
        if(posn.size() > 0) {
          computeDistances(nlist, iter, distQuery, relation);
          if(nlist.size() != relation.size() - (includeSelf ? 0 : 1)) {
            LOG.warning("Neighbor list does not have the desired size: " + nlist.size());
          }
          map += AveragePrecisionEvaluation.STATIC.evaluate(posn, nlist);
          mroc += ROCEvaluation.STATIC.evaluate(posn, nlist);
          KNNEvaluator.STATIC.evaluateKNN(knnperf, nlist, lrelation, counters, label);
          samples += 1;
        }
        LOG.incrementProcessed(objloop);
      }
      LOG.ensureCompleted(objloop);
      if(samples < 1) {
        throw new AbortException("No object matched - are labels parsed correctly?");
      }
      if(!(map >= 0) || !(mroc >= 0)) {
        throw new AbortException("NaN in MAP/ROC.");
      }
  
      map /= samples;
      mroc /= samples;
      LOG.statistics(new DoubleStatistic(PREFIX + ".map", map));
      LOG.statistics(new DoubleStatistic(PREFIX + ".rocauc", mroc));
      LOG.statistics(new DoubleStatistic(PREFIX + ".samples", samples));
      for(int k = 0; k < maxk; k++) {
        knnperf[k] = knnperf[k] / samples;
        LOG.statistics(new DoubleStatistic(PREFIX + ".knn-" + (k + 1), knnperf[k]));
      }
  
      return new RetrievalPerformanceResult(samples, map, mroc, knnperf);
    }