docs: private DoubleMinMax sampleMinMax(Relation<O> relation, DistanceQuery<O> distFunc) {
      int size = relation.size();
      Random rnd = new Random();
      // estimate minimum and maximum.
      int k = (int) Math.max(25, FastMath.pow(relation.size(), 0.2));
      TreeSet<DoubleDBIDPair> minhotset = new TreeSet<>();
      TreeSet<DoubleDBIDPair> maxhotset = new TreeSet<>(Collections.reverseOrder());
  
      int randomsize = (int) Math.max(25, FastMath.pow(relation.size(), 0.2));
      double rprob = ((double) randomsize) / size;
      ArrayModifiableDBIDs randomset = DBIDUtil.newArray(randomsize);
  
      DBIDIter iter = relation.iterDBIDs();
      if(!iter.valid()) {
        throw new EmptyDataException();
      }
      DBID firstid = DBIDUtil.deref(iter);
      iter.advance();
      minhotset.add(DBIDUtil.newPair(Double.MAX_VALUE, firstid));
      maxhotset.add(DBIDUtil.newPair(Double.MIN_VALUE, firstid));
      for(; iter.valid(); iter.advance()) {
        // generate candidates for min distance.
        ArrayList<DoubleDBIDPair> np = new ArrayList<>(k * 2 + randomsize * 2);
        for(DoubleDBIDPair pair : minhotset) {
          // skip the object itself
          if(DBIDUtil.equal(iter, pair)) {
            continue;
          }
          double d = distFunc.distance(iter, pair);
          np.add(DBIDUtil.newPair(d, iter));
          np.add(DBIDUtil.newPair(d, pair));
        }
        for(DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
          double d = distFunc.distance(iter, iter2);
          np.add(DBIDUtil.newPair(d, iter));
          np.add(DBIDUtil.newPair(d, iter2));
        }
        minhotset.addAll(np);
        shrinkHeap(minhotset, k);
  
        // generate candidates for max distance.
        ArrayList<DoubleDBIDPair> np2 = new ArrayList<>(k * 2 + randomsize * 2);
        for(DoubleDBIDPair pair : minhotset) {
          // skip the object itself
          if(DBIDUtil.equal(iter, pair)) {
            continue;
          }
          double d = distFunc.distance(iter, pair);
          np2.add(DBIDUtil.newPair(d, iter));
          np2.add(DBIDUtil.newPair(d, pair));
        }
        for(DBIDIter iter2 = randomset.iter(); iter2.valid(); iter2.advance()) {
          double d = distFunc.distance(iter, iter2);
          np.add(DBIDUtil.newPair(d, iter));
          np.add(DBIDUtil.newPair(d, iter2));
        }
        maxhotset.addAll(np2);
        shrinkHeap(maxhotset, k);
  
        // update random set
        if(randomset.size() < randomsize) {
          randomset.add(iter);
        }
        else if(rnd.nextDouble() < rprob) {
          randomset.set((int) Math.floor(rnd.nextDouble() * randomsize), iter);
        }
      }
      return new DoubleMinMax(minhotset.first().doubleValue(), maxhotset.first().doubleValue());
    }