///|
/// Palette utilities for animated gradients, posterized effects, and export
/// previews.
pub struct Palette2 {
  colors : Array[Rgba]
  name : String
} derive(Debug)

///|
pub fn palette2(name : String, colors : Array[Rgba]) -> Palette2 {
  { colors: colors.copy(), name }
}

///|
pub fn Palette2::name(self : Palette2) -> String {
  self.name
}

///|
pub fn Palette2::colors(self : Palette2) -> Array[Rgba] {
  self.colors.copy()
}

///|
pub fn Palette2::length(self : Palette2) -> Int {
  self.colors.length()
}

///|
pub fn Palette2::sample(self : Palette2, ratio : Double) -> Rgba {
  palette_lerp(self.colors, ratio)
}

///|
pub fn Palette2::nearest(self : Palette2, color : Rgba) -> Rgba {
  palette_nearest(self.colors, color)
}

///|
pub fn Palette2::reverse(self : Palette2) -> Palette2 {
  let colors : Array[Rgba] = []
  let mut index = self.colors.length()
  while index > 0 {
    index = index - 1
    colors.push(self.colors[index])
  }
  palette2(self.name, colors)
}

///|
pub fn Palette2::lerp(
  self : Palette2,
  other : Palette2,
  ratio : Double,
) -> Palette2 {
  let count = self.colors.length().max(other.colors.length())
  let colors : Array[Rgba] = []
  for index in 0.. Palette2 {
  let colors : Array[Rgba] = []
  for color in self.colors {
    colors.push(color.with_alpha(alpha))
  }
  palette2(self.name, colors)
}

///|
pub fn Palette2::map(self : Palette2, mapper : (Rgba) -> Rgba) -> Palette2 {
  let colors : Array[Rgba] = []
  for color in self.colors {
    colors.push(mapper(color))
  }
  palette2(self.name, colors)
}

///|
pub struct PaletteQuantization2 {
  index : Int
  color : Rgba
  error : Double
} derive(Debug)

///|
pub fn PaletteQuantization2::index(self : PaletteQuantization2) -> Int {
  self.index
}

///|
pub fn PaletteQuantization2::color(self : PaletteQuantization2) -> Rgba {
  self.color
}

///|
pub fn PaletteQuantization2::error(self : PaletteQuantization2) -> Double {
  self.error
}

///|
pub fn Palette2::quantize(
  self : Palette2,
  color : Rgba,
) -> PaletteQuantization2 {
  if self.colors.length() == 0 {
    return { index: -1, color, error: 0.0 }
  }
  let mut best_index = 0
  let mut best_error = self.colors[0].distance(color)
  for index in 1.. Array[PaletteQuantization2] {
  let result : Array[PaletteQuantization2] = []
  for color in colors {
    result.push(palette.quantize(color))
  }
  result
}

///|
pub fn palette_average(colors : Array[Rgba]) -> Rgba {
  if colors.length() == 0 {
    return rgba(r=0.0, g=0.0, b=0.0, a=0.0)
  }
  let mut red = 0.0
  let mut green = 0.0
  let mut blue = 0.0
  let mut alpha = 0.0
  for color in colors {
    red = red + color.red()
    green = green + color.green()
    blue = blue + color.blue()
    alpha = alpha + color.alpha()
  }
  let count = colors.length().to_double()
  rgba(r=red / count, g=green / count, b=blue / count, a=alpha / count)
}

///|
pub fn palette_variance(colors : Array[Rgba]) -> Double {
  let average = palette_average(colors)
  if colors.length() == 0 {
    return 0.0
  }
  let mut sum = 0.0
  for color in colors {
    let distance = color.distance(average)
    sum = sum + distance * distance
  }
  sum / colors.length().to_double()
}

///|
pub fn palette_gradient2(start : Rgba, end : Rgba, count : Int) -> Palette2 {
  let colors : Array[Rgba] = []
  let actual = count.max(1)
  for index in 0..