///|
pub(all) struct BundleSplitRange {
  range_id : Int
  start : ProgramPoint?
  end : ProgramPoint?
} derive(Eq, Debug)

///|
pub(all) enum BundleSplitPlan {
  Split(Array[Int], Array[Int])
  Minimal
  SpillWhole
} derive(Debug)

///|
pub fn plan_bundle_split(
  ranges : Array[BundleSplitRange],
  split_point : ProgramPoint?,
  block_order : Array[Int],
  spillset_split_count : Int,
  max_splits_per_spillset : Int,
) -> BundleSplitPlan {
  if spillset_split_count >= max_splits_per_spillset {
    return Minimal
  }
  guard split_point is Some(point) else { return SpillWhole }
  let before_ranges : Array[Int] = []
  let after_ranges : Array[Int] = []
  for range in ranges {
    if range.end is Some(end_point) {
      if end_point.compare_with_order(point, block_order) <= 0 {
        before_ranges.push(range.range_id)
      } else if range.start is Some(start_point) {
        if start_point.compare_with_order(point, block_order) >= 0 {
          after_ranges.push(range.range_id)
        } else {
          after_ranges.push(range.range_id)
        }
      } else {
        after_ranges.push(range.range_id)
      }
    } else {
      after_ranges.push(range.range_id)
    }
  }
  if before_ranges.is_empty() || after_ranges.is_empty() {
    Minimal
  } else {
    Split(before_ranges, after_ranges)
  }
}