///|
pub fn compute_block_point_bases(
  block_order : Array[Int],
  block_lengths : Array[Int],
) -> Array[Int] {
  let block_count = block_order.length()
  let block_idx_by_order : Array[Int] = Array::make(block_count, 0)
  for block_idx in 0..= 0 && ord < block_count {
      block_idx_by_order[ord] = block_idx
    }
  }
  let block_point_base : Array[Int] = Array::make(block_count, 0)
  let mut point_cursor = 0
  for ord in 0..= 0 && block_idx < block_lengths.length() {
      block_lengths[block_idx]
    } else {
      0
    }
    point_cursor = point_cursor + (block_len + 2) * 2
  }
  block_point_base
}

///|
pub fn lower_bound_sorted_int(values : Array[Int], target : Int) -> Int {
  let mut lo = 0
  let mut hi = values.length()
  while lo < hi {
    let mid = lo + (hi - lo) / 2
    if values[mid] < target {
      lo = mid + 1
    } else {
      hi = mid
    }
  }
  lo
}

///|
pub fn upper_bound_sorted_int(values : Array[Int], target : Int) -> Int {
  let mut lo = 0
  let mut hi = values.length()
  while lo < hi {
    let mid = lo + (hi - lo) / 2
    if values[mid] <= target {
      lo = mid + 1
    } else {
      hi = mid
    }
  }
  lo
}

///|
pub fn count_sorted_ints_in_open_range(
  values : Array[Int],
  start : Int,
  end : Int,
) -> Int {
  let first_after_start = upper_bound_sorted_int(values, start)
  let first_at_or_after_end = lower_bound_sorted_int(values, end)
  if first_after_start < first_at_or_after_end {
    first_at_or_after_end - first_after_start
  } else {
    0
  }
}