#include "Matrix.h"
#include "CudaMatrix.h"

#include <cmath>
#include <cstddef>
#include <mkl_cblas.h>
#include <string>
#include <cstring>
#include <iostream>

#include "AuroraDefs.h"

#include <Eigen/Core>
#include <Eigen/Dense>

#include "Eigen/src/Core/Map.h"
#include "Eigen/src/Core/Matrix.h"
#include "Function.h"

#ifdef USE_CUDA
#include <cuda_runtime.h>
#endif

namespace Aurora{
    typedef void(*CalcFuncD)(const MKL_INT n, const float a[], const MKL_INT inca, const float b[],
                             const MKL_INT incb, float r[], const MKL_INT incr);

    typedef void(*CalcFuncZ)(const MKL_INT n, const MKL_Complex8 a[], const MKL_INT inca, const MKL_Complex8 b[],
                             const MKL_INT incb, MKL_Complex8 r[], const MKL_INT incr);

    inline Matrix operatorAxM(CalcFuncD aFunc, float aScalar, const Matrix &aMatrix) {
        float *output = malloc(aMatrix.getDataSize(), aMatrix.getValueType() == Complex);
        //复数时，+和-需要特别操作，只影响实部
        if (aMatrix.getValueType() == Complex && (aFunc == &vsAddI || aFunc == &vsSubI)) {
            aFunc(aMatrix.getDataSize(),  &aScalar, 0,aMatrix.getData() , 2, output ,
                  2);
            float zero = 0.0;
            aFunc(aMatrix.getDataSize(),  &zero, 0,aMatrix.getData()+1 , 2, output+1 ,
                  2);
        } else{
            aFunc(aMatrix.getDataSize(),  &aScalar, 0,aMatrix.getData(), 1, output, 1);
        }
        return Matrix::New(output, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2),
                                   aMatrix.getValueType());
    }

    inline Matrix operatorMxA(CalcFuncD aFunc, float aScalar, const Matrix &aMatrix) {
        float *output = malloc(aMatrix.getDataSize(), aMatrix.getValueType() == Complex);
        //复数时，+和-需要特别操作，只影响实部
        if (aMatrix.getValueType() == Complex && (aFunc == &vsAddI || aFunc == &vsSubI)) {
            aFunc(aMatrix.getDataSize(), aMatrix.getData() , 2, &aScalar, 0, output ,
                  2);
            float zero = 0.0;
            aFunc(aMatrix.getDataSize(), aMatrix.getData()+1 , 2, &zero, 0, output+1 ,
                  2);
        } else{
            aFunc(aMatrix.getDataSize(), aMatrix.getData(), 1, &aScalar, 0, output, 1);
        }
        return Matrix::New(output, aMatrix.getDimSize(0), aMatrix.getDimSize(1), aMatrix.getDimSize(2),
                                   aMatrix.getValueType());
    }

    inline Matrix &operatorAxM_RR(CalcFuncD aFunc, float aScalar, Aurora::Matrix &&aMatrix) {
        if (aMatrix.getValueType() == Complex) {
            //针对实部操作
            aFunc(aMatrix.getDataSize(),  &aScalar, 0,aMatrix.getData(), 2,
                  aMatrix.getData(),
                  2);
            //乘法除法需特别操作，影响虚部
            if (aFunc == &vsDivI || aFunc == &vsMulI){
                aFunc(aMatrix.getDataSize(),  &aScalar, 0,aMatrix.getData() + 1, 2,
                  aMatrix.getData() + 1, 2);
            }
        } else {
            aFunc(aMatrix.getDataSize(),  &aScalar, 0,aMatrix.getData(), 1,
                  aMatrix.getData(),
                  1);
        }
        return aMatrix;
    }

    inline Matrix &operatorMxA_RR(CalcFuncD aFunc, float aScalar, Aurora::Matrix &&aMatrix) {
        if (aMatrix.getValueType() == Complex) {
            //针对实部操作
            aFunc(aMatrix.getDataSize(), aMatrix.getData(), 2, &aScalar, 0,
                  aMatrix.getData(),
                  2);
            //乘法除法需特别操作，影响虚部
            if (aFunc == &vsDivI || aFunc == &vsMulI){
                aFunc(aMatrix.getDataSize(), aMatrix.getData() + 1, 2, &aScalar, 0,
                  aMatrix.getData() + 1, 2);
            }
        } else {
            aFunc(aMatrix.getDataSize(), aMatrix.getData(), 1, &aScalar, 0,
                  aMatrix.getData(),
                  1);
        }
        return aMatrix;
    }


    inline void V_MxM_CN_Calc(
            CalcFuncD aFuncD,
            const int size, float* xC,float* yN,float *output, int DimsStride) {
        aFuncD(size, xC, DimsStride * 2, yN, 1, output, 2);
        if (aFuncD == &vsDivI || aFuncD == &vsMulI){
            aFuncD(size, xC + 1, DimsStride * 2, yN, 1, output + 1, 2);
        }
        else{
            float zero = 0.0;
            aFuncD(size, xC+1 , DimsStride*2, &zero, 0, output + 1, 2);
        }
    }

    inline float* _MxM_CN_Calc(
            CalcFuncD aFuncD,
            const int size, float* xC,float* yN, int dimsStride)
    {
        float *output = malloc(size, true);
        V_MxM_CN_Calc(aFuncD, size, xC, yN, output, dimsStride);
        return output;
    }

    inline void V_MxM_NC_Calc(
            CalcFuncD aFuncD,
            const int size, float* xN,float* yC,float *output, int DimsStride) {
        aFuncD(size, xN, DimsStride, yC, 2, output, 2);
        if (aFuncD == &vsDivI || aFuncD == &vsMulI){
            aFuncD(size, xN , DimsStride, yC+ 1, 2, output + 1, 2);
        }
        else{
            float zero = 0.0;
            aFuncD(size, yC+ 1, 2, &zero, 0, output + 1, 2);
        }
    }

    inline float* _MxM_NC_Calc(
            CalcFuncD aFuncD,
            const int size, float* xN,float* yC, int dimsStride)
    {
        float *output = malloc(size, true);
        V_MxM_NC_Calc(aFuncD, size, xN, yC, output, dimsStride);
        return output;
    }

    inline void V_MxM_NN_Calc(
            CalcFuncD aFuncD,
            const int size, float* x,float* y,float* output, int DimsStride) {
        aFuncD(size, x, DimsStride, y, 1, output,1);
    }

    inline float* _MxM_NN_Calc(
            CalcFuncD aFuncD,
            const int size, float* x,float* y, int DimsStride) {
        float *output = malloc(size);
        V_MxM_NN_Calc(aFuncD, size, x, y, output, DimsStride);
        return output;
    }

    inline void V_MxM_CC_Calc(
            CalcFuncZ aFuncZ, const int size, float* x,float* y,float* output,
            int DimsStride) {
        aFuncZ(size, (std::complex<float> *) x, DimsStride,
               (std::complex<float> *) y, 1, (std::complex<float> *) output, 1);
    }

    inline float* _MxM_CC_Calc(
            CalcFuncZ aFuncZ, const int size, float* x,float* y,
            int DimsStride) {
        float *output = malloc(size, true);
        V_MxM_CC_Calc(aFuncZ, size, x, y, output, DimsStride);
        return output;
    }

    inline Matrix operatorMxM(CalcFuncD aFuncD, CalcFuncZ aFuncZ, const Matrix &aMatrix,
                              const Matrix &aOther) {
        // 2v2,1v1,3v3
        if (aMatrix.compareShape(aOther)) {
            int DimsStride = 1;
            float *data = nullptr;
            if (aMatrix.getValueType() != aOther.getValueType()) {
                if (aMatrix.getValueType() == Normal) {
                    data = _MxM_NC_Calc(aFuncD, aMatrix.getDataSize(), aMatrix.getData(), aOther.getData(),
                                        DimsStride);
                    return Matrix::New(data, aOther);
                } else {
                    data = _MxM_CN_Calc(aFuncD, aMatrix.getDataSize(), aMatrix.getData(), aOther.getData(),
                                        DimsStride);
                    return Matrix::New(data, aMatrix);
                }

            } else if (aMatrix.getValueType() == Normal) {
                data = _MxM_NN_Calc(aFuncD, aMatrix.getDataSize(), aMatrix.getData(), aOther.getData(), DimsStride);
                return Matrix::New(data, aMatrix);
            } else {
                data = _MxM_CC_Calc(aFuncZ, aMatrix.getDataSize(), aMatrix.getData(), aOther.getData(), DimsStride);
                return Matrix::New(data, aMatrix);
            }
        }
        //0v3, 0v2
        else if (aMatrix.getDataSize()==1){
            if (aMatrix.getValueType() ==Normal)return operatorMxA(aFuncD,aMatrix.getData()[0],aOther);
            else{
                std::cerr<<"M * M fail, Complex scalar * not support now!"<<std::endl;
                return Matrix();
            }
        }
        //3v0, 2v0
        else if (aOther.getDataSize()==1){
            if (aOther.getValueType() ==Normal)return operatorMxA(aFuncD,aOther.getData()[0],aMatrix);
            else{
                std::cerr<<"M * M fail, Complex scalar * not support now!"<<std::endl;
                return Matrix();
            }
        }
        //other
        else {
            std::cerr<<"M * M Shape error!If you want do vector * Matrix, use repmat to replicate the vector"<<std::endl;
            return Matrix();
        }
    }

    inline Matrix operatorMxM_RR(CalcFuncD aFuncD, CalcFuncZ aFuncZ, const Aurora::Matrix &aMatrix,
                                 Aurora::Matrix &&aOther,bool oppside = false) {
        if (aMatrix.compareShape(aOther)) {
            int DimsStride = 1;
            float* X = oppside?aOther.getData():aMatrix.getData();
            float* Y = oppside?aMatrix.getData():aOther.getData();
            if (aMatrix.getValueType() != aOther.getValueType()) {
                //aOther is not a complex matrix
                if (aMatrix.getValueType() == Complex) {
                    float *output = oppside?_MxM_NC_Calc(aFuncD,aMatrix.getDataSize(),  aOther.getData(),aMatrix.getData(),DimsStride)
                        :_MxM_CN_Calc(aFuncD,aMatrix.getDataSize(), aMatrix.getData(), aOther.getData(),DimsStride);
                    return Matrix::New(output, aMatrix);
                }
                //aOther is a complex matrix, use aOther as output
                if (oppside){
                    V_MxM_CN_Calc(aFuncD, aMatrix.getDataSize(), aOther.getData(),aMatrix.getData(),aOther.getData(),DimsStride);
                }
                else
                {
                    V_MxM_NC_Calc(aFuncD, aMatrix.getDataSize(), aMatrix.getData(),aOther.getData(),aOther.getData(),DimsStride);
                }
                return aOther;
            } else if (aMatrix.getValueType() == Normal) {
                V_MxM_NN_Calc(aFuncD, aMatrix.getDataSize(), X, Y, aOther.getData(),
                              DimsStride);
                return aOther;
            } else {
                V_MxM_CC_Calc(aFuncZ, aMatrix.getDataSize(), X, Y, aOther.getData(),
                              DimsStride);
                return aOther;
            }
        }
        //0v3, 0v2
        else if (aMatrix.getDataSize()==1){
            if (aMatrix.getValueType() ==Normal){
                return operatorMxA(aFuncD,aMatrix.getData()[0],std::forward<Aurora::Matrix &&>(aOther));
            }
            else{
                std::cerr<<"M * M fail, Complex scalar * not support now!"<<std::endl;
                return Matrix();
            }
        }
            //3v0, 2v0
        else if (aOther.getDataSize()==1){
            if (aOther.getValueType() ==Normal)return operatorMxA(aFuncD,aOther.getData()[0],aMatrix);
            else{
                std::cerr<<"M * M fail, Complex scalar * not support now!"<<std::endl;
                return Matrix();
            }
        }
            //other
        else {
            std::cerr<<"M * M Shape error!If you want do vector * Matrix, use repmat to replicate the vector"<<std::endl;
            return Matrix();
        }
    }
};


