/*
 * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: LicenseRef-NvidiaProprietary
 *
 * NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
 * property and proprietary rights in and to this material, related
 * documentation and any modifications thereto. Any use, reproduction,
 * disclosure or distribution of this material and related documentation
 * without an express license agreement from NVIDIA CORPORATION or
 * its affiliates is strictly prohibited.
 */

#pragma once

#if defined (__powerpc__) || defined (__powerpc64__) || defined (__aarch64__)
#define __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
#endif

#include <complex.h>

namespace __blas_std {

// NOTE: I'm only exposing these extern declarations in a header file
// so that we can keep this a header-only library, for ease of testing
// and installation.  Exposing them in a real production
// implementation is really bad form.  This is because users may
// declare their own extern declarations of BLAS functions, and yours
// will collide with theirs at build time.

// NOTE: I'm assuming a particular BLAS ABI mangling here.  Typical
// BLAS C++ wrappers need to account for a variety of manglings that
// don't necessarily match the system's Fortran compiler (if it has
// one).  Lowercase with trailing underscore is a common pattern.
// Watch out for BLAS functions that return something, esp. a complex
// number.

extern "C" void
scopy_ (const int* __n, const float*  __x, const int* __inc_x, float*  __y, const int* __inc_y);

extern "C" void
dcopy_ (const int* __n, const double* __x, const int* __inc_x, double* __y, const int* __inc_y);

extern "C" void
ccopy_ (const int* __n, const void*   __x, const int* __inc_x, void*   __y, const int* __inc_y);

extern "C" void
zcopy_ (const int* __n, const void*   __x, const int* __inc_x, void*   __y, const int* __inc_y);

extern "C" void
saxpy_ (const int* __n, const float*  __a, const float*  __x, const int* __inc_x, float*  __y, const int* __inc_y);

extern "C" void
daxpy_ (const int* __n, const double* __a, const double* __x, const int* __inc_x, double* __y, const int* __inc_y);

extern "C" void
caxpy_ (const int* __n, const void*   __a, const void*   __x, const int* __inc_x, void*   __y, const int* __inc_y);

extern "C" void
zaxpy_ (const int* __n, const void*   __a, const void*   __x, const int* __inc_x, void*   __y, const int* __inc_y);

extern "C" float
sdot_ (const int* __n, const float*  __x, const int* __inc_x, const float*  __y, const int* __inc_y);

extern "C" double
ddot_ (const int* __n, const double* __x, const int* __inc_x, const double* __y, const int* __inc_y);

#ifdef __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
extern "C" std::complex<float>
cdotu_(const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" std::complex<double>
zdotu_( const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" std::complex<float>
cdotc_(const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" std::complex<double>
zdotc_( const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);
#else
extern "C" void
cdotu_(void* __result, const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" void
zdotu_(void* __result, const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" void
cdotc_(void* __result, const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);

extern "C" void
zdotc_(void* __result, const int* __n, const void*   __x, const int* __inc_x, const void*   __y, const int* __inc_y);
#endif

extern "C" void
sscal_ (const int* __n, const float*  __a, float*  __x, const int* __inc_x);

extern "C" void
dscal_ (const int* __n, const double* __a, double* __x, const int* __inc_x);

extern "C" void
cscal_ (const int* __n, const void*   __a, void*   __x, const int* __inc_x);

extern "C" void
zscal_ (const int* __n, const void*   __a, void*   __x, const int* __inc_x);

extern "C" float
snrm2_ (const int* __n, const float*  __x, const int* __inc_x);

extern "C" double
dnrm2_ (const int* __n, const double* __x, const int* __inc_x);

extern "C" float
scnrm2_(const int* __n, const void*   __x, const int* __inc_x);

extern "C" double
dznrm2_(const int* __n, const void*   __x, const int* __inc_x);

extern "C" void
sgemv_ (const char __trans[], const int* __m, const int* __n,
        const float* __alpha, const float* __a, const int* __lda,
        const float* __x, const int* __inc_x, const float* __beta,
        float *__y, const int* __inc_y);

extern "C" void
dgemv_ (const char __trans[], const int* __m, const int* __n,
        const double* __alpha, const double* __a, const int* __lda,
        const double* __x, const int* __inc_x, const double* __beta,
        double *__y, const int* __inc_y);

extern "C" void
cgemv_ (const char __trans[], const int* __m, const int* __n,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __x, const int* __inc_x, const void* __beta,
        void *__y, const int* __inc_y);

extern "C" void
zgemv_ (const char __trans[], const int* __m, const int* __n,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __x, const int* __inc_x, const void* __beta,
        void *__y, const int* __inc_y);


extern "C" void
strsv_ (const char __uplo[], const char __trans[], const char __diag[],
        const int* __n, const float* __a, const int* __lda,
        float* __x, const int* __inc_x);

extern "C" void
dtrsv_ (const char __uplo[], const char __trans[], const char __diag[],
        const int* __n, const double* __a, const int* __lda,
        double* __x, const int* __inc_x);

extern "C" void
ctrsv_ (const char __uplo[], const char __trans[], const char __diag[],
        const int* __n, const void* __a, const int* __lda,
        void* __x, const int* __inc_x);

extern "C" void
ztrsv_ (const char __uplo[], const char __trans[], const char __diag[],
        const int* __n, const void* __a, const int* __lda,
        void* __x, const int* __inc_x);

extern "C" void
dgemm_ (const char __trans_A[], const char __trans_B[],
        const int* __m, const int* __n, const int* __k,
        const double* __alpha,
        const double* __A, const int* __lda,
        const double* __B, const int* __ldb,
        const double* __beta,
        double* __C, const int* __ldc);

extern "C" void
sgemm_ (const char __trans_A[], const char __trans_B[],
        const int* __m, const int* __n, const int* __k,
        const float* __alpha,
        const float* __A, const int* __lda,
        const float* __B, const int* __ldb,
        const float* __beta,
        float* __C, const int* __ldc);

extern "C" void
cgemm_ (const char __trans_A[], const char __trans_B[],
        const int* __m, const int* __n, const int* __k,
        const void* __alpha,
        const void* __A, const int* __lda,
        const void* __B, const int* __ldb,
        const void* __beta,
        void* __C, const int* __ldc);

extern "C" void
zgemm_ (const char __trans_A[], const char __trans_B[],
        const int* __m, const int* __n, const int* __k,
        const void* __alpha,
        const void* __A, const int* __lda,
        const void* __B, const int* __ldb,
        const void* __beta,
        void* __C, const int* __ldc);

extern "C" void
ssyrk_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
        const float* __alpha, const float* __a, const int* __lda,
        const float* __beta, float* __c, const int* __ldc);

extern "C" void
dsyrk_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
        const double* __alpha, const double* __a, const int* __lda,
        const double* __beta, double* __c, const int* __ldc);

extern "C" void
csyrk_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __beta, void* __c, const int* __ldc);

extern "C" void
zsyrk_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __beta, void* __c, const int* __ldc);

extern "C" void
ssyr2k_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
         const float* __alpha, const float* __a, const int* __lda,
         const float* __b, const int* __ldb,
         const float* __beta, float* __c, const int* __ldc);

extern "C" void
dsyr2k_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
         const double* __alpha, const double* __a, const int* __lda,
         const double* __b, const int* __ldb,
         const double* __beta, double* __c, const int* __ldc);

