///|
/// Return the dimensions carried by a vector field.
pub fn VectorField2D::dimensions(self : VectorField2D) -> Size {
  self.size
}

///|
/// Copy the horizontal component into a scalar field.
pub fn VectorField2D::horizontal_field(self : VectorField2D) -> Field2D {
  { size: self.size, data: self.ux.copy() }
}

///|
/// Copy the vertical component into a scalar field.
pub fn VectorField2D::vertical_field(self : VectorField2D) -> Field2D {
  { size: self.size, data: self.uy.copy() }
}

///|
/// Clamp a vector coordinate to its nearest valid cell.
fn VectorField2D::clamped_coordinate(
  self : VectorField2D,
  x : Int,
  y : Int,
) -> (Int, Int) {
  (
    x.clamp(min=0, max=self.size.width - 1),
    y.clamp(min=0, max=self.size.height - 1),
  )
}

///|
/// Compute a centered finite-difference divergence field.
pub fn VectorField2D::divergence_field(self : VectorField2D) -> Field2D {
  let result = Field2D::new(size=self.size)
  if self.size.width > 0 && self.size.height > 0 {
    for y in 0.. Field2D {
  let result = Field2D::new(size=self.size)
  if self.size.width > 0 && self.size.height > 0 {
    for y in 0.. Double {
  let mut maximum = 0.0
  for value in self.ux {
    maximum = maximum.max(if value < 0.0 { -value } else { value })
  }
  for value in self.uy {
    maximum = maximum.max(if value < 0.0 { -value } else { value })
  }
  maximum
}

///|
/// Sum one named component, with speed as a useful derived option.
pub fn VectorField2D::component_sum(
  self : VectorField2D,
  component : String,
) -> Double {
  match component {
    "ux" => sum_values(self.ux[:])
    "uy" => sum_values(self.uy[:])
    "speed" => self.magnitude_field().statistics().sum
    _ => 0.0
  }
}

///|
/// Compute descriptive statistics for vector speed.
pub fn VectorField2D::speed_statistics(self : VectorField2D) -> FieldStatistics {
  self.magnitude_field().statistics()
}

///|
/// Scale every vector by one factor.
pub fn VectorField2D::scale(
  self : VectorField2D,
  factor : Double,
) -> VectorField2D {
  let result = VectorField2D::new(size=self.size)
  for i in 0.. VectorField2D {
  let result = VectorField2D::new(size=self.size)
  for y in 0.. VectorField2D {
  let result = VectorField2D::new(size=self.size)
  for y in 0.. Field2D {
  let result = Field2D::new(size=self.size)
  for y in 0.. Double {
  let mut total = 0.0
  for i in 0.. Point {
  Point::new(x=sum_values(self.ux[:]), y=sum_values(self.uy[:]))
}

///|
/// Return whether every stored component is finite.
pub fn VectorField2D::is_finite(self : VectorField2D) -> Bool {
  let mut result = true
  for value in self.ux {
    result = result && !value.is_nan() && !value.is_inf()
  }
  for value in self.uy {
    result = result && !value.is_nan() && !value.is_inf()
  }
  result
}

///|
/// Limit vector magnitude while preserving direction.
pub fn VectorField2D::clamp_speed(
  self : VectorField2D,
  maximum : Double,
) -> VectorField2D {
  let result = VectorField2D::new(size=self.size)
  let limit = maximum.max(0.0)
  for y in 0.. VectorField2D {
  let result = VectorField2D::new(size=self.size)
  for y in 0.. VectorField2D {
  let t = clamp_double(weight, low=0.0, high=1.0)
  self.scale(1.0 - t).add(other.scale(t))
}

///|
/// Return the negative of every vector.
pub fn VectorField2D::negate(self : VectorField2D) -> VectorField2D {
  self.scale(-1.0)
}

///|
/// Compute the mean squared magnitude of a vector field.
pub fn VectorField2D::mean_square_speed(self : VectorField2D) -> Double {
  if self.ux.length() == 0 {
    0.0
  } else {
    self.kinetic_energy() * 2.0 / self.ux.length().to_double()
  }
}

///|
/// Compute the total squared gradient of vector components.
pub fn VectorField2D::gradient_energy(self : VectorField2D) -> Double {
  let mut total = 0.0
  if self.size.width > 1 && self.size.height > 1 {
    for y in 1..<(self.size.height - 1) {
      for x in 1..<(self.size.width - 1) {
        let left = self.get(x - 1, y)
        let right = self.get(x + 1, y)
        let bottom = self.get(x, y - 1)
        let top = self.get(x, y + 1)
        let du_dx = (right.x - left.x) * 0.5
        let du_dy = (top.x - bottom.x) * 0.5
        let dv_dx = (right.y - left.y) * 0.5
        let dv_dy = (top.y - bottom.y) * 0.5
        total += du_dx * du_dx + du_dy * du_dy + dv_dx * dv_dx + dv_dy * dv_dy
      }
    }
  }
  total
}

///|
/// L1 norm of a derived divergence field.
pub fn VectorField2D::divergence_l1(self : VectorField2D) -> Double {
  let mut total = 0.0
  for value in self.divergence_field().values() {
    total += if value < 0.0 { -value } else { value }
  }
  total
}

///|
/// L1 norm of a derived curl field.
pub fn VectorField2D::curl_l1(self : VectorField2D) -> Double {
  let mut total = 0.0
  for value in self.curl_field().values() {
    total += if value < 0.0 { -value } else { value }
  }
  total
}