#include "AuroraDefs.h"
#include "Function1D.cuh"
#include <complex>
#include <utility>
#ifdef USE_CUDA
#include "CudaMatrix.h"

#include "Function.h"
#include "Matrix.h"

#include <iostream>
#include <string>

#include <cstddef>
#include <cuda_runtime.h>
#include "CudaMatrixPrivate.cuh"
namespace {
    std::string getMatrixShapeStr(const Aurora::CudaMatrix &aMatrix) {
    std::string ret;
    return "(" + std::to_string(aMatrix.getDimSize(0)) + "," +
            std::to_string(aMatrix.getDimSize(1)) + "," +
            std::to_string(aMatrix.getDimSize(1)) + "," + ")";
    }
}
namespace Aurora{

CudaMatrix::CudaMatrix(std::shared_ptr<float> aData, std::vector<int> aInfo, ValueType aValueType)
    : mValueType(aValueType)
    , mData(aData)
    , mInfo(aInfo)
{
    size_t infoSize = mInfo.size();
    for(; infoSize<3; ++infoSize)
    {
        mInfo.push_back(1);
    }
}

bool CudaMatrix::isNull() const
{
    return !mData || mInfo.empty();
}

bool CudaMatrix::isNan() const
{
    for(size_t i=0; i<getDataSize(); ++i)
    {
        if(mData.get()[i] == mData.get()[i])
        {
            return false;
        }
    }
    return true;
}

bool CudaMatrix::isScalar() const
{
    return (getDimSize(0) == 1 &&
            getDimSize(1) == 1 &&
            getDimSize(2) < 2);
}

float CudaMatrix::getScalar() const
{
    if (isNull()) return 0.0;
    if (isNull()) return 0.0;
    return getValue(0);
}

bool CudaMatrix::isVector() const
{
    if (getDimSize(2)>1) return false;
    if (isScalar()) return false;
    return getDimSize(0) == 1 ||
           getDimSize(1) == 1;
}

int CudaMatrix::getDims() const
{
    if(mInfo[2] > 1)
    {
        return 3;
    }
    return 2;
}

float *CudaMatrix::getData() const
{
    return mData.get();
}

int CudaMatrix::getDimSize(int aIndex) const
{
    if (aIndex >= 0 && aIndex < 3) {
        return mInfo.at(aIndex);
    }
    return 0;
}

size_t CudaMatrix::getDataSize() const
{
    if (!mData.get())return 0;
    size_t ret = 1;
    for (auto v: mInfo) {
        ret *= v;
    }
    return ret;
}

void CudaMatrix::forceReshape(int rows, int columns, int slices)
{
    mInfo = {rows,columns,slices};
}

bool CudaMatrix::compareShape(const CudaMatrix &other) const
{
    if (mInfo[2] == 1 && other.mInfo[2] == 1) {
        if (mInfo[0]==1 && other.mInfo[1] == 1 && mInfo[1] == other.mInfo[0]) return true;
        if (mInfo[1]==1 && other.mInfo[0] == 1 && mInfo[0] == other.mInfo[1]) return true;
    }
    for (int i = 0; i < mInfo.size(); ++i) {
        if (mInfo[i] != other.mInfo[i]) return false;
    }
    return true;
}

CudaMatrix CudaMatrix::fromRawData(float *aData, int aRows, int aCols, int aSlices, ValueType aType)
{
    if (!aData)
    {
        return CudaMatrix();
    }
    std::vector<int> vector{aRows, aCols, aSlices};
    CudaMatrix ret({aData, gpuFree}, vector, aType);
    return ret;
}

CudaMatrix CudaMatrix::copyFromRawData(float *aData, int aRows, int aCols, int aSlices, ValueType aType)
{
    if (!aData)
    {
        return CudaMatrix();
    }
    float* data = nullptr;
    unsigned long long size =  aRows * aCols * aSlices * aType;
    cudaMalloc((void**)&data, sizeof(float) * size);
    cudaMemcpy(data, aData, sizeof(float) * size, cudaMemcpyDeviceToDevice);
    std::vector<int> vector{aRows, aCols, aSlices};
    return CudaMatrix({data, gpuFree}, vector, aType);
}

CudaMatrix CudaMatrix::deepCopy() const
{
    float* data = nullptr;
    unsigned long long size =  getDataSize() * getValueType();
    cudaMalloc((void**)&data, sizeof(float) * size);
    cudaMemcpy(data, mData.get(), sizeof(float) * size, cudaMemcpyDeviceToDevice);
    return CudaMatrix::fromRawData(data, getDimSize(0), getDimSize(1), getDimSize(2), getValueType());
}

CudaMatrix CudaMatrix::deepCopyShape(bool complex) const
{
    float* data = nullptr;
    ValueType newType = complex==true?Complex:getValueType();
    unsigned long long size =  getDataSize() * newType;
    cudaMalloc((void**)&data, sizeof(float) * size);
    return CudaMatrix::fromRawData(data, getDimSize(0), getDimSize(1), getDimSize(2), newType);
}

Matrix CudaMatrix::toHostMatrix() const
{
    if(!mData.get())
    {
        return Matrix();
    }
    unsigned long long size = getDataSize() * getValueType();
    float* data = new float[size];
    cudaMemcpy(data, mData.get(), sizeof(float) * size, cudaMemcpyDeviceToHost);
    return Matrix::fromRawData(data, getDimSize(0), getDimSize(1), getDimSize(2), getValueType());
}

CudaMatrix CudaMatrix::block(int aDim,int aBeginIndex, int aEndIndex) const
{
    if(aDim > 2)
    {
        std::cerr<<"CudaMatrix block only support 1D-3D data!"<<std::endl;
        return CudaMatrix();
    }

    if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0)
    {
        std::cerr<<"CudaMatrix block BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
        return CudaMatrix();
    }

