///|
/// Integrate a scalar field over unit lattice cells.
pub fn Field2D::integral(self : Field2D) -> Double {
  sum_values(self.data)
}

///|
/// Integrate absolute values over unit lattice cells.
pub fn Field2D::absolute_integral(self : Field2D) -> Double {
  let mut total = 0.0
  for value in self.data {
    total += abs_double(value)
  }
  total
}

///|
/// Count values above a threshold.
pub fn Field2D::count_above(self : Field2D, level~ : Double) -> Int {
  let mut count = 0
  for value in self.data {
    if value >= level {
      count += 1
    }
  }
  count
}

///|
/// Count values that are not numerically zero.
pub fn Field2D::nonzero_count(
  self : Field2D,
  tolerance? : Double = 0.000000001,
) -> Int {
  let mut count = 0
  for value in self.data {
    if abs_double(value) > tolerance {
      count += 1
    }
  }
  count
}

///|
/// Return the magnitude of the centered gradient at a cell.
pub fn Field2D::gradient_magnitude(
  self : Field2D,
  x~ : Int,
  y~ : Int,
) -> Double {
  gradient_at(self, x~, y~).length()
}

///|
/// Return the x component of the centered gradient.
pub fn Field2D::gradient_x(self : Field2D, x~ : Int, y~ : Int) -> Double {
  gradient_at(self, x~, y~).x
}

///|
/// Return the y component of the centered gradient.
pub fn Field2D::gradient_y(self : Field2D, x~ : Int, y~ : Int) -> Double {
  gradient_at(self, x~, y~).y
}

///|
/// Return a threshold mask with high values marked solid.
pub fn Field2D::threshold(self : Field2D, level~ : Double) -> DomainMask {
  let mask = DomainMask::new(size=self.size)
  for y in 0..= level {
        mask.set(x~, y~, solid=true)
      }
    }
  }
  mask
}

///|
/// Apply a bounded pointwise transform.
pub fn Field2D::clamp(self : Field2D, low~ : Double, high~ : Double) -> Field2D {
  self.map((_, _, value) => clamp_double(value, low~, high~))
}

///|
/// Normalize a field into a requested range.
pub fn Field2D::normalize(
  self : Field2D,
  low? : Double = 0.0,
  high? : Double = 1.0,
) -> Field2D {
  let statistics = self.statistics()
  let denominator = statistics.maximum - statistics.minimum
  if denominator == 0.0 {
    Field2D::new(size=self.size, initial=low)
  } else {
    self.map((_, _, value) => {
      let t = (value - statistics.minimum) / denominator
      lerp(low, high, t)
    })
  }
}

///|
/// Average each cell with a square neighborhood.
pub fn Field2D::smooth(self : Field2D, radius? : Int = 1) -> Field2D {
  let result = Field2D::new(size=self.size)
  let r = radius.max(0)
  for y in 0.. Field2D {
  let result = Field2D::new(size=self.size)
  for y in 0.. Double {
  let values = Array::new()
  for y in 0.. GridPoint {
  if self.size.width <= 0 || self.size.height <= 0 {
    GridPoint::{ x: 0, y: 0 }
  } else {
    let mut best = self.get(0, 0)
    let mut location = GridPoint::{ x: 0, y: 0 }
    for y in 0.. best {
          best = value
          location = GridPoint::{ x, y }
        }
      }
    }
    location
  }
}

///|
/// Return the coordinate of a minimum value.
pub fn Field2D::minimum_location(self : Field2D) -> GridPoint {
  if self.size.width <= 0 || self.size.height <= 0 {
    GridPoint::{ x: 0, y: 0 }
  } else {
    let mut best = self.get(0, 0)
    let mut location = GridPoint::{ x: 0, y: 0 }
    for y in 0.. Double {
  let n = left.data.length().min(right.data.length())
  let mut total = 0.0
  for i in 0.. Field2D {
  let result = Field2D::new(size=left.size)
  for y in 0.. Field2D {
  let t = clamp_double(weight, low=0.0, high=1.0)
  let result = Field2D::new(size=left.size)
  for y in 0..