// elementwise.mbt

///|
fn unary_output(tensor : Tensor, data : Array[Double], op : Op) -> Tensor {
  let out = Tensor::new(data, tensor.shape, requires_grad=tensor.requires_grad)
  if tensor.requires_grad {
    out.creator = Some(op)
  }
  out
}

///|
/// Flatten all dimensions into one dimension.
pub fn Tensor::flatten(self : Tensor) -> Tensor {
  self.reshape([self.data.length()])
}

///|
/// Insert a dimension of size one.
pub fn Tensor::unsqueeze(self : Tensor, dim : Int) -> Tensor {
  let rank = self.shape.length()
  if dim < 0 || dim > rank {
    panic()
  }
  let shape : Array[Int] = []
  for i in 0.. Tensor {
  if dim < 0 || dim >= self.shape.length() || self.shape[dim] != 1 {
    panic()
  }
  let shape : Array[Int] = []
  for i in 0.. Tensor {
  let data = Array::make(self.data.length(), 0.0)
  for i in 0.. Tensor {
  let data = Array::make(self.data.length(), 0.0)
  for i in 0.. Tensor {
  let data = Array::make(self.data.length(), 0.0)
  for i in 0.. Tensor {
  let data = Array::make(self.data.length(), 0.0)
  for i in 0.. Tensor {
  let data = Array::make(self.data.length(), 0.0)
  for i in 0..= 0 {
        for _ in 0.. Tensor {
  if lower > upper {
    panic()
  }
  let data = Array::make(self.data.length(), 0.0)
  for i in 0.. upper {
      upper
    } else {
      value
    }
  }
  unary_output(self, data, Clamp(self, lower, upper))
}

///|
/// Return true when all corresponding values differ by at most tolerance.
pub fn Tensor::all_close(
  self : Tensor,
  other : Tensor,
  tolerance : Double,
) -> Bool {
  if self.shape != other.shape || tolerance < 0.0 {
    return false
  }
  for i in 0.. tolerance {
      return false
    }
  }
  true
}