    if (aEndIndex>=getDimSize(aDim) || aEndIndex<0)
    {
        std::cerr<<"CudaMatrix block EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
        return CudaMatrix();
    }

    if (aEndIndex < aBeginIndex)
    {
        std::cerr<<"CudaMatrix block EndIndex can not less than BeginIndex ! BeginIndex:"<<aBeginIndex <<", EndIndex:"<<aEndIndex<<std::endl;
        return CudaMatrix();
    }

    int dimLength = aEndIndex - aBeginIndex + 1;
    int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
    float * dataOutput = nullptr;
    cudaMalloc((void**)&dataOutput, sizeof(float) * dataSize * getValueType());
    int colStride = getDimSize(0);
    int sliceStride = getDimSize(0)*getDimSize(1);

    switch (aDim)
    {
        case 0:
        {
            int colStride2 = dimLength;
            int sliceStride2 = dimLength*getDimSize(1);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                for (size_t j = 0; j < getDimSize(1); j++)
                {
                    cudaMemcpy(dataOutput + (colStride2 * j + i * sliceStride2)*getValueType(), 
                               mData.get()+ (aBeginIndex + j * colStride + i * sliceStride)*getValueType(),
                               sizeof(float) * colStride2*getValueType(), cudaMemcpyDeviceToDevice);
                }
            }
            return CudaMatrix::fromRawData(dataOutput,dimLength,getDimSize(1),getDimSize(2),getValueType());
        }
        case 1:
        {
            int colStride2 = getDimSize(0);
            int copySize = dimLength*getDimSize(0);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                cudaMemcpy(dataOutput +  getValueType()*(i * copySize), 
                           mData.get() + getValueType()*(aBeginIndex * colStride + i * sliceStride),
                           sizeof(float) * copySize*getValueType(), cudaMemcpyDeviceToDevice);
            }
            return CudaMatrix::fromRawData(dataOutput,getDimSize(0),dimLength,getDimSize(2),getValueType());
        }
        case 2:
        {
            int copySize = dimLength*sliceStride;
            cudaMemcpy(dataOutput, 
                       mData.get() + aBeginIndex * sliceStride*getValueType(),
                       sizeof(float) * copySize*getValueType(), cudaMemcpyDeviceToDevice);
            return CudaMatrix::fromRawData(dataOutput,getDimSize(0),getDimSize(1),dimLength,getValueType());
        }
        default:
        {
            return CudaMatrix();
        }
    }
}

bool CudaMatrix::setBlockValue(int aDim,int aBeginIndex, int aEndIndex,float aValue)
{
    if(aDim>2 )
    {
        std::cerr<<"CudaMatrix setblockValue only support 1D-3D data!"<<std::endl;
        return false;
    }

    if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0)
    {
        std::cerr<<"CudaMatrix setblockValue BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
        return false;
    }

    if (aEndIndex>=getDimSize(aDim) || aEndIndex<0)
    {
        std::cerr<<"CudaMatrix setblockValue EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
        return false;
    }

    if (aEndIndex < aBeginIndex)
    {
        std::cerr<<"CudaMatrix setblockValue EndIndex can not less than BeginIndex ! BeginIndex:"<<aBeginIndex <<", EndIndex:"<<aEndIndex<<std::endl;
        return false;
    }

    int dimLength = aEndIndex - aBeginIndex + 1;
    int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
    int colStride = getDimSize(0);
    int sliceStride = getDimSize(0)*getDimSize(1);

    switch (aDim)
    {
        case 0:
        {
            int colStride2 = dimLength;
            int sliceStride2 = dimLength*getDimSize(1);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                for (size_t j = 0; j < getDimSize(1); j++)
                {
                    float* begin = mData.get() + (aBeginIndex + j * colStride + i * sliceStride)*getValueType();
                    thrustFill(begin, begin + colStride2*getValueType(), aValue);
                }
            }
            return true;
        }
        case 1:
        {
            int colStride2 = getDimSize(0);
            int copySize = dimLength*getDimSize(0);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                float* begin = mData.get() + getValueType()*(aBeginIndex * colStride + i * sliceStride);
                thrustFill(begin, begin + copySize * getValueType(), aValue);
            }
            return true;
        }
        case 2:
        {
            int copySize = dimLength*sliceStride;
            float* begin = mData.get() + aBeginIndex * sliceStride*getValueType();
            thrustFill(begin, begin + copySize *getValueType(), aValue);
            return true;
        }
        default:
        {
            return false;
        }
    }

    return true;
}

