///|
/// Huber loss with an explicit transition scale.
pub fn huber_loss2(residual~ : Double, delta? : Double = 1.0) -> Double {
  let value = residual.abs()
  if value <= delta {
    0.5 * value * value
  } else {
    delta * (value - 0.5 * delta)
  }
}

///|
pub fn huber_weight2(residual : Double, delta : Double) -> Double {
  let value = residual.abs()
  if value <= delta || value == 0.0 {
    1.0
  } else {
    delta / value
  }
}

///|
pub fn cauchy_loss2(residual : Double, scale : Double) -> Double {
  let normalized = residual / scale.abs().max(0.000000000001)
  @math.ln(1.0 + normalized * normalized)
}

///|
pub fn cauchy_weight2(residual : Double, scale : Double) -> Double {
  let normalized = residual / scale.abs().max(0.000000000001)
  1.0 / (1.0 + normalized * normalized)
}

///|
pub fn tukey_loss2(residual : Double, cutoff : Double) -> Double {
  let normalized = residual / cutoff.abs().max(0.000000000001)
  if normalized.abs() < 1.0 {
    let factor = 1.0 - normalized * normalized
    cutoff * cutoff / 6.0 * (1.0 - factor * factor * factor)
  } else {
    cutoff * cutoff / 6.0
  }
}

///|
pub fn tukey_weight2(residual : Double, cutoff : Double) -> Double {
  let normalized = residual / cutoff.abs().max(0.000000000001)
  if normalized.abs() < 1.0 {
    let factor = 1.0 - normalized * normalized
    factor * factor
  } else {
    0.0
  }
}

///|
pub fn geman_mcclure_loss2(residual : Double, scale : Double) -> Double {
  let square = residual * residual
  let denominator = square + scale * scale
  if denominator <= 0.0 {
    0.0
  } else {
    square / denominator
  }
}

///|
pub fn geman_mcclure_weight2(residual : Double, scale : Double) -> Double {
  let square = residual * residual
  let denominator = square + scale * scale
  if denominator <= 0.0 {
    0.0
  } else {
    scale * scale / denominator
  }
}

///|
pub fn pseudo_huber_loss2(residual : Double, delta : Double) -> Double {
  let normalized = residual / delta.abs().max(0.000000000001)
  delta * delta * ((1.0 + normalized * normalized).sqrt() - 1.0)
}

///|
pub fn pseudo_huber_weight2(residual : Double, delta : Double) -> Double {
  let normalized = residual / delta.abs().max(0.000000000001)
  1.0 / (1.0 + normalized * normalized).sqrt()
}

///|
pub fn robust_loss_batch(
  residuals : ArrayView[Double],
  scale : Double,
) -> Array[Double] {
  let result : Array[Double] = []
  for residual in residuals {
    result.push(huber_loss2(residual~, delta=scale))
  }
  result
}

///|
pub fn robust_weight_batch(
  residuals : ArrayView[Double],
  scale : Double,
) -> Array[Double] {
  let result : Array[Double] = []
  for residual in residuals {
    result.push(huber_weight2(residual, scale))
  }
  result
}

///|
pub fn robust_loss_sum(residuals : ArrayView[Double], scale : Double) -> Double {
  let mut result = 0.0
  for loss in robust_loss_batch(residuals, scale) {
    result += loss
  }
  result
}

///|
pub fn robust_loss_mean(
  residuals : ArrayView[Double],
  scale : Double,
) -> Double {
  if residuals.length() == 0 {
    0.0
  } else {
    robust_loss_sum(residuals, scale) / residuals.length().to_double()
  }
}

///|
pub fn robust_weighted_residual(residual : Double, weight : Double) -> Double {
  residual * weight.max(0.0).sqrt()
}

///|
pub fn robust_weighted_batch(
  residuals : ArrayView[Double],
  weights : ArrayView[Double],
) -> Array[Double] {
  let result : Array[Double] = []
  let count = residuals.length().min(weights.length())
  for i in 0..