///|
/// LZW diagnostics for compression tuning and test fixtures.
pub(all) struct LzwRoundtripReport {
  input_bytes : Int
  compressed_bytes : Int
  decoded_bytes : Int
  minimum_code_size : Int
  exact : Bool
  saving_per_mille : Int
} derive(Debug)

///|
pub fn LzwRoundtripReport::input_bytes(self : LzwRoundtripReport) -> Int {
  self.input_bytes
}

///|
pub fn LzwRoundtripReport::compressed_bytes(self : LzwRoundtripReport) -> Int {
  self.compressed_bytes
}

///|
pub fn LzwRoundtripReport::decoded_bytes(self : LzwRoundtripReport) -> Int {
  self.decoded_bytes
}

///|
pub fn LzwRoundtripReport::minimum_code_size(self : LzwRoundtripReport) -> Int {
  self.minimum_code_size
}

///|
pub fn LzwRoundtripReport::exact(self : LzwRoundtripReport) -> Bool {
  self.exact
}

///|
pub fn LzwRoundtripReport::saving_per_mille(self : LzwRoundtripReport) -> Int {
  self.saving_per_mille
}

///|
/// Encode and decode an index stream, reporting exactness and size.
pub fn lzw_roundtrip(
  indices : ArrayView[Byte],
  minimum_code_size : Int,
) -> LzwRoundtripReport raise GifError {
  let encoded = lzw_encode(indices, minimum_code_size)
  let decoded = lzw_decode(encoded, minimum_code_size, indices.length())
  let mut exact = decoded.length() == indices.length()
  if exact {
    for i in 0.. Int {
  let safe = if colors < 2 { 2 } else if colors > 256 { 256 } else { colors }
  let mut bits = 1
  let mut size = 2
  while size < safe {
    size *= 2
    bits += 1
  }
  if bits < 2 {
    2
  } else {
    bits
  }
}

///|
/// Split indices into bounded slices for streaming quantization work.
pub fn split_indices(
  indices : ArrayView[Byte],
  chunk_size : Int,
) -> Array[Array[Byte]] {
  let safe = if chunk_size < 1 { 1 } else { chunk_size }
  let result : Array[Array[Byte]] = []
  let mut offset = 0
  while offset < indices.length() {
    let part : Array[Byte] = []
    let remaining = indices.length() - offset
    let length = if remaining > safe { safe } else { remaining }
    for i in 0.. Array[Byte] {
  let result : Array[Byte] = []
  for part in parts {
    for byte in part {
      result.push(byte)
    }
  }
  result
}

///|
/// Return the number of runs in a palette index stream.
pub fn lzw_run_count(indices : ArrayView[Byte]) -> Int {
  if indices.length() == 0 {
    0
  } else {
    let mut count = 1
    for i in 1.. Array[Byte] {
  let safe_length = if length < 0 { 0 } else { length }
  let safe_colors = if colors < 1 {
    1
  } else if colors > 256 {
    256
  } else {
    colors
  }
  let result : Array[Byte] = []
  for i in 0..