// Aroon indicator (Tushar Chande): recency of the highest high and lowest
// low within the lookback window.

///|
/// Aroon decomposition: the down and up lines, both of input length.
pub struct AroonResult {
  down : Array[Double]
  up : Array[Double]
}

///|
/// Number of leading NaN values produced by `aroon` for the given period:
/// `period` (the window covers `period + 1` bars including the current one).
pub fn aroon_lookback(period : Int32) -> Int32 {
  period
}

///|
/// Aroon up/down.
///
/// For each bar the window spans the `period` bars before it plus itself
/// (TA-Lib convention, `period + 1` data points). `up` is
/// `100 * (period - bars_since_highest_high) / period` and `down` mirrors it
/// with the lowest low; when several bars share the extreme, the most recent
/// occurrence wins. A window containing NaN yields NaN for that position.
pub fn aroon(
  candles : @base.Candles,
  period : Int32,
) -> AroonResult raise @base.TaError {
  @base.validate_candles(candles)
  if period <= 0 {
    raise @base.InvalidPeriod("period must be positive, got \{period}")
  }
  let n = candles.close.length()
  let down : Array[Double] = []
  let up : Array[Double] = []
  for i in 0..=` / `<=` make the most
    // recent extreme win on ties.
    let mut hh_idx = -1
    let mut ll_idx = -1
    let mut valid = true
    for j in (i - period)..<(i + 1) {
      let h = candles.high[j]
      let l = candles.low[j]
      if @base.is_nan(h) || @base.is_nan(l) {
        valid = false
        break
      }
      if hh_idx == -1 || h >= candles.high[hh_idx] {
        hh_idx = j
      }
      if ll_idx == -1 || l <= candles.low[ll_idx] {
        ll_idx = j
      }
    }
    if !valid {
      down.push(@base.nan())
      up.push(@base.nan())
      continue
    }
    let pd = period.to_double()
    let since_hh = i - hh_idx
    let since_ll = i - ll_idx
    up.push(100.0 * (period - since_hh).to_double() / pd)
    down.push(100.0 * (period - since_ll).to_double() / pd)
  }
  { down, up, }
}

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

///|
/// Aroon Oscillator: `up - down`. Ranges from -100 (downtrend) to +100
/// (uptrend) with the same window rules as `aroon`.
pub fn aroonosc(
  candles : @base.Candles,
  period : Int32,
) -> Array[Double] raise @base.TaError {
  let r = aroon(candles, period)
  let n = candles.close.length()
  let out : Array[Double] = []
  for i in 0..