///|
/// Exact integral of the right-continuous piecewise-constant count path.
pub fn Path::integrated_counts(
  self : Path,
) -> Array[Double] raise KineticsError {
  let result = Array::make(self.states[0].length(), 0.0)
  let mut previous = 0.0
  for k in 0..<(self.channels.length() + 1) {
    let end = if k == self.channels.length() {
      self.horizon
    } else {
      self.times[k]
    }
    for i in 0.. Array[Int32] raise KineticsError {
  verify_path(self, path)
  let counts = Array::make(self.reactions.length(), 0)
  for j in path.channels {
    counts[j] += 1
  }
  counts
}

///|
pub fn Network::integrated_propensities(
  self : Network,
  path : Path,
) -> Array[Double] raise KineticsError {
  verify_path(self, path)
  let result = Array::make(self.reactions.length(), 0.0)
  let mut previous = 0.0
  for k in 0..<(path.channels.length() + 1) {
    let end = if k == path.channels.length() {
      path.horizon
    } else {
      path.times[k]
    }
    let a = self.propensities(path.states[k])
    for j in 0.. (Double, Double) raise KineticsError {
  if observable.length() != self.size() ||
    distribution.probabilities.length() != self.size() {
    raise Invalid("observable state shape")
  }
  let mut magnitude = 0.0
  for x in observable {
    finite(x, "observable")
    if x.abs() > magnitude {
      magnitude = x.abs()
    }
  }
  let value = vector_dot(distribution.probabilities, observable)
  let error = magnitude * distribution.tail
  finite(error, "observable tail bound")
  (value, error)
}

///|
pub fn Generator::mean_counts(
  self : Generator,
  distribution : Distribution,
) -> (Array[Double], Array[Double]) raise KineticsError {
  let means : Array[Double] = []
  let bounds : Array[Double] = []
  for i in 0.. s[i].to_double())
    let (value, error) = self.expectation(distribution, observable)
    means.push(value)
    bounds.push(error)
  }
  (means, bounds)
}