///|
/// Linearly interpolates invalid observations while retaining valid endpoints.
pub fn interpolate_missing(values : Array[Double]) -> Array[Double] {
  let result : Array[Double] = []
  for value in values {
    result.push(value)
  }
  let mut previous = -1
  let mut first_valid = -1
  for i in 0..= 0 && i - previous > 1 {
        let left = result[previous]
        let right = result[i]
        for j in (previous + 1)..= 0 {
    for i in 0..= 0 {
    for i in (previous + 1).. Array[Double] {
  let low = if lower < upper { lower } else { upper }
  let high = if upper > lower { upper } else { lower }
  let result : Array[Double] = []
  for value in values {
    result.push(
      if value < low {
        low
      } else if value > high {
        high
      } else {
        value
      },
    )
  }
  result
}

///|
pub fn winsorize(
  values : Array[Double],
  lower_probability? : Double = 0.05,
  upper_probability? : Double = 0.95,
) -> Array[Double] {
  clip(
    values,
    quantile(values, lower_probability),
    quantile(values, upper_probability),
  )
}

///|
pub fn normalize_zscore(values : Array[Double]) -> Array[Double] {
  let center = mean(values)
  let scale = standard_deviation(values)
  let safe_scale = if scale < 1.0e-12 { 1.0 } else { scale }
  let result : Array[Double] = []
  for value in values {
    result.push((value - center) / safe_scale)
  }
  result
}

///|
pub fn normalize_minmax(values : Array[Double]) -> Array[Double] {
  let low = array_minimum(values)
  let high = array_maximum(values)
  let range = high - low
  let result : Array[Double] = []
  for value in values {
    result.push(if range <= 1.0e-12 { 0.0 } else { (value - low) / range })
  }
  result
}

///|
pub fn difference(values : Array[Double], lag? : Int = 1) -> Array[Double] {
  let result : Array[Double] = []
  let safe_lag = if lag < 1 { 1 } else { lag }
  if values.length() <= safe_lag {
    return result
  }
  for i in safe_lag.. Array[Double] {
  let result : Array[Double] = []
  let mut total = initial
  for value in values {
    total += value
    result.push(total)
  }
  result
}

///|
pub fn moving_average(
  values : Array[Double],
  window_size : Int,
) -> Array[Double] {
  let result : Array[Double] = []
  let window = DoubleWindow::new(window_size)
  for value in values {
    ignore(window.push(value))
    result.push(window.mean())
  }
  result
}

///|
pub fn moving_median(
  values : Array[Double],
  window_size : Int,
) -> Array[Double] {
  let result : Array[Double] = []
  let window = DoubleWindow::new(window_size)
  for value in values {
    ignore(window.push(value))
    result.push(window.median())
  }
  result
}

///|
pub fn downsample_mean(values : Array[Double], factor : Int) -> Array[Double] {
  let result : Array[Double] = []
  let size = if factor < 1 { 1 } else { factor }
  let mut window : Array[Double] = []
  for value in values {
    window.push(value)
    if window.length() == size {
      result.push(mean(window))
      window = []
    }
  }
  if window.length() > 0 {
    result.push(mean(window))
  }
  result
}