/**
 * @brief Functions for dense matrix and vector operations.
 *
 * @author Junyi Wang (jywang)
 *
 * @copyright Copyright (c) 2024, TBPLaS develop team.
 */

#ifndef TBPLAS_TBPM_DENSE_H
#define TBPLAS_TBPM_DENSE_H
#include <algorithm>
#include <cassert>
#include <cmath> // std::pow
#include <iostream>
#include <vector>

#include <eigen3/Eigen/Dense>

#include "aocl_wrapper.h"
#include "base/datatypes.h"

using DENSE_INT = aoclsparse_int;
using namespace tbplas::math::aocl;
namespace tbplas::tbpm {
using tbplas::base::complex_t;

// 优化：可使用std::vector代替指针，可以用变量定义大小，在栈上,可能做了一些优化
// 指针：malloc动态在堆上，手动free
// int size = 5;
// std::vector<double>v(size);
// double* x = v.data();

/**
 * @brief Calculate the inner product between double/complex vectors
 *
 * @param[in] x left operand
 * @param[in] x right operand
 * @return the inner product
 *
 * @note The conjugate of left operand is taken automatically.
 */
// complex_t 向量X的共轭，Y点积 dot=conj(X).trans()*Y
template <typename Derived>
auto vec_dot(const Eigen::MatrixBase<Derived>& x, const Eigen::MatrixBase<Derived>& y)
{
    using Scalar = typename Derived::Scalar;
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    DENSE_INT inc = 1;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar prod = 0.0;
    prod = dotc<Scalar>::call(
        dim,
        x_data,
        inc,
        y_data,
        inc);
    return prod;
}

/**
 * @brief Calculate the inner product between complex vectors without conjugation
 * of left operand.
 *
 * @param[in] x left operand
 * @param[in] x right operand
 * @param[in] check_dim whether to enable the dimension check assertion in debug mode
 * @return the inner product
 *
 * @note Currently, this function is used by fast TBPM algorithms, where we need
 * to calculate the product of two vectors with different dimensions. So we add
 * the check_dim parameter to disable the dimension check assertion in debug mode.
 * The default value of check_dim should be true, in order to be consistent with vec_dot.
 */
inline complex_t vec_dot_no_conj(const Eigen::VectorXcd& x, const Eigen::VectorXcd& y, bool check_dim = true)
{
    DENSE_INT dim = x.size();
    if (check_dim) {
        assert(dim == y.size());
    }
    std::complex<double> prod = 0;
    const std::complex<double>* x_data = x.derived().data();
    std::complex<double>* y_data = (std::complex<double>*)y.derived().data();
    // template< typename T >T dotu(int64_t n,T const *x, int64_t incx,T const *y, int64_t incy )
    prod = blis::dotu(
        dim,
        x_data,
        1,
        y_data,
        1);
    return prod;
}

/**
 * @brief Calculate the norm of vector
 *
 * @tparam vx_t datatype of vector
 * @param[in] x the vector
 * @return norm of vector
 */
// 向量X的二范数，返回double
// double sqrt(X.trans()*X)
// complex_t sqrt(conj(X).trans()*X).real())
// vx_t Eigen::VectorXcd
template <typename vx_t>
double vec_norm(const Eigen::MatrixBase<vx_t>& x)
{
    using Scalar = typename vx_t::Scalar;
    DENSE_INT dim = x.size();
    double prod = 0.0;
    DENSE_INT inc = 1;
    const Scalar* x_data = x.derived().data();
    prod = blis::nrm2(
        dim,
        x_data,
        inc);
    return prod;
}

/**
 * @brief Calculate the norm^2 of vector
 *
 * @tparam vx_t datatype of vector
 * @param[in] x the vector
 * @return norm of vector
 */
template <typename vx_t>
double vec_norm2(const Eigen::MatrixBase<vx_t>& x)
{
    double tmp = vec_norm(x);
    // return tmp * tmp;
    return std::pow(tmp, 2);
}

/**
 * @brief Initialize vector X = a.
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @param[inout] x vector x
 * @param[in] a scalar a
 */
// 初始化，所有元素值为a，aocl无可用
template <typename vx_t, typename sa_t>
void vec_init(Eigen::MatrixBase<vx_t>& x, const sa_t& a)
{
    size_t dim = x.size();

    typedef typename vx_t::Scalar Scalar;
    Scalar* ptr = x.derived().data();
    // #pragma omp parallel for simd
    //         for (size_t i = 0; i < dim; ++i) {
    //             ptr[i] = a;
    //         }
    std::fill(ptr, ptr + dim, a);
}

/**
 * @brief Deep copy X to Y.
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[in] x vector x
 * @param[out] y vector y
 */
// 深拷贝  cblas_?copy
template <typename vx_t, typename vy_t>
void vec_copy(const Eigen::MatrixBase<vx_t>& x, Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    const Scalar* x_data = x.derived().data();
    Scalar* y_data = (Scalar*)y.derived().data();
    blis::copy(
        dim,
        x_data,
        1,
        y_data,
        1);
}

/**
 * @brief Truncate a decreasing vector according to threshold.
 *
 * @tparam vx_t datatype of vector x
 * @param[inout] x vector to truncate
 * @param[in] eps threshold below which elements will be dropped
 */
// eigen类型为vx_t，AOCL没有对应函数
template <typename vx_t>
void vec_trunc(vx_t& x, const double& eps)
{
    size_t dim = x.size();
    assert(dim > 0);
    size_t i_max = 0;
    bool truncate = false;
    typedef typename vx_t::Scalar Scalar;
    Scalar* ptr = x.data();
    for (size_t i = 0; i < dim - 1; ++i) {
        if ((std::abs(ptr[i]) <= eps) && (std::abs(ptr[i + 1]) <= eps)) {
            i_max = i;
            truncate = true;
            break;
        }
    }
    if (truncate) {
        x.conservativeResize(i_max + 1); // +1 to avoid yielding empty vector
    }
}

/**
 * @brief Allocate matrix X as std::vector<Eigen::VectorX*>.
 *
 * @tparam vx_t datatype of matrix X
 * @param[out] x matrix X
 * @param[in] num_row number of rows
 * @param[in] num_col number of columns
 */
template <typename vx_t>
void mat_alloc(std::vector<vx_t>& x, size_t num_row, size_t num_col)
{
    x = std::vector<vx_t>(num_col);
    for (vx_t& v : x) {
        v.resize(num_row);
    }
}

/**
 * @brief Initialize matrix X = a.
 *
 * @tparam vx_t datatype of matrix X
 * @tparam sa_t datatype of scalar a
 * @param[out] x matrix X
 * @param[in] a scalar a
 */
template <typename vx_t, typename sa_t>
void mat_init(std::vector<vx_t>& x, const sa_t& a)
{
    for (vx_t& v : x) {
        vec_init(v, a);
    }
}

/**
 * @brief Deep copy vector X to each column of matrix Y.
 *
 * @tparam vx_t datatype of x
 * @tparam vy_t datatype of y
 * @param[in] x vector x
 * @param[out] y matrix y
 */
template <typename vx_t, typename vy_t>
void mat_col_copy(const Eigen::MatrixBase<vx_t>& x, std::vector<vy_t>& y)
{
    size_t dim = x.size();
    assert(dim == y[0].size());
    for (vy_t& v : y) {
        vec_copy(x, v);
    }
}

/**
 * @brief Calculate Y = a * X + b * Y
 *
 * @tparam sa_t datatype of scalar a
 * @tparam vx_t datatype of vector x
 * @tparam sb_t datatype of scalar b
 * @tparam vy_t datatype of vector y
 * @param[in] a scalar a
 * @param[in] x vector x
 * @param[in] b scalar b
 * @param[inout] y vector y
 */
// cblas_?axpby
template <typename sa_t, typename vx_t, typename sb_t, typename vy_t>
void axpby(const sa_t& a, const Eigen::MatrixBase<vx_t>& x,
    const sb_t& b, Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    Scalar alpha, beta;
    alpha = a;
    beta = b;
    const Scalar* x_data = x.derived().data();
    Scalar* y_data = (Scalar*)y.derived().data();
    DENSE_INT inc = 1;
    blis::cblas_axpby(
        dim,
        alpha,
        x_data,
        inc,
        beta,
        y_data,
        inc);
}

/**
 * @brief Calculate Y = a * X + Y
 *
 * @tparam sa_t datatype of scalar a
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[in] a scalar a
 * @param[in] x vector x
 * @param[inout] y vector y containing the result
 */
// cblas_?axpy
template <typename sa_t, typename vx_t, typename vy_t>
void axpy(const sa_t& a, const Eigen::MatrixBase<vx_t>& x,
    Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    Scalar alpha = a;
    const Scalar* x_data = x.derived().data();
    Scalar* y_data = (Scalar*)y.derived().data();
    DENSE_INT inc = 1;
    blis::axpy(
        dim,
        alpha,
        x_data,
        inc,
        y_data,
        inc);
}

/**
 * @brief Calculate Z = a * X + b * Y + Z
 *
 * @tparam sa_t datatype of scalar a
 * @tparam vx_t datatype of vector x
 * @tparam sb_t datatype of scalar b
 * @tparam vy_y datatype of vector y
 * @tparam vz_t datatype of vector z
 * @param[in] a scalar a
 * @param[in] x vector x
 * @param[in] b scalar b
 * @param[in] y vector y
 * @param[inout] z vector z
 */
template <typename sa_t, typename vx_t, typename sb_t, typename vy_y, typename vz_t>
void axpbypz(const sa_t& a, const Eigen::MatrixBase<vx_t>& x,
    const sb_t& b, const Eigen::MatrixBase<vy_y>& y, Eigen::MatrixBase<vz_t>& z)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vy_y::Scalar;
    Scalar alpha, beta;
    alpha = a;
    beta = b;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar* z_data = (Scalar*)z.derived().data();
    DENSE_INT inc = 1;
    // z=a*x+z
    blis::axpy(
        dim,
        alpha,
        x_data,
        inc,
        z_data,
        inc);
    // z=by+z
    blis::axpy(
        dim,
        beta,
        y_data,
        inc,
        z_data,
        inc);
}

