///|
fn validate_metric_resolution(resolution : Int) -> Result[Unit, H3Error] {
  if resolution < 0 || resolution > MAX_RESOLUTION {
    Err(InvalidResolution(resolution))
  } else {
    Ok(())
  }
}

///|
/// Earth radius in kilometers, matching the H3 reference implementation.
pub const EARTH_RADIUS_KM : Double = 6371.007180918475

///|
/// Returns the total number of H3 cells at a resolution.
pub fn resolution_cell_count(resolution : Int) -> Result[UInt64, H3Error] {
  match validate_metric_resolution(resolution) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let mut power = 1UL
  let mut current = 0
  while current < resolution {
    power *= 7UL
    current += 1
  }
  Ok(2UL + 120UL * power)
}

///|
/// Returns the average H3 cell area in square kilometers.
pub fn average_cell_area_km2(resolution : Int) -> Result[Double, H3Error] {
  match validate_metric_resolution(resolution) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let areas : Array[Double] = [
    4.357449416078383e+06, 6.097884417941332e+05, 8.680178039899720e+04, 1.239343465508816e+04,
    1.770347654491307e+03, 2.529038581819449e+02, 3.612906216441245e+01, 5.161293359717191e+00,
    7.373275975944177e-01, 1.053325134272067e-01, 1.504750190766435e-02, 2.149643129451879e-03,
    3.070918756316060e-04, 4.387026794728296e-05, 6.267181135324313e-06, 8.953115907605790e-07,
  ]
  Ok(areas[resolution])
}

///|
/// Returns the average H3 cell edge length in kilometers.
pub fn average_edge_length_km(resolution : Int) -> Result[Double, H3Error] {
  match validate_metric_resolution(resolution) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let lengths : Array[Double] = [
    1281.256011, 483.0568391, 182.5129565, 68.97922179, 26.07175968, 9.854090990,
    3.724532667, 1.406475763, 0.531414010, 0.200786148, 0.075863783, 0.028663897,
    0.010830188, 0.004092010, 0.001546100, 0.000584169,
  ]
  Ok(lengths[resolution])
}

///|
/// Returns the great-circle distance between two cell centers in kilometers.
pub fn H3Index::great_circle_distance_km(
  self : H3Index,
  other : H3Index,
) -> Double {
  let left = self.to_lat_lng()
  let right = other.to_lat_lng()
  let left_latitude = left.latitude() / DEGREES_PER_RADIAN
  let right_latitude = right.latitude() / DEGREES_PER_RADIAN
  let latitude_delta = right_latitude - left_latitude
  let longitude_delta = (right.longitude() - left.longitude()) /
    DEGREES_PER_RADIAN
  let sin_latitude = @math.sin(latitude_delta * 0.5)
  let sin_longitude = @math.sin(longitude_delta * 0.5)
  let haversine = sin_latitude * sin_latitude +
    @math.cos(left_latitude) *
    @math.cos(right_latitude) *
    sin_longitude *
    sin_longitude
  2.0 *
  @math.atan2(haversine.sqrt(), (1.0 - haversine).sqrt()) *
  EARTH_RADIUS_KM
}