///|
/// Dot product of two numeric vectors.
pub fn dot_product(
  left : ArrayView[Double],
  right : ArrayView[Double],
) -> Double {
  let n = left.length().min(right.length())
  let mut total = 0.0
  for i in 0.. Int {
  if low > high {
    clamp_int(value, low=high, high=low)
  } else {
    value.max(low).min(high)
  }
}

///|
/// Central difference at an index, with one-sided edge handling.
pub fn central_difference(values : ArrayView[Double], index~ : Int) -> Double {
  if values.length() == 0 {
    0.0
  } else if index <= 0 {
    if values.length() == 1 {
      0.0
    } else {
      values[1] - values[0]
    }
  } else if index >= values.length() - 1 {
    values[index.clamp(min=0, max=values.length() - 1)] - values[index - 1]
  } else {
    (values[index + 1] - values[index - 1]) * 0.5
  }
}

///|
/// Return a forward difference vector.
pub fn forward_differences(values : ArrayView[Double]) -> Array[Double] {
  let result = Array::new()
  if values.length() >= 2 {
    for i in 0..<(values.length() - 1) {
      result.push(values[i + 1] - values[i])
    }
  }
  result
}

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

///|
/// Compute a weighted mean with a zero-weight fallback.
pub fn weighted_mean(
  values : ArrayView[Double],
  weights_values : ArrayView[Double],
) -> Double {
  let n = values.length().min(weights_values.length())
  let mut weighted = 0.0
  let mut total_weight = 0.0
  for i in 0.. Array[Double] {
  let sums = prefix_sums(values)
  let total = if sums.length() == 0 { 0.0 } else { sums[sums.length() - 1] }
  let result = Array::new()
  for value in sums {
    result.push(safe_divide(value, total, fallback=0.0))
  }
  result
}

///|
/// Return a vector with a fixed length and a fill value.
pub fn resize_vector(
  values : ArrayView[Double],
  length~ : Int,
  fill? : Double = 0.0,
) -> Array[Double] {
  let result = Array::make(length.max(0), fill)
  for i in 0.. Int? {
  if values.length() == 0 {
    None
  } else {
    let mut index = 0
    let mut maximum = values[0]
    for i in 1.. maximum {
        maximum = values[i]
        index = i
      }
    }
    Some(index)
  }
}

///|
/// Return the index of the first minimum.
pub fn argmin(values : ArrayView[Double]) -> Int? {
  if values.length() == 0 {
    None
  } else {
    let mut index = 0
    let mut minimum = values[0]
    for i in 1.. Double {
  let numerator = dot_product(left, right)
  let denominator = rms_value(left) *
    rms_value(right) *
    left.length().min(right.length()).to_double()
  safe_divide(numerator, denominator, fallback=0.0)
}