///|
/// Validation errors from coupling reorientation; permutation indices are zero-based.
pub(all) enum CouplingOrientationError {
  InvalidCoupling(CouplingError)
  SourcePermutationLength
  TargetPermutationLength
  SourceIndexOutOfRange(Int)
  TargetIndexOutOfRange(Int)
  DuplicateSourceIndex(Int)
  DuplicateTargetIndex(Int)
}

///|
/// Return a fresh target-by-source transpose. No storage is shared with input.
pub fn coupling_transpose(
  coupling : Array[Array[Double]],
) -> Result[Array[Array[Double]], CouplingError] {
  let columns = match validate_coupling(coupling) {
    Err(error) => return Err(error)
    Ok(value) => value
  }
  let result : Array[Array[Double]] = []
  for j in 0.. Result[Array[Array[Double]], CouplingOrientationError] {
  let columns = match validate_coupling(coupling) {
    Err(error) => return Err(CouplingOrientationError::InvalidCoupling(error))
    Ok(value) => value
  }
  if source_order.length() != coupling.length() {
    return Err(CouplingOrientationError::SourcePermutationLength)
  }
  if target_order.length() != columns {
    return Err(CouplingOrientationError::TargetPermutationLength)
  }
  let seen_source = Array::make(coupling.length(), false)
  for i in 0..= coupling.length() {
      return Err(CouplingOrientationError::SourceIndexOutOfRange(i))
    }
    if seen_source[index] {
      return Err(CouplingOrientationError::DuplicateSourceIndex(i))
    }
    seen_source[index] = true
  }
  let seen_target = Array::make(columns, false)
  for j in 0..= columns {
      return Err(CouplingOrientationError::TargetIndexOutOfRange(j))
    }
    if seen_target[index] {
      return Err(CouplingOrientationError::DuplicateTargetIndex(j))
    }
    seen_target[index] = true
  }
  let result : Array[Array[Double]] = []
  for i in 0..