///|
fn zero_matrix(n : Int, p : Int) -> Array[Array[Double]] {
  Array::makei(n, _ => Array::make(p, 0.0))
}

///|
/// Multivariate Welford accumulator; rows must already be validated coordinates.
fn covariance(
  rows : Array[Array[Double]],
  ddof : Int,
) -> Array[Array[Double]] raise CodaError {
  if ddof < 0 || ddof > 1 || rows.length() <= ddof {
    raise Invalid("covariance requires n > ddof with ddof 0 or 1")
  }
  let p = rows[0].length()
  let mean = Array::make(p, 0.0)
  let m2 = zero_matrix(p, p)
  for r, row in rows {
    let delta = Array::makei(p, j => row[j] - mean[j])
    for j in 0..

{ Array::makei(p, j => (m2[i][j] + m2[j][i]) / (2.0 * denom)) }) } ///| pub fn clr_covariance( data : Dataset, ddof? : Int = 1, ) -> Array[Array[Double]] raise CodaError { covariance(data.clr_rows(), ddof) } ///| pub fn ilr_covariance( data : Dataset, b : Basis, ddof? : Int = 1, ) -> Array[Array[Double]] raise CodaError { covariance(data.ilr_rows(b), ddof) } ///| /// Entry (i,j) is sample variance of log(x_i/x_j). pub fn variation_matrix( data : Dataset, ddof? : Int = 1, ) -> Array[Array[Double]] raise CodaError { let c = clr_covariance(data, ddof~) let d = c.length() Array::makei(d, i => { Array::makei(d, j => { let v = c[i][i] + c[j][j] - 2.0 * c[i][j] if v < 0.0 { 0.0 } else { v } }) }) } ///| pub fn total_variance( data : Dataset, ddof? : Int = 1, ) -> Double raise CodaError { let c = clr_covariance(data, ddof~) let mut total = 0.0 for i in 0.. Array[Array[Double]] raise CodaError { covariance(data.cells.map(r => alr(r, reference)), ddof) }