///|
/// Generic batch operations shared by offline and online training loops.
pub fn array_sum(values : Array[Double]) -> Double {
  let mut sum = 0.0
  for value in values {
    sum = sum + value
  }
  sum
}

///|
pub fn array_mean(values : Array[Double]) -> Double {
  if values.length() == 0 {
    0.0
  } else {
    array_sum(values) / values.length().to_double()
  }
}

///|
pub fn array_min(values : Array[Double]) -> Double {
  if values.length() == 0 {
    0.0
  } else {
    let mut result = values[0]
    for value in values {
      if value < result {
        result = value
      }
    }
    result
  }
}

///|
pub fn array_max(values : Array[Double]) -> Double {
  if values.length() == 0 {
    0.0
  } else {
    let mut result = values[0]
    for value in values {
      if value > result {
        result = value
      }
    }
    result
  }
}

///|
pub fn array_l1_distance(left : Array[Double], right : Array[Double]) -> Double {
  let mut result = 0.0
  let length = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  let mut i = 0
  while i < length {
    let distance = left[i] - right[i]
    result = result + (if distance < 0.0 { -distance } else { distance })
    i = i + 1
  }
  result
}

///|
pub fn array_l2_squared_distance(
  left : Array[Double],
  right : Array[Double],
) -> Double {
  let mut result = 0.0
  let length = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  let mut i = 0
  while i < length {
    let distance = left[i] - right[i]
    result = result + distance * distance
    i = i + 1
  }
  result
}

///|
pub fn array_scale_add(
  left : Array[Double],
  scale : Double,
  right : Array[Double],
) -> Array[Double] {
  let result : Array[Double] = []
  let length = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  let mut i = 0
  while i < length {
    result.push(left[i] * scale + right[i])
    i = i + 1
  }
  result
}

///|
pub fn array_weighted_average(
  values : Array[Double],
  weights : Array[Double],
) -> Double {
  let length = if values.length() < weights.length() {
    values.length()
  } else {
    weights.length()
  }
  let mut numerator = 0.0
  let mut denominator = 0.0
  let mut i = 0
  while i < length {
    numerator = numerator + values[i] * weights[i]
    denominator = denominator + weights[i]
    i = i + 1
  }
  if denominator == 0.0 {
    0.0
  } else {
    numerator / denominator
  }
}

///|
pub fn array_take(values : Array[Double], count : Int) -> Array[Double] {
  let result : Array[Double] = []
  let length = if count < values.length() { count } else { values.length() }
  let mut i = 0
  while i < length {
    if i >= 0 {
      result.push(values[i])
    }
    i = i + 1
  }
  result
}

///|
pub fn array_drop(values : Array[Double], count : Int) -> Array[Double] {
  let result : Array[Double] = []
  let start = if count < 0 {
    0
  } else if count > values.length() {
    values.length()
  } else {
    count
  }
  let mut i = start
  while i < values.length() {
    result.push(values[i])
    i = i + 1
  }
  result
}

///|
pub fn array_pad(
  values : Array[Double],
  length : Int,
  fill : Double,
) -> Array[Double] {
  let result : Array[Double] = []
  for value in values {
    if result.length() < length {
      result.push(value)
    }
  }
  while result.length() < length {
    result.push(fill)
  }
  result
}

///|
pub fn array_mask(values : Array[Double], mask : Array[Bool]) -> Array[Double] {
  let result : Array[Double] = []
  let length = if values.length() < mask.length() {
    values.length()
  } else {
    mask.length()
  }
  let mut i = 0
  while i < length {
    if mask[i] {
      result.push(values[i])
    }
    i = i + 1
  }
  result
}

///|
pub fn array_cumulative(values : Array[Double]) -> Array[Double] {
  let result : Array[Double] = []
  let mut running = 0.0
  for value in values {
    running = running + value
    result.push(running)
  }
  result
}

///|
pub fn array_difference(values : Array[Double]) -> Array[Double] {
  let result : Array[Double] = []
  if values.length() == 0 {
    return result
  }
  result.push(values[0])
  let mut i = 1
  while i < values.length() {
    result.push(values[i] - values[i - 1])
    i = i + 1
  }
  result
}