///|
pub fn StaticIntMap::merge(
  self : StaticIntMap,
  other : StaticIntMap,
  policy : MapMergePolicy,
) -> Result[StaticIntMap, FuseError] {
  let entries : Array[FuseValue] = []
  let mut left = 0
  let mut right = 0
  while left < self.len() || right < other.len() {
    if right == other.len() ||
      (
        left < self.len() &&
        self.index.sorted_hashes[left] < other.index.sorted_hashes[right]
      ) {
      entries.push({
        hash: self.index.sorted_hashes[left],
        value: self.values[left],
      })
      left = left + 1
    } else if left == self.len() ||
      other.index.sorted_hashes[right] < self.index.sorted_hashes[left] {
      entries.push({
        hash: other.index.sorted_hashes[right],
        value: other.values[right],
      })
      right = right + 1
    } else {
      let hash = self.index.sorted_hashes[left]
      let left_value = self.values[left]
      let right_value = other.values[right]
      match resolve_value(hash, left_value, right_value, policy) {
        Ok(value) => entries.push({ hash, value })
        Err(error) => return Err(error)
      }
      left = left + 1
      right = right + 1
    }
  }
  StaticIntMap::build(entries)
}

///|
pub fn StaticIntMap::intersection(
  self : StaticIntMap,
  other : StaticIntMap,
  policy : MapMergePolicy,
) -> Result[StaticIntMap, FuseError] {
  let entries : Array[FuseValue] = []
  let mut left = 0
  let mut right = 0
  while left < self.len() && right < other.len() {
    let left_hash = self.index.sorted_hashes[left]
    let right_hash = other.index.sorted_hashes[right]
    if left_hash < right_hash {
      left = left + 1
    } else if right_hash < left_hash {
      right = right + 1
    } else {
      let left_value = self.values[left]
      let right_value = other.values[right]
      match resolve_value(left_hash, left_value, right_value, policy) {
        Ok(value) => entries.push({ hash: left_hash, value })
        Err(error) => return Err(error)
      }
      left = left + 1
      right = right + 1
    }
  }
  StaticIntMap::build(entries)
}

///|
pub fn StaticIntMap::difference(
  self : StaticIntMap,
  other : StaticIntMap,
) -> Result[StaticIntMap, FuseError] {
  let entries : Array[FuseValue] = []
  let mut left = 0
  let mut right = 0
  while left < self.len() {
    if right == other.len() {
      entries.push({
        hash: self.index.sorted_hashes[left],
        value: self.values[left],
      })
      left = left + 1
    } else if self.index.sorted_hashes[left] < other.index.sorted_hashes[right] {
      entries.push({
        hash: self.index.sorted_hashes[left],
        value: self.values[left],
      })
      left = left + 1
    } else if self.index.sorted_hashes[left] == other.index.sorted_hashes[right] {
      left = left + 1
      right = right + 1
    } else {
      right = right + 1
    }
  }
  StaticIntMap::build(entries)
}

///|
pub fn StaticIntMap::merge_stats(
  self : StaticIntMap,
  other : StaticIntMap,
) -> MapMergeStats {
  let mut left = 0
  let mut right = 0
  let mut left_only_count = 0
  let mut right_only_count = 0
  let mut shared_count = 0
  let mut conflicting_count = 0
  while left < self.len() || right < other.len() {
    if right == other.len() ||
      (
        left < self.len() &&
        self.index.sorted_hashes[left] < other.index.sorted_hashes[right]
      ) {
      left_only_count = left_only_count + 1
      left = left + 1
    } else if left == self.len() ||
      other.index.sorted_hashes[right] < self.index.sorted_hashes[left] {
      right_only_count = right_only_count + 1
      right = right + 1
    } else {
      shared_count = shared_count + 1
      if self.values[left] != other.values[right] {
        conflicting_count = conflicting_count + 1
      }
      left = left + 1
      right = right + 1
    }
  }
  { left_only_count, right_only_count, shared_count, conflicting_count }
}

///|
fn resolve_value(
  hash : Int,
  left : Int,
  right : Int,
  policy : MapMergePolicy,
) -> Result[Int, FuseError] {
  if left == right {
    return Ok(left)
  }
  match policy {
    PreferLeft => Ok(left)
    PreferRight => Ok(right)
    RejectConflicts => Err(ConflictingValue(hash))
  }
}