/**
   * For the given value of the alpha smoothing constant, returns the best fit model where the value
   * of the beta (trend) smoothing constant is between betaMin and betaMax. This method will
   * continually try to refine the estimate of beta until a tolerance of less than betaTolerance is
   * achieved.
   *
   * <p>Note that the descriptions of the parameters below include a discussion of valid values.
   * However, since this is a private method and to help improve performance, we don't provide any
   * validation of these parameters. Using invalid values may lead to unexpected results.
   *
   * @param dataSet the data set for which a best fit model is required.
   * @param alpha the (fixed) value of the alpha smoothing constant to use for the best fit model.
   * @param betaMin the minimum value of the beta (trend) smoothing constant accepted in the
   *     resulting best fit model. Must be greater than (or equal to) 0.0 and less than betaMax.
   * @param betaMax the maximum value of the beta (trend) smoothing constant accepted in the
   *     resulting best fit model. Must be greater than betaMin and less than (or equal to) 1.0.
   * @param betaTolerance the tolerance within which the beta value is required. Must be
   *     considerably less than 1.0. However, note that the smaller this value the longer it will
   *     take to diverge on a best fit model.
   */
  private static TripleExponentialSmoothingModel findBestBeta(
      DataSet dataSet, double alpha, double betaMin, double betaMax, double betaTolerance) {
    int stepsPerIteration = 10;

    if (betaMin < 0.0) betaMin = 0.0;
    if (betaMax > 1.0) betaMax = 1.0;

    TripleExponentialSmoothingModel bestModel =
        new TripleExponentialSmoothingModel(alpha, betaMin, 0.0);
    bestModel.init(dataSet);

    double initialMSE = bestModel.getMSE();

    boolean betaImproving = true;
    double betaStep = (betaMax - betaMin) / stepsPerIteration;
    double beta = betaMin + betaStep;
    for (; beta <= betaMax || betaImproving; ) {
      TripleExponentialSmoothingModel model = new TripleExponentialSmoothingModel(alpha, beta, 0.0);
      model.init(dataSet);

      if (model.getMSE() < bestModel.getMSE()) bestModel = model;
      else betaImproving = false;

      beta += betaStep;
      if (beta > 1.0) betaImproving = false;
    }

    // If we're making progress, then try to refine the beta estimate
    if (bestModel.getMSE() < initialMSE && betaStep > betaTolerance) {
      // Can this be further refined - improving efficiency - by
      //  only searching in the range beta-betaStep/2 to
      //  beta+betaStep/2 ?
      return findBestBeta(
          dataSet,
          bestModel.getAlpha(),
          bestModel.getBeta() - betaStep,
          bestModel.getBeta() + betaStep,
          betaTolerance);
    }

    return bestModel;
  }