///|
/// Reporting rounding policies for engineering values.
pub enum RoundingMode {
  Nearest
  Down
  Up
  TowardZero
} derive(Debug, Eq)

///|
/// Select nearest-half-up rounding.
pub fn nearest_rounding() -> RoundingMode {
  Nearest
}

///|
/// Select downward rounding.
pub fn down_rounding() -> RoundingMode {
  Down
}

///|
/// Select upward rounding.
pub fn up_rounding() -> RoundingMode {
  Up
}

///|
/// Select truncation toward zero.
pub fn toward_zero_rounding() -> RoundingMode {
  TowardZero
}

///|
fn decimal_factor(decimals : Int) -> Double {
  if decimals < 0 {
    abort("decimal places must be non-negative")
  }
  let mut factor = 1.0
  for _ in 0.. Double {
  if value >= 0.0 {
    (value + 0.5).floor()
  } else {
    (value - 0.5).ceil()
  }
}

///|
/// Round a value to a fixed number of decimal places.
pub fn round_to(value : Double, decimals : Int, mode : RoundingMode) -> Double {
  let factor = decimal_factor(decimals)
  let scaled = value * factor
  let rounded = match mode {
    Nearest => nearest_integer(scaled)
    Down => scaled.floor()
    Up => scaled.ceil()
    TowardZero => scaled.to_int().to_double()
  }
  rounded / factor
}

///|
/// Round an interval outward so it remains conservative.
pub fn round_interval(value : Interval, decimals : Int) -> Interval {
  let factor = decimal_factor(decimals)
  let lower = (value.lower * factor).floor() / factor
  let upper = (value.upper * factor).ceil() / factor
  Interval::new(lower, upper)
}

///|
/// Apply reporting rounding to a measurement array without changing its order.
pub fn round_values(
  values : Array[Double],
  decimals : Int,
  mode : RoundingMode,
) -> Array[Double] {
  values.map(value => round_to(value, decimals, mode))
}

///|
/// Round a sample summary while preserving its count and extrema semantics.
pub fn round_statistics(
  statistics : SampleStatistics,
  decimals : Int,
) -> SampleStatistics {
  {
    count: statistics.count,
    mean: round_to(statistics.mean, decimals, Nearest),
    variance: round_to(statistics.variance, decimals, Nearest),
    standard_deviation: round_to(
      statistics.standard_deviation,
      decimals,
      Nearest,
    ),
    minimum: round_to(statistics.minimum, decimals, Down),
    maximum: round_to(statistics.maximum, decimals, Up),
  }
}