#ifndef TH_GENERIC_FILE
#define TH_GENERIC_FILE "generic/THTensor.cpp"
#else

#include <new>

/**** access methods ****/
THStorage *THTensor_(storage)(const THTensor *self)
{
  return THTensor_getStoragePtr(self);
}

ptrdiff_t THTensor_(storageOffset)(const THTensor *self)
{
  return self->storage_offset();
}

int THTensor_(nDimensionLegacyNoScalars)(const THTensor *self)
{
  return THTensor_nDimensionLegacyNoScalars(self);
}

int THTensor_(nDimensionLegacyAll)(const THTensor *self)
{
  return THTensor_nDimensionLegacyAll(self);
}

int64_t THTensor_(size)(const THTensor *self, int dim)
{
  THArgCheck((dim >= 0) && (dim < self->dim()), 2, "dimension %d out of range of %dD tensor",
      dim+TH_INDEX_BASE, THTensor_(nDimensionLegacyNoScalars)(self));
  return self->size(dim);
}

int64_t THTensor_(stride)(const THTensor *self, int dim)
{
  THArgCheck((dim >= 0) && (dim < self->dim()), 2, "dimension %d out of range of %dD tensor",
      dim+TH_INDEX_BASE, THTensor_(nDimensionLegacyNoScalars)(self));
  return self->stride(dim);
}

THLongStorage *THTensor_(newSizeOf)(THTensor *self)
{
  THLongStorage *size = THLongStorage_newWithSize(self->dim());
  THLongStorage_rawCopy(size, THTensor_getSizePtr(self));
  return size;
}

THLongStorage *THTensor_(newStrideOf)(THTensor *self)
{
  THLongStorage *stride = THLongStorage_newWithSize(self->dim());
  THLongStorage_rawCopy(stride, THTensor_getStridePtr(self));
  return stride;
}

real *THTensor_(data)(const THTensor *self) {
  return self->data<real>();
}

/**** creation methods ****/

/* Empty init */
THTensor *THTensor_(new)(void)
{
  return new THTensor(THStorage_(new)());
}

/* Pointer-copy init */
THTensor *THTensor_(newWithTensor)(THTensor *tensor)
{
  THTensor *self = new THTensor(THStorage_(new)());
  THTensor_(setStorageNd)(self,
                          THTensor_getStoragePtr(tensor),
                          tensor->storage_offset(),
                          tensor->dim(),
                          THTensor_getSizePtr(tensor),
                          THTensor_getStridePtr(tensor));
  return self;
}

/* Storage init */
THTensor *THTensor_(newWithStorage)(THStorage *storage, ptrdiff_t storageOffset, THLongStorage *size, THLongStorage *stride)
{
  if(size && stride) {
    THArgCheck(size->size == stride->size, 4, "inconsistent size");
  }
  AT_CHECK(size, "size must not be null");

  THTensor *self = new THTensor(THStorage_(new)());
#ifdef DEBUG
  THAssert(size->size <= INT_MAX);
#endif
  THTensor_(setStorageNd)(self,
                          storage,
                          storageOffset,
                          size->size,
                          THLongStorage_data(size),
                          (stride ? THLongStorage_data(stride) : NULL));

  return self;
}

THTensor *THTensor_(newWithStorageIntLists)(THStorage *storage, ptrdiff_t storageOffset, at::IntList sizes, at::IntList strides) {
  AT_CHECK(sizes.size() == strides.size(), "number of sizes and strides must match");
  THTensor *self = new THTensor(THStorage_(new)());
  THTensor_(setStorageNd)(self, storage, storageOffset, sizes.size(),
                          const_cast<int64_t*>(sizes.data()), const_cast<int64_t*>(strides.data()));

  return self;
}

THTensor *THTensor_(newWithStorage1d)(THStorage *storage, ptrdiff_t storageOffset,
                               int64_t size0, int64_t stride0)
{
  return THTensor_(newWithStorageIntLists)(storage, storageOffset, {size0}, {stride0});
}

