stdlib_linalg.fypp Source File


Source Code

#:include "common.fypp"
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
#:set RCI_KINDS_TYPES = RC_KINDS_TYPES + INT_KINDS_TYPES
#:set RHS_SUFFIX = ["one","many"]
#:set RHS_SYMBOL = [ranksuffix(r) for r in [1,2]]
#:set RHS_EMPTY  = [emptyranksuffix(r) for r in [1,2]]
#:set ALL_RHS    = list(zip(RHS_SYMBOL,RHS_SUFFIX,RHS_EMPTY))
#:set EIG_PROBLEM  = ["standard", "generalized"]
#:set EIG_FUNCTION = ["geev","ggev"]
#:set EIG_PROBLEM_LIST = list(zip(EIG_PROBLEM, EIG_FUNCTION))
module stdlib_linalg
  !!Provides a support for various linear algebra procedures
  !! ([Specification](../page/specs/stdlib_linalg.html))
  use stdlib_kinds, only: xdp, int8, int16, int32, int64
  use stdlib_linalg_constants, only: sp, dp, qp, lk, ilp
  use stdlib_error, only: error_stop
  use stdlib_optval, only: optval
  use stdlib_linalg_state, only: linalg_state_type, linalg_error_handling
  implicit none
  private

  public :: chol
  public :: cholesky
  public :: det
  public :: operator(.det.)
  public :: diag
  public :: eig
  public :: eigh
  public :: eigvals
  public :: eigvalsh
  public :: eye
  public :: inv
  public :: invert
  public :: operator(.inv.)
  public :: pinv
  public :: pseudoinvert
  public :: operator(.pinv.)
  public :: lstsq
  public :: lstsq_space
  public :: norm
  public :: mnorm
  public :: get_norm
  public :: solve
  public :: solve_lu  
  public :: solve_lstsq
  public :: trace
  public :: svd
  public :: svdvals
  public :: outer_product
  public :: kronecker_product
  public :: cross_product
  public :: qr
  public :: qr_space
  public :: schur
  public :: schur_space
  public :: is_square
  public :: is_diagonal
  public :: is_symmetric
  public :: is_skew_symmetric
  public :: hermitian
  public :: is_hermitian
  public :: is_triangular
  public :: is_hessenberg
  
  ! Export linalg error handling
  public :: linalg_state_type, linalg_error_handling

  interface chol
    !! version: experimental 
    !!
    !! Computes the Cholesky factorization \( A = L \cdot L^T \), or \( A = U^T \cdot U \). 
    !! ([Specification](../page/specs/stdlib_linalg.html#chol-compute-the-cholesky-factorization-of-a-rank-2-square-array-matrix))
    !! 
    !!### Summary 
    !! Pure function interface for computing the Cholesky triangular factors. 
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the lower- or upper- triangular matrix from the 
    !! Cholesky factorization of a `real` symmetric or `complex` Hermitian matrix.
    !! Supported data types include `real` and `complex`.    
    !! 
    !!@note The solution is based on LAPACK's `*POTRF` methods.
    !!     
     #:for rk,rt,ri in RC_KINDS_TYPES
     pure module function stdlib_linalg_${ri}$_cholesky_fun(a,lower,other_zeroed) result(c)
         !> Input matrix a[m,n]
         ${rt}$, intent(in) :: a(:,:)
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         !> Output matrix with Cholesky factors c[n,n]
         ${rt}$ :: c(size(a,1),size(a,2))                  
     end function stdlib_linalg_${ri}$_cholesky_fun
     #:endfor    
  end interface chol  

  interface cholesky
    !! version: experimental 
    !!
    !! Computes the Cholesky factorization \( A = L \cdot L^T \), or \( A = U^T \cdot U \). 
    !! ([Specification](../page/specs/stdlib_linalg.html#cholesky-compute-the-cholesky-factorization-of-a-rank-2-square-array-matrix))
    !! 
    !!### Summary 
    !! Pure subroutine interface for computing the Cholesky triangular factors. 
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the lower- or upper- triangular matrix from the 
    !! Cholesky factorization of a `real` symmetric or `complex` Hermitian matrix.
    !! Supported data types include `real` and `complex`.    
    !! The factorization is computed in-place if only one matrix argument is present; or returned into 
    !! a second matrix argument, if present. The `lower` `logical` flag allows to select between upper or 
    !! lower factorization; the `other_zeroed` optional `logical` flag allows to choose whether the unused
    !! part of the triangular matrix should be filled with zeroes.
    !! 
    !!@note The solution is based on LAPACK's `*POTRF` methods.
    !!         
     #:for rk,rt,ri in RC_KINDS_TYPES
     pure module subroutine stdlib_linalg_${ri}$_cholesky_inplace(a,lower,other_zeroed,err) 
         !> Input matrix a[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
     end subroutine stdlib_linalg_${ri}$_cholesky_inplace
     
     pure module subroutine stdlib_linalg_${ri}$_cholesky(a,c,lower,other_zeroed,err) 
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Output matrix with Cholesky factors c[n,n]
         ${rt}$, intent(out) :: c(:,:)         
         !> [optional] is the lower or upper triangular factor required? Default = lower
         logical(lk), optional, intent(in) :: lower
         !> [optional] should the unused half of the return matrix be zeroed out? Default: yes
         logical(lk), optional, intent(in) :: other_zeroed
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err     
     end subroutine stdlib_linalg_${ri}$_cholesky
     #:endfor
  end interface cholesky
     
  interface diag
    !! version: experimental
    !!
    !! Creates a diagonal array or extract the diagonal elements of an array
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! diag-create-a-diagonal-array-or-extract-the-diagonal-elements-of-an-array))
      !
      ! Vector to matrix
      !
    #:for k1, t1 in RCI_KINDS_TYPES
      module function diag_${t1[0]}$${k1}$(v) result(res)
        ${t1}$, intent(in) :: v(:)
        ${t1}$ :: res(size(v),size(v))
      end function diag_${t1[0]}$${k1}$
    #:endfor
    #:for k1, t1 in RCI_KINDS_TYPES
      module function diag_${t1[0]}$${k1}$_k(v,k) result(res)
        ${t1}$, intent(in) :: v(:)
        integer, intent(in) :: k
        ${t1}$ :: res(size(v)+abs(k),size(v)+abs(k))
      end function diag_${t1[0]}$${k1}$_k
    #:endfor

      !
      ! Matrix to vector
      !
    #:for k1, t1 in RCI_KINDS_TYPES
      module function diag_${t1[0]}$${k1}$_mat(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        ${t1}$ :: res(minval(shape(A)))
      end function diag_${t1[0]}$${k1}$_mat
    #:endfor
    #:for k1, t1 in RCI_KINDS_TYPES
      module function diag_${t1[0]}$${k1}$_mat_k(A,k) result(res)
        ${t1}$, intent(in) :: A(:,:)
        integer, intent(in) :: k
        ${t1}$ :: res(minval(shape(A))-abs(k))
      end function diag_${t1[0]}$${k1}$_mat_k
    #:endfor
  end interface


  ! Matrix trace
  interface trace
    !! version: experimental
    !!
    !! Computes the trace of a matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! trace-trace-of-a-matrix))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure trace_${t1[0]}$${k1}$
    #:endfor
  end interface

  ! Identity matrix 
  interface eye
    !! version: experimental
    !!
    !! Constructs the identity matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#eye-construct-the-identity-matrix))    
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure eye_${t1[0]}$${k1}$
    #:endfor
  end interface eye

  ! Outer product (of two vectors)
  interface outer_product
    !! version: experimental
    !!
    !! Computes the outer product of two vectors, returning a rank-2 array
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! outer_product-computes-the-outer-product-of-two-vectors))
    #:for k1, t1 in RCI_KINDS_TYPES
      pure module function outer_product_${t1[0]}$${k1}$(u, v) result(res)
        ${t1}$, intent(in) :: u(:), v(:)
        ${t1}$ :: res(size(u),size(v))
      end function outer_product_${t1[0]}$${k1}$
    #:endfor
  end interface outer_product

  interface kronecker_product
    !! version: experimental
    !!
    !! Computes the Kronecker product of two arrays of size M1xN1, and of M2xN2, returning an (M1*M2)x(N1*N2) array
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! kronecker_product-computes-the-kronecker-product-of-two-matrices))
    #:for k1, t1 in RCI_KINDS_TYPES
      pure module function kronecker_product_${t1[0]}$${k1}$(A, B) result(C)
        ${t1}$, intent(in) :: A(:,:), B(:,:)
        ${t1}$ :: C(size(A,dim=1)*size(B,dim=1),size(A,dim=2)*size(B,dim=2))
      end function kronecker_product_${t1[0]}$${k1}$
    #:endfor
  end interface kronecker_product


  ! Cross product (of two vectors)
  interface cross_product
    !! version: experimental
    !!
    !! Computes the cross product of two vectors, returning a rank-1 and size-3 array
    !! ([Specification](../page/specs/stdlib_linalg.html#cross_product-computes-the-cross-product-of-two-3-d-vectors))
    #:for k1, t1 in RCI_KINDS_TYPES
      pure module function cross_product_${t1[0]}$${k1}$(a, b) result(res)
        ${t1}$, intent(in) :: a(3), b(3)
        ${t1}$ :: res(3)
      end function cross_product_${t1[0]}$${k1}$
    #:endfor
  end interface cross_product


  ! Check for squareness
  interface is_square
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is square
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_square-checks-if-a-matrix-is-square))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_square_${t1[0]}$${k1}$
    #:endfor
  end interface is_square


  ! Check for diagonality
  interface is_diagonal
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is diagonal
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_diagonal-checks-if-a-matrix-is-diagonal))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_diagonal_${t1[0]}$${k1}$
    #:endfor
  end interface is_diagonal


  ! Check for symmetry
  interface is_symmetric
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is symmetric
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_symmetric-checks-if-a-matrix-is-symmetric))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_symmetric_${t1[0]}$${k1}$
    #:endfor
  end interface is_symmetric


  ! Check for skew-symmetry
  interface is_skew_symmetric
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is skew-symmetric
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_skew_symmetric-checks-if-a-matrix-is-skew-symmetric))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_skew_symmetric_${t1[0]}$${k1}$
    #:endfor
  end interface is_skew_symmetric


  ! Check for Hermiticity
  interface is_hermitian
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is Hermitian
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_hermitian-checks-if-a-matrix-is-hermitian))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_hermitian_${t1[0]}$${k1}$
    #:endfor
  end interface is_hermitian

  interface hermitian
    !! version: experimental
    !!
    !! Computes the Hermitian version of a rank-2 matrix.
    !! For complex matrices, this returns `conjg(transpose(a))`.
    !! For real or integer matrices, this returns `transpose(a)`.
    !!
    !! Usage:
    !! ```
    !! A  = reshape([(1, 2), (3, 4), (5, 6), (7, 8)], [2, 2])
    !! AH = hermitian(A)
    !! ```
    !!
    !! [Specification](../page/specs/stdlib_linalg.html#hermitian-compute-the-hermitian-version-of-a-rank-2-matrix)
    !!

    #:for k1, t1 in RCI_KINDS_TYPES
    pure module function hermitian_${t1[0]}$${k1}$(a) result(ah)
        ${t1}$, intent(in) :: a(:,:)
        ${t1}$ :: ah(size(a, 2), size(a, 1))
    end function hermitian_${t1[0]}$${k1}$
    #:endfor

  end interface hermitian


  ! Check for triangularity
  interface is_triangular
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is triangular
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_triangular-checks-if-a-matrix-is-triangular))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_triangular_${t1[0]}$${k1}$
    #:endfor
  end interface is_triangular
  

  ! Check for matrix being Hessenberg
  interface is_hessenberg
    !! version: experimental
    !!
    !! Checks if a matrix (rank-2 array) is Hessenberg
    !! ([Specification](../page/specs/stdlib_linalg.html#
    !! is_hessenberg-checks-if-a-matrix-is-hessenberg))
    #:for k1, t1 in RCI_KINDS_TYPES
      module procedure is_Hessenberg_${t1[0]}$${k1}$
    #:endfor
  end interface is_hessenberg

  ! Solve linear system system Ax=b.
  interface solve
     !! version: experimental 
     !!
     !! Solves the linear system \( A \cdot x = b \) for the unknown vector \( x \) from a square matrix \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#solve-solves-a-linear-matrix-equation-or-a-linear-system-of-equations))
     !!
     !!### Summary 
     !! Interface for solving a linear system arising from a general matrix.
     !!
     !!### Description
     !! 
     !! This interface provides methods for computing the solution of a linear matrix system.
     !! Supported data types include `real` and `complex`. No assumption is made on the matrix 
     !! structure. 
     !! The function can solve simultaneously either one (from a 1-d right-hand-side vector `b(:)`) 
     !! or several (from a 2-d right-hand-side vector `b(:,:)`) systems.
     !! 
     !!@note The solution is based on LAPACK's generic LU decomposition based solvers `*GESV`.
     !!    
     #:for nd,ndsuf,nde in ALL_RHS
     #:for rk,rt,ri in RC_KINDS_TYPES
     module function stdlib_linalg_${ri}$_solve_${ndsuf}$(a,b,overwrite_a,err) result(x)
         !> Input matrix a[n,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$     
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), intent(out) :: err
         !> Result array/matrix x[n] or x[n,nrhs]
         ${rt}$, allocatable, target :: x${nd}$
     end function stdlib_linalg_${ri}$_solve_${ndsuf}$
     pure module function stdlib_linalg_${ri}$_pure_solve_${ndsuf}$(a,b) result(x)
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$
         !> Result array/matrix x[n] or x[n,nrhs]
         ${rt}$, allocatable, target :: x${nd}$
     end function stdlib_linalg_${ri}$_pure_solve_${ndsuf}$
     #:endfor
     #:endfor    
  end interface solve

  ! Solve linear system Ax = b using LU decomposition (subroutine interface).
  interface solve_lu
     !! version: experimental 
     !!
     !! Solves the linear system \( A \cdot x = b \) for the unknown vector \( x \) from a square matrix \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#solve-lu-solves-a-linear-matrix-equation-or-a-linear-system-of-equations-subroutine-interface))
     !!
     !!### Summary 
     !! Subroutine interface for solving a linear system using LU decomposition.
     !!
     !!### Description
     !! 
     !! This interface provides methods for computing the solution of a linear matrix system using
     !! a subroutine. Supported data types include `real` and `complex`. No assumption is made on the matrix 
     !! structure. Preallocated space for the solution vector `x` is user-provided, and it may be provided
     !! for the array of pivot indices, `pivot`. If all pre-allocated work spaces are provided, no internal 
     !! memory allocations take place when using this interface.     
     !! The function can solve simultaneously either one (from a 1-d right-hand-side vector `b(:)`) 
     !! or several (from a 2-d right-hand-side vector `b(:,:)`) systems.
     !! 
     !!@note The solution is based on LAPACK's generic LU decomposition based solvers `*GESV`.
     !!        
     #:for nd,ndsuf,nde in ALL_RHS
     #:for rk,rt,ri in RC_KINDS_TYPES
     pure module subroutine stdlib_linalg_${ri}$_solve_lu_${ndsuf}$(a,b,x,pivot,overwrite_a,err)     
         !> Input matrix a[n,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$
         !> Result array/matrix x[n] or x[n,nrhs]     
         ${rt}$, intent(inout), contiguous, target :: x${nd}$
         !> [optional] Storage array for the diagonal pivot indices
         integer(ilp), optional, intent(inout), target :: pivot(:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
     end subroutine stdlib_linalg_${ri}$_solve_lu_${ndsuf}$
     #:endfor
     #:endfor    
  end interface solve_lu     
     
  ! Least squares solution to system Ax=b, i.e. such that the 2-norm abs(b-Ax) is minimized.
  interface lstsq
    !! version: experimental 
    !!
    !! Computes the squares solution to system \( A \cdot x = b \). 
    !! ([Specification](../page/specs/stdlib_linalg.html#lstsq-computes-the-least-squares-solution-to-a-linear-matrix-equation))
    !! 
    !!### Summary 
    !! Interface for computing least squares, i.e. the 2-norm \( || (b-A \cdot x ||_2 \) minimizing solution.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the least squares of a linear matrix system.
    !! Supported data types include `real` and `complex`.
    !! 
    !!@note The solution is based on LAPACK's singular value decomposition `*GELSD` methods.
    !! 
    #:for nd,ndsuf,nde in ALL_RHS
    #:for rk,rt,ri in RC_KINDS_TYPES
      module function stdlib_linalg_${ri}$_lstsq_${ndsuf}$(a,b,cond,overwrite_a,rank,err) result(x)
         !> Input matrix a[n,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$
         !> [optional] cutoff for rank evaluation: singular values s(i)<=cond*maxval(s) are considered 0.
         real(${rk}$), optional, intent(in) :: cond
         !> [optional] Can A,b data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] Return rank of A
         integer(ilp), optional, intent(out) :: rank
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
         !> Result array/matrix x[n] or x[n,nrhs]
         ${rt}$, allocatable, target :: x${nd}$
      end function stdlib_linalg_${ri}$_lstsq_${ndsuf}$
    #:endfor
    #:endfor
  end interface lstsq

  ! Least squares solution to system Ax=b, i.e. such that the 2-norm abs(b-Ax) is minimized.
  interface solve_lstsq
    !! version: experimental 
    !!
    !! Computes the squares solution to system \( A \cdot x = b \). 
    !! ([Specification](../page/specs/stdlib_linalg.html#solve-lstsq-compute-the-least-squares-solution-to-a-linear-matrix-equation-subroutine-interface))
    !! 
    !!### Summary 
    !! Subroutine interface for computing least squares, i.e. the 2-norm \( || (b-A \cdot x ||_2 \) minimizing solution.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the least squares of a linear matrix system using 
    !! a subroutine. Supported data types include `real` and `complex`. If pre-allocated work spaces 
    !! are provided, no internal memory allocations take place when using this interface.
    !! 
    !!@note The solution is based on LAPACK's singular value decomposition `*GELSD` methods.
    !! 
    #:for nd,ndsuf,nde in ALL_RHS
    #:for rk,rt,ri in RC_KINDS_TYPES
      module subroutine stdlib_linalg_${ri}$_solve_lstsq_${ndsuf}$(a,b,x,real_storage,int_storage,&
                        #{if rt.startswith('c')}#cmpl_storage,#{endif}#cond,singvals,overwrite_a,rank,err) 
         !> Input matrix a[n,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$
         !> Result array/matrix x[n] or x[n,nrhs]
         ${rt}$, intent(inout), contiguous, target :: x${nd}$     
         !> [optional] real working storage space
         real(${rk}$), optional, intent(inout), target :: real_storage(:)
         !> [optional] integer working storage space
         integer(ilp), optional, intent(inout), target :: int_storage(:)
         #:if rt.startswith('complex')   
         !> [optional] complex working storage space
         ${rt}$, optional, intent(inout), target :: cmpl_storage(:)                  
         #:endif
         !> [optional] cutoff for rank evaluation: singular values s(i)<=cond*maxval(s) are considered 0.
         real(${rk}$), optional, intent(in) :: cond
         !> [optional] list of singular values [min(m,n)], in descending magnitude order, returned by the SVD
         real(${rk}$), optional, intent(out), target :: singvals(:)                  
         !> [optional] Can A,b data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] Return rank of A
         integer(ilp), optional, intent(out) :: rank
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine stdlib_linalg_${ri}$_solve_lstsq_${ndsuf}$
    #:endfor
    #:endfor
  end interface solve_lstsq

  ! Return the working array space required by the least squares solver
  interface lstsq_space
    !! version: experimental 
    !!
    !! Computes the integer, real [, complex] working space required by the least-squares solver
    !! ([Specification](../page/specs/stdlib_linalg.html#lstsq-space-compute-internal-working-space-requirements-for-the-least-squares-solver))
    !! 
    !!### Description
    !! 
    !! This interface provides sizes of integer, real [, complex] working spaces required by the 
    !! least-squares solver. These sizes can be used to pre-allocated working arrays in case several 
    !! repeated least-squares solutions to a same system are sought. If pre-allocated working arrays 
    !! are provided, no internal allocations will take place.
    !! 
    #:for nd,ndsuf,nde in ALL_RHS
    #:for rk,rt,ri in RC_KINDS_TYPES
      pure module subroutine stdlib_linalg_${ri}$_lstsq_space_${ndsuf}$(a,b,lrwork,liwork#{if rt.startswith('c')}#,lcwork#{endif}#)
         !> Input matrix a[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> Right hand side vector or array, b[n] or b[n,nrhs]
         ${rt}$, intent(in) :: b${nd}$
         !> Size of the working space arrays                
         integer(ilp), intent(out) :: lrwork,liwork#{if rt.startswith('c')}#,lcwork#{endif}#         
      end subroutine stdlib_linalg_${ri}$_lstsq_space_${ndsuf}$
    #:endfor
    #:endfor
  end interface lstsq_space

  ! QR factorization of rank-2 array A
  interface qr
    !! version: experimental 
    !!
    !! Computes the QR factorization of matrix \( A = Q R \). 
    !! ([Specification](../page/specs/stdlib_linalg.html#qr-compute-the-qr-factorization-of-a-matrix))
    !! 
    !!### Summary 
    !! Compute the QR factorization of a `real` or `complex` matrix: \( A = Q R \), where \( Q \)  is orthonormal 
    !! and \( R \) is upper-triangular. Matrix \( A \) has size `[m,n]`, with \( m\ge n \). 
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the QR factorization of a matrix. 
    !! Supported data types include `real` and `complex`. If a pre-allocated work space 
    !! is provided, no internal memory allocations take place when using this interface.
    !!
    !! Given `k = min(m,n)`, one can write \( A = \( Q_1  Q_2 \) \cdot \( \frac{R_1}{0}\) \). 
    !! The user may want the full problem (provide `shape(Q)==[m,m]`, `shape(R)==[m,n]`) or the reduced  
    !! problem only: \( A = Q_1 R_1 \) (provide `shape(Q)==[m,k]`, `shape(R)==[k,n]`).
    !! 
    !!@note The solution is based on LAPACK's QR factorization (`*GEQRF`) and ordered matrix output (`*ORGQR`, `*UNGQR`). 
    !!     
    #:for rk,rt,ri in RC_KINDS_TYPES
      pure module subroutine stdlib_linalg_${ri}$_qr(a,q,r,overwrite_a,storage,err) 
         !> Input matrix a[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Orthogonal matrix Q ([m,m], or [m,k] if reduced)
         ${rt}$, intent(out), contiguous, target :: q(:,:)
         !> Upper triangular matrix R ([m,n], or [k,n] if reduced)
         ${rt}$, intent(out), contiguous, target :: r(:,:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] Provide pre-allocated workspace, size to be checked with qr_space
         ${rt}$, intent(out), optional, target :: storage(:)
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine stdlib_linalg_${ri}$_qr
    #:endfor    
  end interface qr

  ! Return the working array space required by the QR factorization solver
  interface qr_space
    !! version: experimental 
    !!
    !! Computes the working array space required by the QR factorization solver
    !! ([Specification](../page/specs/stdlib_linalg.html#qr-space-compute-internal-working-space-requirements-for-the-qr-factorization))
    !! 
    !!### Description
    !! 
    !! This interface returns the size of the `real` or `complex` working storage required by the 
    !! QR factorization solver. The working size only depends on the kind (`real` or `complex`) and size of
    !! the matrix being factorized. Storage size can be used to pre-allocate a working array in case several 
    !! repeated QR factorizations to a same-size matrix are sought. If pre-allocated working arrays 
    !! are provided, no internal allocations will take place during the factorization.
    !!     
    #:for rk,rt,ri in RC_KINDS_TYPES
      pure module subroutine get_qr_${ri}$_workspace(a,lwork,err)
         !> Input matrix a[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> Minimum workspace size for both operations
         integer(ilp), intent(out) :: lwork
         !> State return flag. Returns an error if the query failed
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine get_qr_${ri}$_workspace
    #:endfor
  end interface qr_space
 
  ! Schur decomposition of rank-2 array A
  interface schur
    !! version: experimental
    !!
    !! Computes the Schur decomposition of matrix \( A = Z T Z^H \).
    !! ([Specification](../page/specs/stdlib_linalg.html#schur-compute-the-schur-decomposition-of-a-matrix))
    !!
    !!### Summary
    !! Compute the Schur decomposition of a `real` or `complex` matrix: \( A = Z T Z^H \), where \( Z \) is
    !! orthonormal/unitary and \( T \) is upper-triangular or quasi-upper-triangular. Matrix \( A \) has size `[m,m]`.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the Schur decomposition of a matrix.
    !! Supported data types include `real` and `complex`. If a pre-allocated workspace is provided, no internal 
    !! memory allocations take place when using this interface.
    !!
    !! The output matrix \( T \) is upper-triangular for `complex` input matrices and quasi-upper-triangular
    !! for `real` input matrices (with possible \( 2 \times 2 \) blocks on the diagonal).
    !!
    !!@note The solution is based on LAPACK's Schur decomposition routines (`*GEES`). 
    !! 
    #:for rk,rt,ri in RC_KINDS_TYPES
      module subroutine stdlib_linalg_${ri}$_schur(a, t, z, eigvals, overwrite_a, storage, err)
         !> Input matrix a[m,m]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Schur form of A: upper-triangular or quasi-upper-triangular matrix T
         ${rt}$, intent(out), contiguous, target :: t(:,:)
         !> Unitary/orthonormal transformation matrix Z
         ${rt}$, optional, intent(out), contiguous, target :: z(:,:)
         !> [optional] Output eigenvalues that appear on the diagonal of T
         complex(${rk}$), optional, intent(out), contiguous, target :: eigvals(:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a                 
         !> [optional] Provide pre-allocated workspace, size to be checked with schur_space         
         ${rt}$, optional, intent(inout), target :: storage(:)
         !> [optional] State return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine stdlib_linalg_${ri}$_schur
      
      ! Schur decomposition subroutine: real eigenvalue interface
      module subroutine stdlib_linalg_real_eig_${ri}$_schur(a,t,z,eigvals,overwrite_a,storage,err)
         !> Input matrix a[m,m]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Schur form of A: upper-triangular or quasi-upper-triangular matrix T
         ${rt}$, intent(out), contiguous, target :: t(:,:)
         !> Unitary/orthonormal transformation matrix Z
         ${rt}$, optional, intent(out), contiguous, target :: z(:,:)
         !> Output real eigenvalues that appear on the diagonal of T
         real(${rk}$), intent(out), contiguous, target :: eigvals(:)
         !> [optional] Provide pre-allocated workspace, size to be checked with schur_space
         ${rt}$, optional, intent(inout), target :: storage(:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a        
         !> [optional] State return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err        
      end subroutine stdlib_linalg_real_eig_${ri}$_schur 
    #:endfor
  end interface schur

  ! Return the working array space required by the Schur decomposition solver
  interface schur_space
    !! version: experimental
    !!
    !! Computes the working array space required by the Schur decomposition solver
    !! ([Specification](../page/specs/stdlib_linalg.html#schur-space-compute-internal-working-space-requirements-for-the-schur-decomposition))
    !!
    !!### Description
    !! 
    !! This interface returns the size of the `real` or `complex` working storage required by the 
    !! Schur decomposition solver. The working size only depends on the kind (`real` or `complex`) and size of
    !! the matrix being decomposed. Storage size can be used to pre-allocate a working array in case several 
    !! repeated Schur decompositions of same-size matrices are sought. If pre-allocated working arrays 
    !! are provided, no internal allocations will take place during the decomposition.
    !!     
    #:for rk,rt,ri in RC_KINDS_TYPES
      module subroutine get_schur_${ri}$_workspace(a,lwork,err)
         !> Input matrix a[m,m]
         ${rt}$, intent(in), target :: a(:,:)
         !> Minimum workspace size for the decomposition operation
         integer(ilp), intent(out) :: lwork
         !> State return flag. Returns an error if the query failed
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine get_schur_${ri}$_workspace
    #:endfor
  end interface schur_space

  interface det
    !! version: experimental 
    !!
    !! Computes the determinant of a square matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#det-computes-the-determinant-of-a-square-matrix))
    !! 
    !!### Summary 
    !! Interface for computing matrix determinant.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the determinant of a matrix.
    !! Supported data types include `real` and `complex`.
    !! 
    !!@note The provided functions are intended for square matrices only.          
    !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
    !! 
    !!### Example
    !!
    !!```fortran
    !!
    !!    real(sp) :: a(3,3), d
    !!    type(linalg_state_type) :: state  
    !!    a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
    !!
    !!    ! ...
    !!    d = det(a,err=state)
    !!    if (state%ok()) then 
    !!       print *, 'Success! det=',d
    !!    else
    !!       print *, state%print()
    !!    endif
    !!    ! ...
    !!```
    !!     
    #:for rk,rt in RC_KINDS_TYPES
    #:if rk!="xdp" 
    module procedure stdlib_linalg_${rt[0]}$${rk}$determinant
    module procedure stdlib_linalg_pure_${rt[0]}$${rk}$determinant
    #:endif
    #:endfor
  end interface det

  interface operator(.det.)
    !! version: experimental 
    !!
    !! Determinant operator of a square matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#det-determinant-operator-of-a-square-matrix))
    !!
    !!### Summary
    !! Pure operator interface for computing matrix determinant.
    !!
    !!### Description
    !! 
    !! This pure operator interface provides a convenient way to compute the determinant of a matrix.
    !! Supported data types include real and complex.
    !!
    !!@note The provided functions are intended for square matrices.
    !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
    !!
    !!### Example
    !!
    !!```fortran
    !!
    !!    ! ...
    !!    real(sp) :: matrix(3,3), d
    !!    matrix = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
    !!    d = .det.matrix
    !!    ! ...
    !! 
    !!```
    !     
    #:for rk,rt in RC_KINDS_TYPES
    #:if rk!="xdp"
    module procedure stdlib_linalg_pure_${rt[0]}$${rk}$determinant
    #:endif
    #:endfor        
  end interface operator(.det.)

  interface
    #:for rk,rt in RC_KINDS_TYPES
    #:if rk!="xdp" 
    module function stdlib_linalg_${rt[0]}$${rk}$determinant(a,overwrite_a,err) result(det)
        !> Input matrix a[m,n]
        ${rt}$, intent(inout), target :: a(:,:)
        !> [optional] Can A data be overwritten and destroyed?
        logical(lk), optional, intent(in) :: overwrite_a
        !> State return flag. 
        type(linalg_state_type), intent(out) :: err
        !> Matrix determinant
        ${rt}$ :: det
    end function stdlib_linalg_${rt[0]}$${rk}$determinant
    pure module function stdlib_linalg_pure_${rt[0]}$${rk}$determinant(a) result(det)
        !> Input matrix a[m,n]
        ${rt}$, intent(in) :: a(:,:)
        !> Matrix determinant
        ${rt}$ :: det                
    end function stdlib_linalg_pure_${rt[0]}$${rk}$determinant
    #:endif
    #:endfor
  end interface  

  ! Matrix Inverse: Function interface
  interface inv
    !! version: experimental 
    !!
    !! Inverse of a square matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#inv-inverse-of-a-square-matrix))
    !!
    !!### Summary
    !! This interface provides methods for computing the inverse of a square `real` or `complex` matrix.
    !! The inverse \( A^{-1} \) is defined such that \( A \cdot A^{-1} = A^{-1} \cdot A = I_n \).
    !!
    !!### Description
    !!     
    !! This function interface provides methods that return the inverse of a square matrix.    
    !! Supported data types include `real` and `complex`. 
    !! The inverse matrix \( A^{-1} \) is returned as a function result. 
    !! Exceptions are raised in case of singular matrix or invalid size, and trigger an `error stop` if 
    !! the state flag `err` is not provided. 
    !!
    !!@note The provided functions are intended for square matrices.
    !!       
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_inverse_${ri}$(a,err) result(inva)
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Output matrix inverse
         ${rt}$, allocatable :: inva(:,:)
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
    end function stdlib_linalg_inverse_${ri}$
    #:endfor
  end interface inv

  ! Matrix Inverse: Subroutine interface - in-place inversion
  interface invert
    !! version: experimental 
    !!
    !! Inversion of a square matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#invert-inversion-of-a-square-matrix))
    !!
    !!### Summary
    !! This interface provides methods for inverting a square `real` or `complex` matrix in-place.
    !! The inverse \( A^{-1} \) is defined such that \( A \cdot A^{-1} = A^{-1} \cdot A = I_n \).
    !!
    !!### Description
    !!     
    !! This subroutine interface provides a way to compute the inverse of a matrix.    
    !! Supported data types include `real` and `complex`. 
    !! The user may provide a unique matrix argument `a`. In this case, `a` is replaced by the inverse matrix.
    !! on output. Otherwise, one may provide two separate arguments: an input matrix `a` and an output matrix 
    !! `inva`. In this case, `a` will not be modified, and the inverse is returned in `inva`.
    !! Pre-allocated storage may be provided for the array of pivot indices, `pivot`. If all pre-allocated 
    !! work spaces are provided, no internal memory allocations take place when using this interface.             
    !!
    !!@note The provided subroutines are intended for square matrices.
    !!      
    #:for rk,rt,ri in RC_KINDS_TYPES
    module subroutine stdlib_linalg_invert_inplace_${ri}$(a,pivot,err)
         !> Input matrix a[n,n]
         ${rt}$, intent(inout) :: a(:,:)
         !> [optional] Storage array for the diagonal pivot indices
         integer(ilp), optional, intent(inout), target :: pivot(:)         
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
    end subroutine stdlib_linalg_invert_inplace_${ri}$
    ! Compute the square matrix inverse of a
    module subroutine stdlib_linalg_invert_split_${ri}$(a,inva,pivot,err)
         !> Input matrix a[n,n].
         ${rt}$, intent(in) :: a(:,:)
         !> Inverse matrix a[n,n]. 
         ${rt}$, intent(out) :: inva(:,:)         
         !> [optional] Storage array for the diagonal pivot indices
         integer(ilp), optional, intent(inout), target :: pivot(:)
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err    
    end subroutine stdlib_linalg_invert_split_${ri}$
    #:endfor
  end interface invert

  ! Matrix Inverse: Operator interface
  interface operator(.inv.)
    !! version: experimental 
    !!
    !! Inverse operator of a square matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#inv-inverse-operator-of-a-square-matrix))
    !!
    !!### Summary
    !! Operator interface for computing the inverse of a square `real` or `complex` matrix.
    !!
    !!### Description
    !! 
    !! This operator interface provides a convenient way to compute the inverse of a matrix.
    !! Supported data types include `real` and `complex`. On input errors or singular matrix, 
    !! NaNs will be returned.
    !!
    !!@note The provided functions are intended for square matrices.
    !!
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_inverse_${ri}$_operator(a) result(inva)
         !> Input matrix a[n,n]
         ${rt}$, intent(in) :: a(:,:)
         !> Result matrix
         ${rt}$, allocatable :: inva(:,:)        
    end function stdlib_linalg_inverse_${ri}$_operator
    #:endfor
  end interface operator(.inv.)


  ! Moore-Penrose Pseudo-Inverse: Function interface
  interface pinv
    !! version: experimental 
    !!
    !! Pseudo-inverse of a matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#pinv-moore-penrose-pseudo-inverse-of-a-matrix))
    !!
    !!### Summary
    !! This interface provides methods for computing the Moore-Penrose pseudo-inverse of a matrix.
    !! The pseudo-inverse \( A^{+} \) is a generalization of the matrix inverse, computed for square, singular, 
    !! or rectangular matrices. It is defined such that it satisfies the conditions:
    !! - \( A \cdot A^{+} \cdot A = A \)
    !! - \( A^{+} \cdot A \cdot A^{+} = A^{+} \)
    !! - \( (A \cdot A^{+})^T = A \cdot A^{+} \)
    !! - \( (A^{+} \cdot A)^T = A^{+} \cdot A \)
    !!
    !!### Description
    !!     
    !! This function interface provides methods that return the Moore-Penrose pseudo-inverse of a matrix.    
    !! Supported data types include `real` and `complex`. 
    !! The pseudo-inverse \( A^{+} \) is returned as a function result. The computation is based on the 
    !! singular value decomposition (SVD). An optional relative tolerance `rtol` is provided to control the 
    !! inclusion of singular values during inversion. Singular values below \( \text{rtol} \cdot \sigma_{\max} \) 
    !! are treated as zero, where \( \sigma_{\max} \) is the largest singular value. If `rtol` is not provided, 
    !! a default threshold is applied.
    !! 
    !! Exceptions are raised in case of computational errors or invalid input, and trigger an `error stop` 
    !! if the state flag `err` is not provided. 
    !!
    !!@note The provided functions are intended for both rectangular and square matrices.
    !!       
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_pseudoinverse_${ri}$(a,rtol,err) result(pinva)
        !> Input matrix a[m,n]
        ${rt}$, intent(in), target :: a(:,:)
        !> [optional] Relative tolerance for singular value cutoff
        real(${rk}$), optional, intent(in) :: rtol         
        !> [optional] State return flag. On error if not requested, the code will stop
        type(linalg_state_type), optional, intent(out) :: err
        !> Output matrix pseudo-inverse [n,m]
        ${rt}$ :: pinva(size(a,2,kind=ilp),size(a,1,kind=ilp))         
     end function stdlib_linalg_pseudoinverse_${ri}$
    #:endfor
  end interface pinv

  ! Moore-Penrose Pseudo-Inverse: Subroutine interface 
  interface pseudoinvert
    !! version: experimental 
    !!
    !! Computation of the Moore-Penrose pseudo-inverse
    !! ([Specification](../page/specs/stdlib_linalg.html#pseudoinvert-moore-penrose-pseudo-inverse-of-a-matrix))
    !!
    !!### Summary
    !! This interface provides methods for computing the Moore-Penrose pseudo-inverse of a rectangular 
    !! or square `real` or `complex` matrix.
    !! The pseudo-inverse \( A^{+} \) generalizes the matrix inverse and satisfies the properties:
    !! - \( A \cdot A^{+} \cdot A = A \)
    !! - \( A^{+} \cdot A \cdot A^{+} = A^{+} \)
    !! - \( (A \cdot A^{+})^T = A \cdot A^{+} \)
    !! - \( (A^{+} \cdot A)^T = A^{+} \cdot A \)
    !!
    !!### Description
    !!     
    !! This subroutine interface provides a way to compute the Moore-Penrose pseudo-inverse of a matrix.    
    !! Supported data types include `real` and `complex`. 
    !! Users must provide two matrices: the input matrix `a` [m,n] and the output pseudo-inverse `pinva` [n,m]. 
    !! The input matrix `a` is used to compute the pseudo-inverse and is not modified. The computed 
    !! pseudo-inverse is stored in `pinva`. The computation is based on the singular value decomposition (SVD).
    !! 
    !! An optional relative tolerance `rtol` is used to control the inclusion of singular values in the 
    !! computation. Singular values below \( \text{rtol} \cdot \sigma_{\max} \) are treated as zero, 
    !! where \( \sigma_{\max} \) is the largest singular value. If `rtol` is not provided, a default 
    !! threshold is applied. 
    !! 
    !! Exceptions are raised in case of computational errors or invalid input, and trigger an `error stop` 
    !! if the state flag `err` is not provided.
    !!
    !!@note The provided subroutines are intended for both rectangular and square matrices.
    !!       
    #:for rk,rt,ri in RC_KINDS_TYPES
    module subroutine stdlib_linalg_pseudoinvert_${ri}$(a,pinva,rtol,err)
        !> Input matrix a[m,n]
        ${rt}$, intent(inout) :: a(:,:)
        !> Output pseudo-inverse matrix [n,m]
        ${rt}$, intent(out) :: pinva(:,:)
        !> [optional] Relative tolerance for singular value cutoff
        real(${rk}$), optional, intent(in) :: rtol
        !> [optional] State return flag. On error if not requested, the code will stop
        type(linalg_state_type), optional, intent(out) :: err
    end subroutine stdlib_linalg_pseudoinvert_${ri}$
    #:endfor
  end interface pseudoinvert

  ! Moore-Penrose Pseudo-Inverse: Operator interface
  interface operator(.pinv.)
    !! version: experimental 
    !!
    !! Pseudo-inverse operator of a matrix
    !! ([Specification](../page/specs/stdlib_linalg.html#pinv-moore-penrose-pseudo-inverse-operator))
    !!
    !!### Summary
    !! Operator interface for computing the Moore-Penrose pseudo-inverse of a `real` or `complex` matrix.
    !!
    !!### Description
    !! 
    !! This operator interface provides a convenient way to compute the Moore-Penrose pseudo-inverse 
    !! of a matrix. Supported data types include `real` and `complex`. The pseudo-inverse \( A^{+} \) 
    !! is computed using singular value decomposition (SVD), with singular values below an internal 
    !! threshold treated as zero.
    !! 
    !! For computational errors or invalid input, the function may return a matrix filled with NaNs.
    !!
    !!@note The provided functions are intended for both rectangular and square matrices.
    !!
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_pinv_${ri}$_operator(a) result(pinva)
         !> Input matrix a[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> Result pseudo-inverse matrix
         ${rt}$ :: pinva(size(a,2,kind=ilp),size(a,1,kind=ilp))
    end function stdlib_linalg_pinv_${ri}$_operator
    #:endfor
  end interface operator(.pinv.)


  ! Eigendecomposition of a square matrix: eigenvalues, and optionally eigenvectors
  interface eig    
     !! version: experimental 
     !!
     !! Solves the eigendecomposition \( A \cdot \bar{v} - \lambda \cdot \bar{v} \) for square matrix \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#eig-eigenvalues-and-eigenvectors-of-a-square-matrix))
     !!
     !!### Summary 
     !! Subroutine interface for computing eigenvalues and eigenvectors of a square matrix.
     !!
     !!### Description
     !! 
     !! This interface provides methods for computing the eigenvalues, and optionally eigenvectors, 
     !! of a general square matrix. Supported data types include `real` and `complex`, and no assumption is 
     !! made on the matrix structure. The user may request either left, right, or both 
     !! eigenvectors to be returned. They are returned as columns of a square matrix with the same size as `A`. 
     !! Preallocated space for both eigenvalues `lambda` and the eigenvector matrices must be user-provided.      
     !! 
     !!@note The solution is based on LAPACK's general eigenproblem solvers `*GEEV`.
     !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
     !!       
    #:for rk,rt,ri in RC_KINDS_TYPES
    #:for ep,ei in EIG_PROBLEM_LIST
    module subroutine stdlib_linalg_eig_${ep}$_${ri}$(a#{if ei=='ggev'}#,b#{endif}#,lambda,right,left, &
                                                      overwrite_a#{if ei=='ggev'}#,overwrite_b#{endif}#,err)
     !! Eigendecomposition of matrix A returning an array `lambda` of eigenvalues, 
     !! and optionally right or left eigenvectors.        
         !> Input matrix A[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         #:if ei=='ggev'
         !> Generalized problem matrix B[n,n]
         ${rt}$, intent(inout), target :: b(:,:)
         #:endif         
         !> Array of eigenvalues
         complex(${rk}$), intent(out) :: lambda(:)
         !> The columns of RIGHT contain the right eigenvectors of A
         complex(${rk}$), optional, intent(out), target :: right(:,:)
         !> The columns of LEFT contain the left eigenvectors of A
         complex(${rk}$), optional, intent(out), target :: left(:,:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         #:if ei=='ggev'
         !> [optional] Can B data be overwritten and destroyed? (default: no)
         logical(lk), optional, intent(in) :: overwrite_b
         #:endif         
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
    end subroutine stdlib_linalg_eig_${ep}$_${ri}$

    module subroutine stdlib_linalg_real_eig_${ep}$_${ri}$(a#{if ei=='ggev'}#,b#{endif}#,lambda,right,left, &
                                                           overwrite_a#{if ei=='ggev'}#,overwrite_b#{endif}#,err)
     !! Eigendecomposition of matrix A returning an array `lambda` of real eigenvalues, 
     !! and optionally right or left eigenvectors. Returns an error if the eigenvalues had
     !! non-trivial imaginary parts.
         !> Input matrix A[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         #:if ei=='ggev'
         !> Generalized problem matrix B[n,n]
         ${rt}$, intent(inout), target :: b(:,:)  
         #:endif
         !> Array of real eigenvalues
         real(${rk}$), intent(out) :: lambda(:)
         !> The columns of RIGHT contain the right eigenvectors of A
         complex(${rk}$), optional, intent(out), target :: right(:,:)
         !> The columns of LEFT contain the left eigenvectors of A
         complex(${rk}$), optional, intent(out), target :: left(:,:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         #:if ei=='ggev'
         !> [optional] Can B data be overwritten and destroyed? (default: no)
         logical(lk), optional, intent(in) :: overwrite_b
         #:endif         
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
    end subroutine stdlib_linalg_real_eig_${ep}$_${ri}$
    #:endfor    
    #:endfor
  end interface eig

  ! Eigenvalues of a square matrix
  interface eigvals
     !! version: experimental 
     !!
     !! Returns the eigenvalues \( lambda \), \( A \cdot \bar{v} - \lambda \cdot \bar{v} \), for square matrix \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#eigvals-eigenvalues-of-a-square-matrix))
     !!
     !!### Summary 
     !! Function interface for computing the eigenvalues of a square matrix.
     !!
     !!### Description
     !! 
     !! This interface provides functions for returning the eigenvalues of a general square matrix. 
     !! Supported data types include `real` and `complex`, and no assumption is made on the matrix structure. 
     !! An `error stop` is thrown in case of failure; otherwise, error information can be returned 
     !! as an optional `type(linalg_state_type)` output flag. 
     !! 
     !!@note The solution is based on LAPACK's general eigenproblem solvers `*GEEV`.
     !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
     !!       
    #:for rk,rt,ri in RC_KINDS_TYPES
    #:for ep,ei in EIG_PROBLEM_LIST
    module function stdlib_linalg_eigvals_${ep}$_${ri}$(a#{if ei=='ggev'}#,b#{endif}#,err) result(lambda)
     !! Return an array of eigenvalues of matrix A.
         !> Input matrix A[m,n]
         ${rt}$, intent(in), dimension(:,:), target :: a 
         #:if ei=='ggev'
         !> Generalized problem matrix B[n,n]
         ${rt}$, intent(inout), dimension(:,:), target :: b         
         #:endif                  
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), intent(out) :: err
         !> Array of singular values
         complex(${rk}$), allocatable :: lambda(:)        
    end function stdlib_linalg_eigvals_${ep}$_${ri}$
    
    module function stdlib_linalg_eigvals_noerr_${ep}$_${ri}$(a#{if ei=='ggev'}#,b#{endif}#) result(lambda)
     !! Return an array of eigenvalues of matrix A.
         !> Input matrix A[m,n]
         ${rt}$, intent(in), dimension(:,:), target :: a 
         #:if ei=='ggev'
         !> Generalized problem matrix B[n,n]
         ${rt}$, intent(inout), dimension(:,:), target :: b         
         #:endif                  
         !> Array of singular values
         complex(${rk}$), allocatable :: lambda(:)
    end function stdlib_linalg_eigvals_noerr_${ep}$_${ri}$
    #:endfor
    #:endfor
  end interface eigvals
     
  ! Eigendecomposition of a real symmetric or complex hermitian matrix
  interface eigh
     !! version: experimental 
     !!
     !! Solves the eigendecomposition \( A \cdot \bar{v} - \lambda \cdot \bar{v} \) for a real symmetric 
     !! \( A = A^T \) or complex Hermitian \( A = A^H \) square matrix. 
     !! ([Specification](../page/specs/stdlib_linalg.html#eigh-eigenvalues-and-eigenvectors-of-a-real-symmetric-or-complex-hermitian-square-matrix))
     !!
     !!### Summary 
     !! Subroutine interface for computing eigenvalues and eigenvectors of a real symmetric or complex Hermitian square matrix.
     !!
     !!### Description
     !! 
     !! This interface provides methods for computing the eigenvalues, and optionally eigenvectors, 
     !! of a real symmetric or complex Hermitian square matrix. Supported data types include `real` and `complex`. 
     !! The matrix must be symmetric (if `real`) or Hermitian (if `complex`). Only the lower or upper 
     !! half of the matrix is accessed, and the user can select which using the optional `upper_a` 
     !! flag (default: use lower half). The vectors are orthogonal, and may be returned as columns of an optional 
     !! matrix `vectors` with the same kind and size as `A`. 
     !! Preallocated space for both eigenvalues `lambda` and the eigenvector matrix must be user-provided.      
     !! 
     !!@note The solution is based on LAPACK's eigenproblem solvers `*SYEV`/`*HEEV`.
     !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
     !!      
    #:for rk,rt,ri in RC_KINDS_TYPES
    module subroutine stdlib_linalg_eigh_${ri}$(a,lambda,vectors,upper_a,overwrite_a,err)
     !! Eigendecomposition of a real symmetric or complex Hermitian matrix A returning an array `lambda` 
     !! of eigenvalues, and optionally right or left eigenvectors.        
         !> Input matrix A[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Array of eigenvalues
         real(${rk}$), intent(out) :: lambda(:)
         !> The columns of vectors contain the orthonormal eigenvectors of A
         ${rt}$, optional, intent(out), target :: vectors(:,:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] Should the upper/lower half of A be used? Default: lower
         logical(lk), optional, intent(in) :: upper_a
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
    end subroutine stdlib_linalg_eigh_${ri}$
    #:endfor        
  end interface eigh
     
  ! Eigenvalues of a real symmetric or complex hermitian matrix
  interface eigvalsh
     !! version: experimental 
     !!
     !! Returns the eigenvalues \( lambda \), \( A \cdot \bar{v} - \lambda \cdot \bar{v} \), for a real
     !! symmetric \( A = A^T \) or complex Hermitian \( A = A^H \) square matrix. 
     !! ([Specification](../page/specs/stdlib_linalg.html#eigvalsh-eigenvalues-of-a-real-symmetric-or-complex-hermitian-square-matrix))
     !!
     !!### Summary 
     !! Function interface for computing the eigenvalues of a real symmetric or complex hermitian square matrix.
     !!
     !!### Description
     !! 
     !! This interface provides functions for returning the eigenvalues of a real symmetric or complex Hermitian
     !! square matrix. Supported data types include `real` and `complex`. The matrix must be symmetric 
     !! (if `real`) or Hermitian (if `complex`). Only the lower or upper half of the matrix is accessed, 
     !! and the user can select which using the optional `upper_a` flag (default: use lower half). 
     !! An `error stop` is thrown in case of failure; otherwise, error information can be returned 
     !! as an optional `type(linalg_state_type)` output flag. 
     !! 
     !!@note The solution is based on LAPACK's eigenproblem solvers `*SYEV`/`*HEEV`.
     !!@note BLAS/LAPACK backends do not currently support extended precision (``xdp``).
     !!         
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_eigvalsh_${ri}$(a,upper_a,err) result(lambda)
     !! Return an array of eigenvalues of real symmetric / complex hermitian A
         !> Input matrix A[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> [optional] Should the upper/lower half of A be used? Default: lower
         logical(lk), optional, intent(in) :: upper_a         
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), intent(out) :: err
         !> Array of singular values
         real(${rk}$), allocatable :: lambda(:)    
    end function stdlib_linalg_eigvalsh_${ri}$
    
    module function stdlib_linalg_eigvalsh_noerr_${ri}$(a,upper_a) result(lambda)
     !! Return an array of eigenvalues of real symmetric / complex hermitian A        
         !> Input matrix A[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> [optional] Should the upper/lower half of A be used? Default: lower
         logical(lk), optional, intent(in) :: upper_a         
         !> Array of singular values
         real(${rk}$), allocatable :: lambda(:)
    end function stdlib_linalg_eigvalsh_noerr_${ri}$
    #:endfor                
  end interface eigvalsh

  ! Singular value decomposition  
  interface svd 
    !! version: experimental 
    !!
    !! Computes the singular value decomposition of a `real` or `complex` 2d matrix.
    !! ([Specification](../page/specs/stdlib_linalg.html#svd-compute-the-singular-value-decomposition-of-a-rank-2-array-matrix))
    !! 
    !!### Summary 
    !! Interface for computing the singular value decomposition of a `real` or `complex` 2d matrix.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the singular value decomposition of a matrix.
    !! Supported data types include `real` and `complex`. The subroutine returns a `real` array of 
    !! singular values, and optionally, left- and right- singular vector matrices, `U` and `V`. 
    !! For a matrix `A` with size [m,n], full matrix storage for `U` and `V` should be [m,m] and [n,n]. 
    !! It is possible to use partial storage [m,k] and [k,n], `k=min(m,n)`, choosing `full_matrices=.false.`.
    !! 
    !!@note The solution is based on LAPACK's singular value decomposition `*GESDD` methods.
    !! 
    !!### Example
    !!
    !!```fortran
    !!    real(sp) :: a(2,3), s(2), u(2,2), vt(3,3) 
    !!    a = reshape([3,2, 2,3, 2,-2],[2,3])
    !!
    !!    call svd(A,s,u,v)
    !!    print *, 'singular values = ',s
    !!```
    !!         
    #:for rk,rt,ri in RC_KINDS_TYPES
    module subroutine stdlib_linalg_svd_${ri}$(a,s,u,vt,overwrite_a,full_matrices,err)
     !!### Summary
     !! Compute singular value decomposition of a matrix \( A = U \cdot S \cdot \V^T \)
     !!
     !!### Description
     !!
     !! This function computes the singular value decomposition of a `real` or `complex` matrix \( A \), 
     !! and returns the array of singular values, and optionally the left matrix \( U \) containing the 
     !! left unitary singular vectors, and the right matrix \( V^T \), containing the right unitary 
     !! singular vectors.
     !!
     !! param: a Input matrix of size [m,n].
     !! param: s Output `real` array of size [min(m,n)] returning a list of singular values.
     !! param: u [optional] Output left singular matrix of size [m,m] or [m,min(m,n)] (.not.full_matrices). Contains singular vectors as columns.
     !! param: vt [optional] Output right singular matrix of size [n,n] or [min(m,n),n] (.not.full_matrices). Contains singular vectors as rows.     
     !! param: overwrite_a [optional] Flag indicating if the input matrix can be overwritten.
     !! param: full_matrices [optional] If `.true.` (default), matrices \( U \) and \( V^T \) have size [m,m], [n,n]. Otherwise, they are [m,k], [k,n] with `k=min(m,n)`.
     !! param: err [optional] State return flag.      
     !!            
         !> Input matrix A[m,n]
         ${rt}$, intent(inout), target :: a(:,:)
         !> Array of singular values
         real(${rk}$), intent(out) :: s(:)
         !> The columns of U contain the left singular vectors 
         ${rt}$, optional, intent(out), target :: u(:,:)
         !> The rows of V^T contain the right singular vectors
         ${rt}$, optional, intent(out), target :: vt(:,:)
         !> [optional] Can A data be overwritten and destroyed?
         logical(lk), optional, intent(in) :: overwrite_a
         !> [optional] full matrices have shape(u)==[m,m], shape(vh)==[n,n] (default); otherwise
         !> they are shape(u)==[m,k] and shape(vh)==[k,n] with k=min(m,n)
         logical(lk), optional, intent(in) :: full_matrices
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
      end subroutine stdlib_linalg_svd_${ri}$
    #:endfor     
  end interface svd

  ! Singular values
  interface svdvals
    !! version: experimental 
    !!
    !! Computes the singular values of a `real` or `complex` 2d matrix.
    !! ([Specification](../page/specs/stdlib_linalg.html#svdvals-compute-the-singular-values-of-a-rank-2-array-matrix))
    !! 
    !!### Summary 
    !! 
    !! Function interface for computing the array of singular values from the singular value decomposition 
    !! of a `real` or `complex` 2d matrix.
    !!
    !!### Description
    !! 
    !! This interface provides methods for computing the singular values a 2d matrix.
    !! Supported data types include `real` and `complex`. The function returns a `real` array of 
    !! singular values, with size [min(m,n)]. 
    !! 
    !!@note The solution is based on LAPACK's singular value decomposition `*GESDD` methods.
    !! 
    !!### Example
    !!
    !!```fortran
    !!    real(sp) :: a(2,3), s(2)
    !!    a = reshape([3,2, 2,3, 2,-2],[2,3])
    !!
    !!    s = svdvals(A)
    !!    print *, 'singular values = ',s
    !!```
    !!   
    #:for rk,rt,ri in RC_KINDS_TYPES
    module function stdlib_linalg_svdvals_${ri}$(a,err) result(s)
     !!### Summary
     !! Compute singular values \(S \) from the singular-value decomposition of a matrix \( A = U \cdot S \cdot \V^T \).
     !!
     !!### Description
     !!
     !! This function returns the array of singular values from the singular value decomposition of a `real` 
     !! or `complex` matrix \( A = U \cdot S \cdot V^T \).
     !!
     !! param: a Input matrix of size [m,n].
     !! param: err [optional] State return flag.      
     !!
     !!### Return value
     !! 
     !! param: s `real` array of size [min(m,n)] returning a list of singular values.
     !!           
         !> Input matrix A[m,n]
         ${rt}$, intent(in), target :: a(:,:)
         !> [optional] state return flag. On error if not requested, the code will stop
         type(linalg_state_type), optional, intent(out) :: err
         !> Array of singular values
         real(${rk}$), allocatable :: s(:)
      end function stdlib_linalg_svdvals_${ri}$
    #:endfor             
  end interface svdvals  


  #! Allow for integer or character norm input: i.e., norm(a,2) or norm(a, '2')
  #:set NORM_INPUT_TYPE    = ["character(len=*)","integer(ilp)"]
  #:set NORM_INPUT_SHORT   = ["char","int"]
  #:set NORM_INPUT_OPTIONS = list(zip(NORM_INPUT_TYPE,NORM_INPUT_SHORT))
  ! Vector norms: function interface
  interface norm
     !! version: experimental 
     !!
     !! Computes the vector norm of a generic-rank array \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#norm-computes-the-vector-norm-of-a-generic-rank-array))
     !! 
     !!### Summary 
     !! Return one of several scalar norm metrics of a `real` or `complex` input array \( A \), 
     !! that can have any rank. For generic rank-n arrays, the scalar norm over the whole 
     !! array is returned by default. If `n>=2` and the optional input dimension `dim` is specified, 
     !! a rank `n-1` array is returned with dimension `dim` collapsed, containing all 1D array norms 
     !! evaluated along dimension `dim` only.
     !! 
     !!
     !!### Description
     !! 
     !! This interface provides methods for computing the vector norm(s) of an array.  
     !! Supported data types include `real` and `complex`. 
     !! Input arrays may have generic rank from 1 to ${MAXRANK}$.
     !!
     !! Norm type input is mandatory, and it is provided via the `order` argument. 
     !! This can be provided as either an `integer` value or a `character` string. 
     !! Allowed metrics are: 
     !! - 1-norm \( \sum_i{ \left|a_i\right| } \): `order` = 1 or '1'    
     !! - Euclidean norm \( \sqrt{\sum_i{ a_i^2 }} \): `order` = 2 or '2'
     !! - p-norm \( \left( \sum_i{ \left|a_i\right|^p }\right) ^{1/p} \): `integer` `order`, order>=3
     !! - Infinity norm \( \max_i{ \left|a_i\right| } \): order = huge(0) or 'inf'
     !! - Minus-infinity norm \( \min_i{ \left|a_i\right| } \): order = -huge(0) or '-inf'
     !! 
     !!### Example
     !!
     !!```fortran
     !!
     !!    real(sp) :: a(3,3), na, rown(3)
     !!    a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
     !!
     !!    ! L2 norm: whole matrix
     !!    na = norm(a, 2)
     !!   
     !!    ! Infinity norm of each row
     !!    rown = norm(a, 'inf', dim=2)
     !!     
     !!```     
     !!
     #:for rk,rt,ri in RC_KINDS_TYPES
     #:for it,ii in NORM_INPUT_OPTIONS
     !> Scalar norms: ${rt}$
     #:for rank in range(1, MAXRANK + 1)        
     pure module function stdlib_linalg_norm_${rank}$D_order_${ii}$_${ri}$(a, order) result(nrm)
        !> Input ${rank}$-d matrix a${ranksuffix(rank)}$
        ${rt}$, intent(in) :: a${ranksuffix(rank)}$
        !> Order of the matrix norm being computed.
        ${it}$, intent(in) :: order
        !> Norm of the matrix.
        real(${rk}$) :: nrm       
     end function stdlib_linalg_norm_${rank}$D_order_${ii}$_${ri}$
     module function stdlib_linalg_norm_${rank}$D_order_err_${ii}$_${ri}$(a, order, err) result(nrm)
        !> Input ${rank}$-d matrix a${ranksuffix(rank)}$
        ${rt}$, intent(in) :: a${ranksuffix(rank)}$
        !> Order of the matrix norm being computed.
        ${it}$, intent(in) :: order
        !> Output state return flag. 
        type(linalg_state_type), intent(out) :: err                         
        !> Norm of the matrix.
        real(${rk}$) :: nrm               
     end function stdlib_linalg_norm_${rank}$D_order_err_${ii}$_${ri}$
     #:endfor
     !> Array norms: ${rt}$
     #:for rank in range(2, MAXRANK + 1)
     pure module function stdlib_linalg_norm_${rank}$D_to_${rank-1}$D_${ii}$_${ri}$(a, order, dim) result(nrm)
        !> Input matrix a[..]
        ${rt}$, intent(in), target :: a${ranksuffix(rank)}$
        !> Order of the matrix norm being computed.
        ${it}$, intent(in) :: order
        !> Dimension the norm is computed along 
        integer(ilp), intent(in) :: dim
        !> Norm of the matrix. (Same shape as `a`, with `dim` dropped).
        real(${rk}$) :: nrm${reduced_shape('a', rank, 'dim')}$   
     end function stdlib_linalg_norm_${rank}$D_to_${rank-1}$D_${ii}$_${ri}$
     module function stdlib_linalg_norm_${rank}$D_to_${rank-1}$D_err_${ii}$_${ri}$(a, order, dim, err) result(nrm)
        !> Input matrix a[..]
        ${rt}$, intent(in), target :: a${ranksuffix(rank)}$
        !> Order of the matrix norm being computed.
        ${it}$, intent(in) :: order
        !> Dimension the norm is computed along
        integer(ilp), intent(in) :: dim
        !> Output state return flag. 
        type(linalg_state_type), intent(out) :: err                                 
        !> Norm of the matrix. (Same shape as `a`, with `dim` dropped).
        real(${rk}$) :: nrm${reduced_shape('a', rank, 'dim')}$     
     end function stdlib_linalg_norm_${rank}$D_to_${rank-1}$D_err_${ii}$_${ri}$
     #:endfor
     #:endfor        
     #:endfor            
  end interface norm  

  !> Vector norm: subroutine interface
  interface get_norm
     !! version: experimental 
     !!
     !! Computes the vector norm of a generic-rank array \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#get-norm-computes-the-vector-norm-of-a-generic-rank-array))
     !! 
     !!### Summary 
     !! Subroutine interface that returns one of several scalar norm metrics of a `real` or `complex` 
     !! input array \( A \), that can have any rank. For generic rank-n arrays, the scalar norm over 
     !! the whole array is returned by default. If `n>=2` and the optional input dimension `dim` is 
     !! specified, a rank `n-1` array is returned with dimension `dim` collapsed, containing all 1D 
     !! array norms evaluated along dimension `dim` only.
     !! 
     !!
     !!### Description
     !! 
     !! This `pure subroutine `interface provides methods for computing the vector norm(s) of an array.  
     !! Supported data types include `real` and `complex`. 
     !! Input arrays may have generic rank from 1 to ${MAXRANK}$.
     !!
     !! Norm type input is mandatory, and it is provided via the `order` argument. 
     !! This can be provided as either an `integer` value or a `character` string. 
     !! Allowed metrics are: 
     !! - 1-norm \( \sum_i{ \left|a_i\right| } \): `order` = 1 or '1'    
     !! - Euclidean norm \( \sqrt{\sum_i{ a_i^2 }} \): `order` = 2 or '2'
     !! - p-norm \( \left( \sum_i{ \left|a_i\right|^p }\right) ^{1/p} \): `integer` `order`, order>=3
     !! - Infinity norm \( \max_i{ \left|a_i\right| } \): order = huge(0) or 'inf'
     !! - Minus-infinity norm \( \min_i{ \left|a_i\right| } \): order = -huge(0) or '-inf'
     !!     
     !!### Example
     !!
     !!```fortran
     !!
     !!    real(sp) :: a(3,3), na, rown(3)
     !!    type(linalg_state_type) :: err
     !!    a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
     !!
     !!    ! L2 norm: whole matrix
     !!    call get_norm(a, na, 2)
     !!   
     !!    ! Infinity norms of each row, with error control
     !!    call get_norm(a, rown, 'inf', dim=2, err=err)     
     !!     
     !!```     
     !!     
     #:for rk,rt,ri in RC_KINDS_TYPES
     #:for it,ii in NORM_INPUT_OPTIONS
        !> Scalar norms: ${rt}$
        #:for rank in range(1, MAXRANK + 1)                    
        pure module subroutine norm_${rank}$D_${ii}$_${ri}$(a, nrm, order, err)
           !> Input ${rank}$-d matrix a${ranksuffix(rank)}$
           ${rt}$, intent(in), target :: a${ranksuffix(rank)}$
           !> Norm of the matrix.
           real(${rk}$), intent(out) :: nrm
           !> Order of the matrix norm being computed.
           ${it}$, intent(in) :: order
           !> [optional] state return flag. On error if not requested, the code will stop
           type(linalg_state_type), intent(out), optional :: err   
        end subroutine norm_${rank}$D_${ii}$_${ri}$
        #:endfor
        !> Array norms: ${rt}$
        #:for rank in range(2, MAXRANK + 1)
        pure module subroutine norm_${rank}$D_to_${rank-1}$D_${ii}$_${ri}$(a, nrm, order, dim, err)
           !> Input matrix a[..]
           ${rt}$, intent(in) :: a${ranksuffix(rank)}$
           !> Dimension the norm is computed along
           integer(ilp), intent(in) :: dim        
           !> Norm of the matrix. (Same shape as `a`, with `dim` dropped).
           real(${rk}$), intent(out) :: nrm${reduced_shape('a', rank, 'dim')}$     
           !> Order of the matrix norm being computed.
           ${it}$, intent(in) :: order
           !> [optional] state return flag. On error if not requested, the code will stop
           type(linalg_state_type), intent(out), optional :: err           
        end subroutine  norm_${rank}$D_to_${rank-1}$D_${ii}$_${ri}$
        #:endfor
     #:endfor
     #:endfor
  end interface get_norm

  !> Matrix norms: function interface
  interface mnorm
     !! version: experimental 
     !!
     !! Computes the matrix norm of a generic-rank array \( A \). 
     !! ([Specification](../page/specs/stdlib_linalg.html#mnorm-computes-the-matrix-norm-of-a-generic-rank-array))
     !! 
     !!### Summary 
     !! Return one of several matrix norm metrics of a `real` or `complex` input array \( A \), 
     !! that can have rank 2 or higher. For rank-2 arrays, the matrix norm is returned.
     !! If rank>2 and the optional input dimensions `dim` are specified, 
     !! a rank `n-2` array is returned with dimensions `dim(1),dim(2)` collapsed, containing all 
     !! matrix norms evaluated over the specified dimensions only. `dim==[1,2]` are assumed as default
     !! dimensions if not specified.
     !! 
     !!### Description
     !! 
     !! This interface provides methods for computing the matrix norm(s) of an array.  
     !! Supported data types include `real` and `complex`. 
     !! Input arrays must have rank >= 2.
     !!
     !! Norm type input is optional, and it is provided via the `order` argument. 
     !! This can be provided as either an `integer` value or a `character` string. 
     !! Allowed metrics are: 
     !! - 1-norm: `order` = 1 or '1'    
     !! - 2-norm: `order` = 2 or '2'
     !! - Euclidean/Frobenius: `order` = 'Euclidean','Frobenius', or argument not specified
     !! - Infinity norm: `order` = huge(0) or 'Inf'
     !! 
     !! If an invalid norm type is provided, the routine returns an error state.
     !!
     !!### Example
     !!
     !!```fortran
     !!    real(sp) :: a(3,3), na
     !!    real(sp) :: b(3,3,4), nb(4)  ! Array of 4 3x3 matrices
     !!    a = reshape([1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3])
     !!    
     !!    ! Euclidean/Frobenius norm of single matrix
     !!    na = mnorm(a)
     !!    na = mnorm(a, 'Euclidean')
     !!   
     !!    ! 1-norm of each 3x3 matrix in b
     !!    nb = mnorm(b, 1, dim=[1,2])
     !!     
     !!    ! Infinity-norm 
     !!    na = mnorm(b, 'inf', dim=[3,2])
     !!```     
     !!
      #:for rk,rt,ri in RC_KINDS_TYPES
      #:for it,ii in NORM_INPUT_OPTIONS   
      
      !> Matrix norms: ${rt}$ rank-2 arrays
      module function matrix_norm_${ii}$_${ri}$(a, order, err) result(nrm)
        !> Input matrix a(m,n)
        ${rt}$, intent(in), target :: a(:,:)
        !> Norm of the matrix.        
        real(${rk}$) :: nrm
        !> Order of the matrix norm being computed.
        ${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
        !> [optional] state return flag. On error if not requested, the code will stop
        type(linalg_state_type), intent(out), optional :: err      
      end function matrix_norm_${ii}$_${ri}$
      
      !> Matrix norms: ${rt}$ higher rank arrays
      #:for rank in range(3, MAXRANK + 1)
      module function matrix_norm_${rank}$D_to_${rank-2}$D_${ii}$_${ri}$(a, order, dim, err) result(nrm)
          !> Input matrix a(m,n)
          ${rt}$, intent(in), contiguous, target :: a${ranksuffix(rank)}$
          !> Norm of the matrix.        
          real(${rk}$), allocatable :: nrm${ranksuffix(rank-2)}$
          !> Order of the matrix norm being computed.
          ${it}$, #{if 'integer' in it}#optional, #{endif}#intent(in) :: order
          !> [optional] dimensions of the sub-matrices the norms should be evaluated at (default = [1,2])
          integer(ilp), optional, intent(in) :: dim(2)
          !> [optional] state return flag. On error if not requested, the code will stop
          type(linalg_state_type), intent(out), optional :: err        
      end function matrix_norm_${rank}$D_to_${rank-2}$D_${ii}$_${ri}$
      #:endfor            
      #:endfor
      #:endfor            
  end interface mnorm

contains


    !> Version: experimental
    !>
    !> Constructs the identity matrix.
    !> ([Specification](../page/specs/stdlib_linalg.html#eye-construct-the-identity-matrix))
    #:for k1, t1 in RCI_KINDS_TYPES
    pure function eye_${t1[0]}$${k1}$(dim1, dim2, mold) result(result)

        integer, intent(in) :: dim1
        integer, intent(in), optional :: dim2
        ${t1}$, intent(in) #{if t1 == 'real(dp)'}#, optional #{endif}#:: mold        
        ${t1}$, allocatable :: result(:, :)

        integer :: dim2_
        integer :: i

        dim2_ = optval(dim2, dim1)
        allocate(result(dim1, dim2_))
        
        result = 0
        do i = 1, min(dim1, dim2_)
            result(i, i) = 1
        end do

    end function eye_${t1[0]}$${k1}$
    #:endfor

    #:for k1, t1 in RCI_KINDS_TYPES
      function trace_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        ${t1}$ :: res
        integer :: i
        res = 0
        do i = 1, minval(shape(A))
          res = res + A(i,i)
        end do
      end function trace_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in RCI_KINDS_TYPES
      pure function is_square_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        res = (size(A,1) == size(A,2))
      end function is_square_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in RCI_KINDS_TYPES
      pure function is_diagonal_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        ${t1}$, parameter :: zero = 0 !zero of relevant type
        integer :: m, n, o, i, j
        m = size(A,1)
        n = size(A,2)
        do j = 1, n !loop over all columns
            o = min(j-1,m) !index of row above diagonal (or last row)
            do i = 1, o !loop over rows above diagonal
                if (A(i,j) /= zero) then
                  res = .false.
                  return
                end if
            end do
            do i = o+2, m !loop over rows below diagonal
                if (A(i,j) /= zero) then
                  res = .false.
                  return
                end if
            end do
        end do
        res = .true. !otherwise A is diagonal
      end function is_diagonal_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in RCI_KINDS_TYPES
      pure function is_symmetric_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        integer :: n, i, j
        if (.not. is_square(A)) then
           res = .false.
           return !nonsquare matrices cannot be symmetric
        end if
        n = size(A,1) !symmetric dimension of A
        do j = 1, n !loop over all columns
            do i = 1, j-1 !loop over all rows above diagonal
                if (A(i,j) /= A(j,i)) then
                  res = .false.
                  return
                end if
            end do
        end do
        res = .true. !otherwise A is symmetric
      end function is_symmetric_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in RCI_KINDS_TYPES
      pure function is_skew_symmetric_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        integer :: n, i, j
        if (.not. is_square(A)) then
           res = .false.
           return !nonsquare matrices cannot be skew-symmetric
        end if
        n = size(A,1) !symmetric dimension of A
        do j = 1, n !loop over all columns
            do i = 1, j !loop over all rows above diagonal (and diagonal)
                if (A(i,j) /= -A(j,i)) then
                  res = .false.
                  return
                end if
            end do
        end do
        res = .true. !otherwise A is skew-symmetric
      end function is_skew_symmetric_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in (REAL_KINDS_TYPES + INT_KINDS_TYPES)
      pure function is_hermitian_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        res = is_symmetric(A) !symmetry and Hermiticity are equivalent for real matrices
      end function is_hermitian_${t1[0]}$${k1}$
    #:endfor
    #:for k1, t1 in CMPLX_KINDS_TYPES
      pure function is_hermitian_${t1[0]}$${k1}$(A) result(res)
        ${t1}$, intent(in) :: A(:,:)
        logical :: res
        integer :: n, i, j
        if (.not. is_square(A)) then
           res = .false.
           return !nonsquare matrices cannot be Hermitian
        end if
        n = size(A,1) !symmetric dimension of A
        do j = 1, n !loop over all columns
            do i = 1, j !loop over all rows above diagonal (and diagonal)
                if (A(i,j) /= conjg(A(j,i))) then
                  res = .false.
                  return
                end if
            end do
        end do
        res = .true. !otherwise A is Hermitian
      end function is_hermitian_${t1[0]}$${k1}$
    #:endfor

    #:for k1, t1 in RCI_KINDS_TYPES
      pure module function hermitian_${t1[0]}$${k1}$(a) result(ah)
        ${t1}$, intent(in) :: a(:,:)
        ${t1}$ :: ah(size(a, 2), size(a, 1))
        #:if t1.startswith('complex')
        ah = conjg(transpose(a))
        #:else
        ah = transpose(a)
        #:endif
      end function hermitian_${t1[0]}$${k1}$
    #:endfor

    #:for k1, t1 in RCI_KINDS_TYPES
      function is_triangular_${t1[0]}$${k1}$(A,uplo) result(res)
        ${t1}$, intent(in) :: A(:,:)
        character, intent(in) :: uplo
        logical :: res
        ${t1}$, parameter :: zero = 0 !zero of relevant type
        integer :: m, n, o, i, j
        m = size(A,1)
        n = size(A,2)
        if ((uplo == 'u') .or. (uplo == 'U')) then !check for upper triangularity
          do j = 1, n !loop over all columns
              o = min(j-1,m) !index of row above diagonal (or last row)
              do i = o+2, m !loop over rows below diagonal
                  if (A(i,j) /= zero) then
                    res = .false.
                    return
                  end if
              end do
          end do
        else if ((uplo == 'l') .or. (uplo == 'L')) then !check for lower triangularity
          do j=1,n !loop over all columns
              o = min(j-1,m) !index of row above diagonal (or last row)
              do i=1,o !loop over rows above diagonal
                  if (A(i,j) /= zero) then
                    res = .false.
                    return
                  end if
              end do
           end do
        else
           call error_stop("ERROR (is_triangular): second argument must be one of {'u','U','l','L'}")
        end if
     
        res = .true. !otherwise A is triangular of the requested type
      end function is_triangular_${t1[0]}$${k1}$
    #:endfor


    #:for k1, t1 in RCI_KINDS_TYPES
      function is_hessenberg_${t1[0]}$${k1}$(A,uplo) result(res)
        ${t1}$, intent(in) :: A(:,:)
        character, intent(in) :: uplo
        logical :: res
        ${t1}$, parameter :: zero = 0 !zero of relevant type
        integer :: m, n, o, i, j
        m = size(A,1)
        n = size(A,2)
        if ((uplo == 'u') .or. (uplo == 'U')) then !check for upper Hessenberg
          do j = 1, n !loop over all columns
              o = min(j-2,m) !index of row two above diagonal (or last row)
              do i = o+4, m !loop over rows two or more below main diagonal
                  if (A(i,j) /= zero) then
                    res = .false.
                    return
                  end if
              end do
          end do
        else if ((uplo == 'l') .or. (uplo == 'L')) then !check for lower Hessenberg
          do j = 1, n !loop over all columns
              o = min(j-2,m) !index of row two above diagonal (or last row)
              do i = 1, o !loop over rows one or more above main diagonal
                  if (A(i,j) /= zero) then
                    res = .false.
                    return
                  end if
              end do
           end do
        else
           call error_stop("ERROR (is_hessenberg): second argument must be one of {'u','U','l','L'}")
        end if
        res = .true. !otherwise A is Hessenberg of the requested type
      end function is_hessenberg_${t1[0]}$${k1}$
    #:endfor
    
end module stdlib_linalg