///|
/// Summary statistics for sampled procedural fields.
pub(all) struct SampleStats {
  minimum : Double
  maximum : Double
  mean : Double
  variance : Double
  count : Int
} derive(Eq, Debug, ToJson)

///|
pub fn sample_stats(values : Array[Double]) -> SampleStats {
  if values.length() == 0 {
    { minimum: 0.0, maximum: 0.0, mean: 0.0, variance: 0.0, count: 0 }
  } else {
    let mut minimum = values[0]
    let mut maximum = values[0]
    let mut sum = 0.0
    for value in values {
      if value < minimum {
        minimum = value
      }
      if value > maximum {
        maximum = value
      }
      sum += value
    }
    let mean = sum / values.length().to_double()
    let mut squared = 0.0
    for value in values {
      let delta = value - mean
      squared += delta * delta
    }
    {
      minimum,
      maximum,
      mean,
      variance: squared / values.length().to_double(),
      count: values.length(),
    }
  }
}

///|
pub fn[N : NoiseSampler] sample_noise_stats(
  sampler : N,
  width : Int,
  height : Int,
  scale : Double,
) -> SampleStats {
  let values = []
  if width > 0 && height > 0 {
    for y in 0.. Array[Int] {
  let result = []
  if bins <= 0 {
    result
  } else {
    for _ in 0.. minimum {
      for value in values {
        let position = ((value - minimum) /
        (maximum - minimum) *
        bins.to_double()).to_int()
        let index = if position < 0 {
          0
        } else if position >= bins {
          bins - 1
        } else {
          position
        }
        result[index] += 1
      }
    }
    result
  }
}

///|
/// Calculates a smoothness estimate from adjacent samples in a grid.
pub fn field_smoothness(
  values : Array[Double],
  width : Int,
  height : Int,
) -> Double {
  if width < 2 || height < 2 || values.length() < width * height {
    0.0
  } else {
    let mut difference = 0.0
    let mut pairs = 0
    for y in 0.. Color {
  let light_ratio = background.contrast_ratio(light)
  let dark_ratio = background.contrast_ratio(dark)
  if light_ratio >= dark_ratio {
    light
  } else {
    dark
  }
}

///|
/// Generates colors from a noise field while keeping alpha stable.
pub fn[N : NoiseSampler] noise_palette(
  sampler : N,
  count : Int,
  low : Color,
  high : Color,
) -> Array[Color] {
  let result = []
  for i in 0..