extern "C" void
csyr2k_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
         const void* __alpha, const void* __a, const int* __lda,
         const void* __b, const int* __ldb,
         const void* __beta, void* __c, const int* __ldc);

extern "C" void
zsyr2k_ (const char __uplo[], const char __trans[], const int* __n, const int* __k,
         const void* __alpha, const void* __a, const int* __lda,
         const void* __b, const int* __ldb,
         const void* __beta, void* __c, const int* __ldc);

extern "C" void
strsm_ (const char __side[], const char __uplo[], const char __trans_a[], const char __diag[],
        const int* __m, const int* __n,
        const float* __alpha, const float* __a, const int* __lda,
        const float* __b, const int* __ldb);

extern "C" void
dtrsm_ (const char __side[], const char __uplo[], const char __trans_a[], const char __diag[],
        const int* __m, const int* __n,
        const double* __alpha, const double* __a, const int* __lda,
        const double* __b, const int* __ldb);

extern "C" void
ctrsm_ (const char __side[], const char __uplo[], const char __trans_a[], const char __diag[],
        const int* __m, const int* __n,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __b, const int* __ldb);

extern "C" void
ztrsm_ (const char __side[], const char __uplo[], const char __trans_a[], const char __diag[],
        const int* __m, const int* __n,
        const void* __alpha, const void* __a, const int* __lda,
        const void* __b, const int* __ldb);

