///|
pub fn abs_double(value : Double) -> Double {
  if value < 0.0 {
    -value
  } else {
    value
  }
}

///|
fn clamp_double(value : Double, lower : Double, upper : Double) -> Double {
  if value < lower {
    lower
  } else if value > upper {
    upper
  } else {
    value
  }
}

///|
fn copy_and_sort(data : Array[Double]) -> Array[Double] {
  let result = []
  for value in data {
    result.push(value)
  }
  result.sort()
  result
}

///|
fn sum_values(data : Array[Double]) -> Double {
  let mut total = 0.0
  for value in data {
    total += value
  }
  total
}

///|
pub fn mean(data : Array[Double]) -> Double {
  if data.length() == 0 {
    0.0
  } else {
    sum_values(data) / data.length().to_double()
  }
}

///|
pub fn min_value(data : Array[Double]) -> Double {
  if data.length() == 0 {
    0.0
  } else {
    let mut result = data[0]
    for index = 1; index < data.length(); index = index + 1 {
      if data[index] < result {
        result = data[index]
      }
    }
    result
  }
}

///|
pub fn max_value(data : Array[Double]) -> Double {
  if data.length() == 0 {
    0.0
  } else {
    let mut result = data[0]
    for index = 1; index < data.length(); index = index + 1 {
      if data[index] > result {
        result = data[index]
      }
    }
    result
  }
}

///|
pub fn sum_absolute(data : Array[Double]) -> Double {
  let mut total = 0.0
  for value in data {
    total += abs_double(value)
  }
  total
}

///|
pub fn sum_squared(data : Array[Double]) -> Double {
  let mut total = 0.0
  for value in data {
    total += value * value
  }
  total
}

///|
pub fn range(data : Array[Double]) -> Double {
  if data.length() == 0 {
    0.0
  } else {
    max_value(data) - min_value(data)
  }
}

///|
fn validate_probability(probability : Double) -> Unit {
  if probability < 0.0 || probability > 1.0 {
    abort("probability must be in [0, 1]")
  }
}

///|
fn validate_trim(trim_percent : Double) -> Unit {
  if trim_percent < 0.0 || trim_percent >= 0.5 {
    abort("trim_percent must be in [0, 0.5)")
  }
}

///|
fn validate_confidence(confidence : Double) -> Unit {
  if confidence <= 0.0 || confidence >= 1.0 {
    abort("confidence must be in (0, 1)")
  }
}

///|
fn dot_product(left : Array[Double], right : Array[Double]) -> Double {
  let mut total = 0.0
  let n = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  for index = 0; index < n; index = index + 1 {
    total += left[index] * right[index]
  }
  total
}

///|
fn copy_array(data : Array[Double]) -> Array[Double] {
  let result = []
  for value in data {
    result.push(value)
  }
  result
}

///|
fn nearly_equal(left : Double, right : Double, tolerance : Double) -> Bool {
  abs_double(left - right) <= tolerance
}

///|
fn sign_double(value : Double) -> Double {
  if value < 0.0 {
    -1.0
  } else if value > 0.0 {
    1.0
  } else {
    0.0
  }
}