THTensor *THTensor_(newWithStorage2d)(THStorage *storage, ptrdiff_t storageOffset,
                               int64_t size0, int64_t stride0,
                               int64_t size1, int64_t stride1)
{
  return THTensor_(newWithStorageIntLists)(storage, storageOffset, {size0, size1}, {stride0, stride1});
}

THTensor *THTensor_(newWithStorage3d)(THStorage *storage, ptrdiff_t storageOffset,
                               int64_t size0, int64_t stride0,
                               int64_t size1, int64_t stride1,
                               int64_t size2, int64_t stride2)
{
  return THTensor_(newWithStorageIntLists)(storage, storageOffset, {size0, size1, size2}, {stride0, stride1, stride2});
}

THTensor *THTensor_(newWithStorage4d)(THStorage *storage, ptrdiff_t storageOffset,
                               int64_t size0, int64_t stride0,
                               int64_t size1, int64_t stride1,
                               int64_t size2, int64_t stride2,
                               int64_t size3, int64_t stride3)
{
  return THTensor_(newWithStorageIntLists)(storage, storageOffset,
                                          {size0, size1, size2, size3},
                                          {stride0, stride1, stride2, stride3});
}

THTensor *THTensor_(newWithSize)(THLongStorage *size, THLongStorage *stride)
{
  return THTensor_(newWithStorage)(NULL, 0, size, stride);
}

THTensor *THTensor_(newWithSizeIntList)(at::IntList sizes) {
  THTensor *self = new THTensor(THStorage_(new)());
  THTensor_(resizeNd)(self, sizes.size(), const_cast<int64_t*>(sizes.data()), nullptr);

  return self;
}

THTensor *THTensor_(newWithSize1d)(int64_t size0)
{
  return THTensor_(newWithSizeIntList)({size0});
}

THTensor *THTensor_(newWithSize2d)(int64_t size0, int64_t size1)
{
  return THTensor_(newWithSizeIntList)({size0, size1});
}

THTensor *THTensor_(newWithSize3d)(int64_t size0, int64_t size1, int64_t size2)
{
  return THTensor_(newWithSizeIntList)({size0, size1, size2});
}

THTensor *THTensor_(newWithSize4d)(int64_t size0, int64_t size1, int64_t size2, int64_t size3)
{
  return THTensor_(newWithSizeIntList)({size0, size1, size2, size3});
}

THTensor *THTensor_(newClone)(THTensor *self)
{
  THTensor *tensor = THTensor_(new)();
  THTensor_(resizeAs)(tensor, self);
  THTensor_(copy)(tensor, self);
  return tensor;
}

THTensor *THTensor_(newContiguous)(THTensor *self)
{
  if(!THTensor_(isContiguous)(self))
    return THTensor_(newClone)(self);
  else
  {
    THTensor_(retain)(self);
    return self;
  }
}

THTensor *THTensor_(newSelect)(THTensor *tensor, int dimension_, int64_t sliceIndex_)
{
  THTensor *self = THTensor_(newWithTensor)(tensor);
  THTensor_(select)(self, NULL, dimension_, sliceIndex_);
  return self;
}

THTensor *THTensor_(newNarrow)(THTensor *tensor, int dimension_, int64_t firstIndex_, int64_t size_)
{
  THTensor *self = THTensor_(newWithTensor)(tensor);
  THTensor_(narrow)(self, NULL, dimension_, firstIndex_, size_);
  return self;
}

THTensor *THTensor_(newTranspose)(THTensor *tensor, int dimension1_, int dimension2_)
{
  THTensor *self = THTensor_(newWithTensor)(tensor);
  THTensor_(transpose)(self, NULL, dimension1_, dimension2_);
  return self;
}

