///|
pub(all) struct SeriesSummary {
  name : String
  count : Int
  minimum : Double
  maximum : Double
  mean : Double
  rms : Double
  span : Double
} derive(Debug, ToJson)

///|
pub fn summarize_series(
  name : String,
  values : ArrayView[Double],
) -> SeriesSummary {
  {
    name,
    count: values.length(),
    minimum: min_value(values, 0.0),
    maximum: max_value(values, 0.0),
    mean: mean(values),
    rms: series_rms(values),
    span: series_range(values),
  }
}

///|
pub fn summary_to_csv(summary : SeriesSummary) -> String {
  "name,count,minimum,maximum,mean,rms,span\n\{summary.name},\{summary.count},\{summary.minimum},\{summary.maximum},\{summary.mean},\{summary.rms},\{summary.span}\n"
}

///|
pub fn percentile(values : ArrayView[Double], fraction : Double) -> Double {
  let quantile = sample_quantiles(values, 101)
  if quantile.length() == 0 {
    0.0
  } else {
    quantile[clamp_int(
      (clamp(fraction, 0.0, 1.0) * 100.0).to_int(),
      0,
      quantile.length() - 1,
    )]
  }
}

///|
pub fn median(values : ArrayView[Double]) -> Double {
  percentile(values, 0.5)
}

///|
pub fn interquartile_range(values : ArrayView[Double]) -> Double {
  percentile(values, 0.75) - percentile(values, 0.25)
}

///|
pub fn z_scores(values : ArrayView[Double]) -> Array[Double] {
  let average = mean(values)
  let variance = sample_variance(values)
  let scale = variance.sqrt()
  if scale == 0.0 {
    values.map(fn(_) { 0.0 })
  } else {
    values.map(fn(value) { (value - average) / scale })
  }
}

///|
pub fn finite_difference_series(
  values : ArrayView[Double],
  spacing : Double,
) -> Array[Double] {
  let output = zeros(values.length())
  if values.length() > 1 {
    output[0] = (values[1] - values[0]) / spacing
    for i in 1..<(values.length() - 1) {
      output[i] = (values[i + 1] - values[i - 1]) / (2.0 * spacing)
    }
    output[values.length() - 1] = (
        values[values.length() - 1] - values[values.length() - 2]
      ) /
      spacing
  }
  output
}

///|
pub fn cumulative_series(
  values : ArrayView[Double],
  spacing : Double,
) -> Array[Double] {
  let output = zeros(values.length())
  for i in 1.. Array[SeriesSummary] {
  Array::makei(values.length(), fn(i) {
    summarize_series("series_\{i}", values[i])
  })
}