// Stochastic oscillator family and Williams %R.

///|
/// Stochastic decomposition: the %K and %D lines, both of input length.
pub struct Stoch {
  k : Array[Double]
  d : Array[Double]
}

///|
/// Raw %K at bar `i`: position of the close within the `period`-bar
/// high/low range, scaled to 0..100. A flat window (highest high equals
/// lowest low) reads 0; a window containing NaN or a NaN close yields NaN.
fn fast_k_at(candles : @base.Candles, i : Int32, period : Int32) -> Double {
  let mut hh = 0.0
  let mut ll = 0.0
  let mut first = true
  let mut valid = true
  for j in (i - period + 1)..<(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 first {
      hh = h
      ll = l
      first = false
    } else {
      if h > hh {
        hh = h
      }
      if l < ll {
        ll = l
      }
    }
  }
  if !valid {
    return @base.nan()
  }
  let c = candles.close[i]
  if @base.is_nan(c) {
    return @base.nan()
  }
  let range = hh - ll
  if range == 0.0 {
    return 0.0
  }
  100.0 * (c - ll) / range
}

///|
/// Fast stochastic: raw %K plus its SMA over `d_period` (%D). The %D line is
/// valid at index `period + d_period - 2`.
pub fn stochf(
  candles : @base.Candles,
  period : Int32,
  d_period~ : Int32,
) -> Stoch raise @base.TaError {
  @base.validate_candles(candles)
  if period <= 0 || d_period <= 0 {
    raise @base.InvalidPeriod("all periods must be positive")
  }
  let n = candles.close.length()
  let k : Array[Double] = []
  for i in 0.. Int32 {
  period - 1 + (k_slowing - 1) + (d_period - 1)
}

///|
/// Stochastic Oscillator (TA-Lib `STOCH`): raw %K is smoothed by an SMA over
/// `k_slowing` bars (slow %K), and %D is an SMA of slow %K over `d_period`
/// bars.
pub fn stoch(
  candles : @base.Candles,
  period : Int32,
  k_slowing~ : Int32,
  d_period~ : Int32,
) -> Stoch raise @base.TaError {
  @base.validate_candles(candles)
  if period <= 0 || k_slowing <= 0 || d_period <= 0 {
    raise @base.InvalidPeriod("all periods must be positive")
  }
  let n = candles.close.length()
  let fast : Array[Double] = []
  for i in 0.. Int32 {
  period - 1
}

///|
/// Williams %R: `-100 * (highest_high - close) / (highest_high - lowest_low)`
/// over the window, computed through the identity `%R = %K - 100`. A flat
/// window therefore reads -100, mirroring the raw %K rule (reads 0).
pub fn willr(
  candles : @base.Candles,
  period : Int32,
) -> Array[Double] raise @base.TaError {
  @base.validate_candles(candles)
  let n = candles.close.length()
  let out : Array[Double] = []
  for i in 0..