# BLAS/LAPACK Raw Prototypes

`blaslapack.h` declares the raw Fortran-linkage BLAS and LAPACK routines [Dense Linear Algebra](densematrix.md)'s `VectorView`/`DenseMatrix` implementation calls directly. It has no corresponding `.cpp` file - it is a pure prototype header linked against an external BLAS/LAPACK library, not implemented in this codebase.

## Why raw prototypes instead of a wrapper API

No system C header exists for calling reference BLAS/LAPACK routines directly - `cblas.h`/`lapacke.h` are different wrapper APIs with different function names and calling conventions. `blaslapack.h` instead declares the underlying Fortran routines itself, the same approach Eigen uses in its own `blas.h`/`lapack.h`. This means the header encodes the gfortran ABI directly:

- Symbol names are the routine's lowercase Fortran name plus a trailing underscore (`dgetrf_`, not `DGETRF` or `dgetrf`).
- Every argument is passed by pointer, including scalars (`const int* n`, not `int n`) - Fortran passes everything by reference.
- A Fortran `DOUBLE COMPLEX` function result (`zdotc_`, the only complex-valued function declared here) is returned by value, in the `XMM0:XMM1` register pair under the x86-64 SysV ABI - confirmed empirically against this system's reference BLAS. This is gfortran's modern calling convention; it differs from the hidden-pointer-argument convention f2c/g77-era compilers (g77, clapack) use for the same case. Consequence: the linked BLAS/LAPACK must be built with gfortran (or another compiler sharing its modern `COMPLEX`-return convention) - an f2c-derived build would silently produce a wrong (zero/garbage) result instead of a link or runtime error, since the call would still type-check.

Because reference BLAS/LAPACK and OpenBLAS share the same sonames, symbol names, and signatures, swapping the linked implementation to OpenBLAS needs no changes to this header - only a `CMakeLists.txt` change, plus an explicit `omp_set_num_threads()` call at startup to cap OpenBLAS's own thread pool. See the BLAS/LAPACK section of the top-level `CMakeLists.txt` for how the library is currently located (`find_package(BLAS)`/`find_package(LAPACK)` on Linux and macOS - Homebrew-provided on the latter, the same way SuiteSparse already is; a bundled OpenBLAS on Windows).

## Declared routines

BLAS (vector operations, real (`d`-prefixed) and complex (`z`-prefixed) variants of each):

- `dcopy_`/`zcopy_` - copy a strided vector.
- `dnrm2_`/`dznrm2_` - Euclidean norm, computed with internal scaling to avoid overflow/underflow on intermediate products.
- `ddot_`/`zdotc_` - dot product; the complex form conjugates its first vector argument (`x`), matching the "conjugate the vector supplied as `other`" convention `VectorView::dot()` documents.
- `daxpy_`/`zaxpy_` - `y := alpha*x + y`.
- `dswap_`/`zswap_` - elementwise interchange of two strided vectors.
- `dscal_`/`zscal_` - `x := alpha*x`.
- `zdscal_` - `x := alpha*x` for a complex vector scaled by a *real* `alpha`; cheaper than `zscal_` since it avoids promoting `alpha` to `Complex` and paying for a complex-complex multiply. Backs `VectorView<Complex>::scale(double)`.
- `idamax_` - 1-based index of the largest-magnitude element (real vectors only; no complex counterpart is declared, see [Dense Linear Algebra](densematrix.md) for why `VectorView<Complex>::maxAbs()` does not use `izamax_`).

LAPACK (dense, column-major - see "Column-major convention" below):

- `dlaset_`/`zlaset_` - fill an M x N matrix's off-diagonal entries with `alpha` and diagonal entries with `beta` (`uplo` selects which triangle; `'A'` touches everything). Used both by `VectorView::operator=(T)` to broadcast a scalar into a strided vector (`m=1`, so the "matrix" filled is a single row) and by `DenseMatrixView::identity()`/`zero()`/scalar `operator=` to fill a whole 2-D block in one call.
- `dlacpy_`/`zlacpy_` - copy all (`uplo='A'`) or part of an M x N matrix `A` into `B`, honoring each matrix's own leading dimension. The 2-D counterpart of `dcopy_`: unlike `dcopy_`'s single stride per vector, this lets `DenseMatrixView::operator=(DenseMatrixView)` copy between two sub-blocks of different parent matrices directly, without a per-row loop, whenever both sides are contiguous in the same direction (both column-major, or both row-major read as transposed column-major).
- `dlange_`/`zlange_` - matrix norm of an M x N matrix; `norm='M'` gives `max(abs(A(i,j)))`. Confirmed empirically that `zlange_`'s `'M'` norm uses the true complex modulus, not `|Re|+|Im|`, so - unlike `VectorView::maxAbs()`'s deliberate avoidance of `izamax_` for `Complex` - it is safe to use for both `double` and `Complex`. `work` is unreferenced for `norm='M'` (only needed for `norm='I'`); `DenseMatrixView::maxAbs()` passes `nullptr`. Backs `DenseMatrixView::maxAbs()`'s fast path.
- `dgetrf_`/`zgetrf_` - LU factorization with partial pivoting of an M x N matrix, in place, with a 1-based pivot vector (`ipiv`). `info==0` is success; `info>0` (`=k`) means the factorization completed but `U(k,k)` is exactly zero (singular); `info<0` (`=-k`) means the k-th argument was invalid - a bug in the caller, not a numerical failure.
- `dgetrs_`/`zgetrs_` - solve `A*X=B` using an existing `dgetrf_`/`zgetrf_` factorization and pivot vector. Unlike `dgetrf_`, there is no `info>0` case - singularity was already reported when the factorization itself was computed.

## Column-major convention

Every LAPACK routine here expects column-major storage (elements within one column of `A` contiguous, `lda` the stride *between* columns) and has no per-element increment parameter the way BLAS does - only a leading dimension. This is why [`DenseMatrix`](densematrix.md)'s LAPACK fast path requires `rowStride_==1` (contiguous columns) before it will call into any of these routines, falling back to its own manual elimination otherwise.