THTensor *THTensor_(newUnfold)(THTensor *tensor, int dimension_, int64_t size_, int64_t step_)
{
  THTensor *self = THTensor_(newWithTensor)(tensor);
  THTensor_(unfold)(self, NULL, dimension_, size_, step_);
  return self;
}

THTensor *THTensor_(newView)(THTensor *tensor, THLongStorage *size)
{
  ptrdiff_t numel = THTensor_(nElement)(tensor);
  THTensor *self = THTensor_(new)();
  THLongStorage *inferred_size = THLongStorage_newInferSize(size, numel);
  auto stride = THTensor_compute_stride(tensor->sizes(),
                                        tensor->strides(),
                                        at::IntList(inferred_size->data<int64_t>(), inferred_size->size));
  THArgCheck(stride.has_value(), 2, "view size is "
    "not compatible with input tensor's size and stride (at least one dimension spans "
    "across two contiguous subspaces). Call .contiguous() before .view().");
  auto stride_value = *stride;
  THLongStorage *new_stride = THLongStorage_newWithSize(stride_value.size());
  THLongStorage_rawCopy(new_stride, stride_value.data());
  THTensor_(setStorage)(self, THTensor_getStoragePtr(tensor), tensor->storage_offset(), inferred_size, new_stride);
  THLongStorage_free(inferred_size);
  THLongStorage_free(new_stride);
  return self;
}

/* Resize */
void THTensor_(resize)(THTensor *self, THLongStorage *size, THLongStorage *stride)
{
  THArgCheck(size != NULL, 2, "invalid size");
  if(stride)
    THArgCheck(stride->size == size->size, 3, "invalid stride");

#ifdef DEBUG
  THAssert(size->size <= INT_MAX);
#endif
  THTensor_(resizeNd)(self, size->size, THLongStorage_data(size), (stride ? THLongStorage_data(stride) : NULL));
}

void THTensor_(resizeAs)(THTensor *self, THTensor *src)
{
  if(!THTensor_(isSameSizeAs)(self, src))
    THTensor_(resizeNd)(self, src->dim(), THTensor_getSizePtr(src), NULL);
}

void THTensor_(resize1d)(THTensor *tensor, int64_t size0)
{
  int64_t size[1] = {size0};
  THTensor_(resizeNd)(tensor, 1, size, nullptr);
}

void THTensor_(resize2d)(THTensor *tensor, int64_t size0, int64_t size1)
{
  int64_t size[2] = {size0, size1};
  THTensor_(resizeNd)(tensor, 2, size, nullptr);
}

void THTensor_(resize3d)(THTensor *tensor, int64_t size0, int64_t size1, int64_t size2)
{
  int64_t size[3] = {size0, size1, size2};
  THTensor_(resizeNd)(tensor, 3, size, nullptr);
}

void THTensor_(resize4d)(THTensor *self, int64_t size0, int64_t size1, int64_t size2, int64_t size3)
{
  int64_t size[4] = {size0, size1, size2, size3};
  THTensor_(resizeNd)(self, 4, size, nullptr);
}

void THTensor_(resize5d)(THTensor *self, int64_t size0, int64_t size1, int64_t size2, int64_t size3, int64_t size4)
{
  int64_t size[5] = {size0, size1, size2, size3, size4};
  THTensor_(resizeNd)(self, 5, size, nullptr);
}

void THTensor_(set)(THTensor *self, THTensor *src)
{
  if(self != src)
    THTensor_(setStorageNd)(self,
                            THTensor_getStoragePtr(src),
                            src->storage_offset(),
                            src->dim(),
                            THTensor_getSizePtr(src),
                            THTensor_getStridePtr(src));
}

void THTensor_(setStorage)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_, THLongStorage *size_, THLongStorage *stride_)
{
  if(size_ && stride_)
    THArgCheck(size_->size == stride_->size, 5, "inconsistent size/stride sizes");

  AT_CHECK(size_, "size must not be null");
#ifdef DEBUG
  THAssert(size_ <= INT_MAX);
#endif
  THTensor_(setStorageNd)(self,
                          storage_,
                          storageOffset_,
                          size_->size,
                          THLongStorage_data(size_),
                          (stride_ ? THLongStorage_data(stride_) : NULL));
}

