///|
/// Computes the distance between two points under this metric.
pub fn DistanceMetric::distance(
  self : DistanceMetric,
  ax : Double,
  ay : Double,
  bx : Double,
  by : Double,
) -> Double {
  let dx = ax - bx
  let dy = ay - by
  match self {
    DistanceMetric::Euclidean => Double::sqrt(dx * dx + dy * dy)
    DistanceMetric::RoundedEuclidean =>
      Double::round(Double::sqrt(dx * dx + dy * dy))
    DistanceMetric::Manhattan => Double::abs(dx) + Double::abs(dy)
    DistanceMetric::Chebyshev => Double::max(Double::abs(dx), Double::abs(dy))
  }
}

///|
/// Returns the conventional name used by benchmark formats.
pub fn DistanceMetric::name(self : DistanceMetric) -> String {
  match self {
    DistanceMetric::Euclidean => "EUCLIDEAN"
    DistanceMetric::RoundedEuclidean => "EUC_2D"
    DistanceMetric::Manhattan => "MANHATTAN"
    DistanceMetric::Chebyshev => "CHEBYSHEV"
  }
}

///|
/// All built-in metrics are symmetric and satisfy d(a, a) = 0.
pub fn DistanceMetric::is_symmetric(self : DistanceMetric) -> Bool {
  match self {
    DistanceMetric::Euclidean => true
    DistanceMetric::RoundedEuclidean => true
    DistanceMetric::Manhattan => true
    DistanceMetric::Chebyshev => true
  }
}

///|
/// Returns a compact summary of a matrix for regression tests and diagnostics.
pub fn VrpInstance::distance_summary(
  self : VrpInstance,
) -> (Double, Double, Double) {
  let mut minimum = 1_000_000_000.0
  let mut maximum = 0.0
  let mut total = 0.0
  let mut count = 0
  for row in self.distance_matrix {
    for value in row {
      if value > 0.0 {
        minimum = Double::min(minimum, value)
        maximum = Double::max(maximum, value)
        total += value
        count += 1
      }
    }
  }
  let average = if count == 0 { 0.0 } else { total / Double::from_int(count) }
  (minimum, maximum, average)
}

///|
/// Returns the nearest customer indices, ordered by distance from a point.
pub fn VrpInstance::nearest_customers(
  self : VrpInstance,
  from_index : Int,
  limit : Int,
) -> Array[Int] {
  let ranked : Array[(Int, Double)] = []
  for index in 0..