/// Walks the bundled animation and prints every composited frame with its
/// duration and a checksum. Each frame is already the whole canvas, so the
/// pictures below show what a viewer would actually display.

///|
fn channel(pixel : UInt, shift : Int) -> Int {
  (pixel >> shift).reinterpret_as_int() & 0xff
}

///|
fn over_gray(pixel : UInt) -> (Int, Int, Int) {
  let alpha = channel(pixel, 24)
  let rest = 255 - alpha
  (
    (channel(pixel, 16) * alpha + 96 * rest + 127) / 255,
    (channel(pixel, 8) * alpha + 96 * rest + 127) / 255,
    (channel(pixel, 0) * alpha + 96 * rest + 127) / 255,
  )
}

///|
fn render(width : Int, height : Int, pixels : Array[UInt]) -> Unit {
  let mut y = 0
  while y < height {
    let line = StringBuilder()
    let mut top_painted : (Int, Int, Int) = (-1, -1, -1)
    let mut bottom_painted : (Int, Int, Int) = (-1, -1, -1)
    for x in 0.. UInt {
  let mut hash = 2166136261U
  for pixel in pixels {
    for shift in [24, 16, 8, 0] {
      hash = (hash ^ ((pixel >> shift) & 0xffU)) * 16777619U
    }
  }
  hash
}

///|
fn hex(value : UInt) -> String {
  let builder = StringBuilder()
  for shift in [28, 24, 20, 16, 12, 8, 4, 0] {
    let digit = ((value >> shift) & 0xfU).reinterpret_as_int()
    let code = if digit < 10 { 48 + digit } else { 87 + digit }
    builder.write_char(code.unsafe_to_char())
  }
  builder.to_string()
}

///|
/// How many pixels this frame changed compared with the previous one.
fn changes(previous : Array[UInt]?, current : Array[UInt]) -> Int {
  match previous {
    None => current.length()
    Some(before) => {
      let mut count = 0
      for index in 0..