docs: private void approximateKnnDistances(MkCoPLeafEntry entry, KNNList knnDistances) {
      StringBuilder msg = LOG.isDebugging() ? new StringBuilder(1000) : null;
      if(msg != null) {
        msg.append("knnDistances ").append(knnDistances);
      }
  
      DoubleDBIDListIter iter = knnDistances.iter();
      // count the zero distances
      int k_0 = 0;
      for(iter.seek(0); iter.valid(); iter.advance()) {
        if(iter.doubleValue() != 0) {
          break;
        }
        k_0++;
      }
  
      // init variables
      double[] log_k = new double[settings.kmax - k_0];
      System.arraycopy(this.log_k, k_0, log_k, 0, settings.kmax - k_0);
  
      double sum_log_kDist = 0, sum_log_k_kDist = 0;
      double[] log_kDist = new double[settings.kmax - k_0];
  
      iter.seek(k_0);
      for(int i = 0; iter.valid(); iter.advance(), i++) {
        final double logd = log_kDist[i] = FastMath.log(iter.doubleValue());
        sum_log_kDist += logd;
        sum_log_k_kDist += logd * log_k[i];
      }
  
      double sum_log_k = 0, sum_log_k2 = 0;
      for(int i = 0; i < log_k.length; i++) {
        final double logki = log_k[i];
        sum_log_k += logki;
        sum_log_k2 += logki * logki;
      }
  
      if(msg != null) {
        msg.append("\nk_0 ").append(k_0) //
            .append("\nk_max ").append(settings.kmax) //
            .append("\nlog_k(").append(log_k.length).append(") ").append(FormatUtil.format(log_k)) //
            .append("\nsum_log_k ").append(sum_log_k) //
            .append("\nsum_log_k^2 ").append(sum_log_k2) //
            .append("\nkDists ").append(knnDistances) //
            .append("\nlog_kDist(").append(log_kDist.length).append(") ").append(FormatUtil.format(log_kDist)) //
            .append("\nsum_log_kDist ").append(sum_log_kDist) //
            .append("\nsum_log_k_kDist ").append(sum_log_k_kDist);
      }
  
      // lower and upper hull
      ConvexHull convexHull = new ConvexHull(log_k, log_kDist);
  
      // approximate upper hull
      ApproximationLine conservative = approximateUpperHull(convexHull, log_k, log_kDist);
  
      ApproximationLine c2 = approximateUpperHullPaper(convexHull, log_k, sum_log_k, sum_log_k2, log_kDist, sum_log_kDist, sum_log_k_kDist);
  
      double err1 = ssqerr(k_0, settings.kmax, log_k, log_kDist, conservative.getM(), conservative.getT());
      double err2 = ssqerr(k_0, settings.kmax, log_k, log_kDist, c2.getM(), c2.getT());
  
      if(msg != null) {
        msg.append("err1 ").append(err1).append("err2 ").append(err2);
      }
  
      if(err1 > err2 && err1 - err2 > 0.000000001) {
        // if (err1 > err2) {
  
        StringBuilder warning = new StringBuilder(10000);
        int u = convexHull.getNumberOfPointsInUpperHull();
        int[] upperHull = convexHull.getUpperHull();
        warning.append("\nentry ").append(entry.getRoutingObjectID()) //
            .append("\nlower Hull ").append(convexHull.getNumberOfPointsInLowerHull()).append(' ').append(FormatUtil.format(convexHull.getLowerHull())) //
            .append("\nupper Hull ").append(convexHull.getNumberOfPointsInUpperHull()).append(' ').append(FormatUtil.format(convexHull.getUpperHull())) //
            .append("\nerr1 ").append(err1) //
            .append("\nerr2 ").append(err2) //
            .append("\nconservative1 ").append(conservative) //
            .append("\nconservative2 ").append(c2);
  
        for(int i = 0; i < u; i++) {
          warning.append("\nlog_k[").append(upperHull[i]).append("] = ").append(log_k[upperHull[i]]) //
              .append("\nlog_kDist[").append(upperHull[i]).append("] = ").append(log_kDist[upperHull[i]]);
        }
        LOG.warning(warning.toString());
      }
  
      // approximate lower hull
      ApproximationLine progressive = approximateLowerHull(convexHull, log_k, sum_log_k, sum_log_k2, log_kDist, sum_log_kDist, sum_log_k_kDist);
  
      entry.setConservativeKnnDistanceApproximation(conservative);
      entry.setProgressiveKnnDistanceApproximation(progressive);
  
      if(msg != null) {
        LOG.debugFine(msg.toString());
      }
    }