/**
 * @brief Calculate OUT = a * X + b * Y + c * Z
 *
 * @tparam sa_t datatype of scalar a
 * @tparam vx_t datatype of vector x
 * @tparam sb_t datatype of scalar b
 * @tparam vy_y datatype of vector y
 * @tparam sc_t datatype of scalar c
 * @tparam vz_t datatype of vector z
 * @tparam vout_t datatype of vector out
 * @param[in] a scalar a
 * @param[in] x vector x
 * @param[in] b scalar b
 * @param[in] y vector y
 * @param[in] c scalar c
 * @param[in] z vector z
 * @param[out] out vector out
 */
template <typename sa_t, typename vx_t,
    typename sb_t, typename vy_y,
    typename sc_t, typename vz_t,
    typename vout_t>
void axpbypcz_out(const sa_t& a, const Eigen::MatrixBase<vx_t>& x, const sb_t& b, const Eigen::MatrixBase<vy_y>& y,
    const sc_t& c, const Eigen::MatrixBase<vz_t>& z, Eigen::MatrixBase<vout_t>& out)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vy_y::Scalar;
    Scalar alpha, beta, gamma;
    alpha = a;
    beta = b;
    gamma = c;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    const Scalar* z_data = z.derived().data();
    Scalar* out_data = (Scalar*)out.derived().data();
    DENSE_INT inc = 1;
    // out=z
    blis::copy(
        dim,
        z_data,
        inc,
        out_data,
        inc);
    // out=a*x+c*out
    blis::cblas_axpby(
        dim,
        alpha,
        x_data,
        inc,
        gamma,
        out_data,
        inc);
    // out=b*y+out
    blis::axpy(
        dim,
        beta,
        y_data,
        inc,
        out_data,
        inc);
}