void THTensor_(setStorageIntLists)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_,
                                   at::IntList sizes, at::IntList strides)
{
  AT_CHECK(sizes.size() == strides.size(), "number of sizes and strides must match");

  THTensor_(setStorageNd)(self, storage_, storageOffset_, sizes.size(),
                          const_cast<int64_t *>(sizes.data()), const_cast<int64_t *>(strides.data()));
}

void THTensor_(setStorage1d)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_,
                             int64_t size0_, int64_t stride0_)
{
  THTensor_(setStorageIntLists)(self, storage_, storageOffset_,
                                {size0_}, {stride0_});
}

void THTensor_(setStorage2d)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_,
                             int64_t size0_, int64_t stride0_,
                             int64_t size1_, int64_t stride1_)
{
  THTensor_(setStorageIntLists)(self, storage_, storageOffset_,
                                {size0_, size1_},
                                {stride0_, stride1_});
}

void THTensor_(setStorage3d)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_,
                             int64_t size0_, int64_t stride0_,
                             int64_t size1_, int64_t stride1_,
                             int64_t size2_, int64_t stride2_)
{
  THTensor_(setStorageIntLists)(self, storage_, storageOffset_,
                                {size0_, size1_, size2_},
                                {stride0_, stride1_, stride2_});
}

void THTensor_(setStorage4d)(THTensor *self, THStorage *storage_, ptrdiff_t storageOffset_,
                             int64_t size0_, int64_t stride0_,
                             int64_t size1_, int64_t stride1_,
                             int64_t size2_, int64_t stride2_,
                             int64_t size3_, int64_t stride3_)
{

  int64_t size[4] = {size0_, size1_, size2_, size3_};
  int64_t stride[4] = {stride0_, stride1_, stride2_, stride3_};

  THTensor_(setStorageIntLists)(self, storage_, storageOffset_, size, stride);
}


void THTensor_(narrow)(THTensor *self, THTensor *src, int dimension, int64_t firstIndex, int64_t size)
{
  if(!src)
    src = self;

  THArgCheck( (dimension >= 0) && (dimension < src->dim()), 2, "out of range");
  THArgCheck( firstIndex >= 0, 3, "out of range");
#ifdef USE_TH_SIZE_ZERO_DIM
  THArgCheck( size >= 0, 4, "out of range");
#else
  THArgCheck( size > 0, 4, "out of range");
#endif
  THArgCheck(firstIndex <= src->size(dimension) - size, 4, "out of range");

  THTensor_(set)(self, src);

  if (firstIndex > 0) {
    THTensor_setStorageOffset(self, self->storage_offset() + firstIndex*self->stride(dimension));
  }

  THTensor_setSizeAtDim(self, dimension, size);
}

void THTensor_(select)(THTensor *self, THTensor *src, int dimension, int64_t sliceIndex)
{
  int d;

  if(!src)
    src = self;

#ifndef USE_TH_SIZE_ZERO_DIM
  THArgCheck(THTensor_nDimensionLegacyAll(src) > 1, 1, "cannot select on a vector");
#else
#ifndef USE_TH_SCALAR
  THArgCheck(src->dim() > 1, 1, "cannot select on a vector");
#endif
#endif
  THArgCheck((dimension >= 0) && (dimension < src->dim()), 2, "out of range");
  THArgCheck((sliceIndex >= 0) && (sliceIndex < src->size(dimension)), 3, "out of range");

  THTensor_(set)(self, src);
  THTensor_(narrow)(self, NULL, dimension, sliceIndex, 1);
  for(d = dimension; d < self->dim()-1; d++)
  {
    THTensor_setSizeAtDim(self, d, self->size(d+1));
    THTensor_setStrideAtDim(self, d, self->stride(d+1));
  }
  THTensor_resizeDim(self, self->dim() - 1);
}