namespace Aurora {
    Matrix::Matrix(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);
        }
    }

    Matrix::Matrix(const Matrix::MatrixSlice& slice) {
        auto temp = slice.toMatrix();
        this->mData = temp.mData;
        this->mInfo = temp.mInfo;
        this->mValueType = temp.mValueType;
    }

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

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

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

    float Matrix::getScalar() const {
        if (isNull()) return 0.0;
        if (isNull()) return 0.0;
        return getData()[0];
    }

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

    bool Matrix::isMKLAllocated() const
    {
        return mMKL_Allocated;
    }

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

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

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


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

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

    bool Matrix::compareShape(const Matrix &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;
    }

    Matrix Matrix::New(float *data, int rows, int cols, int slices, ValueType type) {
        if (!data) return Matrix();
        std::vector<int> vector(3);
        vector[0]=rows;
        vector[1] = (cols > 0?cols:1);
        vector[2] = (slices > 0?slices:1);
        Matrix ret({data, free}, vector);
        if (type != Normal)ret.setValueType(type);
        ret.mMKL_Allocated = true;
        return ret;
    }

#ifdef USE_CUDA
    CudaMatrix Matrix::toDeviceMatrix() const
    {
        float* deviceData = nullptr;
        cudaMalloc((void**)&deviceData, sizeof(float) * getDataSize() * getValueType() );
        cudaMemcpy(deviceData, mData.get(), sizeof(float) * getDataSize() * getValueType(), cudaMemcpyHostToDevice);
        return CudaMatrix::fromRawData(deviceData, mInfo[0], mInfo[1], mInfo[2], getValueType());
    }
