///|
fn longest_route_index(solution : @core.Solution) -> Int? {
  if solution.routes.is_empty() {
    return None
  }
  let mut chosen = 0
  let mut longest = -1.0
  for index, route in solution.routes {
    if route.metrics.distance > longest {
      chosen = index
      longest = route.metrics.distance
    }
  }
  Some(chosen)
}

///|
fn route_destroy_impl(
  instance : @core.VrpInstance,
  solution : @core.Solution,
  remove_count : Int,
  rng : @rng.Rng,
  config : @core.SolveConfig,
) -> @core.Solution {
  let route_index = match longest_route_index(solution) {
    None => return solution.copy()
    Some(index) => index
  }
  let route = solution.routes[route_index]
  let pool = route.stops.copy()
  if pool.is_empty() {
    return solution.copy()
  }
  let target = remove_count.max(1).min(pool.length())
  let picks : Array[Int] = []
  for _ in 0.. @core.Solution {
  let count = remove_count.max(1)
  let assigned = solution.assigned_customers()
  if assigned.is_empty() {
    return solution.copy()
  }
  let seed = assigned[rng.next_int(assigned.length())]
  let ranked = assigned.copy()
  ranked.sort_by(fn(left, right) {
    let left_customer = instance.customer(left)
    let right_customer = instance.customer(right)
    let left_score = instance.distance(seed, left) +
      Double::abs(left_customer.demand() - instance.customer(seed).demand())
    let right_score = instance.distance(seed, right) +
      Double::abs(right_customer.demand() - instance.customer(seed).demand())
    Double::compare(left_score, right_score)
  })
  remove_customers(
    instance,
    solution,
    ranked[:count.min(ranked.length())].to_owned(),
    config,
  )
}

///|
fn customer_tightness(
  instance : @core.VrpInstance,
  customer_index : Int,
) -> Double {
  let customer = instance.customer(customer_index)
  customer.due_time() - customer.ready_time()
}

///|
fn time_critical_destroy_impl(
  instance : @core.VrpInstance,
  solution : @core.Solution,
  remove_count : Int,
  config : @core.SolveConfig,
) -> @core.Solution {
  let ranked = solution.assigned_customers()
  ranked.sort_by(fn(left, right) {
    let order = Double::compare(
      customer_tightness(instance, left),
      customer_tightness(instance, right),
    )
    if order == 0 {
      left - right
    } else {
      order
    }
  })
  let take = remove_count.max(1).min(ranked.length())
  remove_customers(instance, solution, ranked[:take].to_owned(), config)
}

///|
/// Removes a bounded fraction of assigned stops with a selected strategy.
pub fn destroy_fraction(
  instance : @core.VrpInstance,
  solution : @core.Solution,
  operator : @core.DestroyOperator,
  numerator : Int,
  denominator : Int,
  seed : Int,
  config : @core.SolveConfig,
) -> @core.Solution {
  let assigned = solution.assigned_customers().length()
  let safe_denominator = denominator.max(1)
  let count = (assigned * numerator.max(0) / safe_denominator)
    .max(1)
    .min(assigned)
  destroy_solution(instance, solution, operator, count, seed~, config)
}