///|
pub fn value_at_risk(data : Array[Double], probability : Double) -> Double {
  quantile(data, probability)
}

///|
pub fn conditional_value_at_risk(
  data : Array[Double],
  probability : Double,
) -> Double {
  lower_tail_mean(data, probability)
}

///|
pub fn upper_conditional_value_at_risk(
  data : Array[Double],
  probability : Double,
) -> Double {
  upper_tail_mean(data, probability)
}

///|
pub fn downside_deviation(data : Array[Double], target : Double) -> Double {
  let mut total = 0.0
  let mut count = 0
  for value in data {
    if value < target {
      let difference = value - target
      total += difference * difference
      count += 1
    }
  }
  if count == 0 {
    0.0
  } else {
    (total / count.to_double()).sqrt()
  }
}

///|
pub fn upside_deviation(data : Array[Double], target : Double) -> Double {
  let mut total = 0.0
  let mut count = 0
  for value in data {
    if value > target {
      let difference = value - target
      total += difference * difference
      count += 1
    }
  }
  if count == 0 {
    0.0
  } else {
    (total / count.to_double()).sqrt()
  }
}

///|
pub fn robust_sharpe_ratio(
  data : Array[Double],
  risk_free? : Double = 0.0,
) -> Double {
  let excess = difference_from_baseline(data, risk_free)
  let scale = mad(excess)
  if scale == 0.0 {
    0.0
  } else {
    median(excess) / scale
  }
}

///|
pub fn robust_sortino_ratio(
  data : Array[Double],
  target? : Double = 0.0,
) -> Double {
  let center = median(data) - target
  let scale = downside_deviation(data, target)
  if scale == 0.0 {
    0.0
  } else {
    center / scale
  }
}

///|
pub fn maximum_drawdown(data : Array[Double]) -> Double {
  if data.length() == 0 {
    return 0.0
  }
  let mut peak = data[0]
  let mut maximum = 0.0
  for value in data {
    if value > peak {
      peak = value
    }
    if peak != 0.0 {
      let drawdown = (peak - value) / abs_double(peak)
      if drawdown > maximum {
        maximum = drawdown
      }
    }
  }
  maximum
}

///|
pub fn drawdown_series(data : Array[Double]) -> Array[Double] {
  let result = []
  if data.length() == 0 {
    return result
  }
  let mut peak = data[0]
  for value in data {
    if value > peak {
      peak = value
    }
    result.push(
      if peak == 0.0 {
        0.0
      } else {
        (peak - value) / abs_double(peak)
      },
    )
  }
  result
}

///|
pub fn recovery_index(data : Array[Double]) -> Double {
  let drawdowns = drawdown_series(data)
  if drawdowns.length() == 0 {
    0.0
  } else {
    1.0 - maximum_drawdown(data)
  }
}

///|
pub fn robust_tail_ratio(data : Array[Double], probability : Double) -> Double {
  let upper = upper_tail_mean(data, 1.0 - probability)
  let lower = abs_double(lower_tail_mean(data, probability))
  if lower == 0.0 {
    0.0
  } else {
    upper / lower
  }
}

///|
pub fn robust_loss(data : Array[Double], target : Double) -> Double {
  let mut total = 0.0
  for value in data {
    total += huber_loss(value - target, mad(data) + 0.000001)
  }
  if data.length() == 0 {
    0.0
  } else {
    total / data.length().to_double()
  }
}

///|
pub fn robust_expected_loss(
  data : Array[Double],
  target : Double,
  probability : Double,
) -> Double {
  let tail = []
  for value in data {
    if value <= target {
      tail.push(value)
    }
  }
  if tail.length() == 0 {
    0.0
  } else {
    probability * mean(tail)
  }
}

///|
pub fn robust_stress_score(
  baseline : Array[Double],
  stressed : Array[Double],
) -> Double {
  if baseline.length() == 0 || stressed.length() == 0 {
    return 0.0
  }
  let baseline_scale = if mad(baseline) == 0.0 { 1.0 } else { mad(baseline) }
  abs_double(median(stressed) - median(baseline)) / baseline_scale
}

///|
pub fn robust_risk_summary(data : Array[Double]) -> Array[Double] {
  [
    value_at_risk(data, 0.05),
    conditional_value_at_risk(data, 0.05),
    maximum_drawdown(data),
    downside_deviation(data, 0.0),
    robust_sharpe_ratio(data),
  ]
}

///|
pub fn tail_event_indices(
  data : Array[Double],
  probability : Double,
  lower? : Bool = true,
) -> Array[Int] {
  let threshold = if lower {
    quantile(data, probability)
  } else {
    quantile(data, 1.0 - probability)
  }
  let result = []
  for index = 0; index < data.length(); index = index + 1 {
    if lower && data[index] <= threshold {
      result.push(index)
    } else if !lower && data[index] >= threshold {
      result.push(index)
    }
  }
  result
}

///|
pub fn expected_shortfall_gap(
  data : Array[Double],
  probability : Double,
) -> Double {
  abs_double(
    conditional_value_at_risk(data, probability) -
    value_at_risk(data, probability),
  )
}

///|
pub fn robust_return_center(data : Array[Double]) -> Double {
  huber_location(data)
}

///|
pub fn robust_return_scale(data : Array[Double]) -> Double {
  mad(data)
}