///|
pub struct GaugeStudy {
  readings : Array[Double]
  reference : Double
  repeatability : Double
  bias : Double
  resolution : Double
  percent_tolerance : Double
  adequate : Bool
} derive(Debug, ToJson)

///|
fn ms_mean(values : Array[Double]) -> Double {
  if values.length() == 0 {
    0.0
  } else {
    let mut total = 0.0
    for value in values {
      total += value
    }
    total / values.length().to_double()
  }
}

///|
fn ms_sd(values : Array[Double], mean : Double) -> Double {
  if values.length() < 2 {
    0.0
  } else {
    let mut total = 0.0
    for value in values {
      let d = value - mean
      total += d * d
    }
    (total / (values.length() - 1).to_double()).sqrt()
  }
}

///|
pub fn gauge_study(
  readings~ : Array[Double],
  reference~ : Double,
  tolerance~ : Double,
  resolution~ : Double,
) -> GaugeStudy {
  let mean = ms_mean(readings)
  let sd = ms_sd(readings, mean)
  let bias = mean - reference
  {
    readings,
    reference,
    repeatability: sd,
    bias,
    resolution,
    percent_tolerance: if tolerance <= 0.0 {
      0.0
    } else {
      100.0 * sd / tolerance
    },
    adequate: tolerance > 0.0 &&
    sd <= tolerance * 0.1 &&
    resolution <= tolerance * 0.1,
  }
}

///|
pub fn gauge_bias_percent(study : GaugeStudy, tolerance : Double) -> Double {
  if tolerance <= 0.0 {
    0.0
  } else {
    study.bias.abs() / tolerance * 100.0
  }
}

///|
pub fn gauge_resolution_ratio(study : GaugeStudy, tolerance : Double) -> Double {
  if tolerance <= 0.0 {
    0.0
  } else {
    study.resolution / tolerance
  }
}

///|
pub fn gauge_repeatability_ratio(
  study : GaugeStudy,
  tolerance : Double,
) -> Double {
  if tolerance <= 0.0 {
    0.0
  } else {
    study.repeatability / tolerance
  }
}

///|
pub fn gauge_status(study : GaugeStudy) -> String {
  if study.adequate {
    "adequate"
  } else {
    "review"
  }
}

///|
pub fn operator_average(
  readings : Array[Double],
  operators : Int,
) -> Array[Double] {
  if operators <= 0 {
    return []
  }
  let result : Array[Double] = []
  let per = readings.length() / operators
  for operator in 0.. Double {
  ms_sd(values, ms_mean(values))
}

///|
pub fn repeatability_by_group(groups : Array[Array[Double]]) -> Double {
  if groups.length() == 0 {
    0.0
  } else {
    let mut total = 0.0
    for group in groups {
      total += ms_sd(group, ms_mean(group))
    }
    total / groups.length().to_double()
  }
}

///|
pub fn ndc(process_sd : Double, gauge_sd : Double) -> Double {
  if gauge_sd <= 0.0 {
    0.0
  } else {
    1.41 * process_sd / gauge_sd
  }
}

///|
pub fn grr_percent(gauge_sd : Double, tolerance : Double) -> Double {
  if tolerance <= 0.0 {
    0.0
  } else {
    5.15 * gauge_sd / tolerance * 100.0
  }
}

///|
pub fn measurement_system_rating(ndc_value : Double, grr : Double) -> String {
  if ndc_value >= 5.0 && grr <= 10.0 {
    "good"
  } else if ndc_value >= 2.0 && grr <= 30.0 {
    "conditional"
  } else {
    "poor"
  }
}

///|
pub fn linearity_slope(
  references : Array[Double],
  readings : Array[Double],
) -> Double {
  if references.length() < 2 || references.length() != readings.length() {
    0.0
  } else {
    let x = ms_mean(references)
    let y = ms_mean(readings)
    let mut num = 0.0
    let mut den = 0.0
    for i in 0.. Array[Double] {
  let result : Array[Double] = []
  for i in 0.. Double {
  if values.length() == 0 {
    0.0
  } else {
    let mut low = values[0]
    let mut high = values[0]
    for value in values {
      low = low.min(value)
      high = high.max(value)
    }
    high - low
  }
}