void THTensor_(transpose)(THTensor *self, THTensor *src, int dimension1, int dimension2)
{
  int64_t z;

  if(!src)
    src = self;

  THArgCheck( (dimension1 >= 0) && (dimension1 < src->dim()), 1, "out of range");
  THArgCheck( (dimension2 >= 0) && (dimension2 < src->dim()), 2, "out of range");

  THTensor_(set)(self, src);

  if(dimension1 == dimension2)
    return;

  z = self->stride(dimension1);
  THTensor_setStrideAtDim(self, dimension1, self->stride(dimension2));
  THTensor_setStrideAtDim(self, dimension2, z);
  z = self->size(dimension1);
  THTensor_setSizeAtDim(self, dimension1, self->size(dimension2));
  THTensor_setSizeAtDim(self, dimension2, z);
}

void THTensor_(unfold)(THTensor *self, THTensor *src, int dimension, int64_t size, int64_t step)
{
  int d;

  if(!src)
    src = self;

#ifndef USE_TH_SIZE_ZERO_DIM
  THArgCheck(!src->is_empty(), 1, "cannot unfold an empty tensor");
#endif
  THArgCheck((dimension >= 0) && (dimension < src->dim()), 2, "out of range");
  THArgCheck(size <= src->size(dimension), 3, "out of range");
  THArgCheck(step > 0, 4, "invalid step");

  THTensor_(set)(self, src);

  std::vector<int64_t> newSize(/* size */ self->dim()+1);
  std::vector<int64_t> newStride(/* size */ self->dim()+1);

  newSize[self->dim()] = size;
  newStride[self->dim()] = self->stride(dimension);
  for(d = 0; d < self->dim(); d++)
  {
    if(d == dimension)
    {
      newSize[d] = (self->size(d) - size) / step + 1;
      newStride[d] = step*self->stride(d);
    }
    else
    {
      newSize[d] = self->size(d);
      newStride[d] = self->stride(d);
    }
  }

  THTensor_setSizesAndStrides(self, std::move(newSize), std::move(newStride));
}

/* we have to handle the case where the result is a number */
void THTensor_(squeeze)(THTensor *self, THTensor *src)
{
  int ndim = 0;
  int d;

  if(!src)
    src = self;

  THTensor_(set)(self, src);

  for(d = 0; d < src->dim(); d++)
  {
    if(src->size(d) != 1)
    {
      if(d != ndim)
      {
        THTensor_setSizeAtDim(self, ndim, src->size(d));
        THTensor_setStrideAtDim(self, ndim, src->stride(d));
      }
      ndim++;
    }
  }

#ifndef USE_TH_SCALAR
  /* right now, we do not handle 0-dimension tensors */
  if(ndim == 0 && src->dim() > 0)
  {
    THTensor_setSizeAtDim(self, 0, 1);
    THTensor_setStrideAtDim(self, 0, 1);
    ndim = 1;
  }
#endif
  THTensor_resizeDim(self, ndim);
}

void THTensor_(squeeze1d)(THTensor *self, THTensor *src, int dimension)
{
  int d;

  if(!src)
    src = self;

  THArgCheck((dimension >= 0) && (dimension < src->dim()), 2, "dimension out of range");

  THTensor_(set)(self, src);

#ifdef USE_TH_SCALAR
  if(src->size(dimension) == 1)
#else
  if(src->size(dimension) == 1 && src->dim() > 1)
#endif
  {
    for(d = dimension; d < self->dim()-1; d++)
    {
      THTensor_setSizeAtDim(self, d, self->size(d+1));
      THTensor_setStrideAtDim(self, d, self->stride(d+1));
    }
    THTensor_resizeDim(self, self->dim() - 1);
  }
}