bool CudaMatrix::setBlockComplexValue(int aDim,int aBeginIndex, int aEndIndex, std::complex<float> aValue)
{
    if(getValueType() != Complex)
    {
        std::cerr<<"CudaMatrix setBlockComplexValue only support complex matrix"<<std::endl;
        return false;
    }
    
    if(aDim>2 )
    {
        std::cerr<<"CudaMatrix setBlockComplexValue only support 1D-3D data!"<<std::endl;
        return false;
    }

    if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0)
    {
        std::cerr<<"CudaMatrix setBlockComplexValue BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
        return false;
    }

    if (aEndIndex>=getDimSize(aDim) || aEndIndex<0)
    {
        std::cerr<<"CudaMatrix setBlockComplexValue EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
        return false;
    }

    if (aEndIndex < aBeginIndex)
    {
        std::cerr<<"CudaMatrix setBlockComplexValue EndIndex can not less than BeginIndex ! BeginIndex:"<<aBeginIndex <<", EndIndex:"<<aEndIndex<<std::endl;
        return false;
    }

    int dimLength = aEndIndex - aBeginIndex + 1;
    int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
    int colStride = getDimSize(0);
    int sliceStride = getDimSize(0)*getDimSize(1);

    switch (aDim)
    {
        case 0:
        {
            int colStride2 = dimLength;
            int sliceStride2 = dimLength*getDimSize(1);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                for (size_t j = 0; j < getDimSize(1); j++)
                {
                    float* begin = mData.get() + (aBeginIndex + j * colStride + i * sliceStride)*getValueType();
                    thrustFill(begin, begin + colStride2*getValueType(), aValue);
                }
            }
            return true;
        }
        case 1:
        {
            int colStride2 = getDimSize(0);
            int copySize = dimLength*getDimSize(0);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                float* begin = mData.get() + getValueType()*(aBeginIndex * colStride + i * sliceStride);
                thrustFill(begin, begin + copySize * getValueType(), aValue);
            }
            return true;
        }
        case 2:
        {
            int copySize = dimLength*sliceStride;
            float* begin = mData.get() + aBeginIndex * sliceStride*getValueType();
            thrustFill(begin, begin + copySize *getValueType(), aValue);
            return true;
        }
        default:
        {
            return false;
        }
    }

    return true;
}

