/**
   * if there is more than one peak in the histogram, reduce the histogram to only that peak, else
   * leave unaltered.
   *
   * @param hist
   * @param values
   * @param valueErrors
   * @return
   */
  public static HistogramHolder reduceHistogramToFirstPeak(
      HistogramHolder hist, float[] values, float[] valueErrors) {

    int yPeakIdx = findFirstPeakIndex(hist);

    if (yPeakIdx == -1) {
      return hist;
    }

    int yMinPeakIdx = findFirstMinimaFollowingPeak(hist, yPeakIdx);

    if (yMinPeakIdx == -1) {
      return hist;
    }

    int n = yMinPeakIdx + 1;

    HistogramHolder tmp = new HistogramHolder();
    tmp.setXHist(Arrays.copyOfRange(hist.getXHist(), 0, n));
    tmp.setYHistFloat(Arrays.copyOfRange(hist.getYHistFloat(), 0, n));
    tmp.setXErrors(Arrays.copyOfRange(hist.getXErrors(), 0, n));
    tmp.setYErrors(Arrays.copyOfRange(hist.getYErrors(), 0, n));

    return tmp;
  }
  public static HistogramHolder createSimpleHistogram(
      final float xMin, final float xMax, int nBins, float[] values, float[] valueErrors) {

    if (values == null || valueErrors == null || values.length != valueErrors.length) {

      throw new IllegalArgumentException(
          "values and valueErrors cannot be null and must be the same length");
    }

    float binWidth = calculateBinWidth(xMin, xMax, nBins);

    float[] xHist = new float[nBins];
    int[] yHist = new int[nBins];

    Histogram.createHistogram(values, nBins, xMin, xMax, xHist, yHist, binWidth);

    float[] yHistFloat = new float[yHist.length];
    for (int i = 0; i < yHist.length; i++) {
      yHistFloat[i] = (float) yHist[i];
    }

    float[] yErrors = new float[xHist.length];
    float[] xErrors = new float[xHist.length];

    calulateHistogramBinErrors(xHist, yHist, values, valueErrors, xErrors, yErrors);

    HistogramHolder histogram = new HistogramHolder();
    histogram.setXHist(xHist);
    histogram.setYHist(yHist);
    histogram.setYHistFloat(yHistFloat);
    histogram.setYErrors(yErrors);
    histogram.setXErrors(xErrors);

    return histogram;
  }
  public static HistogramHolder createSimpleHistogram(int binWidth, List<Integer> theValues) {

    if (theValues == null || theValues.isEmpty()) {

      throw new IllegalArgumentException(
          "values and valueErrors cannot be null and must be the same length");
    }

    float[] values = new float[theValues.size()];
    for (int i = 0; i < theValues.size(); ++i) {
      int v = theValues.get(i).intValue();
      values[i] = v;
    }

    float[] valueErrors = Errors.populateYErrorsBySqrt(values);

    float[] minMax = MiscMath.calculateOuterRoundedMinAndMax(values);

    int nBins = (int) Math.ceil(((minMax[1] - minMax[0])) / binWidth);
    if (nBins < 0) {
      nBins *= -1;
    }

    float[] xHist = new float[nBins];
    int[] yHist = new int[nBins];

    Histogram.createHistogram(values, nBins, minMax[0], minMax[1], xHist, yHist, binWidth);

    float[] yHistFloat = new float[yHist.length];
    for (int i = 0; i < yHist.length; i++) {
      yHistFloat[i] = (float) yHist[i];
    }

    float[] yErrors = new float[xHist.length];
    float[] xErrors = new float[xHist.length];

    calulateHistogramBinErrors(xHist, yHist, values, valueErrors, xErrors, yErrors);

    HistogramHolder histogram = new HistogramHolder();
    histogram.setXHist(xHist);
    histogram.setYHist(yHist);
    histogram.setYHistFloat(yHistFloat);
    histogram.setYErrors(yErrors);
    histogram.setXErrors(xErrors);

    return histogram;
  }
  public static HistogramHolder createSimpleHistogram(
      float binWidth, float[] values, float[] valueErrors) {

    if (values == null || valueErrors == null || values.length != valueErrors.length) {

      throw new IllegalArgumentException(
          "values and valueErrors cannot be null and must be the same length");
    }

    float[] minMax = MiscMath.calculateOuterRoundedMinAndMax(values);

    int nBins = (int) Math.ceil(((minMax[1] - minMax[0])) / binWidth);
    if (nBins < 0) {
      nBins *= -1;
    }

    float[] xHist = new float[nBins];
    int[] yHist = new int[nBins];

    Histogram.createHistogram(values, nBins, minMax[0], minMax[1], xHist, yHist, binWidth);

    float[] yHistFloat = new float[yHist.length];
    for (int i = 0; i < yHist.length; i++) {
      yHistFloat[i] = (float) yHist[i];
    }

    float[] yErrors = new float[xHist.length];
    float[] xErrors = new float[xHist.length];

    calulateHistogramBinErrors(xHist, yHist, values, valueErrors, xErrors, yErrors);

    HistogramHolder histogram = new HistogramHolder();
    histogram.setXHist(xHist);
    histogram.setYHist(yHist);
    histogram.setYHistFloat(yHistFloat);
    histogram.setYErrors(yErrors);
    histogram.setXErrors(xErrors);

    return histogram;
  }
  public static HistogramHolder calculateSturgesHistogramRemoveZeroTail(
      float[] values, float[] valueErrors) {

    if (values == null || valueErrors == null || values.length != valueErrors.length) {

      throw new IllegalArgumentException(
          "values and valueErrors cannot be null and must be the same length");
    }

    int nIntervalsSturges = (int) Math.ceil(Math.log(values.length) / Math.log(2));

    // int nItervalsRice = (int)(2*Math.pow(values.length, 0.3333));

    int nBins = 25;

    if (values.length > 10000) {
      nBins = 40;
    }

    nBins = Math.max(nIntervalsSturges, nBins);

    float[] xHist = new float[nBins];
    int[] yHist = new int[nBins];

    float minx = MiscMath.findMin(values);
    float maxx = MiscMath.findMax(values);

    float binWidth = calculateBinWidth(minx, maxx, nBins);

    Histogram.createHistogram(values, nBins, minx, maxx, xHist, yHist, binWidth);

    float maxy = MiscMath.findMax(yHist);

    int minCountsLimit = (int) Math.max(5, 0.03f * maxy);
    int countsBelowMinAtTail = 0;
    int lastLowCountIdx = yHist.length - 1;
    for (int i = (yHist.length - 1); i > -1; i--) {
      if (yHist[i] < minCountsLimit) {
        countsBelowMinAtTail++;
        lastLowCountIdx = i;
      } else {
        break;
      }
    }

    if (countsBelowMinAtTail > 0) {

      maxx = xHist[lastLowCountIdx];

      // keep nbins the same?
      binWidth = calculateBinWidth(minx, maxx, nBins);

      Histogram.createHistogram(values, nBins, minx, maxx, xHist, yHist, binWidth);

      if (countsBelowMinAtTail > (nBins >> 1)) {
        // one more round of trimming
        maxy = MiscMath.findMax(yHist);
        minCountsLimit = (int) Math.max(5, 0.03f * maxy);
        countsBelowMinAtTail = 0;
        lastLowCountIdx = yHist.length - 1;
        for (int i = (yHist.length - 1); i > -1; i--) {
          if (yHist[i] < minCountsLimit) {
            countsBelowMinAtTail++;
            lastLowCountIdx = i;
          } else {
            break;
          }
        }
        if (countsBelowMinAtTail > 0) {
          maxx = xHist[lastLowCountIdx];
          binWidth = calculateBinWidth(minx, maxx, nBins);
          Histogram.createHistogram(values, nBins, minx, maxx, xHist, yHist, binWidth);
        }
      }
    }

    if (values.length > 100) {
      // if there are a large number of points, we'd like to increase the
      // resolution of the peak if needed
      int nLeftOfPeak = MiscMath.findYMaxIndex(yHist);
      int nIter = 0;
      while (nIter < 30 && nLeftOfPeak < 3 && (yHist[nLeftOfPeak] > 100)) {
        binWidth *= 0.8f;
        Histogram.createHistogram(values, nBins, minx, maxx, xHist, yHist, binWidth);
        nLeftOfPeak = MiscMath.findYMaxIndex(yHist);
        nIter++;
      }
    }

    float[] yHistFloat = new float[yHist.length];
    for (int i = 0; i < yHist.length; i++) {
      yHistFloat[i] = (float) yHist[i];
    }

    float[] yErrors = new float[xHist.length];
    float[] xErrors = new float[xHist.length];

    calulateHistogramBinErrors(xHist, yHist, values, valueErrors, xErrors, yErrors);

    HistogramHolder histogram = new HistogramHolder();
    histogram.setXHist(xHist);
    histogram.setYHist(yHist);
    histogram.setYHistFloat(yHistFloat);
    histogram.setYErrors(yErrors);
    histogram.setXErrors(xErrors);

    return histogram;
  }