docs: private ApproximationLine approximateLowerHull(ConvexHull convexHull, double[] log_k, double sum_log_k, double sum_log_k2, double[] log_kDist, double sum_log_kDist, double sum_log_k_kDist) {
      // StringBuilder msg = new StringBuilder(1000);
      int[] lowerHull = convexHull.getLowerHull();
      int l = convexHull.getNumberOfPointsInLowerHull();
      int k_0 = settings.kmax - lowerHull.length + 1;
  
      // linear search on all line segments on the lower convex hull
      // msg.append("lower hull l = ").append(l).append('\n');
      double low_error = Double.MAX_VALUE;
      double low_m = 0.0;
      double low_t = 0.0;
  
      for(int i = 1; i < l; i++) {
        double cur_m = (log_kDist[lowerHull[i]] - log_kDist[lowerHull[i - 1]]) / (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]]);
        double cur_t = log_kDist[lowerHull[i]] - cur_m * log_k[lowerHull[i]];
        double cur_error = ssqerr(k_0, settings.kmax, log_k, log_kDist, cur_m, cur_t);
        // msg.append(" Segment = ").append(i).append(" m =
        // ").append(cur_m).append(" t = ").append(cur_t).append(" lowerror =
        // ").append(cur_error).append('\n');
        if(cur_error < low_error) {
          low_error = cur_error;
          low_m = cur_m;
          low_t = cur_t;
        }
      }
  
      // linear search on all points of the lower convex hull
      boolean is_right = true; // NEEDED FOR PROOF CHECK
      for(int i = 0; i < l; i++) {
        double cur_m = optimize(k_0, settings.kmax, sum_log_k, sum_log_k2, log_k[lowerHull[i]], log_kDist[lowerHull[i]], sum_log_k_kDist, sum_log_kDist);
        double cur_t = log_kDist[lowerHull[i]] - cur_m * log_k[lowerHull[i]];
        // only valid if both neighboring points are underneath y=mx+t
        if((i == 0 || log_kDist[lowerHull[i - 1]] >= log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && (i == l - 1 || log_kDist[lowerHull[i + 1]] >= log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) {
          double cur_error = ssqerr(k_0, settings.kmax, log_k, log_kDist, cur_m, cur_t);
          if(cur_error < low_error) {
            low_error = cur_error;
            low_m = cur_m;
            low_t = cur_t;
          }
        }
  
        // check proof of bisection search
        if(!(i > 0 && log_kDist[lowerHull[i - 1]] < log_kDist[lowerHull[i]] - cur_m * (log_k[lowerHull[i]] - log_k[lowerHull[i - 1]])) && !is_right) {
          LOG.warning("ERROR lower: The bisection search will not work properly!");
          if(!(i < l - 1 && log_kDist[lowerHull[i + 1]] < log_kDist[lowerHull[i]] + cur_m * (log_k[lowerHull[i + 1]] - log_k[lowerHull[i]]))) {
            is_right = false;
          }
        }
      }
      return new ApproximationLine(k_0, low_m, low_t);
    }