bool CudaMatrix::setBlock(int aDim,int aBeginIndex, int aEndIndex, const CudaMatrix& aMatrix)
{
    if( aDim>2 )
    {
        std::cerr<<"CudaMatrix setBlock only support 1D-3D data!"<<std::endl;
        return false;
    }

    if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0)
    {
        std::cerr<<"CudaMatrix setBlock BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
        return false;
    }
    
    if (aEndIndex>=getDimSize(aDim) || aEndIndex<0)
    {
        std::cerr<<"CudaMatrix setBlock EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
        return false;
    }

    int dimLength = std::abs(aEndIndex-aBeginIndex)+1;
    size_t newdims[3]{0};

    switch (aDim)
    {
        case 0:
        {
            newdims[0] = dimLength;
            newdims[1] = getDimSize(1);
            newdims[2] = getDimSize(2);
            break;
        }
        case 1:
        {
            newdims[0] = getDimSize(0);
            newdims[1] = dimLength;
            newdims[2] = getDimSize(2);
            break;
        }
        case 2:
        {
            newdims[0] = getDimSize(0);
            newdims[1] = getDimSize(1);
            newdims[2] = dimLength;
            break;
        }
    }

    if (aMatrix.getDimSize(0)!= newdims[0] 
        || aMatrix.getDimSize(1)!= newdims[1] 
        || aMatrix.getDimSize(2)!= newdims[2])
    {
        std::cerr << "CudaMatrix setBlock src Matrix(" << aMatrix.getDimSize(0) << ","
                  << aMatrix.getDimSize(1) << "," << aMatrix.getDimSize(2)
                  << ") not match the des shape(" << newdims[0] << ","
                  << newdims[1] << "," << newdims[2] << ")"
                  << std::endl;
        return false;
    }

    int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
    int colStride = getDimSize(0);
    int sliceStride = getDimSize(0)*getDimSize(1);
    switch (aDim)
    {
        case 0:
        {
            int colStride2 = dimLength;
            int sliceStride2 = dimLength*getDimSize(1);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                for (size_t j = 0; j < getDimSize(1); j++)
                {
                    cudaMemcpy(mData.get() + (aBeginIndex + j * colStride + i * sliceStride)*getValueType(),
                               aMatrix.getData() + (colStride2 * j + i * sliceStride2)*getValueType(),
                               sizeof(float) * colStride2*getValueType(), cudaMemcpyDeviceToDevice);
                }
            }
            return true;
        }
        case 1:
        {
            int colStride2 = getDimSize(0);
            int copySize = dimLength*getDimSize(0);
            for (size_t i = 0; i < getDimSize(2); i++)
            {
                cudaMemcpy(mData.get() + getValueType()*(aBeginIndex * colStride + i * sliceStride),
                           aMatrix.getData() +  getValueType()*(i * copySize),
                           sizeof(float) * copySize*getValueType(), cudaMemcpyDeviceToDevice);
            }
            return true;
        }
        case 2:
        {
            int copySize = dimLength*sliceStride;
            cudaMemcpy(mData.get() + aBeginIndex * sliceStride*getValueType(),
                       aMatrix.getData(),
                       sizeof(float) * copySize*getValueType(), cudaMemcpyDeviceToDevice);
            return true;
        }
        default:
        {
            return false;
        }
    }    
}
//--Add----------------------------------------------------------------
CudaMatrix CudaMatrix::operator+(float aScalar) const{
    if (isComplex()) {
        std::complex<float> v(aScalar,0);
        return (*this)+v;
    } else {
        auto out = deepCopyShape();
        unaryAdd(getData(), aScalar, out.getData(), getDataSize());
        return out;
    }
    
}

CudaMatrix operator+(float aScalar, const CudaMatrix &aMatrix) {
    return aMatrix + aScalar;
}

CudaMatrix &operator+(float aScalar, CudaMatrix &&aMatrix) {
    return std::forward<CudaMatrix &&>(aMatrix) + aScalar;
}

CudaMatrix &operator+(CudaMatrix &&aMatrix, float aScalar) {
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        return std::forward<CudaMatrix &&>(aMatrix)+v;
    } else {
        unaryAdd(aMatrix.getData(), aScalar, aMatrix.getData(),
                     aMatrix.getDataSize());
        return aMatrix;
    }
    
}

