content: /*
   * Copyright (c) 2017-present, Workday, Inc.
   * All rights reserved.
   *
   * This source code is licensed under the MIT license found in the LICENSE file in the root repository.
   */
  
  package com.workday.insights.timeseries.arima.struct;
  
  import com.workday.insights.timeseries.arima.ArimaSolver;
  
  /**
   * ARIMA Forecast Result
   */
  public class ForecastResult {
  
    private final double[] forecast;
    private final double[] forecastUpperConf;
    private final double[] forecastLowerConf;
    private final double dataVariance;
    private final StringBuilder log;
    private double modelRMSE;
    private double maxNormalizedVariance;
  
    /**
     * Constructor for ForecastResult
     *
     * @param pForecast     forecast data
     * @param pDataVariance data variance of the original data
     */
    public ForecastResult(final double[] pForecast,
                          final double pDataVariance) {
  
      this.forecast = pForecast;
  
      this.forecastUpperConf = new double[pForecast.length];
      System.arraycopy(pForecast, 0, forecastUpperConf, 0, pForecast.length);
  
      this.forecastLowerConf = new double[pForecast.length];
      System.arraycopy(pForecast, 0, forecastLowerConf, 0, pForecast.length);
  
      this.dataVariance = pDataVariance;
  
      this.modelRMSE = -1;
      this.maxNormalizedVariance = -1;
  
      this.log = new StringBuilder();
    }
  
    /**
     * Compute normalized variance
     *
     * @param v variance
     * @return Normalized variance
     */
    private double getNormalizedVariance(final double v) {
      if (v < -0.5 || dataVariance < -0.5) {
        return -1;
      } else if (dataVariance < 0.0000001) {
        return v;
      } else {
        return Math.abs(v / dataVariance);
      }
    }
  
    /**
     * Getter for Root Mean-Squared Error
     *
     * @return Root Mean-Squared Error
     */
    public double getRMSE() {
      return this.modelRMSE;
    }
  
    /**
     * Setter for Root Mean-Squared Error
     *
     * @param rmse Root Mean-Squared Error
     */
    void setRMSE(double rmse) {
      this.modelRMSE = rmse;
    }
  
    /**
     * Getter for Max Normalized Variance
     *
     * @return Max Normalized Variance
     */
    public double getMaxNormalizedVariance() {
      return maxNormalizedVariance;
    }
  
    /**
     * Compute and set confidence intervals
     *
     * @param constant          confidence interval constant
     * @param cumulativeSumOfMA cumulative sum of MA coefficients
     * @return Max Normalized Variance
     */
    public double setConfInterval(final double constant,
                                  final double[] cumulativeSumOfMA) {
      double maxNormalizedVariance = -1.0;
      double bound = 0;
      for (int i = 0; i < forecast.length; i++) {
        bound = constant * modelRMSE * cumulativeSumOfMA[i];
        this.forecastUpperConf[i] = this.forecast[i] + bound;
        this.forecastLowerConf[i] = this.forecast[i] - bound;
        final double normalizedVariance = getNormalizedVariance(Math.pow(bound, 2));
        if (normalizedVariance > maxNormalizedVariance) {
          maxNormalizedVariance = normalizedVariance;
        }
      }
      return maxNormalizedVariance;
    }
  
    /**
     * Compute and set Sigma2 and prediction confidence interval.
     *
     * @param params ARIMA parameters from the model
     */
    public void setSigma2AndPredicationInterval(ArimaParams params) {
      maxNormalizedVariance = ArimaSolver
          .setSigma2AndPredicationInterval(params, this, forecast.length);
    }
  
    /**
     * Getter for forecast data
     *
     * @return forecast data
     */
    public double[] getForecast() {
      return forecast;
    }
  
    /**
     * Getter for upper confidence bounds
     *
     * @return array of upper confidence bounds
     */
    public double[] getForecastUpperConf() {
      return forecastUpperConf;
    }
  
    /**
     * Getter for lower confidence bounds
     *
     * @return array of lower confidence bounds
     */
    public double[] getForecastLowerConf() {
      return forecastLowerConf;
    }
  
    /**
     * Append message to log of forecast result
     *
     * @param message string message
     */
    public void log(String message) {
      this.log.append(message + "\n");
    }
  
    /**
     * Getter for log of the forecast result
     *
     * @return full log of the forecast result
     */
    public String getLog() {
      return log.toString();
    }
  }
  