///|
pub(all) struct VlasovSummary {
  density_mean : Double
  density_minimum : Double
  density_maximum : Double
  momentum_mean : Double
  kinetic_energy : Double
  entropy : Double
} derive(Debug, ToJson)

///|
pub fn distribution_minimum(state : VlasovState) -> Double {
  min_value(state.distribution, 0.0)
}

///|
pub fn distribution_maximum(state : VlasovState) -> Double {
  max_value(state.distribution, 0.0)
}

///|
pub fn distribution_integral(state : VlasovState) -> Double {
  sum_values(state.distribution) * state.config.grid.dx * state.config.dv()
}

///|
pub fn distribution_positive_part(state : VlasovState) -> Array[Double] {
  state.distribution.map(fn(value) { value.max(0.0) })
}

///|
pub fn distribution_negative_mass(state : VlasovState) -> Double {
  state.distribution.fold(init=0.0, fn(acc, value) {
    if value < 0.0 {
      acc - value
    } else {
      acc
    }
  }) *
  state.config.grid.dx *
  state.config.dv()
}

///|
pub fn distribution_entropy(state : VlasovState) -> Double {
  let positive = distribution_positive_part(state)
  let mut entropy = 0.0
  for value in positive {
    if value > 0.0 {
      entropy = entropy - value * approximate_log(value)
    }
  }
  entropy * state.config.grid.dx * state.config.dv()
}

///|
pub fn velocity_marginal(state : VlasovState) -> Array[Double] {
  let output = zeros(state.config.velocity_bins)
  for iv in 0.. Array[Double] {
  let output = zeros(state.config.grid.cells)
  for ix in 0.. Double {
  let mut total = 0.0
  for ix in 0.. VlasovSummary {
  let moments = vlasov_moments(state)
  {
    density_mean: mean(moments.density),
    density_minimum: min_value(moments.density, 0.0),
    density_maximum: max_value(moments.density, 0.0),
    momentum_mean: mean(moments.momentum),
    kinetic_energy: distribution_kinetic_energy(state, mass),
    entropy: distribution_entropy(state),
  }
}

///|
pub fn vlasov_density_error(state : VlasovState, expected : Double) -> Double {
  (mean(position_marginal(state)) - expected).abs()
}

///|
pub fn vlasov_time_series(
  state : VlasovState,
  steps : Int,
) -> Array[VlasovSummary] {
  let output : Array[VlasovSummary] = []
  let mut current = state
  for _ in 0.. String {
  let output = StringBuilder()
  output.write_string("x,velocity,distribution\n")
  for ix in 0.. VlasovState {
  let distribution = zeros(state.distribution.length())
  for ix in 0.. Double {
  (distribution_integral(first) - distribution_integral(second)).abs()
}

///|
pub fn vlasov_summary_to_csv(values : ArrayView[VlasovSummary]) -> String {
  let output = StringBuilder()
  output.write_string(
    "density_mean,density_minimum,density_maximum,momentum_mean,kinetic_energy,entropy\n",
  )
  for value in values {
    output.write_string(
      "\{value.density_mean},\{value.density_minimum},\{value.density_maximum},\{value.momentum_mean},\{value.kinetic_energy},\{value.entropy}\n",
    )
  }
  output.to_string()
}