void THTensor_(unsqueeze1d)(THTensor *self, THTensor *src, int dimension)
{
  int d;

  if(!src)
    src = self;

  THArgCheck((dimension >= 0) && (dimension <= src->dim()), 2, "dimension out of range");
#ifndef USE_TH_SIZE_ZERO_DIM
  THArgCheck(!src->is_empty(), 2, "cannot unsqueeze empty tensor");
#endif

  THTensor_(set)(self, src);

  THTensor_resizeDim(self, self->dim() + 1);
  for (d = self->dim()-1; d > dimension; d--) {
    THTensor_setSizeAtDim(self, d, self->size(d-1));
    THTensor_setStrideAtDim(self, d, self->stride(d-1));
  }
  if (dimension+1 < self->dim()) {
    THTensor_setStrideAtDim(self, dimension, self->size(dimension+1) * self->stride(dimension+1));
  } else {
    THTensor_setStrideAtDim(self, dimension, 1);
  }
  THTensor_setSizeAtDim(self, dimension, 1);
}

int THTensor_(isTransposed)(const THTensor *self)
{
  if (THTensor_(isContiguous)(self)) {
    return 0;
  }
  int64_t max_stride = 1;
  int64_t size_max_stride = 1;
  int64_t z = 1;
  int d;
  for (d = 0; d < THTensor_nDimensionLegacyAll(self); ++d) {
    if (self->stride(d) == 0 && self->size(d) != 1)
      return 0;
    if (self->stride(d) > max_stride) {
      max_stride = self->stride(d);
      size_max_stride = self->size(d);
    }
    z *= self->size(d);
  }
  if (z == max_stride * size_max_stride) {
    return 1;
  }
  return 0;
}

int THTensor_(isContiguous)(const THTensor *self)
{
  if (self->is_empty()) return 1;
  int64_t z = 1;
  int d;
  for(d = self->dim()-1; d >= 0; d--)
  {
    if(self->size(d) != 1)
    {
      if(self->stride(d) == z)
        z *= self->size(d);
      else
        return 0;
    }
  }
  return 1;
}

int THTensor_(isSize)(const THTensor *self, const THLongStorage *dims)
{
  int d;
  if (THTensor_nDimensionLegacyAll(self) != dims->size)
    return 0;

  for(d = 0; d < THTensor_nDimensionLegacyAll(self); ++d)
  {
    if(self->size(d) != THLongStorage_data(dims)[d])
      return 0;
  }
  return 1;
}

int THTensor_(isSameSizeAs)(const THTensor *self, const THTensor* src)
{
  int d;
  if (self->dim() != src->dim())
    return 0;
  for(d = 0; d < self->dim(); ++d)
  {
    if(self->size(d) != src->size(d))
      return 0;
  }
  return 1;
}

int THTensor_(isSetTo)(const THTensor *self, const THTensor* src)
{
  if (!THTensor_getStoragePtr(self))
    return 0;
  if (THTensor_getStoragePtr(self) == THTensor_getStoragePtr(src) &&
      self->storage_offset() == src->storage_offset() &&
      THTensor_nDimensionLegacyAll(self) == THTensor_nDimensionLegacyAll(src))
  {
    int d;
    for (d = 0; d < THTensor_nDimensionLegacyAll(self); ++d)
    {
      if (self->size(d) != src->size(d) || self->stride(d) != src->stride(d))
        return 0;
    }
    return 1;
  }
  return 0;
}

ptrdiff_t THTensor_(nElement)(const THTensor *self)
{
  if(THTensor_nDimensionLegacyAll(self) == 0)
    return 0;
  else
  {
    ptrdiff_t nElement = 1;
    int d;
    for(d = 0; d < THTensor_nDimensionLegacyAll(self); d++)
      nElement *= self->size(d);
    return nElement;
  }
}