#endif
    Matrix Matrix::New(float *data, const Matrix &shapeMatrix) {
        return New(data,
                   shapeMatrix.getDimSize(0),
                   shapeMatrix.getDimSize(1),
                   shapeMatrix.getDimSize(2),
                   shapeMatrix.getValueType());
    }

    Matrix Matrix::New(const Matrix &shapeMatrix) {
        float *newBuffer = malloc(shapeMatrix.getDataSize(), shapeMatrix.getValueType());
        return New(newBuffer, shapeMatrix);
    }

    Matrix Matrix::fromRawData(float *data, int rows, int cols, int slices, ValueType type) {
        if (!data) return Matrix();
        std::vector<int> vector;
        vector.push_back(rows);
        if (cols > 0)vector.push_back(cols);
        if (slices > 0)vector.push_back(slices);
        Matrix ret({data, std::default_delete<float[]>()}, vector);
        if (type != Normal)ret.setValueType(type);
        return ret;
    }

    Matrix Matrix::copyFromRawData(float *data, int rows, int cols, int slices, ValueType type) {
        int colsize = cols>0?cols:1;
        int slicesize = slices>0?slices:1;
        int size = rows*colsize*slicesize;
        float *newBuffer = Aurora::malloc(size, type);
        cblas_scopy(size*type,data,1,newBuffer,1);
        return New(newBuffer,rows,cols,slices,type);
    }

    Matrix Matrix::deepCopy() const {
        float *newBuffer = malloc(getDataSize(), getValueType()==Complex);
        cblas_scopy(getDataSize() * getValueType(),getData(),1,newBuffer,1);
        return New(newBuffer,
                   getDimSize(0),
                   getDimSize(1),
                   getDimSize(2),
                   getValueType());
    }
    //operation +
    Matrix Matrix::operator+(float aScalar) const { return operatorMxA(&vsAddI, aScalar, *this);}
    Matrix operator+(float aScalar, const Matrix &matrix) {return matrix + aScalar;}
    Matrix Matrix::operator+(const Matrix &matrix) const {
        if (isScalar()){
            return getScalar()+matrix;
        }
        if (matrix.isScalar()){
            return (*this)+matrix.getScalar();
        }
        return operatorMxM(vsAddI, vcAddI, *this, matrix);
    }
    Matrix &operator+(float aScalar, Matrix &&matrix) {
        return operatorMxA_RR(&vsAddI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix &operator+(Matrix &&matrix,float aScalar) {
        return operatorMxA_RR(&vsAddI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix Matrix::operator+(Matrix &&aMatrix) const {
        if (isScalar()){
            return getScalar()+aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)+aMatrix.getScalar();
        }
        return operatorMxM_RR(&vsAddI,&vcAddI,*this,std::forward<Matrix&&>(aMatrix));
    }

    Matrix operator+(Matrix &&aMatrix, Matrix &aOther) {
        if (aOther.isScalar()){
            return aOther.getScalar()+aMatrix;
        }
        if (aMatrix.isScalar()){
            return aOther+aMatrix.getScalar();
        }
        return operatorMxM_RR(&vsAddI,&vcAddI,aOther,std::forward<Matrix&&>(aMatrix),true);
    }
    //operation -
    Matrix Matrix::operator-(float aScalar) const { return operatorMxA(&vsSubI, aScalar, *this);}
    Matrix operator-(float aScalar, const Matrix &matrix) {return operatorAxM(&vsSubI, aScalar, matrix);}
    Matrix Matrix::operator-(const Matrix &aMatrix) const {
        if (isScalar()){
            return getScalar()-aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)-aMatrix.getScalar();
        }
        return operatorMxM(vsSubI, vcSubI, *this, aMatrix);
    }
    Matrix &operator-(float aScalar, Matrix &&matrix) {
        return operatorMxA_RR(&vsSubI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix &operator-(Matrix &&matrix,float aScalar) {
        return operatorMxA_RR(&vsSubI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix Matrix::operator-(Matrix &&aMatrix) const {
        if (isScalar()){
            return getScalar()-aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)-aMatrix.getScalar();
        }
        return operatorMxM_RR(&vsSubI,&vcSubI,*this,std::forward<Matrix&&>(aMatrix));
    }

    Matrix operator-(Matrix &&aMatrix, Matrix &aOther) {
        if (aOther.isScalar()){
            return aMatrix-aOther.getScalar();
        }
        if (aMatrix.isScalar()){
            return aMatrix.getScalar()-aOther;
        }
        return operatorMxM_RR(&vsSubI,&vcSubI,aOther,std::forward<Matrix&&>(aMatrix),true);
    }
    //operation *
    Matrix Matrix::operator*(float aScalar) const { return operatorMxA(&vsMulI, aScalar, *this);}
    Matrix operator*(float aScalar, const Matrix &matrix) {return matrix * aScalar;}
    Matrix Matrix::operator*(const Matrix &aMatrix) const {
        if (isScalar()){
            return getScalar()*aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)*aMatrix.getScalar();
        }
        return operatorMxM(vsMulI, vcMulI, *this, aMatrix);
    }
    Matrix &operator*(float aScalar, Matrix &&matrix) {
        return operatorMxA_RR(&vsMulI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix &operator*(Matrix &&aMatrix,float aScalar) {
        return operatorMxA_RR(&vsMulI,aScalar, std::forward<Matrix&&>(aMatrix));
    }
    Matrix Matrix::operator*(Matrix &&aMatrix) const {
        if (isScalar()){
            return getScalar()*aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)*aMatrix.getScalar();
        }
        return operatorMxM_RR(&vsMulI,&vcMulI,*this,std::forward<Matrix&&>(aMatrix));
    }

    Matrix operator*(Matrix &&aMatrix, Matrix &aOther) {
        if (aOther.isScalar()){
            return aMatrix*aOther.getScalar();
        }
        if (aMatrix.isScalar()){
            return aMatrix.getScalar()*aOther;
        }
        return operatorMxM_RR(&vsMulI,&vcMulI,aOther,std::forward<Matrix&&>(aMatrix),true);
    }
    //operation /
    Matrix Matrix::operator/(float aScalar) const { return operatorMxA(&vsDivI, aScalar, *this);}
    Matrix operator/(float aScalar, const Matrix &matrix) {return operatorAxM(&vsDivI, aScalar, matrix);}
    Matrix Matrix::operator/(const Matrix &aMatrix) const {
        if (isScalar()){
            return getScalar()/aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)/aMatrix.getScalar();
        }
        return operatorMxM(vsDivI, vcDivI, *this, aMatrix);
    }
    Matrix &operator/(float aScalar, Matrix &&matrix) {
        return operatorAxM_RR(&vsDivI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix &operator/(Matrix &&matrix,float aScalar) {
        return operatorMxA_RR(&vsDivI,aScalar, std::forward<Matrix&&>(matrix));
    }
    Matrix Matrix::operator/(Matrix &&aMatrix) const {
        if (isScalar()){
            return getScalar()/aMatrix;
        }
        if (aMatrix.isScalar()){
            return (*this)/aMatrix.getScalar();
        }
        return operatorMxM_RR(&vsDivI,&vcDivI,*this,std::forward<Matrix&&>(aMatrix));
    }

    Matrix operator/(Matrix &&aMatrix, Matrix &aOther) {
        if (aOther.isScalar()){
            return aMatrix/aOther.getScalar();
        }
        if (aMatrix.isScalar()){
            return aMatrix.getScalar()/aOther;
        }
        return operatorMxM_RR(&vsDivI,&vcDivI,aOther,std::forward<Matrix&&>(aMatrix),true);
    }
    //operator ^ (pow)
    Matrix Matrix::operator^(int times) const { return operatorMxA(&vsPowI, times, *this);}

    Matrix operator^( Matrix &&matrix,int times) {
        return operatorMxA(&vsPowI, times, std::forward<Matrix&&>(matrix));
    }

    float& Matrix::operator[](size_t index) { return getData()[index];}
    float Matrix::operator[](size_t index) const { return getData()[index];}

    Matrix Matrix::block(int aDim,int aBeginIndex, int aEndIndex) const{
        if(aDim>2 ){
            std::cerr<<"block only support 1D-3D data!"<<std::endl;
            return Matrix();
        }
        //横向vector，切面为0，为1，都强制设置aDim为1来处理
        if (isVector() && aDim == 0 && getDimSize(1)>1){
            aDim = 1;
        }
        if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0){
            std::cerr<<"block BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
            return Matrix();
        }
        if (aEndIndex>=getDimSize(aDim) || aEndIndex<0){
            std::cerr<<"block EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
            return Matrix();
        }
        int dimLength = std::abs(aEndIndex-aBeginIndex)+1;
        int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
        float * dataOutput = malloc(dataSize,isComplex());
        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++)
                    {
                        cblas_scopy(
                            dimLength,
                            getData() + (aBeginIndex + j * colStride +
                                i * sliceStride)*getValueType(),
                            getValueType(), dataOutput + (colStride2 * j + i * sliceStride2)*getValueType(),
                            getValueType());
                        if(isComplex()){
                            cblas_scopy(
                            dimLength,
                            getData()+1 + (aBeginIndex + j * colStride +
                                i * sliceStride)*getValueType(),
                            getValueType(), dataOutput+1 + (colStride2 * j + i * sliceStride2)*getValueType(),
                            getValueType());
                        }
                    }
                }
                return Matrix::New(dataOutput,dimLength,getDimSize(1),getDimSize(2),getValueType());
            }
            case 1:{
                int colStride2 = getDimSize(0);
                int sliceStride2 = dimLength*getDimSize(0);
                int copySize = sliceStride2;
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    cblas_scopy(copySize,
                                getData() + getValueType()*(aBeginIndex * colStride +
                                    i * sliceStride),
                                 getValueType(), dataOutput +  getValueType()*(i * copySize), 
                                 getValueType());
                    if (isComplex())
                    {
                        cblas_scopy(copySize,
                                getData()+1 + getValueType()*(aBeginIndex * colStride +
                                    i * sliceStride),
                                 getValueType(), dataOutput+1 +  getValueType()*(i * copySize), 
                                 getValueType());
                    }
                }
                return Matrix::New(dataOutput,getDimSize(0),dimLength,getDimSize(2),getValueType());
            }
            case 2:{
                int copySize = dimLength*sliceStride;
                cblas_scopy(copySize*getValueType(), getData() + aBeginIndex * sliceStride*getValueType() ,1, dataOutput, 1);
                return Matrix::New(dataOutput,getDimSize(0),getDimSize(1),dimLength,getValueType());
            }
        }

        
    }

    bool Matrix::setBlockValue(int aDim,int aBeginIndex, int aEndIndex, float value) {
        if(aDim>2 ){
            std::cerr<<"setBlockValue only support 1D-3D data!"<<std::endl;
            return false;
        }
        if(isComplex()){
            std::cerr<<"Comple matrix please use setBlockComplexValue!"<<std::endl;
            return false;
        }
        //横向vector，切面为0，为1，都强制设置aDim为1来处理
        if (isVector() && aDim == 0 && getDimSize(1)>1){
            aDim = 1;
        }
        if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0){
            std::cerr<<"setBlockValue BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
            return false;
        }
        if (aEndIndex>=getDimSize(aDim) || aEndIndex<0){
            std::cerr<<"setBlockValue EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
            return false;
        }
        int dimLength = std::abs(aEndIndex-aBeginIndex)+1;
        int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
        int colStride = getDimSize(0);
        int sliceStride = getDimSize(0)*getDimSize(1);
        switch (aDim) {
            case 0:{
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    for (size_t j = 0; j < getDimSize(1); j++)
                    {
                        cblas_scopy(
                            dimLength,
                            &value,0,
                            getData() + aBeginIndex + j * colStride +
                                i * sliceStride,
                            1);
                    }
                }
                return true;
            }
            case 1:{
                int colStride2 = getDimSize(0);
                int sliceStride2 = dimLength*getDimSize(0);
                int copySize = sliceStride2;
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    cblas_scopy(copySize,
                                &value, 0 ,
                                getData() + aBeginIndex * colStride +
                                    i * sliceStride, 1);
                }
                return true;
            }
            case 2:{
                int copySize = dimLength*sliceStride;
                cblas_scopy(copySize, &value, 0 ,getData() + aBeginIndex * sliceStride ,1);
                return true;
            }
        }

        
    }

    bool Matrix::setBlockComplexValue(int aDim,int aBeginIndex, int aEndIndex, std::complex<float> value) {
        if(aDim>2 ){
            std::cerr<<"setBlockValue only support 1D-3D data!"<<std::endl;
            return false;
        }
        if(!isComplex()){
            std::cerr<<"Normal matrix please use setBlockValue!"<<std::endl;
            return false;
        }
        //横向vector，切面为0，为1，都强制设置aDim为1来处理
        if (isVector() && aDim == 0 && getDimSize(1)>1){
            aDim = 1;
        }
        if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0){
            std::cerr<<"setBlockValue BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
            return false;
        }
        if (aEndIndex>=getDimSize(aDim) || aEndIndex<0){
            std::cerr<<"setBlockValue EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
            return false;
        }
        int dimLength = std::abs(aEndIndex-aBeginIndex)+1;
        int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
        int colStride = getDimSize(0);
        int sliceStride = getDimSize(0)*getDimSize(1);
        switch (aDim) {
            case 0:{
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    for (size_t j = 0; j < getDimSize(1); j++)
                    {
                        float real = value.real();
                        float imag = value.imag();
                        cblas_scopy(
                            dimLength,
                            &real,0,
                            getData() + aBeginIndex*2 + j * colStride*2 +
                                i * sliceStride*2,
                            2);
                        cblas_scopy(
                            dimLength,
                            &imag,0,
                            getData() +1 + aBeginIndex*2 + j * colStride*2 +
                                i * sliceStride*2,
                            2);
                    }
                }
                return true;
            }
            case 1:{
                int colStride2 = getDimSize(0);
                int sliceStride2 = dimLength*getDimSize(0);
                int copySize = sliceStride2;
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    float real = value.real();
                    float imag = value.imag();
                    cblas_scopy(copySize,
                                &real, 0 ,
                                getData() + aBeginIndex * colStride*2 +
                                    i * sliceStride*2,2);
                    cblas_scopy(copySize,
                                &imag, 0 ,
                                getData() +1 + aBeginIndex * colStride*2 +
                                    i * sliceStride*2, 2);
                }
                return true;
            }
            case 2:{
                int copySize = dimLength*sliceStride;
                float real = value.real();
                float imag = value.imag();
                cblas_scopy(copySize, &real, 0 ,getData() + aBeginIndex * sliceStride*2 ,2);
                cblas_scopy(copySize, &imag, 0 ,getData() +1 + aBeginIndex * sliceStride*2 ,2);
                return true;
            }
        }

        
    }

    bool Matrix::setBlock(int aDim,int aBeginIndex, int aEndIndex, const Matrix& src){
        if(aDim>2 ){
            std::cerr<<"setBlock only support 1D-3D data!"<<std::endl;
            return false;
        }
        //横向vector，切面为0，为1，都强制设置aDim为1来处理
        if (isVector() && aDim == 0 && getDimSize(1)>1){
            aDim = 1;
        }
        if (aBeginIndex>=getDimSize(aDim) || aBeginIndex<0){
            std::cerr<<"setBlock BeginIndx error!BeginIndx:"<<aBeginIndex<<std::endl;
            return false;
        }
        if (aEndIndex>=getDimSize(aDim) || aEndIndex<0){
            std::cerr<<"block EndIndex error!EndIndex:"<<aEndIndex<<std::endl;
            return false;
        }
        
        int dimLength = std::abs(aEndIndex-aBeginIndex)+1;
        size_t newdims[3]{0};
                int dataSize = getDataSize()/getDimSize(aDim)*dimLength;
        int colStride = getDimSize(0);
        int sliceStride = getDimSize(0)*getDimSize(1);
        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 (src.getDimSize(0)!= newdims[0] 
            || src.getDimSize(1)!= newdims[1] 
            || src.getDimSize(2)!= newdims[2])
        {
            std::cerr << "setBlock src Matrix(" << src.getDimSize(0) << ","
                        << src.getDimSize(1) << "," << src.getDimSize(2)
                        << ") not match the des shape(" << newdims[0] << ","
                        << newdims[1] << "," << newdims[2] << ")"
                        << std::endl;
            return false;
        }
        switch (aDim) {
            //copy row
            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++)
                    {
                        cblas_scopy(dimLength,
                                    src.getData() +
                                        j * colStride2 * getValueType() +
                                        i * sliceStride2 * getValueType(),
                                    getValueType(),
                                    getData() + aBeginIndex * getValueType() +
                                        j * colStride * getValueType() +
                                        i * sliceStride * getValueType(),
                                    getValueType());
                        if (isComplex()){
                            cblas_scopy(dimLength ,
                                    src.getData() + 1 +
                                        j * colStride2 * getValueType() +
                                        i * sliceStride2 * getValueType(),
                                    getValueType(),
                                    getData() + 1 + aBeginIndex * getValueType() +
                                        j * colStride * getValueType() +
                                        i * sliceStride * getValueType(),
                                    getValueType());
                        }
                    }
                }
                return true;
            }
            // copy column
            case 1:{
                int colStride2 = getDimSize(0);
                int sliceStride2 = dimLength*getDimSize(0);
                int copySize = sliceStride2;
                for (size_t i = 0; i < getDimSize(2); i++)
                {
                    cblas_scopy(copySize,
                                src.getData()+i * sliceStride2*getValueType(),getValueType(),
                                getData() + aBeginIndex * colStride*getValueType() +
                                    i * sliceStride*getValueType(), getValueType());
                    if (isComplex())
                    {
                        cblas_scopy(copySize,
                                src.getData()+1 +i * sliceStride2*getValueType(),getValueType(),
                                getData()+1 + aBeginIndex * colStride*getValueType() +
                                    i * sliceStride*getValueType(), getValueType());
                    }
                }
                return true;
            }
            //copy slice
            case 2:{
                int copySize = dimLength*sliceStride;
                cblas_scopy(copySize,src.getData(),getValueType(),getData() + aBeginIndex * sliceStride*getValueType() ,getValueType());
                if(isComplex()){
                    cblas_scopy(copySize,src.getData()+1,getValueType(),getData()+1 + aBeginIndex * sliceStride*getValueType() ,getValueType());
                }
                return true;
            }
        }
    }

    void Matrix::printf() {
        if(isNull())
        {
            ::printf("null matrix\n");
            return;
        }
        int k_count = getDimSize(2);
        int j_count = getDimSize(1);
        int complexstep = 1;
        const char* mark = "+";
        if(mValueType == Complex)
        {
            complexstep = 2;
        }
        for (int k = 0; k <k_count; ++k) {
        ::printf("slice %d:\r\n[",k);
        for (int i = 0; i < getDimSize(0); ++i) {
            ::printf("[");
            for (int j = 0; j < j_count; ++j) {
                ::printf("%f2",getData()[(k*getDimSize(1)*getDimSize(0)+j*getDimSize(0)+i)*complexstep]);
                if(mValueType == Complex)
                {
                    float value = getData()[(k*getDimSize(1)*getDimSize(0)+j*getDimSize(0)+i)*complexstep+1];
                    if(value<0)
                    {
                        mark = "";
                    }
                    ::printf("%s%f2i",mark,value);
                    mark = "+";
                }
                ::printf(", ");
            }
            ::printf("]\r\n");
        }
        ::printf("]\r\n");
        }
    }

    void Matrix::printfShape() {
        std::cerr << "Matrix shape:(" << getDimSize(0) << "," << getDimSize(1) << ","
                  << getDimSize(2) << ")" << std::endl;
    }

    Matrix::MatrixSlice Matrix::operator()(int aRowIdx, int aColIdx, int aSliceIdx) const {
        std::vector<int> dims = {aRowIdx, aColIdx, aSliceIdx};
        std::vector<int> allDimIndex;
        int mode = 0;
        for (int j = 0; j < 3; ++j) {
            if (dims[j]==$ && getDims()>j){
                ++mode;
                allDimIndex.push_back(j);
            }
        }
        if (isScalar()){
            mode = 0;
        }
        if (isVector()){
            if (getDimSize(0)>1 && aRowIdx!=$) mode = 0;
            if (getDimSize(1)>1 && aColIdx!=$) mode = 0;
        }
        int rowStride = 1;
        int colStride =  getDimSize(0);
        int sliceStride =  getDimSize(0)*getDimSize(1);
        int strides[3] = {rowStride, colStride, sliceStride};
        int rowOffset = aRowIdx == $ ? 0 : aRowIdx;
        int colOffset = aColIdx == $ ? 0 : aColIdx;
        int sliceOffset = aSliceIdx == $ ? 0 : aSliceIdx;
        float *startPointer = getData() + (rowStride * rowOffset
                                            + colStride * colOffset
                                            + sliceStride * sliceOffset) * getValueType();
        int size1 = allDimIndex.empty()?1:getDimSize(allDimIndex[0]);
        int stride1 = allDimIndex.empty()?1:strides[allDimIndex[0]];
        switch (mode) {
            //matrix mode
            case 2:{
                int size2 = getDimSize(allDimIndex[1]);
                int stride2 = strides[allDimIndex[1]];
                return Matrix::MatrixSlice(size1, stride1, startPointer, getValueType(), mode, size2, stride2);
            }
            //vector mode & default
            case 1:
            {
                return Matrix::MatrixSlice(size1, stride1, startPointer, getValueType(), mode);
            }
            //scalar mode or ALL $
            case 0:
            default: {
                return Matrix::MatrixSlice(1 , 1, startPointer,getValueType(), 0);
            }
        }
    }

    Matrix Matrix::operator>(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() > aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator>(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar>v.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator>(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]>matrix;
        }
        if(matrix.isScalar()){
            return (*this)>matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() > v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator>=(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() >= aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator>=(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar >= v.array() ).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator>=(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]<=matrix;
        }
        if(matrix.isScalar()){
            return (*this)>=matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() >= v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator<(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() < aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator<(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar < v.array() ).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator<(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]<matrix;
        }
        if(matrix.isScalar()){
            return (*this)<matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() < v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator<=(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() <= aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator<=(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar <= v.array() ).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator<=(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]<=matrix;
        }
        if(matrix.isScalar()){
            return (*this)<=matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() <= v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator==(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() == aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator==(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar == v.array() ).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator==(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]==matrix;
        }
        if(matrix.isScalar()){
            return (*this)==matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() == v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator!=(float aScalar) const {
        if (isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        float *ret = malloc(getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, getDataSize());
        result.setConstant(0.0);
        result = (v.array() != aScalar).select(1.0, result);
        return New(ret, getDimSize(0), getDimSize(1), getDimSize(2));
    }

    Matrix operator!=(float aScalar, const Matrix &matrix) {
        if (matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        Eigen::Map<Eigen::VectorXf> v(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (aScalar != v.array() ).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix Matrix::operator!=(const Matrix &matrix) const {
        if (isComplex() || matrix.isComplex()) {
            std::cerr << "Complex cann't compare!" << std::endl;
            return Matrix();
        }
        if (!compareShape(matrix) && !isScalar() && !matrix.isScalar()) {
            std::cerr << "Matrix not equal, matrix 1(" << matrix.getDimSize(0) << "," << matrix.getDimSize(1) << ","
                      << matrix.getDimSize(2) << "), matrix 2(" << getDimSize(0) << "," << getDimSize(1) << ","
                      << getDimSize(2) << ")" << std::endl;
            return Matrix();
        }
        if(isScalar()){
            return getData()[0]!=matrix;
        }
        if(matrix.isScalar()){
            return (*this)!=matrix.getData()[0];
        }
        Eigen::Map<Eigen::VectorXf> v(getData(), getDataSize());
        Eigen::Map<Eigen::VectorXf> v2(matrix.getData(), matrix.getDataSize());
        float *ret = malloc(matrix.getDataSize());
        Eigen::Map<Eigen::VectorXf> result(ret, matrix.getDataSize());
        result.setConstant(0.0);
        result = (v.array() != v2.array()).select(1.0, result);
        return Matrix::New(ret, matrix.getDimSize(0), matrix.getDimSize(1), matrix.getDimSize(2));
    }

    Matrix operator-(Matrix &&aMatrix) {
        int size = aMatrix.getDataSize()*aMatrix.getValueType();
        float zero = 0.0;
        vsSubI(size,&zero,0,aMatrix.getData(),1,aMatrix.getData(),1);
        return aMatrix;
    }

    Matrix operator-(const Matrix &aMatrix) {
        float *newBuffer = malloc(aMatrix.getDataSize(), aMatrix.getValueType()==Complex);
        float zero = 0.0;
        if (aMatrix.isComplex()){
            vsSubI( aMatrix.getDataSize(),&zero,0,aMatrix.getData(),2,newBuffer,2);
            vsSubI( aMatrix.getDataSize(),&zero,0,aMatrix.getData()+1,2,newBuffer+1,2);
        }
        else{
            vsSubI( aMatrix.getDataSize(),&zero,0,aMatrix.getData(),1,newBuffer,1);
        }
        
        return Matrix::New(newBuffer,aMatrix);
    }

    Matrix Matrix::mul(const Matrix& aOther) const {
        if (isNull() || aOther.isNull()){
            std::cerr<<"Mul operation fail, Matrix is null!"<<std::endl;
            return Matrix();
        }
        if (getDimSize(2)>1 || aOther.getDimSize(2)>1){
            std::cerr<<"In matrix mul operation not support 3d data!"<<std::endl;
            return Matrix();
        }
        // V x ?
        if (isVector())
        {
            // row vector
            if (getDimSize(0) > 1){
                std::cerr<<"In matrix mul operation Left vector must be a column vector!"<<std::endl;
                return Matrix();
            }
            //V x Scalar
            if (aOther.isScalar())
            {
                return (*this)*aOther.getScalar();
            }
            else{
                //right size
                if(getDimSize(1) == aOther.getDimSize(0))
                {
                    auto result = deepCopy();
                    cblas_sgemv(CblasColMajor,CblasTrans,aOther.getDimSize(0),aOther.getDimSize(1),1.0,
                    aOther.getData(),aOther.getDimSize(0),getData(),1,0,result.getData(),1);
                    return result;
                }
                std::cerr << "Matrix mul operation fail, can't do Matrix("
                          << getDimSize(0) << "," << getDimSize(1)
                          << ") * Matrix(" << aOther.getDimSize(0) << ","
                          << aOther.getDimSize(1) << ")" << std::endl;
            }
        }
        else if(isScalar()){
            return getScalar()*aOther;
        }
        // M x ?
        else if(aOther.isScalar()){
            return aOther.getScalar()*(*this);
        } else if (aOther.isVector()) {
            // right size
            if (getDimSize(1) == aOther.getDimSize(0)) {
                auto result = aOther.deepCopy();
                cblas_sgemv(CblasColMajor, CblasNoTrans, getDimSize(0),
                            getDimSize(1), 1.0, getData(),
                            getDimSize(0), aOther.getData(), 1, 0,
                            result.getData(), 1);
                return result;
            }
            std::cerr << "Matrix mul operation fail, can't do Matrix("
                      << getDimSize(0) << "," << getDimSize(1) << ") * Matrix("
                      << aOther.getDimSize(0) << "," << aOther.getDimSize(1)
                      << ")" << std::endl;
            return Matrix();
        }
        //MxM
        else {
            // fit size
            if (getDimSize(1) == aOther.getDimSize(0))
            {
                float * output = malloc(getDimSize(0)*aOther.getDimSize(1));
                int M =  getDimSize(0);
                int N =  aOther.getDimSize(1);
                int K =  getDimSize(1);
                cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, M, N, K, 1.0, getData(), M, aOther.getData(),K, 0, output, M);
                return Matrix::New(output,M,N);
            }
            std::cerr << "Matrix mul operation fail, can't do Matrix("
                      << getDimSize(0) << "," << getDimSize(1) << ") * Matrix("
                      << aOther.getDimSize(0) << "," << aOther.getDimSize(1)
                      << ")" << std::endl;
            return Matrix();
        }
    }

    Matrix::MatrixSlice::MatrixSlice(int aSize,int aStride, float* aData, ValueType aType, int aSliceMode,int aSize2, int aStride2):
    mSliceMode(aSliceMode),mData(aData),
    mSize(aSize),mSize2(aSize2),
    mStride(aStride),mStride2(aStride2),
    mType(aType)
    {

    }

    Matrix::MatrixSlice &Matrix::MatrixSlice::operator=(const Matrix::MatrixSlice &slice) {
        if (this==&slice) return *this;
        if(!mData){
            std::cerr <<"Assign value fail!Des data pointer is null!";
            return *this;
        }
        if(!slice.mData){
            std::cerr <<"Assign value fail!Src data pointer is null!"<<std::endl;
            return *this;
        }
        if (slice.mSliceMode!=mSliceMode) {
            std::cerr << "Assign value fail!Src slice(dims count:" << slice.mSliceMode
                      << "), not match of des(dims count:" << mSliceMode << ")!" << std::endl;
            return *this;
        }
        if (slice.mSize!=mSize) {
            std::cerr <<"Assign value fail!Src slice(dim 1 size:"<< slice.mSize <<"), not match of des(dim 1 size:"<<mSize<<")!"<<std::endl;
            return *this;
        }
        if (slice.mSize2!=mSize2) {
            std::cerr <<"Assign value fail!Src slice(dim 2 size:"<< slice.mSize2 <<"), not match of des(dim 2 size:"<<mSize2<<")!"<<std::endl;
            return *this;
        }
        if (slice.mType!=mType) {
            std::cerr <<"Assign value fail!Src slice(value type:"<< slice.mType <<"), not match of des(value type:"<<mType<<")!"<<std::endl;
            return *this;
        }
        switch (mSliceMode) {
            case 2:{
                if (mType== Normal) {
//                    cblas_scopy_batch_strided(mSize, slice.mData, slice.mStride, slice.mStride2, mData, mStride,
//                                              mStride2, mSize2);
                    for (int i = 0; i < mSize2; ++i) {
                        cblas_scopy(mSize,slice.mData + i*slice.mStride2,slice.mStride,mData+i*mStride2,mStride);
                    }
                }
                else {
//                    cblas_zcopy_batch_strided(mSize,(std::complex<float>*)slice.mData,slice.mStride,slice.mStride2,
//                            (std::complex<float>*)mData,mStride,mStride2,mSize2);
                    for (int i = 0; i < mSize2; ++i) {
                        cblas_ccopy(mSize, (std::complex<float> *) (slice.mData + i * slice.mStride2), slice.mStride,
                                    (std::complex<float> *) (mData + i * mStride2), mStride);
                    }
                }
                break;
            }
            case 1:{
                if (mType== Normal){
                    cblas_scopy(mSize,slice.mData,slice.mStride,mData,mStride);
                }
                else {
                    cblas_ccopy(mSize,  (std::complex<float> *)slice.mData, slice.mStride,  (std::complex<float> *)mData, mStride);
                }
                break;
            }
            case 0:
            default:{
                mData[0] = slice.mData[0];
                if (mType != Normal)mData[1] = slice.mData[1];
            }
        }
        return *this;
    }

    Matrix::MatrixSlice &Matrix::MatrixSlice::operator=(const Matrix &matrix) {
        if(!mData){
            std::cerr <<"Assign value fail!Des data pointer is null!";
            return *this;
        }
        if(!matrix.getData()){
            std::cerr <<"Assign value fail!Src data pointer is null!";
            return *this;
        }
        switch (mSliceMode) {
            case 2://matrix mode
            {
                if(matrix.getDims()!=2 || matrix.getDimSize(1)<=1)
                {
                    std::cerr <<"Assign value fail!Src matrix(dims count:"<< matrix.getDims() <<"), not match of des(dims count:"<<mSliceMode<<")!";
                    return *this;
                }
                if (matrix.getDimSize(1)!=mSize2) {
                    std::cerr <<"Assign value fail!Src slice(dim 2 size:"<< matrix.getDimSize(1) <<"), not match of des(dim 2 size:"<<mSize2<<")!";
                    return *this;
                }
                if (matrix.getDimSize(0)!=mSize) {
                    std::cerr <<"Assign value fail!Src matrix(dim 1 size:"<< matrix.getDimSize(0)<<"), not match of des(dim 1 size:"<<mSize<<")!";
                    return *this;
                }
                break;
            }
            case 1:{
                if(!matrix.isVector())
                {
                    std::cerr <<"Assign value fail!Src matrix(dims count:"<< matrix.getDims() <<"), not match of des(dims count:"<<mSliceMode<<")!";
                    return *this;
                }
                if (matrix.getDimSize(0)!=mSize && matrix.getDimSize(1)!=mSize) {
                    std::cerr <<"Assign value fail!Src matrix(dim 1 size:"<< matrix.getDimSize(0)<<"), not match of des(dim 1 size:"<<mSize<<")!";
                    return *this;
                }
                break;
            }
            case 0:{
                if(!matrix.isScalar()){
                    std::cerr <<"Assign value fail!Src matrix(dims count:"<< matrix.getDims() <<"), not match of des(dims count:"<<mSliceMode<<")!";
                    return *this;
                }
            }
        }

        if (matrix.getValueType()!=mType) {
            std::cerr <<"Assign value fail!Src slice(value type:"<< matrix.getValueType() <<"), not match of des(value type:"<<mType<<")!";
            return *this;
        }
        switch (mSliceMode) {
            //matrix mode
            case 2:{
                if (mType== Normal) {
                    cblas_scopy_batch_strided(mSize, matrix.getData(), 1, matrix.getDimSize(0), mData, mStride,
                                              mStride2, mSize2);
                }
                else {
                    cblas_ccopy_batch_strided(mSize,(std::complex<float>*)matrix.getData(),1,matrix.getDimSize(0),
                                              (std::complex<float>*)mData,mStride,mStride2,mSize2);
                }
                break;
            }
            //vector mode
            case 1:{
                if (mType== Normal){
                    cblas_scopy(mSize,matrix.getData(),1,mData,mStride);
                }
                else {
                    cblas_ccopy(mSize, (std::complex<float> *) matrix.getData(),1,
                                (std::complex<float> *) mData, mStride);
                }
                break;
            }
            //scalar mode
            case 0:
            default:{
                mData[0] = matrix.getData()[0];
                if (mType != Normal)mData[1] = matrix.getData()[1];
            }
        }
        return *this;
    }

    Matrix::MatrixSlice &Matrix::MatrixSlice::operator=(float value) {
        if(!mData){
            std::cerr <<"Assign value fail!Des data pointer is null!";
            return *this;
        }
        if (mSliceMode!=0) {
            std::cerr <<"Assign value fail!Des slicemode is"<<mSliceMode<<", not scalar mode!";
            return *this;
        }
        if (mSize!=1) {
            std::cerr <<"Assign value fail!Des  size:"<<mSize<<", not scalar mode!";
            return *this;
        }
        if (mType!=Normal) {
            std::cerr <<"Assign value fail!Des type is complex!";
            return *this;
        }
        mData[0]=value;
        return *this;
    }

    Matrix::MatrixSlice &Matrix::MatrixSlice::operator=(std::complex<float> value) {
        if(!mData){
            std::cerr <<"Assign value fail!Des data pointer is null!";
            return *this;
        }
        if (mSliceMode!=0) {
            std::cerr <<"Assign value fail!Des slicemode is"<<mSliceMode<<", not scalar mode!";
            return *this;
        }
        if (mSize!=1) {
            std::cerr <<"Assign value fail!Des  size:"<<mSize<<", not scalar mode!";
            return *this;
        }
        if (mType!=Complex) {
            std::cerr <<"Assign value fail!Des type is not complex!";
            return *this;
        }
        mData[0]=value.real();
        mData[1]=value.imag();
        return *this;
    }

    Matrix Matrix::MatrixSlice::toMatrix() const {
        float * data = (float *) malloc(mSize*(mSize2>0?mSize2:1) ,mType == Complex);

        switch (mSliceMode) {
            case 2:{
                if (mType== Normal) {
                    cblas_scopy_batch_strided(mSize,  mData, mStride,
                                              mStride2,data, 1, mSize, mSize2);
                }
                else {
                    cblas_ccopy_batch_strided(mSize, (std::complex<float> *) mData, mStride, mStride2,
                                              (std::complex<float> *) data, 1, mSize,
                                              mSize2);
                }
                break;
            }
            case 1:{
                if (mType== Normal){
                    cblas_scopy(mSize,mData,mStride,data,1);
                }
                else {
                    cblas_ccopy(mSize, (std::complex<float> *) mData, mStride,
                                (std::complex<float> *) data, 1);
                }
                break;
            }
            case 0:
            default:{
                data[0]= mData[0];
                if (mType != Normal) data[1] = mData[1];
            }
        }

        return Matrix::New(data,mSize,mSize2,1,mType);
    }
}