/**
 * @brief Calculate prod = x.conj() * m * y for diagonal matrix m
 *
 * @tparam vx_t datatype of vector x
 * @tparam vm_t datatype of diagonal matrix m
 * @tparam vy_t datatype of vector y
 * @param[in] x vector x
 * @param[in] m diagonal matrix m
 * @param[in] y vector y
 * @return the product
 */
// m可能是对角阵保留对角线元素的向量
template <typename vx_t, typename vm_t, typename vy_t>
complex_t xmy_diag(const Eigen::MatrixBase<vx_t>& x,
    const Eigen::MatrixBase<vm_t>& m,
    const Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == m.size());
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    const Scalar* m_data = m.derived().data();
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    double real_part = 0.0;
    double imag_part = 0.0;
    complex_t tmp = 0.0;
#pragma omp parallel for reduction(+ : real_part, imag_part) private(tmp) schedule(static)
    for (DENSE_INT i = 0; i < dim; ++i) {
        tmp = std::conj(x[i]) * m[i] * y[i];
        real_part += tmp.real();
        imag_part += tmp.imag();
    }
    return { real_part, imag_part };
}

/* -------- Generated with gen_cpp.py -------- */

// AOCL无mkl中对应vml部分函数，无对向量元素进行运算（加减乘除）的函数
/**
 * @brief Calculate Z = X + Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @tparam vz_t datatype of vector z
 * @param[in] x left operand
 * @param[in] y right operand
 * @param[out] z result
 */
