///|
pub fn Feedback::to_wire(self : Feedback) -> Symbols {
  let generation = self.generation()
  let out : Array[Int32] = [
    77,
    82,
    70,
    49,
    generation.count(),
    generation.width() >> 8,
    generation.width() & 255,
    self.rank(),
  ]
  write_u32(out, generation.id().reinterpret_as_uint())
  write_u32(out, generation.length().reinterpret_as_uint())
  write_u32(out, (24 + self.rank()).reinterpret_as_uint())
  for pivot in self.pivot_values {
    out.push(pivot)
  }
  let checksum = crc32({ values: out, })
  write_u32(out, checksum)
  { values: out, }
}

///|
pub fn feedback_from_wire(frame : Symbols) -> Feedback raise CodeError {
  if frame.length() < 24 {
    raise Truncated
  }
  if frame.length() > 88 {
    raise Limit("feedback frame exceeds 88 bytes")
  }
  let values = frame.values
  if values[0] != 77 || values[1] != 82 || values[2] != 70 || values[3] != 49 {
    raise Corrupt("unknown feedback magic/version")
  }
  let count = values[4]
  let width = (values[5] << 8) | values[6]
  let rank = values[7]
  if count < 1 || count > 64 || width < 1 || width > 4096 || rank > count {
    raise Limit("feedback dimensions/rank invalid")
  }
  if read_u31(values, 16) != 24 + rank || frame.length() != 24 + rank {
    raise Shape("feedback frame length mismatch")
  }
  let end = frame.length() - 4
  if read_u32(values, end) != crc32(frame.slice(0, end)) {
    raise Corrupt("feedback CRC32 mismatch")
  }
  let generation = Generation::new(
    read_u31(values, 8),
    count,
    width,
    read_u31(values, 12),
  )
  let pivots : Array[Int32] = []
  for i in 20..