///|
pub struct ImageStats {
  min : Int
  max : Int
  mean_x1000 : Int
  variance_x1000 : Int
  nonzero : Int
  count : Int
} derive(Debug, Eq)

///|
pub fn ImageStats::from_gray(image : GrayImage) -> ImageStats {
  let count = image.pixels.length()
  if count == 0 {
    { min: 0, max: 0, mean_x1000: 0, variance_x1000: 0, nonzero: 0, count: 0 }
  } else {
    let mut low = 255
    let mut high = 0
    let mut sum = 0
    let mut nonzero = 0
    for value in image.pixels {
      low = min_i(low, value)
      high = max_i(high, value)
      sum += value
      if value != 0 {
        nonzero += 1
      }
    }
    let mean = sum / count * 1000 + sum % count * 1000 / count
    let mut variance_sum = 0
    for value in image.pixels {
      let delta = value * 1000 - mean
      variance_sum += delta * delta / 1000
    }
    {
      min: low,
      max: high,
      mean_x1000: mean,
      variance_x1000: variance_sum / count,
      nonzero,
      count,
    }
  }
}

///|
pub fn ImageStats::dynamic_range(self : ImageStats) -> Int {
  self.max - self.min
}

///|
pub fn ImageStats::is_constant(self : ImageStats) -> Bool {
  self.min == self.max
}

///|
pub fn GrayImage::stats(self : GrayImage) -> ImageStats {
  ImageStats::from_gray(self)
}

///|
pub fn GrayImage::mean(self : GrayImage) -> Int {
  self.stats().mean_x1000 / 1000
}

///|
pub fn GrayImage::sum(self : GrayImage) -> Int {
  let mut total = 0
  for value in self.pixels {
    total += value
  }
  total
}

///|
pub fn GrayImage::count_value(self : GrayImage, value : Int) -> Int {
  let mut total = 0
  for pixel in self.pixels {
    if pixel == clamp_u8(value) {
      total += 1
    }
  }
  total
}

///|
pub fn GrayImage::count_in_range(
  self : GrayImage,
  low : Int,
  high : Int,
) -> Int {
  let lo = clamp_u8(low)
  let hi = clamp_u8(high)
  let mut total = 0
  for pixel in self.pixels {
    if pixel >= lo && pixel <= hi {
      total += 1
    }
  }
  total
}

///|
pub fn GrayImage::mean_abs_error(self : GrayImage, other : GrayImage) -> Int {
  let count = min_i(self.pixels.length(), other.pixels.length())
  if count == 0 {
    0
  } else {
    let mut total = 0
    for i in 0.. Int {
  let count = min_i(self.pixels.length(), other.pixels.length())
  if count == 0 {
    0
  } else {
    let mut total = 0
    for i in 0.. Int {
  let mse = self.mse_x1000(other)
  if mse == 0 {
    999999
  } else {
    1000 * 10 * 1000 / mse
  }
}

///|
pub fn GrayImage::tile(self : GrayImage, tile_size : Size) -> Array[GrayImage] {
  let out : Array[GrayImage] = []
  let tw = max_i(1, tile_size.width)
  let th = max_i(1, tile_size.height)
  for y in 0.. GrayImage {
  let b = max_i(0, border)
  let out = GrayImage::new(
    width=self.width + b * 2,
    height=self.height + b * 2,
    fill~,
  )
  for y in 0.. GrayImage {
  let w = max_i(0, min_i(self.width - max_i(0, rect.x), rect.width))
  let h = max_i(0, min_i(self.height - max_i(0, rect.y), rect.height))
  let out = GrayImage::new(width=w, height=h)
  for y in 0.. GrayImage {
  let out = GrayImage::new(width=self.width, height=self.height)
  for y in 0.. GrayImage {
  let out = GrayImage::new(width=self.width, height=self.height)
  for y in 0.. GrayImage {
  self.flip_horizontal().flip_vertical()
}

///|
pub fn GrayImage::resize_nearest(self : GrayImage, size : Size) -> GrayImage {
  let out = GrayImage::new(
    width=max_i(0, size.width),
    height=max_i(0, size.height),
  )
  if self.width == 0 || self.height == 0 {
    out
  } else {
    for y in 0..