///|
/// Return a copy sorted by inclusive first position, then last position.
pub fn sort_ranges(input : Array[ConcreteRange]) -> Array[ConcreteRange] {
  let ranges = input.copy()
  ranges.sort_by(fn(a, b) {
    if a.first() < b.first() {
      -1
    } else if a.first() > b.first() {
      1
    } else if a.last() < b.last() {
      -1
    } else if a.last() > b.last() {
      1
    } else {
      0
    }
  })
  ranges
}

///|
/// Merge overlapping ranges, preserving gaps and not merging adjacency.
pub fn merge_overlapping_ranges(
  input : Array[ConcreteRange],
) -> Array[ConcreteRange] {
  merge_sorted(input, false)
}

///|
/// Merge ranges that overlap or touch at one byte boundary.
pub fn merge_adjacent_ranges(
  input : Array[ConcreteRange],
) -> Array[ConcreteRange] {
  merge_sorted(input, true)
}

///|
/// Server-side planning helper: sort, then merge overlap and adjacency.
pub fn coalesce_ranges(input : Array[ConcreteRange]) -> Array[ConcreteRange] {
  merge_sorted(input, true)
}

///|
fn merge_sorted(
  input : Array[ConcreteRange],
  include_adjacent : Bool,
) -> Array[ConcreteRange] {
  let sorted = sort_ranges(input)
  let output : Array[ConcreteRange] = []
  for current in sorted {
    if output.is_empty() {
      output.push(current)
      continue
    }
    let previous = output[output.length() - 1]
    let overlap = current.first() <= previous.last()
    let adjacent = include_adjacent &&
      previous.last() != @int64.MAX_VALUE &&
      current.first() == previous.last() + 1L
    if overlap || adjacent {
      let last = if current.last() > previous.last() {
        current.last()
      } else {
        previous.last()
      }
      output[output.length() - 1] = concrete_range(previous.first(), last)
    } else {
      output.push(current)
    }
  }
  output
}