inline void __blas_copy (const int __n, const float* __X, int __inc_x, float* __y, int __inc_y)
{
  scopy_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_copy (const int __n, const double* __X, int __inc_x, double* __y, int __inc_y)
{
  dcopy_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_copy (const int __n, const std::complex<float>* __X, int __inc_x, std::complex<float>* __y, int __inc_y)
{
  ccopy_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_copy (const int __n, const std::complex<double>* __X, int __inc_x, std::complex<double>* __y, int __inc_y)
{
  zcopy_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_axpy (const int __n, const float __A, const float* __X, int __inc_x, float* __y, int __inc_y)
{
  saxpy_ (&__n, &__A, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_axpy (const int __n, const double __A, const double* __X, int __inc_x, double* __y, int __inc_y)
{
  daxpy_ (&__n, &__A, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_axpy (const int __n, const std::complex<float> __A, const std::complex<float>* __X, int __inc_x, std::complex<float>* __y, int __inc_y)
{
  caxpy_ (&__n, &__A, __X, &__inc_x, __y, &__inc_y);
}

inline void __blas_axpy (const int __n, const std::complex<double> __A, const std::complex<double>* __X, int __inc_x, std::complex<double>* __y, int __inc_y)
{
  zaxpy_ (&__n, &__A, __X, &__inc_x, __y, &__inc_y);
}

inline float __blas_dot (const int __n, const float* __X, int __inc_x, const float* __y, int __inc_y)
{
  return sdot_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline double __blas_dot (int __n, const double* __X, int __inc_x, const double* __y, int __inc_y)
{
  return ddot_ (&__n, __X, &__inc_x, __y, &__inc_y);
}

inline std::complex<float> __blas_dot (int __n, const std::complex<float>* __X, int __inc_x, const std::complex<float>* __y, int __inc_y)
{
#ifdef __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
  return cdotu_(&__n, __X, &__inc_x, __y, &__inc_y);
#else
  std::complex<float> __res;
  cdotu_ (&__res, &__n, __X, &__inc_x, __y, &__inc_y);
  return __res;
#endif
}

inline std::complex<double> __blas_dot (const int __n, const std::complex<double>* __X, int __inc_x, const std::complex<double>* __y, int __inc_y)
{
#ifdef __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
  return zdotu_(&__n, __X, &__inc_x, __y, &__inc_y);
#else
  std::complex<double> __res;
  zdotu_ (&__res, &__n, __X, &__inc_x, __y, &__inc_y);
  return __res;
#endif
}

inline std::complex<float> __blas_dotc (int __n, const std::complex<float>* __X, int __inc_x, const std::complex<float>* __y, int __inc_y)
{
#ifdef __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
  return cdotc_(&__n, __X, &__inc_x, __y, &__inc_y);
#else
  std::complex<float> __res;
  cdotc_ (&__res, &__n, __X, &__inc_x, __y, &__inc_y);
  return __res;
#endif
}

inline std::complex<double> __blas_dotc (const int __n, const std::complex<double>* __X, int __inc_x, const std::complex<double>* __y, int __inc_y)
{
#ifdef __USE_BLAS_DOT_RETURN_VALUE_INTERFACE
  return zdotc_(&__n, __X, &__inc_x, __y, &__inc_y);
#else
  std::complex<double> __res;
  zdotc_ (&__res, &__n, __X, &__inc_x, __y, &__inc_y);
  return __res;
#endif
}

inline void __blas_scal (int __n, float __A, float* __X, int __inc_x)
{
  sscal_ (&__n, &__A, __X, &__inc_x);
}

inline void __blas_scal (int __n, double __A, double* __X, int __inc_x)
{
  dscal_ (&__n, &__A, __X, &__inc_x);
}

inline void __blas_scal (int __n, std::complex<float> __A, std::complex<float>* __X, int __inc_x)
{
  cscal_ (&__n, &__A, __X, &__inc_x);
}

inline void __blas_scal (int __n, std::complex<double> __A, std::complex<double>* __X, int __inc_x)
{
  zscal_ (&__n, &__A, __X, &__inc_x);
}

inline float __blas_nrm2 (int __n, const float* __X, int __inc_x)
{
  return snrm2_ (&__n, __X, &__inc_x);
}

inline double __blas_nrm2 (int __n, const double* __X, int __inc_x)
{
  return dnrm2_ (&__n, __X, &__inc_x);
}

inline float __blas_nrm2 (int __n, const std::complex<float>* __X, int __inc_x)
{
  return scnrm2_ (&__n, __X, &__inc_x);
}

inline double __blas_nrm2 (int __n, const std::complex<double>* __X, int __inc_x)
{
  return dznrm2_ (&__n, __X, &__inc_x);
}

inline void __blas_gemv (const char __trans_A[], const int __m, const int __n, const float __alpha,
                         const float* __A, const int __lda, const float* __X, const int __inc_x,
                         const float __beta, float* __y, const int __inc_y)
{
  sgemv_ (__trans_A, &__m, &__n, &__alpha, __A, &__lda, __X, &__inc_x, &__beta, __y, &__inc_y);
}

inline void __blas_gemv (const char __trans_A[], const int __m, const int __n, const double __alpha,
                         const double* __A, const int __lda, const double* __X, const int __inc_x,
                         const double __beta, double* __y, const int __inc_y)
{
  dgemv_ (__trans_A, &__m, &__n, &__alpha, __A, &__lda, __X, &__inc_x, &__beta, __y, &__inc_y);
}

inline void __blas_gemv (const char __trans_A[], const int __m, const int __n, const std::complex<float> __alpha,
                         const std::complex<float>* __A, const int __lda, const std::complex<float>* __X, const int __inc_x,
                         const std::complex<float> __beta, std::complex<float>* __y, const int __inc_y)
{
  cgemv_ (__trans_A, &__m, &__n, &__alpha, __A, &__lda, __X, &__inc_x, &__beta, __y, &__inc_y);
}

inline void __blas_gemv (const char __trans_A[], const int __m, const int __n, const std::complex<double> __alpha,
                         const std::complex<double>* __A, const int __lda, const std::complex<double>* __X, const int __inc_x,
                         const std::complex<double> __beta, std::complex<double>* __y, const int __inc_y)
{
  zgemv_ (__trans_A, &__m, &__n, &__alpha, __A, &__lda, __X, &__inc_x, &__beta, __y, &__inc_y);
}


inline void __blas_trsv (const char __uplo[], const char __trans[], const char __diag[],
                         const int __n, const float* __A, const int __lda, float* __X, const int __inc_x)
{
  strsv_ (__uplo, __trans, __diag, &__n, __A, &__lda, __X, &__inc_x);
}

inline void __blas_trsv (const char __uplo[], const char __trans[], const char __diag[],
                         const int __n, const double* __A, const int __lda, double* __X, const int __inc_x)
{
  dtrsv_ (__uplo, __trans, __diag, &__n, __A, &__lda, __X, &__inc_x);
}

inline void __blas_trsv (const char __uplo[], const char __trans[], const char __diag[], const int __n,
                         const std::complex<float>* __A, const int __lda, std::complex<float>* __X, const int __inc_x)
{
  ctrsv_ (__uplo, __trans, __diag, &__n, __A, &__lda, __X, &__inc_x);
}

inline void __blas_trsv (const char __uplo[], const char __trans[], const char __diag[], const int __n,
                         const std::complex<double>* __A, const int __lda, std::complex<double>* __X, const int __inc_x)
{
  ztrsv_ (__uplo, __trans, __diag, &__n, __A, &__lda, __X, &__inc_x);
}

inline void __blas_gemm (const char __trans_A[], const char __trans_B[],
                         const int __m, const int __n, const int __k,
                         const double __alpha,
                         const double* __A, const int __lda,
                         const double* __B, const int __ldb,
                         const double __beta,
                         double* __C, const int __ldc)
{
  dgemm_ (__trans_A, __trans_B, &__m, &__n, &__k,
          &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_gemm (const char __trans_A[], const char __trans_B[],
                         const int __m, const int __n, const int __k,
                         const float __alpha,
                         const float* __A, const int __lda,
                         const float* __B, const int __ldb,
                         const float __beta,
                         float* __C, const int __ldc)
{
  sgemm_ (__trans_A, __trans_B, &__m, &__n, &__k,
          &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_gemm (const char __trans_A[], const char __trans_B[],
                         const int __m, const int __n, const int __k,
                         const std::complex<double> __alpha,
                         const std::complex<double>* __A, const int __lda,
                         const std::complex<double>* __B, const int __ldb,
                         const std::complex<double> __beta,
                         std::complex<double>* __C, const int __ldc)
{
  zgemm_ (__trans_A, __trans_B, &__m, &__n, &__k,
          &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_gemm (const char __trans_A[], const char __trans_B[],
                         const int __m, const int __n, const int __k,
                         const std::complex<float> __alpha,
                         const std::complex<float>* __A, const int __lda,
                         const std::complex<float>* __B, const int __ldb,
                         const std::complex<float> __beta,
                         std::complex<float>* __C, const int __ldc)
{
  cgemm_ (__trans_A, __trans_B, &__m, &__n, &__k,
          &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_syrk (const char __uplo[], const char __trans[], const int __m, const int __k,
                         const float __alpha, const float* __A, const int __lda,
                         const float __beta, float* __C, const int __ldc)
{
  ssyrk_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, &__beta, __C, &__ldc);
}

inline void __blas_syrk (const char __uplo[], const char __trans[], const int __m, const int __k,
                         const double __alpha, const double* __A, const int __lda,
                         const double __beta, double* __C, const int __ldc)
{
  dsyrk_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, &__beta, __C, &__ldc);
}

inline void __blas_syrk (const char __uplo[], const char __trans[], const int __m, const int __k,
                         const std::complex<float> __alpha, const std::complex<float>* __A, const int __lda,
                         const std::complex<float> __beta, std::complex<float>* __C, const int __ldc)
{
  csyrk_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, &__beta, __C, &__ldc);
}

inline void __blas_syrk (const char __uplo[], const char __trans[], const int __m, const int __k,
                         const std::complex<double> __alpha, const std::complex<double>* __A, const int __lda,
                         const std::complex<double> __beta, std::complex<double>* __C, const int __ldc)
{
  zsyrk_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, &__beta, __C, &__ldc);
}

inline void __blas_syr2k (const char __uplo[], const char __trans[], const int __m, const int __k,
                          const float __alpha, const float* __A, const int __lda,
                          const float* __B, const int __ldb,
                          const float __beta, float* __C, const int __ldc)
{
  ssyr2k_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_syr2k (const char __uplo[], const char __trans[], const int __m, const int __k,
                          const double __alpha, const double* __A, const int __lda,
                          const double* __B, const int __ldb,
                          const double __beta, double* __C, const int __ldc)
{
  dsyr2k_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_syr2k (const char __uplo[], const char __trans[], const int __m, const int __k,
                          const std::complex<float> __alpha, const std::complex<float>* __A, const int __lda,
                          const std::complex<float>* __B, const int __ldb,
                          const std::complex<float> __beta, std::complex<float>* __C, const int __ldc)
{
  csyr2k_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_syr2k (const char __uplo[], const char __trans[], const int __m, const int __k,
                          const std::complex<double> __alpha, const std::complex<double>* __A, const int __lda,
                          const std::complex<double>* __B, const int __ldb,
                          const std::complex<double> __beta, std::complex<double>* __C, const int __ldc)
{
  zsyr2k_ (__uplo, __trans, &__m, &__k, &__alpha, __A, &__lda, __B, &__ldb, &__beta, __C, &__ldc);
}

inline void __blas_trsm (const char __side[], const char __uplo[], const char __trans_A[], const char __diag[],
                         const int __m, const int __n,
                         const float __alpha, const float* __A, const int __lda,
                         const float* __B, const int __ldb)
{
  strsm_ (__side, __uplo, __trans_A, __diag, &__m, &__n, &__alpha, __A, &__lda, __B, &__ldb);
}

inline void __blas_trsm (const char __side[], const char __uplo[], const char __trans_A[], const char __diag[],
                         const int __m, const int __n,
                         const double __alpha, const double* __A, const int __lda,
                         const double* __B, const int __ldb)
{
  dtrsm_ (__side, __uplo, __trans_A, __diag, &__m, &__n, &__alpha, __A, &__lda, __B, &__ldb);
}

inline void __blas_trsm (const char __side[], const char __uplo[], const char __trans_A[], const char __diag[],
                         const int __m, const int __n,
                         const std::complex<float> __alpha, const std::complex<float>* __A, const int __lda,
                         const std::complex<float>* __B, const int __ldb)
{
  ctrsm_ (__side, __uplo, __trans_A, __diag, &__m, &__n, &__alpha, __A, &__lda, __B, &__ldb);
}

inline void __blas_trsm (const char __side[], const char __uplo[], const char __trans_A[], const char __diag[],
                         const int __m, const int __n,
                         const std::complex<double> __alpha, const std::complex<double>* __A, const int __lda,
                         const std::complex<double>* __B, const int __ldb)
{
  ztrsm_ (__side, __uplo, __trans_A, __diag, &__m, &__n, &__alpha, __A, &__lda, __B, &__ldb);
}

// __blas_conj: in-place and in/out overloads

template<class _Scalar>
void __blas_conj( const int __n, _Scalar * __x, const int __inc = 1 ) {}

template<class _Scalar>
void __blas_conj( const int __n, std::complex<_Scalar> * __x, const int __inc = 1 )
{
  _Scalar const alpha{-1};
  __blas_scal( __n, alpha, reinterpret_cast< _Scalar * >(__x) + 1, 2 * __inc );
}

template<class _Scalar>
void __blas_conj( const int __n, _Scalar const * __in, _Scalar * __out, const int __inc = 1 )
{
  __blas_copy( __n, __in, __out, __inc );
}

template<class _Scalar>
void __blas_conj( const int __n, std::complex<_Scalar> const * __in, std::complex<_Scalar> * __out, const int __inc = 1 )
{
  _Scalar const alpha{-1};
  __blas_copy( __n, __in, __inc, __out, __inc );
  __blas_scal(  __n, alpha, reinterpret_cast< _Scalar * >(__out) + 1, 2 * __inc );
}

template<class _Scalar>
void __blas_conj_2d (const int __m, const int __n, _Scalar* __X, const int __ldx)
{
  for ( int __i_col = 0; __i_col < __n; ++__i_col )
  {
    __blas_conj( __m, __X + __i_col * __ldx );
  }
}

template<class _Scalar>
void __blas_conj_2d (const int __m, const int __n, _Scalar const* __in, _Scalar* __out, const int __ldx)
{
  for ( int __i_col = 0; __i_col < __n; ++__i_col )
  {
    __blas_conj( __m, __in + __i_col * __ldx, __out + __i_col * __ldx );
  }
}

template<class _Scalar>
void __blas_copy_2d (const char __trans, const int __m, const int __n, const _Scalar* __X, int __ldx, _Scalar* __y, int __ldy)
{
  switch ( __trans )
  {
    case 'N':
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_copy( __m, __X + __i_col * __ldx, 1, __y + __i_col * __ldy, 1 );
      }
      break;
    case 'T':
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_copy( __m, __X + __i_col * __ldx, 1, __y + __i_col, __ldy );
      }
      break;
    case 'C':
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_copy( __m, __X + __i_col * __ldx, 1, __y + __i_col, __ldy );
        __blas_conj( __m, __y + __i_col, __ldy );
      }
      break;
    default:
      break;
  }
}

template<class _Scalar>
void __blas_transpose (const char __op, const int __m, const int __n, const _Scalar* __in, int __ldin, _Scalar* __out, int __ldout)
{
  __blas_copy_2d( __op, __m, __n, __in, __ldin, __out, __ldout );
}

template<class _Scalar1, class _Scalar2>
void __blas_scal_2d (int __m, int __n, _Scalar1 __alpha, _Scalar2* __X, int __ldx)
{
  for ( std::size_t __i_col = 0; __i_col < static_cast<std::size_t>(__n); ++__i_col )
  {
    __blas_scal( __m, static_cast<_Scalar2>(__alpha), __X + __i_col * __ldx, 1 );
  }
}

template<class _Scalar1, class _Scalar2>
void __blas_axpy_2d (char __trans, int __m, int __n, _Scalar1 __A, const _Scalar2* __X, int __ldx, _Scalar2* __y, int __ldy)
{
  switch ( __trans )
  {
    case 'N':
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_axpy( __m, __A, __X + __i_col * __ldx, 1, __y + __i_col * __ldy, 1 );
      }
      break;
    case 'T':
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_axpy( __m, __A, __X + __i_col * __ldx, 1, __y + __i_col, __ldy );
      }
      break;
    case 'C':
      if constexpr ( std::linalg::is_complex<_Scalar2>::value ) { __A = std::conj( __A ); }
      for ( int __i_col = 0; __i_col < __n; ++__i_col )
      {
        __blas_conj( __m, __y + __i_col, __ldy );
        __blas_axpy( __m, __A, __X + __i_col * __ldx, 1, __y + __i_col, __ldy );
        __blas_conj( __m, __y + __i_col, __ldy );
      }
      break;
    default:
      break;
  }
}

}
