///|
pub fn mean_absolute_error(
  reference : ArrayView[Double],
  estimate : ArrayView[Double],
) -> Double raise SpectrumError {
  validate_metric_inputs(reference, estimate)
  let mut total = 0.0
  for i in 0.. Double raise SpectrumError {
  validate_metric_inputs(reference, estimate)
  let mut total = 0.0
  for i in 0.. Double raise SpectrumError {
  validate_metric_inputs(reference, estimate)
  let mut signal_power = 0.0
  let mut noise_power = 0.0
  for i in 0.. Double raise SpectrumError {
  validate_metric_inputs(left, right)
  let left_stats = signal_stats(left)
  let right_stats = signal_stats(right)
  if left_stats.variance == 0.0 || right_stats.variance == 0.0 {
    raise InvalidArgument(message="correlation needs non-constant signals")
  }
  let mut covariance = 0.0
  for i in 0.. Array[Double] raise SpectrumError {
  let stats = signal_stats(signal)
  if upper <= lower {
    raise InvalidArgument(message="upper range must exceed lower range")
  }
  if stats.maximum == stats.minimum {
    return Array::make(stats.count, (lower + upper) / 2.0)
  }
  let scale = (upper - lower) / (stats.maximum - stats.minimum)
  signal.map(value => lower + (value - stats.minimum) * scale)
}

///|
fn validate_metric_inputs(
  left : ArrayView[Double],
  right : ArrayView[Double],
) -> Unit raise SpectrumError {
  if left.length() == 0 || right.length() == 0 {
    raise EmptySignal
  }
  if left.length() != right.length() {
    raise LengthMismatch
  }
}