///|
pub fn alr(x : Array[Double], reference : Int) -> Array[Double] raise CodaError {
  positive(x)
  if reference < 0 || reference >= x.length() {
    raise Shape("ALR reference outside composition")
  }
  let base = @math.ln(x[reference])
  let out = []
  for i, v in x {
    if i != reference {
      out.push(@math.ln(v) - base)
    }
  }
  out
}

///|
pub fn alr_inverse(
  z : Array[Double],
  reference : Int,
) -> Array[Double] raise CodaError {
  vector_size(z.length() + 1)
  real_vector(z)
  if reference < 0 || reference > z.length() {
    raise Shape("ALR reference outside reconstructed composition")
  }
  let logits = []
  let mut index = 0
  for i in 0..<(z.length() + 1) {
    if i == reference {
      logits.push(0.0)
    } else {
      logits.push(z[index])
      index += 1
    }
  }
  clr_inverse(logits)
}