// z[i]=x[i]+y[i]
template <typename vx_t, typename vy_t, typename vz_t>
void vec_add_vec(const Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y,
    Eigen::MatrixBase<vz_t>& z)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vz_t::Scalar;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar* z_data = (Scalar*)z.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        z[i] = x[i] + y[i];
    }
}

/**
 * @brief Calculate Z = X - Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @tparam vz_t datatype of vector z
 * @param[in] x left operand
 * @param[in] y right operand
 * @param[out] z result
 */
// z[i] = x[i] - y[i]; v?Sub
template <typename vx_t, typename vy_t, typename vz_t>
void vec_sub_vec(const Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y,
    Eigen::MatrixBase<vz_t>& z)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vz_t::Scalar;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar* z_data = (Scalar*)z.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        z[i] = x[i] - y[i];
    }
}

/**
 * @brief Calculate Z = X * Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @tparam vz_t datatype of vector z
 * @param[in] x left operand
 * @param[in] y right operand
 * @param[out] z result
 */
// 对应元素的乘积 z[i]=x[i]*y[i]
// v?Mul
template <typename vx_t, typename vy_t, typename vz_t>
void vec_mul_vec(const Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y,
    Eigen::MatrixBase<vz_t>& z)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vz_t::Scalar;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar* z_data = (Scalar*)z.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        z_data[i] = x_data[i] * y_data[i];
    }
}

/**
 * @brief Calculate Z = X / Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @tparam vz_t datatype of vector z
 * @param[in] x left operand
 * @param[in] y right operand
 * @param[out] z result
 */
// r[i] = a[i] / b[i]   v?Div
template <typename vx_t, typename vy_t, typename vz_t>
void vec_div_vec(const Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y,
    Eigen::MatrixBase<vz_t>& z)
{

    DENSE_INT dim = x.size();
    assert(dim == y.size());
    assert(dim == z.size());
    using Scalar = typename vz_t::Scalar;
    const Scalar* x_data = x.derived().data();
    const Scalar* y_data = y.derived().data();
    Scalar* z_data = (Scalar*)z.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        z_data[i] = x_data[i] / y_data[i];
    }
}

/**
 * @brief Calculate Y = X + a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @tparam vy_t datatype of vector y
 * @param[in] x left operand
 * @param[in] a right operand
 * @param[out] y result
 */
// y[i] = x[i] + a[i]
template <typename vx_t, typename sa_t, typename vy_t>
void vec_add_sca(const Eigen::MatrixBase<vx_t>& x, const sa_t& a,
    Eigen::MatrixBase<vy_t>& y)
{
    // y={a,a,a...}
    // Y=X+Y
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    const Scalar* x_data = x.derived().data();
    Scalar* y_data = (Scalar*)y.derived().data();
    Scalar alpha = a;
    std::fill(y_data, y_data + dim, alpha);
    Scalar unit = 1.0;
    blis::axpy(
        dim,
        unit,
        x_data,
        1,
        y_data,
        1);
}

/**
 * @brief Calculate Y = X - a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @tparam vy_t datatype of vector y
 * @param[in] x left operand
 * @param[in] a right operand
 * @param[out] y result
 */
// y[i] = x[i] - a
// y[i] = x[i] - a[i] v?Sub
template <typename vx_t, typename sa_t, typename vy_t>
void vec_sub_sca(const Eigen::MatrixBase<vx_t>& x, const sa_t& a,
    Eigen::MatrixBase<vy_t>& y)
{
    vec_add_sca(x, -a, y);
}

/**
 * @brief Calculate Y = X * a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @tparam vy_t datatype of vector y
 * @param[in] x left operand
 * @param[in] a right operand
 * @param[out] y result
 */
// y[i] = x[i] * a;

template <typename vx_t, typename sa_t, typename vy_t>
void vec_mul_sca(const Eigen::MatrixBase<vx_t>& x, const sa_t& a,
    Eigen::MatrixBase<vy_t>& y)
{
    double b = 0.0;
    axpby(a, x, b, y);

    // y.setZero();
    // axpy(a, x, y);
}

/**
 * @brief Calculate Y = X / a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @tparam vy_t datatype of vector y
 * @param[in] x left operand
 * @param[in] a right operand
 * @param[out] y result
 */
