// Rate-of-change family: ROC, MOM, CMO, APO and PPO.

///|
/// Number of leading NaN values produced by `roc` for the given period.
pub fn roc_lookback(period : Int32) -> Int32 {
  period
}

///|
/// Rate of change in percent: `100 * (x[i] - x[i - period]) / x[i - period]`.
/// A zero base value yields NaN.
pub fn roc(
  data : Array[Double],
  period : Int32,
) -> Array[Double] raise @base.TaError {
  @base.validate_series(data, period)
  let n = data.length()
  let out : Array[Double] = []
  for i in 0.. Int32 {
  period
}

///|
/// Momentum: `x[i] - x[i - period]`. NaN inputs yield NaN.
pub fn mom(
  data : Array[Double],
  period : Int32,
) -> Array[Double] raise @base.TaError {
  @base.validate_series(data, period)
  let n = data.length()
  let out : Array[Double] = []
  for i in 0.. Int32 {
  period
}

///|
/// Chande Momentum Oscillator:
/// `100 * (sum_up - sum_down) / (sum_up + sum_down)` over the last `period`
/// price changes. A completely flat window reads 0.
pub fn cmo(
  data : Array[Double],
  period : Int32,
) -> Array[Double] raise @base.TaError {
  @base.validate_series(data, period)
  let n = data.length()
  // Price changes computed once per bar; each window then only sums.
  let diffs : Array[Double] = []
  for j in 0.. 0.0 {
        up += d
      } else {
        down -= d
      }
    }
    if !valid {
      out.push(@base.nan())
      continue
    }
    let total = up + down
    if total == 0.0 {
      out.push(0.0)
    } else {
      out.push(100.0 * (up - down) / total)
    }
  }
  out
}

///|
/// Absolute Price Oscillator: `fast_MA - slow_MA` with a selectable MA flavor.
pub fn apo(
  data : Array[Double],
  fast_period : Int32,
  slow_period : Int32,
  ma_type~ : @base.MAType,
) -> Array[Double] raise @base.TaError {
  @base.validate_series(data, slow_period)
  if fast_period <= 0 || slow_period <= 0 {
    raise @base.InvalidPeriod("all periods must be positive")
  }
  if fast_period >= slow_period {
    raise @base.InvalidPeriod("fast_period must be smaller than slow_period")
  }
  let fast_ma = @overlap.moving_average(data, fast_period, ma_type~)
  let slow_ma = @overlap.moving_average(data, slow_period, ma_type~)
  let n = data.length()
  let out : Array[Double] = []
  for i in 0.. Array[Double] raise @base.TaError {
  @base.validate_series(data, slow_period)
  if fast_period <= 0 || slow_period <= 0 {
    raise @base.InvalidPeriod("all periods must be positive")
  }
  if fast_period >= slow_period {
    raise @base.InvalidPeriod("fast_period must be smaller than slow_period")
  }
  let fast_ma = @overlap.moving_average(data, fast_period, ma_type~)
  let slow_ma = @overlap.moving_average(data, slow_period, ma_type~)
  let n = data.length()
  let out : Array[Double] = []
  for i in 0.. Array[Double] raise @base.TaError {
  @base.validate_candles(candles)
  let n = candles.close.length()
  let out : Array[Double] = []
  for i in 0..