///|
/// Per-replica difference between two causal frontiers.
pub(all) struct VectorDifference {
  replica : String
  left_counter : Int
  right_counter : Int
} derive(Eq, Debug)

///|
/// Component differences, including replicas present on only one side.
pub fn vector_differences(
  left : VersionVector,
  right : VersionVector,
) -> Array[VectorDifference] {
  let output : Array[VectorDifference] = []
  for entry in left.entries() {
    let remote = right.counter(entry.replica)
    if entry.counter != remote {
      output.push({
        replica: entry.replica,
        left_counter: entry.counter,
        right_counter: remote,
      })
    }
  }
  for entry in right.entries() {
    if left.counter(entry.replica) == 0 && entry.counter != 0 {
      output.push({
        replica: entry.replica,
        left_counter: 0,
        right_counter: entry.counter,
      })
    }
  }
  output
}

///|
/// Component-wise minimum, representing history known by both sides.
pub fn common_frontier(
  left : VersionVector,
  right : VersionVector,
) -> VersionVector {
  let entries : Array[VectorEntry] = []
  for entry in left.entries() {
    let remote = right.counter(entry.replica)
    let minimum = if entry.counter < remote { entry.counter } else { remote }
    if minimum > 0 {
      entries.push({ replica: entry.replica, counter: minimum })
    }
  }
  VersionVector::from_entries(entries).unwrap()
}

///|
/// Number of events present on the left but absent on the right.
pub fn events_missing_on_right(
  left : VersionVector,
  right : VersionVector,
) -> Int {
  let mut total = 0
  for entry in left.entries() {
    let remote = right.counter(entry.replica)
    if entry.counter > remote {
      total = total + entry.counter - remote
    }
  }
  total
}

///|
/// Symmetric Manhattan distance between causal frontiers.
pub fn vector_distance(left : VersionVector, right : VersionVector) -> Int {
  events_missing_on_right(left, right) + events_missing_on_right(right, left)
}

///|
/// Whether two frontiers have exactly the same replica counters.
pub fn same_frontier(left : VersionVector, right : VersionVector) -> Bool {
  left.compare(right) == Equal
}

///|
/// Replicas on which the left frontier is ahead.
pub fn replicas_ahead(
  left : VersionVector,
  right : VersionVector,
) -> Array[String] {
  let output : Array[String] = []
  for entry in left.entries() {
    if entry.counter > right.counter(entry.replica) {
      output.push(entry.replica)
    }
  }
  output
}