CudaMatrix CudaMatrix::operator+(std::complex<float> aScalar) const{
    
    if (isComplex()) {
        auto out = deepCopyShape();
        unaryAddc((std::complex<float>*)getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(*this);
        unaryAddc((std::complex<float>*)out.getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    }
}

CudaMatrix operator+(std::complex<float> aScalar, const CudaMatrix &aMatrix) {
    return aMatrix + aScalar;
}

CudaMatrix &operator+(std::complex<float> aScalar, CudaMatrix &&aMatrix) {
    return std::forward<CudaMatrix &&>(aMatrix) + aScalar;
}

CudaMatrix &operator+(CudaMatrix &&aMatrix, std::complex<float> aScalar) {
    if (aMatrix.isComplex()) {
        unaryAddc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unaryAddc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix CudaMatrix::operator+(const CudaMatrix &aMatrix) const {
    auto thisMatrix = *this;
    if (thisMatrix.isScalar()) {
        return thisMatrix.getScalar() + aMatrix;
    }
    if (aMatrix.isScalar()) {
        return thisMatrix + aMatrix.getScalar();
    }
    if (thisMatrix.compareShape(aMatrix)) {
        //输出需考虑是否需要装变为complex
        auto out = thisMatrix.deepCopyShape(aMatrix.isComplex()||thisMatrix.isComplex());
        unaryAddmm(aMatrix.getData(), thisMatrix.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, thisMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != thisMatrix.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : (thisMatrix);
        auto mat = aMatrix.isVector() ? (thisMatrix) : aMatrix;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryAddmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryAddmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M + M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(thisMatrix)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix CudaMatrix::operator+(CudaMatrix &&aMatrix) const {
    return std::forward<CudaMatrix&&>(aMatrix)+ *this;
}

CudaMatrix operator+(CudaMatrix &&aMatrix, CudaMatrix &aOther) {
    if (aOther.isScalar()) {
        return aOther.getScalar() + std::forward<CudaMatrix &&>(aMatrix);
    }
    if (aMatrix.isScalar()) {
        return aOther + aMatrix.getScalar();
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unaryAddmm(aMatrix.getData(), aOther.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, aOther.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryAddmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryAddmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M + M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}

//--mul-----------------------------------------------------
CudaMatrix CudaMatrix::operator*(float aScalar) const{
    if (isComplex()) {
        std::complex<float> v(aScalar,0);
        return (*this)*v;
    } else {
        auto out = deepCopyShape();
        unaryMul(getData(), aScalar, out.getData(), getDataSize());
        return out;
    }
    
}

CudaMatrix operator*(float aScalar, const CudaMatrix &aMatrix) {
    return aMatrix * aScalar;
}

CudaMatrix &operator*(float aScalar, CudaMatrix &&aMatrix) {
    return std::forward<CudaMatrix &&>(aMatrix) * aScalar;
}

CudaMatrix &operator*(CudaMatrix &&aMatrix, float aScalar) {
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        return std::forward<CudaMatrix &&>(aMatrix)*v;
    } else {
        unaryMul(aMatrix.getData(), aScalar, aMatrix.getData(),
                     aMatrix.getDataSize());
        return aMatrix;
    }
    
}

CudaMatrix CudaMatrix::operator*(std::complex<float> aScalar) const{
    
    if (isComplex()) {
        auto out = deepCopyShape();
        unaryMulc((std::complex<float>*)getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(*this);
        unaryMulc((std::complex<float>*)out.getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    }
}

CudaMatrix operator*(std::complex<float> aScalar, const CudaMatrix &aMatrix) {
    return aMatrix * aScalar;
}

CudaMatrix &operator*(std::complex<float> aScalar, CudaMatrix &&aMatrix) {
    return std::forward<CudaMatrix &&>(aMatrix) * aScalar;
}

CudaMatrix &operator*(CudaMatrix &&aMatrix, std::complex<float> aScalar) {
    if (aMatrix.isComplex()) {
        unaryMulc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unaryMulc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix CudaMatrix::operator*(const CudaMatrix &aMatrix) const {
    auto thisMatrix = *this;
    if (thisMatrix.isScalar()) {
        return thisMatrix.getScalar() * aMatrix;
    }
    if (aMatrix.isScalar()) {
        return thisMatrix * aMatrix.getScalar();
    }
    if (thisMatrix.compareShape(aMatrix)) {
        //输出需考虑是否需要装变为complex
        auto out = thisMatrix.deepCopyShape(aMatrix.isComplex()||thisMatrix.isComplex());
        unaryMulmm(aMatrix.getData(), thisMatrix.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, thisMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != thisMatrix.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : (thisMatrix);
        auto mat = aMatrix.isVector() ? (thisMatrix) : aMatrix;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryMulmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryMulmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M * M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(thisMatrix)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix CudaMatrix::operator*(CudaMatrix &&aMatrix) const {
    return std::forward<CudaMatrix&&>(aMatrix)* (*this);
}

CudaMatrix operator*(CudaMatrix &&aMatrix, CudaMatrix &aOther) {
    if (aOther.isScalar()) {
        return aOther.getScalar() * std::forward<CudaMatrix &&>(aMatrix);
    }
    if (aMatrix.isScalar()) {
        return aOther * aMatrix.getScalar();
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unaryMulmm(aMatrix.getData(), aOther.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, aOther.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryMulmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryMulmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M * M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}
    
//--Sub-----------------------------------------------------------------
CudaMatrix CudaMatrix::operator-(float aScalar) const{
    if (isComplex()) {
        std::complex<float> v(aScalar,0);
        return (*this)-v;
    } else {
        auto out = deepCopyShape();
        unarySub(getData(), aScalar, out.getData(), getDataSize());
        return out;
    }
    
}

CudaMatrix operator-(float aScalar, const CudaMatrix &aMatrix) {
    auto out = aMatrix.deepCopyShape();
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        unarySubc(v, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)out.getData(),
                  aMatrix.getDataSize());
        return out;
    } else {
        unarySub(aScalar, out.getData(),out.getData(),
                  out.getDataSize());  
        return out;
    }
}

CudaMatrix &operator-(float aScalar, CudaMatrix &&aMatrix) {
    if (aMatrix.isComplex()) {
        unarySubc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        unarySub(aScalar,aMatrix.getData(),aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix &operator-(CudaMatrix &&aMatrix, float aScalar) {
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        return std::forward<CudaMatrix &&>(aMatrix)-v;
    } else {
        unarySub(aMatrix.getData(), aScalar, aMatrix.getData(),
                     aMatrix.getDataSize());
        return aMatrix;
    }
    
}

CudaMatrix CudaMatrix::operator-(std::complex<float> aScalar) const{
    
    if (isComplex()) {
        auto out = deepCopyShape();
        unarySubc((std::complex<float>*)getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(*this);
        unarySubc((std::complex<float>*)out.getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    }
}

CudaMatrix operator-(std::complex<float> aScalar, const CudaMatrix &aMatrix) {
    if (aMatrix.isComplex()) {
        auto out = aMatrix.deepCopyShape();
        unarySubc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)out.getData(),
                  aMatrix.getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(aMatrix);
        unarySubc(aScalar, (std::complex<float> *)out.getData(),
                  (std::complex<float> *)out.getData(),
                  out.getDataSize());  
        return out;
    }
}

CudaMatrix &operator-(std::complex<float> aScalar, CudaMatrix &&aMatrix) {
    if (aMatrix.isComplex()) {
        unarySubc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unarySubc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix &operator-(CudaMatrix &&aMatrix, std::complex<float> aScalar) {
    if (aMatrix.isComplex()) {
        unarySubc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unarySubc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix CudaMatrix::operator-(const CudaMatrix &aMatrix) const {
    auto thisMatrix = *this;
    if (thisMatrix.isScalar()) {
        return thisMatrix.getScalar() - aMatrix;
    }
    if (aMatrix.isScalar()) {
        return thisMatrix - aMatrix.getScalar();
    }
    if (thisMatrix.compareShape(aMatrix)) {
        //输出需考虑是否需要装变为complex
        auto out = thisMatrix.deepCopyShape(aMatrix.isComplex()||thisMatrix.isComplex());
        unarySubmm(thisMatrix.getData(), aMatrix.getData(), out.getData(),
                   out.getDataSize(), thisMatrix.mValueType, aMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != thisMatrix.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : (thisMatrix);
        auto mat = aMatrix.isVector() ? (thisMatrix) : aMatrix;
        int direction = thisMatrix.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row, direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Column, direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(thisMatrix)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix CudaMatrix::operator-(CudaMatrix &&aMatrix) const {
    auto aOther = *this;
    if (isScalar()) {
        return getScalar() - std::forward<CudaMatrix &&>(aMatrix);
    }
    if (aMatrix.isScalar()) {
        return aOther - aMatrix.getScalar();
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unarySubmm(aOther.getData(), aMatrix.getData(), out.getData(),
                   out.getDataSize(), aOther.mValueType, aMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        int direction = aOther.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row,direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType,Column,direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix operator-(CudaMatrix &&aMatrix, CudaMatrix &aOther) {
    if (aOther.isScalar()) {
        return std::forward<CudaMatrix &&>(aMatrix) - aOther.getScalar();
    }
    if (aMatrix.isScalar()) {
        return aMatrix.getScalar() - aOther;
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unarySubmm(aMatrix.getData(), aOther.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, aOther.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        int direction = aMatrix.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row,direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unarySubmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType,Column,direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}

// div
CudaMatrix CudaMatrix::operator/(float aScalar) const{
    if (isComplex()) {
        std::complex<float> v(aScalar,0);
        return (*this)/v;
    } else {
        auto out = deepCopyShape();
        unaryDiv(getData(), aScalar, out.getData(), getDataSize());
        return out;
    }
    
}

CudaMatrix operator/(float aScalar, const CudaMatrix &aMatrix) {
    auto out = aMatrix.deepCopyShape();
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        unaryDivc(v, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)out.getData(),
                  aMatrix.getDataSize());
        return out;
    } else {
        unaryDiv(aScalar, out.getData(),out.getData(),
                  out.getDataSize());  
        return out;
    }
}

CudaMatrix &operator/(float aScalar, CudaMatrix &&aMatrix) {
    if (aMatrix.isComplex()) {
        unaryDivc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        unaryDiv(aScalar,aMatrix.getData(),aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix &operator/(CudaMatrix &&aMatrix, float aScalar) {
    if (aMatrix.isComplex()) {
        std::complex<float> v(aScalar,0);
        return std::forward<CudaMatrix &&>(aMatrix)/v;
    } else {
        unaryDiv(aMatrix.getData(), aScalar, aMatrix.getData(),
                     aMatrix.getDataSize());
        return aMatrix;
    }
    
}

CudaMatrix CudaMatrix::operator/(std::complex<float> aScalar) const{
    
    if (isComplex()) {
        auto out = deepCopyShape();
        unaryDivc((std::complex<float>*)getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(*this);
        unaryDivc((std::complex<float>*)out.getData(), aScalar, (std::complex<float>*)out.getData(), getDataSize());
        return out;
    }
}

CudaMatrix operator/(std::complex<float> aScalar, const CudaMatrix &aMatrix) {
    if (aMatrix.isComplex()) {
        auto out = aMatrix.deepCopyShape();
        unaryDivc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)out.getData(),
                  aMatrix.getDataSize());
        return out;
    } else {
        auto out = Aurora::complex(aMatrix);
        unaryDivc(aScalar, (std::complex<float> *)out.getData(),
                  (std::complex<float> *)out.getData(),
                  out.getDataSize());  
        return out;
    }
}

CudaMatrix &operator/(std::complex<float> aScalar, CudaMatrix &&aMatrix) {
    if (aMatrix.isComplex()) {
        unaryDivc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unaryDivc(aScalar, (std::complex<float> *)aMatrix.getData(),
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix &operator/(CudaMatrix &&aMatrix, std::complex<float> aScalar) {
    if (aMatrix.isComplex()) {
        unaryDivc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());
    } else {
        aMatrix = Aurora::complex(aMatrix);
        unaryDivc((std::complex<float> *)aMatrix.getData(), aScalar,
                  (std::complex<float> *)aMatrix.getData(),
                  aMatrix.getDataSize());  
    }
    return aMatrix;
}

CudaMatrix CudaMatrix::operator/(const CudaMatrix &aMatrix) const {
    auto thisMatrix = *this;
    if (thisMatrix.isScalar()) {
        return thisMatrix.getScalar() / aMatrix;
    }
    if (aMatrix.isScalar()) {
        return thisMatrix / aMatrix.getScalar();
    }
    if (thisMatrix.compareShape(aMatrix)) {
        //输出需考虑是否需要装变为complex
        auto out = thisMatrix.deepCopyShape(aMatrix.isComplex()||thisMatrix.isComplex());
        unaryDivmm(thisMatrix.getData(), aMatrix.getData(), out.getData(),
                   out.getDataSize(), thisMatrix.mValueType, aMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != thisMatrix.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : (thisMatrix);
        auto mat = aMatrix.isVector() ? (thisMatrix) : aMatrix;
        int direction = thisMatrix.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row, direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            //输出需考虑是否需要装变为complex
            auto out =  mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Column, direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(thisMatrix)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix CudaMatrix::operator/(CudaMatrix &&aMatrix) const {
    auto aOther = *this;
    if (isScalar()) {
        return getScalar() / std::forward<CudaMatrix &&>(aMatrix);
    }
    if (aMatrix.isScalar()) {
        return aOther / aMatrix.getScalar();
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unaryDivmm(aOther.getData(), aMatrix.getData(), out.getData(),
                   out.getDataSize(), aOther.mValueType, aMatrix.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        int direction = aOther.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row,direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType,Column,direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}

CudaMatrix operator/(CudaMatrix &&aMatrix, CudaMatrix &aOther) {
    if (aOther.isScalar()) {
        return std::forward<CudaMatrix &&>(aMatrix) / aOther.getScalar();
    }
    if (aMatrix.isScalar()) {
        return aMatrix.getScalar() / aOther;
    }
    if (aOther.compareShape(aMatrix)) {
        //aMatrix的数据类型与aOther一致或者体积比aOther大时，仍旧用CudaMatrix &&aMatrix作为输出
        auto out = aMatrix.mValueType >= aOther.mValueType? aMatrix : aOther.deepCopyShape();
        unaryDivmm(aMatrix.getData(), aOther.getData(), out.getData(),
                   out.getDataSize(), aMatrix.mValueType, aOther.mValueType);
        return out;
    }
    // 一个是向量一个是矩阵
    else if (aMatrix.isVector() != aOther.isVector()) {
        auto vec = aMatrix.isVector() ? aMatrix : aOther;
        auto mat = aMatrix.isVector() ? aOther : aMatrix;
        int direction = aMatrix.isVector()?1:0;
        // 行向量
        if (vec.getDimSize(0) == 1 && vec.getDimSize(1) == mat.getDimSize(1)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType, Aurora::Row,direction);
            return out;
        }
        // 列向量
        else if (vec.getDimSize(1) == 1 &&
                 vec.getDimSize(0) == mat.getDimSize(0)) {
            // 判定aMatrix是矩阵，且数据类型与输出结果一致时，继续使用CudaMatrix
            // &&aMatrix作为输出
            auto out = mat.deepCopyShape(vec.isComplex() || mat.isComplex());
            unaryDivmv(mat.getData(), vec.getData(), out.getData(),
                       out.getDataSize(), vec.getDataSize(), mat.mValueType,
                       vec.mValueType,Column,direction);
            return out;
        }
        // 尺寸不匹配
        else {
            std::cerr
                << "M - M fail, matrix shape not match the operation rule!"
                << "with matrix1 shape:"
                << getMatrixShapeStr(aMatrix)
                << " and matrix2 shape:"
                << getMatrixShapeStr(aOther)<< std::endl;
        }
    }
    return CudaMatrix();
}

    // -----------pow-------------------------------------------------------
    CudaMatrix CudaMatrix::operator^(int times) const{
        if (isComplex())
        {
            std::cerr<<"Complex matrix not support operator^(int times)"<<std::endl;
            return  CudaMatrix();
        }
        float scalar = (float)times;
        float* data = nullptr;
        unsigned long long size =  getDataSize() * getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, getDimSize(0), getDimSize(1), getDimSize(2), getValueType());
        unaryPow(getData(),scalar,out.getData(),getDataSize());
        return out;
    }
    CudaMatrix operator^(CudaMatrix &&aMatrix,int times){
        if (aMatrix.isComplex())
        {
            std::cerr<<"Complex matrix not support operator^(int times)"<<std::endl;
            return  CudaMatrix();
        }
        float scalar = (float)times;
        unaryPow(aMatrix.getData(),scalar,aMatrix.getData(),aMatrix.getDataSize());
        return aMatrix;
    }

    //----------negetive------------------------------------------------
    CudaMatrix operator-(CudaMatrix &&aMatrix){
        unaryNeg(aMatrix.getData(),aMatrix.getData(),aMatrix.getDataSize());
        return aMatrix;
    }
    CudaMatrix operator-(const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryNeg(aMatrix.getData(),out.getData(),aMatrix.getDataSize());
        return out;
    }

    //----compare---------------------------------------------------
    CudaMatrix CudaMatrix::operator>(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::G);
        return out;
    }
    CudaMatrix operator>(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::G);
        return out;
    }
    CudaMatrix CudaMatrix::operator>(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::G);
        return out;
    }

    CudaMatrix CudaMatrix::operator<(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::L);
        return out;
    }
    CudaMatrix operator<(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::L);
        return out;
    }
    CudaMatrix CudaMatrix::operator<(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::L);
        return out;
    }

    CudaMatrix CudaMatrix::operator>=(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::GE);
        return out;
    }

    CudaMatrix operator>=(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::GE);
        return out;
    }

    CudaMatrix CudaMatrix::operator>=(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::GE);
        return out;
    }
    CudaMatrix CudaMatrix::operator<=(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::LE);
        return out;
    }
    CudaMatrix operator<=(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::LE);
        return out;
    }
    CudaMatrix CudaMatrix::operator<=(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::LE);
        return out;
    }
    CudaMatrix CudaMatrix::operator==(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::E);
        return out;
    }
    CudaMatrix operator==(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::E);
        return out;
    }
    CudaMatrix CudaMatrix::operator==(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::E);
        return out;
    }
    CudaMatrix CudaMatrix::operator!=(float aScalar) const{
        float* data = nullptr;
        unsigned long long size =  this->getDataSize() * this->getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, this->getDimSize(0), this->getDimSize(1), this->getDimSize(2), this->getValueType());
        unaryCompare(this->getData(),aScalar,data,this->getDataSize(),::NE);
        return out;
    }
    CudaMatrix operator!=(float aScalar, const CudaMatrix &aMatrix){
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(aScalar,aMatrix.getData(),data,aMatrix.getDataSize(),::NE);
        return out;
    }
    CudaMatrix CudaMatrix::operator!=(const CudaMatrix &aMatrix) const{
        float* data = nullptr;
        unsigned long long size =  aMatrix.getDataSize() * aMatrix.getValueType();
        cudaMalloc((void**)&data, sizeof(float) * size);
        auto out =  CudaMatrix::fromRawData(data, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2), aMatrix.getValueType());
        unaryCompare(this->getData(),aMatrix.getData(),data,this->getDataSize(),::NE);
        return out;
    }
    float CudaMatrix::getValue(size_t index) const{
        float result;
        cudaError_t cuda_error = cudaMemcpy(&result, getData() + index, sizeof(float), cudaMemcpyDeviceToHost);

        if (cuda_error != cudaSuccess) {
            fprintf(stderr, "CUDA error: %s\n", cudaGetErrorString(cuda_error));
            return nan("");
        }
        return result;
    }
    void CudaMatrix::setValue(size_t index, const float& value){
        cudaError_t cuda_error = cudaMemcpy( getData() + index,&value, sizeof(float), cudaMemcpyHostToDevice);
        if (cuda_error != cudaSuccess) {
            fprintf(stderr, "CUDA error: %s\n", cudaGetErrorString(cuda_error));
        }
    }
}
#endif // USE_CUDA
