#pragma once

#include "ATen/Tensor.h"
#include "ATen/Scalar.h"
#include "ATen/SparseTensorRef.h"

namespace at {

inline Tensor Tensor::toType(const Type & t) const {
  if(type() == t)
    return *this;
  return t.copy(*this);
}

inline Tensor & Tensor::copy_(const Tensor & src) {
  type().copy(src, *this);
  return *this;
}

inline Tensor Tensor::toType(ScalarType t) const {
  return toType(type().toScalarType(t));
}

inline Tensor Tensor::toBackend(Backend b) const {
  return toType(type().toBackend(b));
}


// all static inline to allow for inlining of the non-dynamic part of dispatch
inline int64_t Tensor::storage_offset() const {
    return type().m_storage_offset(*this);
}
inline Tensor & Tensor::resize_(IntList size) {
    return type().m_resize_(*this, size);
}
inline int64_t Tensor::numel() const {
    return type().numel(*this);
}
inline Tensor & Tensor::set_(Storage & storage) {
    return type().m_set_(*this, storage);
}
inline Tensor & Tensor::set_(Storage & sourceStorage, int64_t storage_offset, IntList size, IntList stride) {
    return type().m_set_(*this, sourceStorage, storage_offset, size, stride);
}
inline Tensor & Tensor::set_(const Tensor & source) {
    return type().m_set_(*this, source);
}
inline Tensor & Tensor::set_() {
    return type().m_set_(*this);
}
inline Tensor & Tensor::fill_(Scalar value) {
    return type().m_fill_(*this, value);
}
inline bool Tensor::is_contiguous() const {
    return type().m_is_contiguous(*this);
}
inline bool Tensor::is_set_to(const Tensor & tensor) const {
    return type().m_is_set_to(*this, tensor);
}
inline Tensor & Tensor::masked_fill_(const Tensor & mask, Scalar value) {
    return type().m_masked_fill_(*this, mask, value);
}
inline Tensor & Tensor::masked_scatter_(const Tensor & mask, const Tensor & source) {
    return type().m_masked_scatter_(*this, mask, source);
}
inline Tensor Tensor::masked_select(const Tensor & mask) const {
    return type().masked_select(*this, mask);
}
inline Tensor Tensor::transpose(int64_t dim0, int64_t dim1) const {
    return type().transpose(*this, dim0, dim1);
}
inline Tensor & Tensor::transpose_(int64_t dim0, int64_t dim1) {
    return type().m_transpose_(*this, dim0, dim1);
}
inline Tensor Tensor::t() const {
    return type().t(*this);
}
inline Tensor & Tensor::t_() {
    return type().m_t_(*this);
}
inline Tensor Tensor::squeeze(int64_t dim) const {
    return type().squeeze(*this, dim);
}
inline Tensor Tensor::squeeze() const {
    return type().squeeze(*this);
}
inline Tensor & Tensor::squeeze_(int64_t dim) {
    return type().m_squeeze_(*this, dim);
}
inline Tensor & Tensor::squeeze_() {
    return type().m_squeeze_(*this);
}
inline Tensor Tensor::unsqueeze(int64_t dim) const {
    return type().unsqueeze(*this, dim);
}
inline Tensor & Tensor::unsqueeze_(int64_t dim) {
    return type().m_unsqueeze_(*this, dim);
}
inline Tensor Tensor::nonzero() const {
    return type().nonzero(*this);
}
inline Tensor Tensor::contiguous() const {
    return type().m_contiguous(*this);
}
inline Tensor Tensor::clone() const {
    return type().m_clone(*this);
}
inline Tensor Tensor::view(IntList size) const {
    return type().m_view(*this, size);
}
inline Tensor Tensor::expand(IntList size) const {
    return type().m_expand(*this, size);
}
inline Tensor & Tensor::resize_as_(const Tensor & the_template) {
    return type().m_resize_as_(*this, the_template);
}
inline Tensor Tensor::index_select(int64_t dim, const Tensor & index) const {
    return type().index_select(*this, dim, index);
}
inline Tensor & Tensor::index_copy_(int64_t dim, const Tensor & index, const Tensor & source) {
    return type().m_index_copy_(*this, dim, index, source);
}
inline Tensor Tensor::take(const Tensor & index) const {
    return type().take(*this, index);
}
inline Tensor & Tensor::put_(const Tensor & index, const Tensor & source, bool accumulate) {
    return type().m_put_(*this, index, source, accumulate);
}
inline Tensor & Tensor::index_add_(int64_t dim, const Tensor & index, const Tensor & source) {
    return type().m_index_add_(*this, dim, index, source);
}
inline Tensor & Tensor::index_fill_(int64_t dim, const Tensor & index, Scalar value) {
    return type().m_index_fill_(*this, dim, index, value);
}
inline Tensor Tensor::narrow(int64_t dimension, int64_t start, int64_t length) const {
    return type().m_narrow(*this, dimension, start, length);
}
inline Tensor Tensor::unfold(int64_t dimension, int64_t size, int64_t step) const {
    return type().m_unfold(*this, dimension, size, step);
}
inline Tensor & Tensor::scatter_(int64_t dim, const Tensor & index, const Tensor & src) {
    return type().m_scatter_(*this, dim, index, src);
}
inline Tensor & Tensor::scatter_(int64_t dim, const Tensor & index, Scalar value) {
    return type().m_scatter_(*this, dim, index, value);
}
inline Tensor & Tensor::scatter_add_(int64_t dim, const Tensor & index, const Tensor & src) {
    return type().m_scatter_add_(*this, dim, index, src);
}
inline Tensor Tensor::gather(int64_t dim, const Tensor & index) const {
    return type().gather(*this, dim, index);
}
inline void* Tensor::data_ptr() const {
    return type().m_data_ptr(*this);
}
inline bool Tensor::equal(const Tensor & other) const {
    return type().equal(*this, other);
}
inline Tensor Tensor::__and__(Scalar other) const {
    return type().__and__(*this, other);
}
inline Tensor Tensor::__and__(const Tensor & other) const {
    return type().__and__(*this, other);
}
inline Tensor & Tensor::__iand__(Scalar other) {
    return type().__iand__(*this, other);
}
inline Tensor & Tensor::__iand__(const Tensor & other) {
    return type().__iand__(*this, other);
}
inline Tensor Tensor::__or__(Scalar other) const {
    return type().__or__(*this, other);
}
inline Tensor Tensor::__or__(const Tensor & other) const {
    return type().__or__(*this, other);
}
inline Tensor & Tensor::__ior__(Scalar other) {
    return type().__ior__(*this, other);
}
inline Tensor & Tensor::__ior__(const Tensor & other) {
    return type().__ior__(*this, other);
}
inline Tensor Tensor::__xor__(Scalar other) const {
    return type().__xor__(*this, other);
}
inline Tensor Tensor::__xor__(const Tensor & other) const {
    return type().__xor__(*this, other);
}
inline Tensor & Tensor::__ixor__(Scalar other) {
    return type().__ixor__(*this, other);
}
inline Tensor & Tensor::__ixor__(const Tensor & other) {
    return type().__ixor__(*this, other);
}
inline Tensor Tensor::__lshift__(Scalar other) const {
    return type().__lshift__(*this, other);
}
inline Tensor Tensor::__lshift__(const Tensor & other) const {
    return type().__lshift__(*this, other);
}
inline Tensor & Tensor::__ilshift__(Scalar other) {
    return type().__ilshift__(*this, other);
}
inline Tensor & Tensor::__ilshift__(const Tensor & other) {
    return type().__ilshift__(*this, other);
}
inline Tensor Tensor::__rshift__(Scalar other) const {
    return type().__rshift__(*this, other);
}
inline Tensor Tensor::__rshift__(const Tensor & other) const {
    return type().__rshift__(*this, other);
}
inline Tensor & Tensor::__irshift__(Scalar other) {
    return type().__irshift__(*this, other);
}
inline Tensor & Tensor::__irshift__(const Tensor & other) {
    return type().__irshift__(*this, other);
}
inline Tensor Tensor::lt(Scalar other) const {
    return type().lt(*this, other);
}
inline Tensor Tensor::lt(const Tensor & other) const {
    return type().lt(*this, other);
}
inline Tensor & Tensor::lt_(Scalar other) {
    return type().m_lt_(*this, other);
}
inline Tensor & Tensor::lt_(const Tensor & other) {
    return type().m_lt_(*this, other);
}
inline Tensor Tensor::gt(Scalar other) const {
    return type().gt(*this, other);
}
inline Tensor Tensor::gt(const Tensor & other) const {
    return type().gt(*this, other);
}
inline Tensor & Tensor::gt_(Scalar other) {
    return type().m_gt_(*this, other);
}
inline Tensor & Tensor::gt_(const Tensor & other) {
    return type().m_gt_(*this, other);
}
inline Tensor Tensor::le(Scalar other) const {
    return type().le(*this, other);
}
inline Tensor Tensor::le(const Tensor & other) const {
    return type().le(*this, other);
}
inline Tensor & Tensor::le_(Scalar other) {
    return type().m_le_(*this, other);
}
inline Tensor & Tensor::le_(const Tensor & other) {
    return type().m_le_(*this, other);
}
inline Tensor Tensor::ge(Scalar other) const {
    return type().ge(*this, other);
}
inline Tensor Tensor::ge(const Tensor & other) const {
    return type().ge(*this, other);
}
inline Tensor & Tensor::ge_(Scalar other) {
    return type().m_ge_(*this, other);
}
inline Tensor & Tensor::ge_(const Tensor & other) {
    return type().m_ge_(*this, other);
}
inline Tensor Tensor::eq(Scalar other) const {
    return type().eq(*this, other);
}
inline Tensor Tensor::eq(const Tensor & other) const {
    return type().eq(*this, other);
}
inline Tensor & Tensor::eq_(Scalar other) {
    return type().m_eq_(*this, other);
}
inline Tensor & Tensor::eq_(const Tensor & other) {
    return type().m_eq_(*this, other);
}
inline Tensor Tensor::ne(Scalar other) const {
    return type().ne(*this, other);
}
inline Tensor Tensor::ne(const Tensor & other) const {
    return type().ne(*this, other);
}
inline Tensor & Tensor::ne_(Scalar other) {
    return type().m_ne_(*this, other);
}
inline Tensor & Tensor::ne_(const Tensor & other) {
    return type().m_ne_(*this, other);
}
inline std::tuple<Tensor,Tensor> Tensor::min(int64_t dim, bool keepdim) const {
    return type().min(*this, dim, keepdim);
}
inline Tensor Tensor::min(const Tensor & other) const {
    return type().min(*this, other);
}
inline Scalar Tensor::min() const {
    return type().min(*this);
}
inline std::tuple<Tensor,Tensor> Tensor::max(int64_t dim, bool keepdim) const {
    return type().max(*this, dim, keepdim);
}
inline Tensor Tensor::max(const Tensor & other) const {
    return type().max(*this, other);
}
inline Scalar Tensor::max() const {
    return type().max(*this);
}
inline std::tuple<Tensor,Tensor> Tensor::kthvalue(int64_t k, int64_t dim, bool keepdim) const {
    return type().kthvalue(*this, k, dim, keepdim);
}
inline std::tuple<Tensor,Tensor> Tensor::mode(int64_t dim, bool keepdim) const {
    return type().mode(*this, dim, keepdim);
}
inline std::tuple<Tensor,Tensor> Tensor::median(int64_t dim, bool keepdim) const {
    return type().median(*this, dim, keepdim);
}
inline Scalar Tensor::median() const {
    return type().median(*this);
}
inline std::tuple<Tensor,Tensor> Tensor::sort(int64_t dim, bool descending) const {
    return type().sort(*this, dim, descending);
}
inline std::tuple<Tensor,Tensor> Tensor::topk(int64_t k, int64_t dim, bool largest, bool sorted) const {
    return type().topk(*this, k, dim, largest, sorted);
}
inline bool Tensor::all() const {
    return type().m_all(*this);
}
inline bool Tensor::any() const {
    return type().m_any(*this);
}
inline int64_t Tensor::get_device() const {
    return type().m_get_device(*this);
}
inline Tensor Tensor::abs() const {
    return type().abs(*this);
}
inline Tensor & Tensor::abs_() {
    return type().m_abs_(*this);
}
inline Tensor & Tensor::sigmoid_() {
    return type().m_sigmoid_(*this);
}
inline Tensor Tensor::sigmoid() const {
    return type().sigmoid(*this);
}
inline Tensor & Tensor::log_() {
    return type().m_log_(*this);
}
inline Tensor Tensor::log() const {
    return type().log(*this);
}
inline Tensor & Tensor::log1p_() {
    return type().m_log1p_(*this);
}
inline Tensor Tensor::log1p() const {
    return type().log1p(*this);
}
inline Tensor Tensor::lgamma() const {
    return type().lgamma(*this);
}
inline Tensor & Tensor::lgamma_() {
    return type().m_lgamma_(*this);
}
inline Tensor & Tensor::exp_() {
    return type().m_exp_(*this);
}
inline Tensor Tensor::exp() const {
    return type().exp(*this);
}
inline Tensor & Tensor::cos_() {
    return type().m_cos_(*this);
}
inline Tensor Tensor::cos() const {
    return type().cos(*this);
}
inline Tensor & Tensor::acos_() {
    return type().m_acos_(*this);
}
inline Tensor Tensor::acos() const {
    return type().acos(*this);
}
inline Tensor & Tensor::cosh_() {
    return type().m_cosh_(*this);
}
inline Tensor Tensor::cosh() const {
    return type().cosh(*this);
}
inline Tensor & Tensor::sin_() {
    return type().m_sin_(*this);
}
inline Tensor Tensor::sin() const {
    return type().sin(*this);
}
inline Tensor & Tensor::asin_() {
    return type().m_asin_(*this);
}
inline Tensor Tensor::asin() const {
    return type().asin(*this);
}
inline Tensor & Tensor::sinh_() {
    return type().m_sinh_(*this);
}
inline Tensor Tensor::sinh() const {
    return type().sinh(*this);
}
inline Tensor & Tensor::tan_() {
    return type().m_tan_(*this);
}
inline Tensor Tensor::tan() const {
    return type().tan(*this);
}
inline Tensor & Tensor::atan_() {
    return type().m_atan_(*this);
}
inline Tensor Tensor::atan() const {
    return type().atan(*this);
}
inline Tensor & Tensor::tanh_() {
    return type().m_tanh_(*this);
}
inline Tensor Tensor::tanh() const {
    return type().tanh(*this);
}
inline Tensor & Tensor::erf_() {
    return type().m_erf_(*this);
}
inline Tensor Tensor::erf() const {
    return type().erf(*this);
}
inline Tensor & Tensor::erfinv_() {
    return type().m_erfinv_(*this);
}
inline Tensor Tensor::erfinv() const {
    return type().erfinv(*this);
}
inline Tensor & Tensor::sqrt_() {
    return type().m_sqrt_(*this);
}
inline Tensor Tensor::sqrt() const {
    return type().sqrt(*this);
}
inline Tensor & Tensor::rsqrt_() {
    return type().m_rsqrt_(*this);
}
inline Tensor Tensor::rsqrt() const {
    return type().rsqrt(*this);
}
inline Tensor & Tensor::ceil_() {
    return type().m_ceil_(*this);
}
inline Tensor Tensor::ceil() const {
    return type().ceil(*this);
}
inline Tensor & Tensor::floor_() {
    return type().m_floor_(*this);
}
inline Tensor Tensor::floor() const {
    return type().floor(*this);
}
inline Tensor & Tensor::round_() {
    return type().m_round_(*this);
}
inline Tensor Tensor::round() const {
    return type().round(*this);
}
inline Tensor & Tensor::trunc_() {
    return type().m_trunc_(*this);
}
inline Tensor Tensor::trunc() const {
    return type().trunc(*this);
}
inline Tensor & Tensor::frac_() {
    return type().m_frac_(*this);
}
inline Tensor Tensor::frac() const {
    return type().frac(*this);
}
inline Tensor Tensor::mean(int64_t dim, bool keepdim) const {
    return type().mean(*this, dim, keepdim);
}
inline Scalar Tensor::mean() const {
    return type().mean(*this);
}
inline Tensor Tensor::var(int64_t dim, bool unbiased, bool keepdim) const {
    return type().var(*this, dim, unbiased, keepdim);
}
inline Scalar Tensor::var(bool unbiased) const {
    return type().var(*this, unbiased);
}
inline Tensor Tensor::std(int64_t dim, bool unbiased, bool keepdim) const {
    return type().std(*this, dim, unbiased, keepdim);
}
inline Scalar Tensor::std(bool unbiased) const {
    return type().std(*this, unbiased);
}
inline Tensor Tensor::norm(Scalar p, int64_t dim, bool keepdim) const {
    return type().norm(*this, p, dim, keepdim);
}
inline Scalar Tensor::norm(Scalar p) const {
    return type().norm(*this, p);
}
inline Tensor Tensor::renorm(Scalar p, int64_t dim, Scalar maxnorm) const {
    return type().renorm(*this, p, dim, maxnorm);
}
inline Tensor & Tensor::renorm_(Scalar p, int64_t dim, Scalar maxnorm) {
    return type().m_renorm_(*this, p, dim, maxnorm);
}
inline Scalar Tensor::dist(const Tensor & other, Scalar p) const {
    return type().dist(*this, other, p);
}
inline Tensor Tensor::reciprocal() const {
    return type().reciprocal(*this);
}
inline Tensor & Tensor::reciprocal_() {
    return type().m_reciprocal_(*this);
}
inline Tensor Tensor::neg() const {
    return type().neg(*this);
}
inline Tensor & Tensor::neg_() {
    return type().m_neg_(*this);
}
inline Tensor Tensor::atan2(const Tensor & other) const {
    return type().atan2(*this, other);
}
inline Tensor & Tensor::atan2_(const Tensor & other) {
    return type().m_atan2_(*this, other);
}
inline Tensor Tensor::pow(Scalar exponent) const {
    return type().pow(*this, exponent);
}
inline Tensor Tensor::pow(const Tensor & exponent) const {
    return type().pow(*this, exponent);
}
inline Tensor & Tensor::pow_(Scalar exponent) {
    return type().m_pow_(*this, exponent);
}
inline Tensor & Tensor::pow_(const Tensor & exponent) {
    return type().m_pow_(*this, exponent);
}
inline Tensor Tensor::lerp(const Tensor & end, Scalar weight) const {
    return type().lerp(*this, end, weight);
}
inline Tensor & Tensor::lerp_(const Tensor & end, Scalar weight) {
    return type().m_lerp_(*this, end, weight);
}
inline Tensor Tensor::histc(int64_t bins, Scalar min, Scalar max) const {
    return type().histc(*this, bins, min, max);
}
inline Tensor & Tensor::zero_() {
    return type().m_zero_(*this);
}
inline Tensor Tensor::sum(int64_t dim, bool keepdim) const {
    return type().sum(*this, dim, keepdim);
}
inline Scalar Tensor::sum() const {
    return type().sum(*this);
}
inline Tensor Tensor::prod(int64_t dim, bool keepdim) const {
    return type().prod(*this, dim, keepdim);
}
inline Scalar Tensor::prod() const {
    return type().prod(*this);
}
inline Tensor Tensor::cumsum(int64_t dim) const {
    return type().cumsum(*this, dim);
}
inline Tensor Tensor::cumprod(int64_t dim) const {
    return type().cumprod(*this, dim);
}
inline Tensor Tensor::sign() const {
    return type().sign(*this);
}
inline Tensor & Tensor::sign_() {
    return type().m_sign_(*this);
}
inline Scalar Tensor::trace() const {
    return type().trace(*this);
}
inline Tensor Tensor::add(Scalar other, Scalar alpha) const {
    return type().add(*this, other, alpha);
}
inline Tensor Tensor::add(const Tensor & other, Scalar alpha) const {
    return type().add(*this, other, alpha);
}
inline Tensor Tensor::add(SparseTensor other, Scalar alpha) const {
    return type().add(*this, other, alpha);
}
inline Tensor & Tensor::add_(Scalar other, Scalar alpha) {
    return type().m_add_(*this, other, alpha);
}
inline Tensor & Tensor::add_(const Tensor & other, Scalar alpha) {
    return type().m_add_(*this, other, alpha);
}
inline Tensor & Tensor::add_(SparseTensor other, Scalar alpha) {
    return type().m_add_(*this, other, alpha);
}
inline Tensor Tensor::sub(Scalar other, Scalar alpha) const {
    return type().sub(*this, other, alpha);
}
inline Tensor Tensor::sub(const Tensor & other, Scalar alpha) const {
    return type().sub(*this, other, alpha);
}
inline Tensor & Tensor::sub_(Scalar other, Scalar alpha) {
    return type().m_sub_(*this, other, alpha);
}
inline Tensor & Tensor::sub_(const Tensor & other, Scalar alpha) {
    return type().m_sub_(*this, other, alpha);
}
inline Tensor Tensor::mul(Scalar other) const {
    return type().mul(*this, other);
}
inline Tensor Tensor::mul(const Tensor & other) const {
    return type().mul(*this, other);
}
inline Tensor & Tensor::mul_(Scalar other) {
    return type().m_mul_(*this, other);
}
inline Tensor & Tensor::mul_(const Tensor & other) {
    return type().m_mul_(*this, other);
}
inline Tensor Tensor::div(Scalar other) const {
    return type().div(*this, other);
}
inline Tensor Tensor::div(const Tensor & other) const {
    return type().div(*this, other);
}
inline Tensor & Tensor::div_(Scalar other) {
    return type().m_div_(*this, other);
}
inline Tensor & Tensor::div_(const Tensor & other) {
    return type().m_div_(*this, other);
}
inline Tensor Tensor::fmod(Scalar other) const {
    return type().fmod(*this, other);
}
inline Tensor Tensor::fmod(const Tensor & other) const {
    return type().fmod(*this, other);
}
inline Tensor & Tensor::fmod_(Scalar other) {
    return type().m_fmod_(*this, other);
}
inline Tensor & Tensor::fmod_(const Tensor & other) {
    return type().m_fmod_(*this, other);
}
inline Tensor Tensor::remainder(Scalar other) const {
    return type().remainder(*this, other);
}
inline Tensor Tensor::remainder(const Tensor & other) const {
    return type().remainder(*this, other);
}
inline Tensor & Tensor::remainder_(Scalar other) {
    return type().m_remainder_(*this, other);
}
inline Tensor & Tensor::remainder_(const Tensor & other) {
    return type().m_remainder_(*this, other);
}
inline Tensor Tensor::clamp(Scalar min, Scalar max) const {
    return type().clamp(*this, min, max);
}
inline Tensor Tensor::clamp(Scalar min) const {
    return type().clamp(*this, min);
}
inline Tensor & Tensor::clamp_(Scalar min, Scalar max) {
    return type().m_clamp_(*this, min, max);
}
inline Tensor & Tensor::clamp_(Scalar min) {
    return type().m_clamp_(*this, min);
}
inline Scalar Tensor::dot(const Tensor & tensor) const {
    return type().dot(*this, tensor);
}
inline Tensor Tensor::tril(int64_t diagonal) const {
    return type().tril(*this, diagonal);
}
inline Tensor & Tensor::tril_(int64_t diagonal) {
    return type().m_tril_(*this, diagonal);
}
inline Tensor Tensor::triu(int64_t diagonal) const {
    return type().triu(*this, diagonal);
}
inline Tensor & Tensor::triu_(int64_t diagonal) {
    return type().m_triu_(*this, diagonal);
}
inline Tensor Tensor::cross(const Tensor & other, int64_t dim) const {
    return type().cross(*this, other, dim);
}
inline Tensor Tensor::diag(int64_t diagonal) const {
    return type().diag(*this, diagonal);
}
inline Tensor Tensor::addmm(const Tensor & mat1, const Tensor & mat2, Scalar beta, Scalar alpha) const {
    return type().addmm(*this, mat1, mat2, beta, alpha);
}
inline Tensor & Tensor::addmm_(const Tensor & mat1, const Tensor & mat2, Scalar beta, Scalar alpha) {
    return type().m_addmm_(*this, mat1, mat2, beta, alpha);
}
inline Tensor Tensor::addmv(const Tensor & mat, const Tensor & vec, Scalar beta, Scalar alpha) const {
    return type().addmv(*this, mat, vec, beta, alpha);
}
inline Tensor & Tensor::addmv_(const Tensor & mat, const Tensor & vec, Scalar beta, Scalar alpha) {
    return type().m_addmv_(*this, mat, vec, beta, alpha);
}
inline Tensor Tensor::addr(const Tensor & vec1, const Tensor & vec2, Scalar beta, Scalar alpha) const {
    return type().addr(*this, vec1, vec2, beta, alpha);
}
inline Tensor & Tensor::addr_(const Tensor & vec1, const Tensor & vec2, Scalar beta, Scalar alpha) {
    return type().m_addr_(*this, vec1, vec2, beta, alpha);
}
inline Tensor Tensor::ger(const Tensor & vec2) const {
    return type().ger(*this, vec2);
}
inline Tensor Tensor::mv(const Tensor & vec) const {
    return type().mv(*this, vec);
}
inline Tensor Tensor::mm(const Tensor & mat2) const {
    return type().mm(*this, mat2);
}
inline Tensor Tensor::bmm(const Tensor & mat2) const {
    return type().bmm(*this, mat2);
}
inline Tensor Tensor::addbmm(const Tensor & batch1, const Tensor & batch2, Scalar beta, Scalar alpha) const {
    return type().addbmm(*this, batch1, batch2, beta, alpha);
}
inline Tensor & Tensor::addbmm_(const Tensor & batch1, const Tensor & batch2, Scalar beta, Scalar alpha) {
    return type().m_addbmm_(*this, batch1, batch2, beta, alpha);
}
inline Tensor Tensor::baddbmm(const Tensor & batch1, const Tensor & batch2, Scalar beta, Scalar alpha) const {
    return type().baddbmm(*this, batch1, batch2, beta, alpha);
}
inline Tensor & Tensor::baddbmm_(const Tensor & batch1, const Tensor & batch2, Scalar beta, Scalar alpha) {
    return type().m_baddbmm_(*this, batch1, batch2, beta, alpha);
}
inline Tensor Tensor::addcmul(const Tensor & tensor1, const Tensor & tensor2, Scalar value) const {
    return type().addcmul(*this, tensor1, tensor2, value);
}
inline Tensor & Tensor::addcmul_(const Tensor & tensor1, const Tensor & tensor2, Scalar value) {
    return type().m_addcmul_(*this, tensor1, tensor2, value);
}
inline Tensor Tensor::addcdiv(const Tensor & tensor1, const Tensor & tensor2, Scalar value) const {
    return type().addcdiv(*this, tensor1, tensor2, value);
}
inline Tensor & Tensor::addcdiv_(const Tensor & tensor1, const Tensor & tensor2, Scalar value) {
    return type().m_addcdiv_(*this, tensor1, tensor2, value);
}
inline std::tuple<Tensor,Tensor> Tensor::gesv(const Tensor & A) const {
    return type().gesv(*this, A);
}
inline std::tuple<Tensor,Tensor> Tensor::gels(const Tensor & A) const {
    return type().gels(*this, A);
}
inline std::tuple<Tensor,Tensor> Tensor::trtrs(const Tensor & A, bool upper, bool transpose, bool unitriangular) const {
    return type().trtrs(*this, A, upper, transpose, unitriangular);
}
inline std::tuple<Tensor,Tensor> Tensor::symeig(bool eigenvectors, bool upper) const {
    return type().symeig(*this, eigenvectors, upper);
}
inline std::tuple<Tensor,Tensor> Tensor::eig(bool eigenvectors) const {
    return type().eig(*this, eigenvectors);
}
inline std::tuple<Tensor,Tensor,Tensor> Tensor::svd(bool some) const {
    return type().svd(*this, some);
}
inline Tensor Tensor::inverse() const {
    return type().inverse(*this);
}
inline Tensor Tensor::potrf(bool upper) const {
    return type().potrf(*this, upper);
}
inline Tensor Tensor::potrs(const Tensor & input2, bool upper) const {
    return type().potrs(*this, input2, upper);
}
inline Tensor Tensor::potri(bool upper) const {
    return type().potri(*this, upper);
}
inline std::tuple<Tensor,Tensor> Tensor::pstrf(bool upper, Scalar tol) const {
    return type().pstrf(*this, upper, tol);
}
inline std::tuple<Tensor,Tensor> Tensor::qr() const {
    return type().qr(*this);
}
inline std::tuple<Tensor,Tensor> Tensor::geqrf() const {
    return type().geqrf(*this);
}
inline Tensor Tensor::orgqr(const Tensor & input2) const {
    return type().orgqr(*this, input2);
}
inline Tensor Tensor::ormqr(const Tensor & input2, const Tensor & input3, bool left, bool transpose) const {
    return type().ormqr(*this, input2, input3, left, transpose);
}
inline std::tuple<Tensor,Tensor> Tensor::btrifact(const Tensor & info, bool pivot) const {
    return type().btrifact(*this, info, pivot);
}
inline Tensor Tensor::btrisolve(const Tensor & LU_data, const Tensor & LU_pivots) const {
    return type().btrisolve(*this, LU_data, LU_pivots);
}
inline Tensor & Tensor::random_(int64_t from, int64_t to, Generator * generator) {
    return type().m_random_(*this, from, to, generator);
}
inline Tensor & Tensor::random_(int64_t to, Generator * generator) {
    return type().m_random_(*this, to, generator);
}
inline Tensor & Tensor::random_(Generator * generator) {
    return type().m_random_(*this, generator);
}
inline Tensor Tensor::multinomial(int64_t num_samples, bool replacement, Generator * generator) const {
    return type().multinomial(*this, num_samples, replacement, generator);
}
inline Tensor & Tensor::uniform_(double from, double to, Generator * generator) {
    return type().m_uniform_(*this, from, to, generator);
}
inline Tensor & Tensor::normal_(double mean, double std, Generator * generator) {
    return type().m_normal_(*this, mean, std, generator);
}
inline Tensor & Tensor::cauchy_(double median, double sigma, Generator * generator) {
    return type().m_cauchy_(*this, median, sigma, generator);
}
inline Tensor & Tensor::log_normal_(double mean, double std, Generator * generator) {
    return type().m_log_normal_(*this, mean, std, generator);
}
inline Tensor & Tensor::exponential_(double lambd, Generator * generator) {
    return type().m_exponential_(*this, lambd, generator);
}
inline Tensor & Tensor::geometric_(double p, Generator * generator) {
    return type().m_geometric_(*this, p, generator);
}
inline int64_t Tensor::size(int64_t dim) const {
    return type().m_size(*this, dim);
}
inline int64_t Tensor::stride(int64_t dim) const {
    return type().m_stride(*this, dim);
}
inline Tensor Tensor::select(int64_t dim, int64_t sliceIndex) const {
    return type().select(*this, dim, sliceIndex);
}
inline Tensor Tensor::_unnarrow(int64_t dimension, int64_t offset, int64_t dimSize) const {
    return type()._unnarrow(*this, dimension, offset, dimSize);
}
inline Tensor & Tensor::assign_(const Tensor & src) {
    return type().m_assign_(*this, src);
}
inline Tensor Tensor::as_strided(IntList size, IntList stride) const {
    return type().as_strided(*this, size, stride);
}
inline Tensor & Tensor::as_strided_(IntList size, IntList stride, int64_t storage_offset) {
    return type().as_strided_(*this, size, stride, storage_offset);
}
inline Tensor & Tensor::reshape_(IntList size, IntList stride) {
    return type().m_reshape_(*this, size, stride);
}
inline std::vector<Tensor> Tensor::split(int64_t split_size, int64_t dim) const {
    return type().split(*this, split_size, dim);
}
inline std::vector<Tensor> Tensor::chunk(int64_t chunks, int64_t dim) const {
    return type().chunk(*this, chunks, dim);
}
inline bool Tensor::is_same_size(Tensor other) const {
    return type().is_same_size(*this, other);
}
inline Tensor Tensor::permute(IntList dims) const {
    return type().permute(*this, dims);
}

template<typename T>
inline T* Tensor::data() const {
  runtime_error("data() cast to unexpected type.");
}
#define DEFINE_CAST(T,name,_) \
template<> \
inline T* Tensor::data() const { \
  AT_ASSERT(type().scalarType() == ScalarType::name, \
    "expected scalar type % s but found %s", #name, \
    at::toString(type().scalarType())); \
  return static_cast<T*>(this->data_ptr()); \
} \
inline T* Tensor::to##name##Data() const { return data<T>(); }

AT_FORALL_SCALAR_TYPES(DEFINE_CAST)
#undef DEFINE_CAST

} //namespace at