void THTensor_(retain)(THTensor *self)
{
  self->retain();
}

void THTensor_(free)(THTensor *self)
{
  THTensor_free(self);
}

void THTensor_(freeCopyTo)(THTensor *self, THTensor *dst)
{
  if(self != dst)
    THTensor_(copy)(dst, self);

  THTensor_(free)(self);
}

/*******************************************************************************/

void THTensor_(setStorageNd)(THTensor *self, THStorage *storage, ptrdiff_t storageOffset, int nDimension, int64_t *size, int64_t *stride)
{
  /* storage */
  if(THTensor_getStoragePtr(self) != storage)
  {
    if(THTensor_getStoragePtr(self))
      THStorage_(free)(THTensor_getStoragePtr(self));

    if(storage)
    {
      THTensor_stealAndSetStoragePtr(self, storage);
      THStorage_(retain)(THTensor_getStoragePtr(self));
    }
    else
      THTensor_stealAndSetStoragePtr(self, THStorage_(new)());
  }

  /* storageOffset */
  if(storageOffset < 0)
    THError("Tensor: invalid storage offset");
  THTensor_setStorageOffset(self, storageOffset);

  /* size and stride */
  THTensor_(resizeNd)(self, nDimension, size, stride);
}

void THTensor_(resizeNd)(THTensor *self, int nDimension, int64_t *size, int64_t *stride)
{
  int d;
  ptrdiff_t totalSize;
  bool hascorrectsize = true;

#ifndef USE_TH_SCALAR
  AT_CHECK(nDimension > 0, "resizeNd nDimension must be greater than 0");
#else
  AT_CHECK(nDimension >= 0, "resizeNd nDimension must be non-negative");
#endif

  for(d = 0; d < nDimension; d++)
  {
#ifndef USE_TH_SIZE_ZERO_DIM
    // we can't support this unless we have arbitrary 0-sized dimensions, but some calls to this
    // currently exist and expect a size [0] tensor to be returned.
    if (d == 0 && size[d] == 0) {
      nDimension = 1;
    } else {
      AT_CHECK(size[d] > 0, "sizes must be non-negative");
    }
#endif
    if((self->dim() > d) && (size[d] != self->size(d))) {
      hascorrectsize = false;
    }

    // NB: this used to test that stride[d] was >= 0
    if((self->dim() > d) && stride && (stride[d] != self->stride(d))) {
      hascorrectsize = false;
    }
  }

  if(nDimension != self->dim()) {
    hascorrectsize = false;
  }

  if(hascorrectsize) {
    return;
  }

  if(nDimension != self->dim())
  {
    THTensor_resizeDim(self, nDimension);
  }

  totalSize = 1;
  for(d = nDimension-1; d >= 0; d--)
  {
    THTensor_setSizeAtDim(self, d, size[d]);
    if(stride && (stride[d] >= 0) ) {
      THTensor_setStrideAtDim(self, d, stride[d]);
    } else {
      if(d == nDimension-1) {
        THTensor_setStrideAtDim(self, d, 1);
      } else {
        // Keep stride monotonically increasing to match NumPy.
        THTensor_setStrideAtDim(self, d, std::max<int64_t>(self->size(d+1), 1)*self->stride(d+1));
      }
    }
    totalSize += (self->size(d)-1)*self->stride(d);
  }

  if(totalSize+self->storage_offset() > 0)
  {
    if(!THTensor_getStoragePtr(self)) {
      THTensor_stealAndSetStoragePtr(self, THStorage_(new)());
    }
    if(totalSize+self->storage_offset() > THTensor_getStoragePtr(self)->size) {
      THStorage_(resize)(THTensor_getStoragePtr(self), totalSize+self->storage_offset());
    }
  }
}

void THTensor_(set1d)(THTensor *tensor, int64_t x0, real value)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 1, 1, "tensor must have one dimension");
  THArgCheck( (x0 >= 0) && (x0 < tensor->size(0)), 2, "out of range");
  THStorage_(set)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0), value);
}

