///|
pub struct ForecastPoint {
  step : Int
  value : Double
  lower : Double
  upper : Double
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn ForecastPoint::step(self : ForecastPoint) -> Int {
  self.step
}

///|
pub fn ForecastPoint::value(self : ForecastPoint) -> Double {
  self.value
}

///|
pub fn ForecastPoint::interval(self : ForecastPoint) -> (Double, Double) {
  (self.lower, self.upper)
}

///|
pub fn naive_forecast(
  values : Array[Double],
  horizon : Int,
) -> Array[ForecastPoint] {
  if values.length() == 0 || horizon < 0 {
    abort("naive_forecast: invalid inputs")
  }
  let last = values[values.length() - 1]
  let deviation = OnlineStats::new().push_all(values).standard_deviation()
  let result = []
  for step in 1..<=horizon {
    let margin = 1.96 * deviation
    result.push({
      step,
      value: last,
      lower: last - margin,
      upper: last + margin,
    })
  }
  result
}

///|
pub fn drift_forecast(
  values : Array[Double],
  horizon : Int,
) -> Array[ForecastPoint] {
  if values.length() < 2 || horizon < 0 {
    abort("drift_forecast: at least two values are required")
  }
  let n = values.length()
  let mean_x = (n - 1).to_double() / 2.0
  let mean_y = mean(values)
  let mut numerator = 0.0
  let mut denominator = 0.0
  for index in 0.. Array[Double] {
  points.map(fn(point) { point.value() })
}

///|
pub fn seasonal_naive(
  values : Array[Double],
  season : Int,
  horizon : Int,
) -> Array[ForecastPoint] {
  if season <= 0 || values.length() < season || horizon < 0 {
    abort("seasonal_naive: invalid season or horizon")
  }
  let error = OnlineStats::new().push_all(values).standard_deviation()
  let result = []
  for step in 1..<=horizon {
    let value = values[values.length() - season + (step - 1) % season]
    result.push({
      step,
      value,
      lower: value - 1.96 * error,
      upper: value + 1.96 * error,
    })
  }
  result
}