///|
/// Robust preprocessing helpers for production telemetry.
pub fn winsorize_robust(
  values : Array[Double],
  lower_probability : Double,
  upper_probability : Double,
) -> Array[Double] {
  if values.length() == 0 {
    return []
  }
  let low = quantile(values, lower_probability)
  let high = quantile(values, upper_probability)
  let result : Array[Double] = []
  for value in values {
    if value < low {
      result.push(low)
    } else if value > high {
      result.push(high)
    } else {
      result.push(value)
    }
  }
  result
}

///|
pub fn robust_z_score(
  value : Double,
  center : Double,
  scale : Double,
) -> Double {
  if scale <= 0.0 {
    0.0
  } else {
    absolute(value - center) / scale
  }
}

///|
pub fn robust_z_scores(values : Array[Double]) -> Array[Double] {
  let center = median(values)
  let scale = robust_scale(values)
  let result : Array[Double] = []
  for value in values {
    result.push(robust_z_score(value, center, scale))
  }
  result
}

///|
pub fn outlier_mask(
  values : Array[Double],
  threshold? : Double = 3.5,
) -> Array[Bool] {
  let safe = if threshold < 0.0 { 0.0 } else { threshold }
  let scores = robust_z_scores(values)
  let result : Array[Bool] = []
  for score in scores {
    result.push(score > safe)
  }
  result
}

///|
pub fn hampel_filter(
  values : Array[Double],
  window_size? : Int = 7,
  threshold? : Double = 3.0,
) -> Array[Double] {
  let safe_window = if window_size < 1 { 1 } else { window_size }
  let result : Array[Double] = []
  for i in 0.. 0.0 && absolute(values[i] - center) > threshold * scale) {
      result.push(center)
    } else {
      result.push(values[i])
    }
  }
  result
}

///|
pub fn clip_values(
  values : Array[Double],
  minimum : Double,
  maximum : Double,
) -> Array[Double] {
  let low = if minimum <= maximum { minimum } else { maximum }
  let high = if minimum <= maximum { maximum } else { minimum }
  let result : Array[Double] = []
  for value in values {
    result.push(
      if value < low {
        low
      } else if value > high {
        high
      } else {
        value
      },
    )
  }
  result
}

///|
pub fn normalize_range(
  values : Array[Double],
  minimum? : Double = 0.0,
  maximum? : Double = 1.0,
) -> Array[Double] {
  let source_low = array_minimum(values)
  let source_high = array_maximum(values)
  let target_low = minimum
  let target_high = maximum
  let result : Array[Double] = []
  for value in values {
    if source_high == source_low {
      result.push((target_low + target_high) / 2.0)
    } else {
      result.push(
        target_low +
        (value - source_low) *
        (target_high - target_low) /
        (source_high - source_low),
      )
    }
  }
  result
}

///|
pub fn first_difference(values : Array[Double]) -> Array[Double] {
  if values.length() < 2 {
    return []
  }
  let result : Array[Double] = []
  for i in 1.. Array[Double] {
  first_difference(first_difference(values))
}

///|
pub fn cumulative_total(values : Array[Double]) -> Array[Double] {
  let result : Array[Double] = []
  let mut total = 0.0
  for value in values {
    total += value
    result.push(total)
  }
  result
}

///|
pub fn lag_values(values : Array[Double], lag : Int) -> Array[Double] {
  let result : Array[Double] = []
  if lag <= 0 {
    return values
  }
  for i in 0.. Array[Double] {
  let result : Array[Double] = []
  let safe = if window_size < 1 { 1 } else { window_size }
  for i in 0.. Array[Double] {
  let result : Array[Double] = []
  let safe = if window_size < 1 { 1 } else { window_size }
  for i in 0.. Array[Double] {
  let result : Array[Double] = []
  let safe = if window_size < 2 { 2 } else { window_size }
  for i in 0.. Array[Double] {
  let result : Array[Double] = []
  if values.length() == 0 {
    return result
  }
  let rate = clamp_probability(alpha)
  let mut level = values[0]
  result.push(level)
  for i in 1.. Array[Double] {
  let result : Array[Double] = []
  let mut last = fallback
  for value in values {
    if is_finite(value) {
      last = value
    }
    result.push(last)
  }
  result
}

///|
pub fn robust_scale(values : Array[Double]) -> Double {
  let scale = median_absolute_deviation(values) * 1.4826
  if scale == 0.0 {
    standard_deviation(values)
  } else {
    scale
  }
}