///|
pub(all) struct QualityMetrics {
  samples : Int32
  minimum : Double
  maximum : Double
  mean : Double
  deviation : Double
  rms : Double
  negative_fraction : Double
  first_difference_mad : Double
  curvature_rms : Double
  uniform_axis : Bool
}

///|
/// Descriptive QA metrics, not a detector of chemical identity or noise truth.
/// First differences and curvature are in sample-index coordinates.
pub fn quality(input : Spectrum) -> QualityMetrics raise SpectralError {
  let y = input.values
  let n = y.length()
  let mut minimum = y[0]
  let mut maximum = y[0]
  let mut negatives = 0
  let differences = Array::make(n - 1, 0.0)
  let curvature = Array::make(n - 2, 0.0)
  for i in 0.. maximum {
      maximum = y[i]
    }
    if y[i] < 0.0 {
      negatives = negatives + 1
    }
    if i >= 1 {
      differences[i - 1] = y[i] - y[i - 1]
    }
    if i >= 2 {
      curvature[i - 2] = y[i] - 2.0 * y[i - 1] + y[i - 2]
    }
  }
  {
    samples: n,
    minimum,
    maximum,
    mean: mean(y),
    deviation: sample_deviation(y),
    rms: root_mean_square(y),
    negative_fraction: negatives.to_double() / n.to_double(),
    first_difference_mad: median_absolute_deviation(differences),
    curvature_rms: root_mean_square(curvature),
    uniform_axis: is_uniform_axis(input),
  }
}

///|
/// Pearson correlation on exactly aligned axes; constant inputs are refused.
pub fn spectral_correlation(
  left : Spectrum,
  right : Spectrum,
) -> Double raise SpectralError {
  require_same_axis(left, right, 0.0)
  let a = snv(left).values
  let b = snv(right).values
  let mut product = 0.0
  for i in 0.. 1.0 {
    1.0
  } else if value < -1.0 {
    -1.0
  } else {
    value
  }
}

///|
/// Spectral angle in radians; meaningful for nonzero aligned vectors.
pub fn spectral_angle(
  left : Spectrum,
  right : Spectrum,
) -> Double raise SpectralError {
  require_same_axis(left, right, 0.0)
  let a = unit_vector(left).values
  let b = unit_vector(right).values
  let mut cosine = 0.0
  for i in 0.. 1.0 {
    1.0
  } else if cosine < -1.0 {
    -1.0
  } else {
    cosine
  }
  @math.acos(bounded)
}