///|
// Version vector tracking per-peer counters.
pub struct VersionVector {
  entries : Map[PeerID, Counter]
}

///|
pub fn VersionVector::new() -> VersionVector {
  VersionVector::{ entries: Map::new() }
}

///|
pub fn VersionVector::get(self : VersionVector, peer : PeerID) -> Counter {
  match self.entries.get(peer) {
    Some(v) => v
    None => 0
  }
}

///|
pub fn VersionVector::set(
  self : VersionVector,
  peer : PeerID,
  counter : Counter,
) -> Unit {
  self.entries[peer] = counter
}

///|
pub fn VersionVector::merge(
  self : VersionVector,
  other : VersionVector,
) -> Unit {
  for peer, counter in other.entries {
    let current = self.get(peer)
    if counter > current {
      self.entries[peer] = counter
    }
  }
}

///|
pub fn VersionVector::includes_id(self : VersionVector, id : ID) -> Bool {
  self.get(id.peer) > id.counter
}

///|
pub fn VersionVector::extend_to_include_last_id(
  self : VersionVector,
  id : ID,
) -> Unit {
  let next = id.counter + 1
  if next > self.get(id.peer) {
    self.entries[id.peer] = next
  }
}

///|
pub fn VersionVector::is_empty(self : VersionVector) -> Bool {
  self.entries.length() == 0
}

///|
pub fn VersionVector::to_map(self : VersionVector) -> Map[PeerID, Counter] {
  self.entries
}

///|
// A minimal frontiers set (current heads of the DAG).
pub struct Frontiers {
  mut ids : Array[ID]
} derive(Show)

///|
pub fn Frontiers::new() -> Frontiers {
  Frontiers::{ ids: [] }
}

///|
pub fn Frontiers::clone(self : Frontiers) -> Frontiers {
  let ids : Array[ID] = []
  for id in self.ids {
    ids.push(id)
  }
  Frontiers::{ ids, }
}

///|
pub fn Frontiers::from_array(ids : Array[ID]) -> Frontiers {
  Frontiers::{ ids, }
}

///|
pub fn Frontiers::from_id(id : ID) -> Frontiers {
  Frontiers::{ ids: [id] }
}

///|
pub fn Frontiers::as_view(self : Frontiers) -> ArrayView[ID] {
  self.ids[:]
}

///|
pub fn Frontiers::push(self : Frontiers, id : ID) -> Unit {
  let mut replaced = false
  for i = 0; i < self.ids.length(); i = i + 1 {
    if self.ids[i].peer == id.peer {
      if self.ids[i].counter < id.counter {
        self.ids[i] = id
      }
      replaced = true
      break
    }
  }
  if !replaced {
    self.ids.push(id)
  }
}

///|
pub fn Frontiers::len(self : Frontiers) -> Int {
  self.ids.length()
}

///|
pub fn Frontiers::is_empty(self : Frontiers) -> Bool {
  self.ids.length() == 0
}

///|
pub fn Frontiers::as_single(self : Frontiers) -> ID? {
  if self.ids.length() == 1 {
    Some(self.ids[0])
  } else {
    None
  }
}

///|
pub fn Frontiers::iter(self : Frontiers) -> ArrayView[ID] {
  self.ids[:]
}

///|
pub fn Frontiers::contains(self : Frontiers, id : ID) -> Bool {
  for item in self.ids {
    if item == id {
      return true
    }
  }
  false
}

///|
pub fn Frontiers::remove(self : Frontiers, id : ID) -> Unit {
  let next : Array[ID] = []
  for item in self.ids {
    if item != id {
      next.push(item)
    }
  }
  self.ids = next
}

///|
pub fn Frontiers::update_on_new_change(
  self : Frontiers,
  id_last : ID,
  deps : Frontiers,
) -> Unit {
  let next : Array[ID] = []
  for item in self.ids {
    if !deps.contains(item) {
      next.push(item)
    }
  }
  self.ids = next
  self.push(id_last)
}