///|
pub fn Tensor::reshape(self : Tensor, shape : Array[Int]) -> Tensor {
  if shape_size(shape) != self.data.length() {
    abort("reshape must preserve tensor element count")
  }
  if self.requires_grad {
    match (self.context, self.node_ref) {
      (Some(ctx), Some(id)) => {
        let node_ref = ctx.push_tape_node(
          shape,
          [{ target: Some(id), shape: copy_ints(self.shape) }],
          ReshapeBackward::{  },
        )
        {
          data: copy_doubles(self.data),
          shape: copy_ints(shape),
          requires_grad: true,
          context: Some(ctx),
          node_ref: Some(node_ref),
        }
      }
      _ => abort("differentiable tensor operation requires an autograd context")
    }
  } else {
    Tensor::from_array(self.data, shape)
  }
}

///|
pub fn Tensor::swap_axes(self : Tensor, axis_a : Int, axis_b : Int) -> Tensor {
  let rank = self.shape.length()
  let a = normalize_axis(axis_a, rank)
  let b = normalize_axis(axis_b, rank)
  let out_shape = copy_ints(self.shape)
  out_shape[a] = self.shape[b]
  out_shape[b] = self.shape[a]
  let out = Array::make(self.data.length(), 0.0)
  for i in 0.. Tensor {
  if self.shape.length() != 2 {
    abort("transpose2d requires a rank-2 tensor")
  }
  self.swap_axes(0, 1)
}