///|
pub(all) struct PoissonResult {
  potential : Array[Double]
  residual : Double
  iterations : Int
} derive(Debug, ToJson)

///|
pub fn poisson_residual(
  grid : Grid1D,
  potential : ArrayView[Double],
  source : ArrayView[Double],
) -> Double {
  let laplacian = periodic_laplacian(grid, potential)
  let length = if laplacian.length() < source.length() {
    laplacian.length()
  } else {
    source.length()
  }
  if length == 0 {
    0.0
  } else {
    let error = Array::makei(length, fn(i) { -laplacian[i] - source[i] })
    l2_norm(error) / length.to_double().sqrt()
  }
}

///|
fn zero_mean(values : Array[Double]) -> Unit {
  let average = mean(values)
  for i in 0.. PoissonResult {
  let potential = zeros(source.length())
  zero_mean(potential)
  let mut residual = poisson_residual(grid, potential, source)
  let mut iterations = 0
  for _ in 0.. tolerance {
      let next = zeros(source.length())
      for i in 0.. Field1D {
  let result = solve_periodic_poisson(grid, source, max_iterations~, tolerance~)
  let electric = periodic_gradient(grid, result.potential).map(fn(value) {
    -value
  })
  let rho = source.to_owned()
  { grid, charge_density: rho, electric, potential: result.potential }
}