///|
pub fn winsorize(data : Array[Double], trim_percent : Double) -> Array[Double] {
  if data.length() == 0 {
    return []
  }
  if trim_percent < 0.0 || trim_percent >= 0.5 {
    abort("trim_percent must be in [0, 0.5)")
  }

  let sorted = []
  for v in data {
    sorted.push(v)
  }
  sorted.sort()

  let n = sorted.length()
  let k = (n.to_double() * trim_percent).to_int()

  if k * 2 >= n {
    return sorted
  }

  let lower_bound = sorted[k]
  let upper_bound = sorted[n - 1 - k]

  let result = []
  for v in data {
    if v < lower_bound {
      result.push(lower_bound)
    } else if v > upper_bound {
      result.push(upper_bound)
    } else {
      result.push(v)
    }
  }
  result
}

///|
pub fn winsorized_mean(data : Array[Double], trim_percent : Double) -> Double {
  let w_data = winsorize(data, trim_percent)
  if w_data.length() == 0 {
    return 0.0
  }
  let mut sum = 0.0
  for v in w_data {
    sum += v
  }
  sum / w_data.length().to_double()
}