///|
fn vector_dot(
  a : Array[Double],
  b : Array[Double],
) -> Double raise KineticsError {
  if a.length() != b.length() {
    raise Invalid("dot product shape")
  }
  let mut sum = 0.0
  for i in 0.. Array[Double] raise KineticsError {
  if weights.length() != self.names.length() {
    raise Invalid("conservation shape")
  }
  let s = self.stoichiometry()
  let result = Array::make(self.reactions.length(), 0.0)
  for j in 0.. Array[Array[Double]] raise KineticsError {
  finite(tolerance, "tolerance")
  if tolerance <= 0.0 || tolerance > 1.0e-3 {
    raise Invalid("conservation tolerance")
  }
  let n = self.names.length()
  let a = self.reactions.map(r => {
    Array::makei(n, i => (r.output[i] - r.input[i]).to_double())
  })
  let pivots : Array[Int32] = []
  let mut row = 0
  for col in 0.. magnitude {
        best = k
        magnitude = a[k][col].abs()
      }
    }
    if magnitude == tolerance {
      continue
    }
    let temp = a[row]
    a[row] = a[best]
    a[best] = temp
    let pivot = a[row][col]
    for j in col.. tolerance * 100.0 {
        raise Numerical("unstable conservation basis")
      }
    }
    basis.push(vector)
  }
  basis
}

///|
pub fn conserved_value(
  weights : Array[Double],
  values : Array[Double],
) -> Double raise KineticsError {
  vector_dot(weights, values)
}