real THTensor_(get1d)(const THTensor *tensor, int64_t x0)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 1, 1, "tensor must have one dimension");
  THArgCheck( (x0 >= 0) && (x0 < tensor->size(0)), 2, "out of range");
  return THStorage_(get)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0));
}

void THTensor_(set2d)(THTensor *tensor, int64_t x0, int64_t x1, real value)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 2, 1, "tensor must have two dimensions");
  THArgCheck((x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)), 2, "out of range");
  THStorage_(set)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1), value);
}

real THTensor_(get2d)(const THTensor *tensor, int64_t x0, int64_t x1)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 2, 1, "tensor must have two dimensions");
  THArgCheck((x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)), 2, "out of range");
  return THStorage_(get)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1));
}

void THTensor_(set3d)(THTensor *tensor, int64_t x0, int64_t x1, int64_t x2, real value)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 3, 1, "tensor must have three dimensions");
  THArgCheck( (x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)) && (x2 >= 0) && (x2 < tensor->size(2)), 2, "out of range");
  THStorage_(set)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1)+x2*tensor->stride(2), value);
}

real THTensor_(get3d)(const THTensor *tensor, int64_t x0, int64_t x1, int64_t x2)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 3, 1, "tensor must have three dimensions");
  THArgCheck( (x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)) && (x2 >= 0) && (x2 < tensor->size(2)), 2, "out of range");
  return THStorage_(get)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1)+x2*tensor->stride(2));
}

void THTensor_(set4d)(THTensor *tensor, int64_t x0, int64_t x1, int64_t x2, int64_t x3, real value)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 4, 1, "tensor must have four dimensions");
  THArgCheck((x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)) && (x2 >= 0) && (x2 < tensor->size(2)) && (x3 >= 0) && (x3 < tensor->size(3)), 2, "out of range");
  THStorage_(set)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1)+x2*tensor->stride(2)+x3*tensor->stride(3), value);
}

real THTensor_(get4d)(const THTensor *tensor, int64_t x0, int64_t x1, int64_t x2, int64_t x3)
{
  THArgCheck(THTensor_nDimensionLegacyAll(tensor) == 4, 1, "tensor must have four dimensions");
  THArgCheck((x0 >= 0) && (x0 < tensor->size(0)) && (x1 >= 0) && (x1 < tensor->size(1)) && (x2 >= 0) && (x2 < tensor->size(2)) && (x3 >= 0) && (x3 < tensor->size(3)), 2, "out of range");
  return THStorage_(get)(THTensor_getStoragePtr(tensor), tensor->storage_offset()+x0*tensor->stride(0)+x1*tensor->stride(1)+x2*tensor->stride(2)+x3*tensor->stride(3));
}

THDescBuff THTensor_(desc)(const THTensor *tensor) {
  const int L = TH_DESC_BUFF_LEN;
  THDescBuff buf;
  char *str = buf.str;
  int n = 0;
#define _stringify(x) #x
  n += snprintf(str, L-n, "torch." _stringify(x) "Tensor of size ");
#undef _stringify
  int i;
  for(i = 0; i < THTensor_nDimensionLegacyAll(tensor); i++) {
    if(n >= L) break;
    n += snprintf(str+n, L-n, "%" PRId64, tensor->size(i));
    if(i < THTensor_nDimensionLegacyAll(tensor)-1) {
      n += snprintf(str+n, L-n, "x");
    }
  }
  if(n >= L) {
    snprintf(str+L-4, 4, "...");
  }
  return buf;
}

THDescBuff THTensor_(sizeDesc)(const THTensor *tensor) {
  THLongStorage *size = THTensor_(newSizeOf)((THTensor*)tensor);
  THDescBuff buf = THLongStorage_sizeDesc(size);
  THLongStorage_free(size);
  return buf;
}

#endif