// y[i] = x[i] / a;
template <typename vx_t, typename sa_t, typename vy_t>
void vec_div_sca(const Eigen::MatrixBase<vx_t>& x, const sa_t& a,
    Eigen::MatrixBase<vy_t>& y)
{
    double b = 0.0;
    axpby(1.0 / a, x, b, y);

    // y.setZero();
    // axpy(1.0/a, x, y);
}

/*
 * @brief Calculate X += Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[inout] x left operand
 * @param[in] y right operand
 */
// x[i] += y[i];  X=1*Y+X  cblas_?axpy
template <typename vx_t, typename vy_t>
void vec_self_add_vec(Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y)
{
    axpy(1.0, y, x);
}

/**
 * @brief Calculate X -= Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[inout] x left operand
 * @param[in] y right operand
 */
template <typename vx_t, typename vy_t>
void vec_self_sub_vec(Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y)
{
    axpy(-1.0, y, x);
}

/**
 * @brief Calculate X *= Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[inout] x left operand
 * @param[in] y right operand
 */
// x[i] *= y[i];
template <typename vx_t, typename vy_t>
void vec_self_mul_vec(Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    Scalar* x_data = (Scalar*)x.derived().data();
    const Scalar* y_data = y.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        x[i] *= y[i];
    }
}

/**
 * @brief Calculate X /= Y
 *
 * @tparam vx_t datatype of vector x
 * @tparam vy_t datatype of vector y
 * @param[inout] x left operand
 * @param[in] y right operand
 */
template <typename vx_t, typename vy_t>
void vec_self_div_vec(Eigen::MatrixBase<vx_t>& x, const Eigen::MatrixBase<vy_t>& y)
{
    DENSE_INT dim = x.size();
    assert(dim == y.size());
    using Scalar = typename vy_t::Scalar;
    Scalar* x_data = (Scalar*)x.derived().data();
    const Scalar* y_data = y.derived().data();
#pragma omp parallel for simd
    for (DENSE_INT i = 0; i < dim; ++i) {
        x[i] /= y[i];
    }
}

/**
 * @brief Calculate X += a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @param[inout] x left operand
 * @param[in] a right operand
 */
// x[i]+=a    X=a*ones+X
template <typename vx_t, typename sa_t>
void vec_self_add_sca(Eigen::MatrixBase<vx_t>& x, const sa_t& a)
{
    // y={a,a,a...}
    // Y=X+Y
    DENSE_INT dim = x.size();
    using Scalar = typename vx_t::Scalar;
    Scalar* temp = (Scalar*)malloc(dim * sizeof(Scalar));
    if (temp == NULL) {
        std::cerr << "Malloc failed!\n";
        std::exit(-1);
    }
    Scalar alpha = a;
    std::fill(temp, temp + dim, alpha);
    Scalar* x_data = x.derived().data();
    Scalar unit = 1.0;
    DENSE_INT inc = 1;
    blis::axpy(
        dim,
        unit,
        temp,
        inc,
        x_data,
        inc);
    free(temp);
}

/**
 * @brief Calculate X -= a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @param[inout] x left operand
 * @param[in] a right operand
 */
template <typename vx_t, typename sa_t>
void vec_self_sub_sca(Eigen::MatrixBase<vx_t>& x, const sa_t& a)
{
    vec_self_add_sca(x, -a);
}

/**
 * @brief Calculate X *= a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @param[inout] x left operand
 * @param[in] a right operand
 */
// x[i] *= a;  cblas_?scal
template <typename vx_t, typename sa_t>
void vec_self_mul_sca(Eigen::MatrixBase<vx_t>& x, const sa_t& a)
{
    DENSE_INT dim = x.size();
    using Scalar = typename vx_t::Scalar;
    Scalar alpha = a;
    Scalar* x_data = x.derived().data();
    blis::scal(
        dim,
        alpha,
        x_data,
        1);
}

/**
 * @brief Calculate X /= a
 *
 * @tparam vx_t datatype of vector x
 * @tparam sa_t datatype of scalar a
 * @param[inout] x left operand
 * @param[in] a right operand
 */
template <typename vx_t, typename sa_t>
void vec_self_div_sca(Eigen::MatrixBase<vx_t>& x, const sa_t& a)
{
    vec_self_mul_sca(x, 1.0 / a);
}

} // namespace tbplas::tbpm
#endif
