///|
pub fn covariance(a : Array[Double], b : Array[Double]) -> Double {
  if a.length() == 0 || a.length() != b.length() {
    return 0.0
  }
  let mut result = 0.0
  let mean_a = mean(a)
  let mean_b = mean(b)
  for i in 0.. Double {
  let denominator = std_dev(a) * std_dev(b)
  if denominator == 0.0 {
    0.0
  } else {
    covariance(a, b) / denominator
  }
}

///|
pub fn r_squared(
  outcomes : Array[Double],
  predictions : Array[Double],
) -> Double {
  let n = if outcomes.length() < predictions.length() {
    outcomes.length()
  } else {
    predictions.length()
  }
  if n == 0 {
    return 0.0
  }
  let center = mean(outcomes)
  let mut total = 0.0
  let mut residual = 0.0
  for i in 0.. Double {
  let n = if probabilities.length() < treatment.length() {
    probabilities.length()
  } else {
    treatment.length()
  }
  if n == 0 {
    return 0.0
  }
  let mut result = 0.0
  for i in 0.. Double {
  let n = if probabilities.length() < treatment.length() {
    probabilities.length()
  } else {
    treatment.length()
  }
  if n == 0 {
    return 0.0
  }
  let mut result = 0.0
  for i in 0.. Double {
  let n = if outcomes.length() < predictions.length() {
    outcomes.length()
  } else {
    predictions.length()
  }
  if n == 0 {
    return 0.0
  }
  let mut result = 0.0
  for i in 0.. Double {
  let n = if outcomes.length() < predictions.length() {
    outcomes.length()
  } else {
    predictions.length()
  }
  if n == 0 {
    return 0.0
  }
  let mut result = 0.0
  for i in 0.. Double {
  if standard_error == 0.0 {
    0.0
  } else {
    estimate / standard_error
  }
}

///|
pub fn calibration_error(
  probabilities : Array[Double],
  treatment : Array[Bool],
  bins : Int,
) -> Double {
  let calibration = propensity_calibration(probabilities, treatment, bins)
  let mut result = 0.0
  for value in calibration {
    result += value.abs()
  }
  if calibration.length() == 0 {
    0.0
  } else {
    result / calibration.length().to_double()
  }
}

///|
pub fn standardized_effect_size(
  estimate : Double,
  outcome : Array[Double],
) -> Double {
  let scale = std_dev(outcome, sample=false)
  if scale == 0.0 {
    0.0
  } else {
    estimate / scale
  }
}