///|
pub fn StateVector::is_empty(self : StateVector) -> Bool {
  self.clocks.is_empty()
}

///|
pub fn StateVector::length(self : StateVector) -> Int32 {
  self.clocks.length()
}

///|
pub fn StateVector::contains_client(
  self : StateVector,
  client : UInt64,
) -> Bool {
  self.clocks.contains(client)
}

///|
/// Upstream containment includes the boundary clock. Use Snapshot::is_visible
/// when testing whether a concrete item is visible in a document version.
pub fn StateVector::contains(self : StateVector, id : Id) -> Bool {
  id.clock <= self.get(id.client)
}

///|
pub fn StateVector::inc_by(
  self : StateVector,
  client : UInt64,
  delta : UInt32,
) -> Unit {
  if delta > 0U {
    let clock = self.get(client)
    guard delta <= 0xffff_ffffU - clock else {
      abort("state vector clock overflow")
    }
    self.clocks[client] = clock + delta
  }
}

///|
pub fn StateVector::set_min(
  self : StateVector,
  client : UInt64,
  clock : UInt32,
) -> Unit {
  self.clocks[client] = self.clocks
    .get(client)
    .map(fn(old) { old.min(clock) })
    .unwrap_or(clock)
}

///|
pub fn StateVector::merge(self : StateVector, other : StateVector) -> Unit {
  for client, clock in other.clocks {
    self.set_max(client, clock)
  }
}

///|
/// Returns -1, 0 or 1 for ordered vectors, or None for concurrent versions.
pub fn StateVector::partial_compare(
  self : StateVector,
  other : StateVector,
) -> Int32? {
  let mut less = false
  let mut greater = false
  for (client, _) in self.entries() + other.entries() {
    let a = self.get(client)
    let b = other.get(client)
    if a < b {
      less = true
    }
    if a > b {
      greater = true
    }
    if less && greater {
      return None
    }
  }
  Some(if less { -1 } else if greater { 1 } else { 0 })
}

///|
pub impl Eq for StateVector with fn equal(self, other) {
  self.entries() == other.entries()
}

///|
pub fn StateVector::iter(self : StateVector) -> Iter[(UInt64, UInt32)] {
  self.entries().iter()
}

///|
fn metadata_v2(bytes : Bytes) -> Bytes raise DecodeError {
  let writer = UpdateWriter::make(true)
  for byte in bytes {
    writer.raw.bytes.push(byte)
  }
  writer.finish()
}

///|
fn metadata_v1(bytes : Bytes) -> Bytes raise DecodeError {
  let reader = UpdateReader::make(bytes, true, 1000000)
  let payload = reader.raw.bytes[reader.raw.offset:].to_owned()
  reader.raw.offset = reader.raw.bytes.length()
  reader.finish()
  payload
}

///|
pub fn StateVector::encode_v2(self : StateVector) -> Bytes raise DecodeError {
  metadata_v2(self.encode_v1())
}

///|
pub fn StateVector::decode_v2(bytes : Bytes) -> StateVector raise DecodeError {
  StateVector::decode_v1(metadata_v1(bytes))
}

///|
pub fn StickyIndex::encode_v2(self : StickyIndex) -> Bytes raise DecodeError {
  let writer = UpdateWriter::make(true)
  match self.scope {
    Relative(id) => {
      writer.raw.uint(0UL)
      writer.raw.id(id)
    }
    Root(name) => {
      writer.raw.uint(1UL)
      writer.string(name)
    }
    Nested(id) => {
      writer.raw.uint(2UL)
      writer.raw.id(id)
    }
  }
  writer.raw.signed(if self.assoc == Before { -1L } else { 0L })
  writer.finish()
}

///|
pub fn StickyIndex::decode_v2(bytes : Bytes) -> StickyIndex raise DecodeError {
  let reader = UpdateReader::make(bytes, true, 1000000)
  let scope : IndexScope = match reader.raw.uint() {
    0UL => Relative(reader.raw.id())
    1UL => Root(reader.string())
    2UL => Nested(reader.raw.id())
    _ => raise InvalidData("invalid sticky index scope")
  }
  let assoc = reader.raw.signed()
  guard assoc >= -128L && assoc <= 127L else {
    raise InvalidData("invalid association")
  }
  reader.finish()
  { scope, assoc: if assoc < 0L { Before } else { After }, }
}

///|
pub impl Eq for Snapshot with fn equal(self, other) {
  self.state == other.state && self.deletes == other.deletes
}

///|
pub extend StateVector with Eq::{equal, not_equal}

///|
pub extend Snapshot with Eq::{equal, not_equal}

///|
pub extend StateVector with Debug::{to_repr}

///|
pub extend Snapshot with Debug::{to_repr}