///|
pub fn order_sort(
  entries : Array[OrderEntry],
  bias_right? : Bool = false,
) -> OrderResult {
  let parts = partition(entries, entry => entry.barycenter is Some(_))
  let sortable = parts.lhs
  let unsortable = parts.rhs
  unsortable.sort_by((a, b) => b.i - a.i)
  sortable.sort_by(order_compare_with_bias(bias_right))
  let vs : Array[String] = []
  let mut sum = 0.0
  let mut weight = 0.0
  let mut vs_index = 0
  vs_index = order_consume_unsortable(vs, unsortable, vs_index)
  for entry in sortable {
    vs_index = vs_index + entry.vs.length()
    for v in entry.vs {
      vs.push(v)
    }
    if entry.barycenter is Some(entry_bary) &&
      entry.weight is Some(entry_weight) {
      sum = sum + entry_bary * entry_weight
      weight = weight + entry_weight
    }
    vs_index = order_consume_unsortable(vs, unsortable, vs_index)
  }
  if weight > 0.0 {
    { vs, barycenter: Some(sum / weight), weight: Some(weight) }
  } else {
    { vs, barycenter: None, weight: None }
  }
}

///|
fn order_consume_unsortable(
  vs : Array[String],
  unsortable : Array[OrderEntry],
  index : Int,
) -> Int {
  let mut out_index = index
  while !unsortable.is_empty() {
    let last = unsortable[unsortable.length() - 1]
    if last.i <= out_index {
      ignore(unsortable.pop())
      for v in last.vs {
        vs.push(v)
      }
      out_index = out_index + 1
    } else {
      break
    }
  }
  out_index
}

///|
fn order_compare_with_bias(bias : Bool) -> (OrderEntry, OrderEntry) -> Int {
  (entry_v : OrderEntry, entry_w : OrderEntry) => {
    match (entry_v.barycenter, entry_w.barycenter) {
      (Some(v), Some(w)) =>
        if v < w {
          -1
        } else if v > w {
          1
        } else if !bias {
          entry_v.i - entry_w.i
        } else {
          entry_w.i - entry_v.i
        }
      _ => 0
    }
  }
}