diff --git a/doc/specs/stdlib_linalg.md b/doc/specs/stdlib_linalg.md index 7ca3b9198..749aeed10 100644 --- a/doc/specs/stdlib_linalg.md +++ b/doc/specs/stdlib_linalg.md @@ -176,7 +176,7 @@ Trace of a matrix (rank-2 array) ### Syntax -`result = [stdlib_linalg(module):trace(interface)](A)` +`result = [[stdlib_linalg(module):trace(interface)]](A)` ### Arguments @@ -234,3 +234,271 @@ program demo_outer_product !A = reshape([3., 6., 9., 4., 8., 12.], [3,2]) end program demo_outer_product ``` + +## `is_square` - Checks if a matrix is square + +### Status + +Experimental + +### Description + +Checks if a matrix is square + +### Syntax + +`d = [[stdlib_linalg(module):is_square(interface)]](A)` + +### Arguments + +`A`: Shall be a rank-2 array + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is square, and `.false.` otherwise. + +### Example + +```fortran +program demo_is_square + use stdlib_linalg, only: is_square + implicit none + real :: A(2,2), B(3,2) + logical :: res + A = reshape([1., 2., 3., 4.], shape(A)) + B = reshape([1., 2., 3., 4., 5., 6.], shape(B)) + res = is_square(A) ! returns .true. + res = is_square(B) ! returns .false. +end program demo_is_square +``` + +## `is_diagonal` - Checks if a matrix is diagonal + +### Status + +Experimental + +### Description + +Checks if a matrix is diagonal + +### Syntax + +`d = [[stdlib_linalg(module):is_diagonal(interface)]](A)` + +### Arguments + +`A`: Shall be a rank-2 array + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is diagonal, and `.false.` otherwise. +Note that nonsquare matrices may be diagonal, so long as `a_ij = 0` when `i /= j`. + +### Example + +```fortran +program demo_is_diagonal + use stdlib_linalg, only: is_diagonal + implicit none + real :: A(2,2), B(2,2) + logical :: res + A = reshape([1., 0., 0., 4.], shape(A)) + B = reshape([1., 0., 3., 4.], shape(B)) + res = is_diagonal(A) ! returns .true. + res = is_diagonal(B) ! returns .false. +end program demo_is_diagonal +``` + +## `is_symmetric` - Checks if a matrix is symmetric + +### Status + +Experimental + +### Description + +Checks if a matrix is symmetric + +### Syntax + +`d = [[stdlib_linalg(module):is_symmetric(interface)]](A)` + +### Arguments + +`A`: Shall be a rank-2 array + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is symmetric, and `.false.` otherwise. + +### Example + +```fortran +program demo_is_symmetric + use stdlib_linalg, only: is_symmetric + implicit none + real :: A(2,2), B(2,2) + logical :: res + A = reshape([1., 3., 3., 4.], shape(A)) + B = reshape([1., 0., 3., 4.], shape(B)) + res = is_symmetric(A) ! returns .true. + res = is_symmetric(B) ! returns .false. +end program demo_is_symmetric +``` + +## `is_skew_symmetric` - Checks if a matrix is skew-symmetric + +### Status + +Experimental + +### Description + +Checks if a matrix is skew-symmetric + +### Syntax + +`d = [[stdlib_linalg(module):is_skew_symmetric(interface)]](A)` + +### Arguments + +`A`: Shall be a rank-2 array + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is skew-symmetric, and `.false.` otherwise. + +### Example + +```fortran +program demo_is_skew_symmetric + use stdlib_linalg, only: is_skew_symmetric + implicit none + real :: A(2,2), B(2,2) + logical :: res + A = reshape([0., -3., 3., 0.], shape(A)) + B = reshape([0., 3., 3., 0.], shape(B)) + res = is_skew_symmetric(A) ! returns .true. + res = is_skew_symmetric(B) ! returns .false. +end program demo_is_skew_symmetric +``` + +## `is_hermitian` - Checks if a matrix is Hermitian + +### Status + +Experimental + +### Description + +Checks if a matrix is Hermitian + +### Syntax + +`d = [[stdlib_linalg(module):is_hermitian(interface)]](A)` + +### Arguments + +`A`: Shall be a rank-2 array + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is Hermitian, and `.false.` otherwise. + +### Example + +```fortran +program demo_is_hermitian + use stdlib_linalg, only: is_hermitian + implicit none + complex :: A(2,2), B(2,2) + logical :: res + A = reshape([cmplx(1.,0.), cmplx(3.,-1.), cmplx(3.,1.), cmplx(4.,0.)], shape(A)) + B = reshape([cmplx(1.,0.), cmplx(3.,1.), cmplx(3.,1.), cmplx(4.,0.)], shape(B)) + res = is_hermitian(A) ! returns .true. + res = is_hermitian(B) ! returns .false. +end program demo_is_hermitian +``` + +## `is_triangular` - Checks if a matrix is triangular + +### Status + +Experimental + +### Description + +Checks if a matrix is triangular + +### Syntax + +`d = [[stdlib_linalg(module):is_triangular(interface)]](A,uplo)` + +### Arguments + +`A`: Shall be a rank-2 array + +`uplo`: Shall be a single character from `{'u','U','l','L'}` + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is the type of triangular specified by `uplo` (upper or lower), and `.false.` otherwise. +Note that the definition of triangular used in this implementation allows nonsquare matrices to be triangular. +Specifically, upper triangular matrices satisfy `a_ij = 0` when `j < i`, and lower triangular matrices satisfy `a_ij = 0` when `j > i`. + +### Example + +```fortran +program demo_is_triangular + use stdlib_linalg, only: is_triangular + implicit none + real :: A(3,3), B(3,3) + logical :: res + A = reshape([1., 0., 0., 4., 5., 0., 7., 8., 9.], shape(A)) + B = reshape([1., 0., 3., 4., 5., 0., 7., 8., 9.], shape(B)) + res = is_triangular(A,'u') ! returns .true. + res = is_triangular(B,'u') ! returns .false. +end program demo_is_triangular +``` + +## `is_hessenberg` - Checks if a matrix is hessenberg + +### Status + +Experimental + +### Description + +Checks if a matrix is Hessenberg + +### Syntax + +`d = [[stdlib_linalg(module):is_hessenberg(interface)]](A,uplo)` + +### Arguments + +`A`: Shall be a rank-2 array + +`uplo`: Shall be a single character from `{'u','U','l','L'}` + +### Return value + +Returns a `logical` scalar that is `.true.` if the input matrix is the type of Hessenberg specified by `uplo` (upper or lower), and `.false.` otherwise. +Note that the definition of Hessenberg used in this implementation allows nonsquare matrices to be Hessenberg. +Specifically, upper Hessenberg matrices satisfy `a_ij = 0` when `j < i-1`, and lower Hessenberg matrices satisfy `a_ij = 0` when `j > i+1`. + +### Example + +```fortran +program demo_is_hessenberg + use stdlib_linalg, only: is_hessenberg + implicit none + real :: A(3,3), B(3,3) + logical :: res + A = reshape([1., 2., 0., 4., 5., 6., 7., 8., 9.], shape(A)) + B = reshape([1., 2., 3., 4., 5., 6., 7., 8., 9.], shape(B)) + res = is_hessenberg(A,'u') ! returns .true. + res = is_hessenberg(B,'u') ! returns .false. +end program demo_is_hessenberg +``` diff --git a/src/Makefile.manual b/src/Makefile.manual index c271b6aa6..424bb4c8f 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -134,10 +134,13 @@ stdlib_io_npy_save.o: \ stdlib_strings.o stdlib_linalg.o: \ stdlib_kinds.o \ - stdlib_optval.o + stdlib_optval.o \ + stdlib_error.o stdlib_linalg_diag.o: \ stdlib_linalg.o \ stdlib_kinds.o +stdlib_linalg_outer_product.o: \ + stdlib_linalg.o stdlib_logger.o: stdlib_ascii.o stdlib_optval.o stdlib_optval.o: stdlib_kinds.o stdlib_quadrature.o: stdlib_kinds.o @@ -224,7 +227,6 @@ stdlib_math_logspace.o: \ stdlib_math_linspace.o stdlib_math_arange.o: \ stdlib_math.o -stdlib_linalg_outer_product.o: stdlib_linalg.o stdlib_math_is_close.o: \ stdlib_math.o stdlib_math_all_close.o: \ diff --git a/src/stdlib_linalg.fypp b/src/stdlib_linalg.fypp index c309b71a2..bc1017f0a 100644 --- a/src/stdlib_linalg.fypp +++ b/src/stdlib_linalg.fypp @@ -5,6 +5,7 @@ module stdlib_linalg !! ([Specification](../page/specs/stdlib_linalg.html)) use stdlib_kinds, only: sp, dp, xdp, qp, & int8, int16, int32, int64 + use stdlib_error, only: error_stop use stdlib_optval, only: optval implicit none private @@ -13,12 +14,20 @@ module stdlib_linalg public :: eye public :: trace public :: outer_product + public :: is_square + public :: is_diagonal + public :: is_symmetric + public :: is_skew_symmetric + public :: is_hermitian + public :: is_triangular + public :: is_hessenberg interface diag !! version: experimental !! !! Creates a diagonal array or extract the diagonal elements of an array - !! ([Specification](../page/specs/stdlib_linalg.html#description)) + !! ([Specification](../page/specs/stdlib_linalg.html# + !! diag-create-a-diagonal-array-or-extract-the-diagonal-elements-of-an-array)) ! ! Vector to matrix ! @@ -60,7 +69,8 @@ module stdlib_linalg !! version: experimental !! !! Computes the trace of a matrix - !! ([Specification](../page/specs/stdlib_linalg.html#description_2)) + !! ([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 @@ -72,7 +82,8 @@ module stdlib_linalg !! version: experimental !! !! Computes the outer product of two vectors, returning a rank-2 array - !! ([Specification](../page/specs/stdlib_linalg.html#description_3)) + !! ([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(:) @@ -81,8 +92,100 @@ module stdlib_linalg #:endfor end interface outer_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 + + + ! 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 + contains + !> Version: experimental !> !> Constructs the identity matrix. @@ -118,4 +221,192 @@ contains 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 + 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 diff --git a/src/tests/linalg/CMakeLists.txt b/src/tests/linalg/CMakeLists.txt index d13abb213..4a315f545 100644 --- a/src/tests/linalg/CMakeLists.txt +++ b/src/tests/linalg/CMakeLists.txt @@ -1,8 +1,9 @@ set( fppFiles "test_linalg.fypp" + "test_linalg_matrix_property_checks.fypp" ) fypp_f90("${fyppFlags}" "${fppFiles}" outFiles) ADDTEST(linalg) - +ADDTEST(linalg_matrix_property_checks) diff --git a/src/tests/linalg/Makefile.manual b/src/tests/linalg/Makefile.manual index 89d905d89..db51c62d4 100644 --- a/src/tests/linalg/Makefile.manual +++ b/src/tests/linalg/Makefile.manual @@ -1,6 +1,9 @@ SRCFYPP = \ - test_linalg.fypp + test_linalg.fypp \ + test_linalg_matrix_property_checks.fypp + SRCGEN = $(SRCFYPP:.fypp=.f90) + PROGS_SRC = \ $(SRCGEN) diff --git a/src/tests/linalg/test_linalg_matrix_property_checks.fypp b/src/tests/linalg/test_linalg_matrix_property_checks.fypp new file mode 100644 index 000000000..b2e8b2116 --- /dev/null +++ b/src/tests/linalg/test_linalg_matrix_property_checks.fypp @@ -0,0 +1,547 @@ +#:include "common.fypp" +#:set RCI_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES + INT_KINDS_TYPES + +module test_linalg_matrix_property_checks + use testdrive, only : new_unittest, unittest_type, error_type, check, skip_test + use stdlib_kinds, only: sp, dp, xdp, qp, int8, int16, int32, int64 + use stdlib_linalg, only: is_square ,is_diagonal, is_symmetric, & + is_skew_symmetric, is_hermitian, is_triangular, is_hessenberg + + implicit none + + real(sp), parameter :: sptol = 1000 * epsilon(1._sp) + real(dp), parameter :: dptol = 1000 * epsilon(1._dp) +#:if WITH_QP + real(qp), parameter :: qptol = 1000 * epsilon(1._qp) +#:endif + + #! create new list that contains test subroutine suffix (rsp, cdp, int64, etc.) + #! alongside kind and type + #:set RCI_KINDS_TYPES_SUFFIXES = [] + #:for k1, t1 in RCI_KINDS_TYPES + #:if t1[0] == 'i' + #:set SUFFIX_START = '' + #:else + #:set SUFFIX_START = t1[0] + #:endif + $:RCI_KINDS_TYPES_SUFFIXES.append((k1,t1,SUFFIX_START+k1)) + #:endfor + + +contains + + + !> Collect all exported unit tests + subroutine collect_linalg_matrix_property_checks(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + + #:set IMPLEMENTED_TESTS = ['is_square','is_diagonal','is_symmetric','is_skew_symmetric', & + 'is_hermitian', 'is_triangular', 'is_hessenberg'] + + #:set NUM_TESTS = int(len(IMPLEMENTED_TESTS)*len(RCI_KINDS_TYPES_SUFFIXES)) + + #! set testsuite dynamically + testsuite = [ & + #:set TESTS_WRITTEN = 0 + #:for cur_test in IMPLEMENTED_TESTS + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + #! note that one has to use set directives to increment variable + #:set TESTS_WRITTEN = TESTS_WRITTEN + 1 + #! last test in list should not have comma + #:if TESTS_WRITTEN < NUM_TESTS + new_unittest("${cur_test}$_${s1}$", test_${cur_test}$_${s1}$), & + #:else + new_unittest("${cur_test}$_${s1}$", test_${cur_test}$_${s1}$) & + #:endif + #:endfor + #:endfor + ] + end subroutine collect_linalg_matrix_property_checks + + + !is_square + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_square_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true(2,2), A_false(2,3) + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + A_true = reshape([1.,2.,3.,4.],[2,2]) + A_false = reshape([1.,2.,3.,4.,5.,6.],[2,3]) + #:elif s1[0] == 'c' + A_true = reshape([cmplx(1.,0.),cmplx(2.,1.),cmplx(3.,0.),cmplx(4.,1.)],[2,2]) + A_false = reshape([cmplx(1.,0.),cmplx(2.,1.),cmplx(3.,0.), & + cmplx(4.,1.),cmplx(5.,0.),cmplx(6.,1.)],[2,3]) + #:elif s1[0] == 'i' + A_true = reshape([1,2,3,4],[2,2]) + A_false = reshape([1,2,3,4,5,6],[2,3]) + #:endif + + #! error check calls are type/kind independent + call check(error, is_square(A_true), & + "is_square(A_true) failed.") + if (allocated(error)) return + call check(error, (.not. is_square(A_false)), & + "(.not. is_square(A_false)) failed.") + if (allocated(error)) return + end subroutine test_is_square_${s1}$ + #:endfor + + + !is_diagonal + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_diagonal_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true_s(2,2), A_false_s(2,2) !square matrices + ${t1}$ :: A_true_sf(2,3), A_false_sf(2,3) !short and fat matrices + ${t1}$ :: A_true_ts(3,2), A_false_ts(3,2) !tall and skinny matrices + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + A_true_s = reshape([1.,0.,0.,4.],[2,2]) + A_false_s = reshape([1.,0.,3.,4.],[2,2]) + A_true_sf = reshape([1.,0.,0.,4.,0.,0.],[2,3]) + A_false_sf = reshape([1.,0.,3.,4.,0.,0.],[2,3]) + A_true_ts = reshape([1.,0.,0.,0.,5.,0.],[3,2]) + A_false_ts = reshape([1.,0.,0.,0.,5.,6.],[3,2]) + #:elif s1[0] == 'c' + A_true_s = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(0.,0.),cmplx(4.,1.)],[2,2]) + A_false_s = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(3.,1.),cmplx(4.,1.)],[2,2]) + A_true_sf = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(0.,0.),cmplx(4.,1.), & + cmplx(0.,0.),cmplx(0.,0.)],[2,3]) + A_false_sf = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(3.,1.),cmplx(4.,1.), & + cmplx(0.,0.),cmplx(0.,0.)],[2,3]) + A_true_ts = reshape([cmplx(1.,1.),cmplx(0.,0.),cmplx(0.,0.), & + cmplx(0.,0.),cmplx(5.,1.),cmplx(0.,0.)],[3,2]) + A_false_ts = reshape([cmplx(1.,1.),cmplx(0.,0.),cmplx(0.,0.), & + cmplx(0.,0.),cmplx(5.,1.),cmplx(6.,1.)],[3,2]) + #:elif s1[0] == 'i' + A_true_s = reshape([1,0,0,4],[2,2]) + A_false_s = reshape([1,0,3,4],[2,2]) + A_true_sf = reshape([1,0,0,4,0,0],[2,3]) + A_false_sf = reshape([1,0,3,4,0,0],[2,3]) + A_true_ts = reshape([1,0,0,0,5,0],[3,2]) + A_false_ts = reshape([1,0,0,0,5,6],[3,2]) + #:endif + + #! error check calls are type/kind independent + call check(error, is_diagonal(A_true_s), & + "is_diagonal(A_true_s) failed.") + if (allocated(error)) return + call check(error, (.not. is_diagonal(A_false_s)), & + "(.not. is_diagonal(A_false_s)) failed.") + if (allocated(error)) return + call check(error, is_diagonal(A_true_sf), & + "is_diagonal(A_true_sf) failed.") + if (allocated(error)) return + call check(error, (.not. is_diagonal(A_false_sf)), & + "(.not. is_diagonal(A_false_sf)) failed.") + if (allocated(error)) return + call check(error, is_diagonal(A_true_ts), & + "is_diagonal(A_true_ts) failed.") + if (allocated(error)) return + call check(error, (.not. is_diagonal(A_false_ts)), & + "(.not. is_diagonal(A_false_ts)) failed.") + if (allocated(error)) return + end subroutine test_is_diagonal_${s1}$ + #:endfor + + + !is_symmetric + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_symmetric_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true(2,2), A_false_1(2,2), A_false_2(3,2) + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + A_true = reshape([1.,2.,2.,4.],[2,2]) + A_false_1 = reshape([1.,2.,3.,4.],[2,2]) + A_false_2 = reshape([1.,2.,3.,2.,5.,6.],[3,2]) !nonsquare matrix + #:elif s1[0] == 'c' + A_true = reshape([cmplx(1.,1.),cmplx(2.,1.), & + cmplx(2.,1.),cmplx(4.,1.)],[2,2]) + A_false_1 = reshape([cmplx(1.,1.),cmplx(2.,1.), & + cmplx(3.,1.),cmplx(4.,1.)],[2,2]) + A_false_2 = reshape([cmplx(1.,1.),cmplx(2.,1.),cmplx(3.,1.), & + cmplx(2.,1.),cmplx(5.,1.),cmplx(6.,2.)],[3,2]) !nonsquare matrix + #:elif s1[0] == 'i' + A_true = reshape([1,2,2,4],[2,2]) + A_false_1 = reshape([1,2,3,4],[2,2]) + A_false_2 = reshape([1,2,3,2,5,6],[3,2]) !nonsquare matrix + #:endif + + #! error check calls are type/kind independent + call check(error, is_symmetric(A_true), & + "is_symmetric(A_true) failed.") + if (allocated(error)) return + call check(error, (.not. is_symmetric(A_false_1)), & + "(.not. is_symmetric(A_false_1)) failed.") + if (allocated(error)) return + call check(error, (.not. is_symmetric(A_false_2)), & + "(.not. is_symmetric(A_false_2)) failed.") + if (allocated(error)) return + end subroutine test_is_symmetric_${s1}$ + #:endfor + + + !is_skew_symmetric + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_skew_symmetric_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true(2,2), A_false_1(2,2), A_false_2(3,2) + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + A_true = reshape([0.,2.,-2.,0.],[2,2]) + A_false_1 = reshape([0.,2.,-3.,0.],[2,2]) + A_false_2 = reshape([0.,2.,3.,-2.,0.,6.],[3,2]) !nonsquare matrix + #:elif s1[0] == 'c' + A_true = reshape([cmplx(0.,0.),cmplx(2.,1.), & + -cmplx(2.,1.),cmplx(0.,0.)],[2,2]) + A_false_1 = reshape([cmplx(0.,0.),cmplx(2.,1.), & + -cmplx(3.,1.),cmplx(0.,0.)],[2,2]) + A_false_2 = reshape([cmplx(0.,0.),cmplx(2.,1.),cmplx(3.,0.), & + -cmplx(2.,1.),cmplx(0.,0.),cmplx(6.,0.)],[3,2]) !nonsquare matrix + #:elif s1[0] == 'i' + A_true = reshape([0,2,-2,0],[2,2]) + A_false_1 = reshape([0,2,-3,0],[2,2]) + A_false_2 = reshape([0,2,3,-2,0,6],[3,2]) !nonsquare matrix + #:endif + + #! error check calls are type/kind independent + call check(error, is_skew_symmetric(A_true), & + "is_skew_symmetric(A_true) failed.") + if (allocated(error)) return + call check(error, (.not. is_skew_symmetric(A_false_1)), & + "(.not. is_skew_symmetric(A_false_1)) failed.") + if (allocated(error)) return + call check(error, (.not. is_skew_symmetric(A_false_2)), & + "(.not. is_skew_symmetric(A_false_2)) failed.") + if (allocated(error)) return + end subroutine test_is_skew_symmetric_${s1}$ + #:endfor + + + !is_hermitian + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_hermitian_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true(2,2), A_false_1(2,2), A_false_2(3,2) + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + A_true = reshape([1.,2.,2.,4.],[2,2]) + A_false_1 = reshape([1.,2.,3.,4.],[2,2]) + A_false_2 = reshape([1.,2.,3.,2.,5.,6.],[3,2]) !nonsquare matrix + #:elif s1[0] == 'c' + A_true = reshape([cmplx(1.,0.),cmplx(2.,-1.), & + cmplx(2.,1.),cmplx(4.,0.)],[2,2]) + A_false_1 = reshape([cmplx(1.,0.),cmplx(2.,-1.), & + cmplx(3.,1.),cmplx(4.,0.)],[2,2]) + A_false_2 = reshape([cmplx(1.,0.),cmplx(2.,-1.),cmplx(3.,-1.), & + cmplx(2.,1.),cmplx(5.,0.),cmplx(6.,-1.)],[3,2]) !nonsquare matrix + #:elif s1[0] == 'i' + A_true = reshape([1,2,2,4],[2,2]) + A_false_1 = reshape([1,2,3,4],[2,2]) + A_false_2 = reshape([1,2,3,2,5,6],[3,2]) !nonsquare matrix + #:endif + + #! error check calls are type/kind independent + call check(error, is_hermitian(A_true), & + "is_hermitian(A_true) failed.") + if (allocated(error)) return + call check(error, (.not. is_hermitian(A_false_1)), & + "(.not. is_hermitian(A_false_1)) failed.") + if (allocated(error)) return + call check(error, (.not. is_hermitian(A_false_2)), & + "(.not. is_hermitian(A_false_2)) failed.") + if (allocated(error)) return + end subroutine test_is_hermitian_${s1}$ + #:endfor + + + !is_triangular + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_triangular_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true_s_u(2,2), A_false_s_u(2,2) !square matrices (upper triangular) + ${t1}$ :: A_true_sf_u(2,3), A_false_sf_u(2,3) !short and fat matrices + ${t1}$ :: A_true_ts_u(3,2), A_false_ts_u(3,2) !tall and skinny matrices + ${t1}$ :: A_true_s_l(2,2), A_false_s_l(2,2) !square matrices (lower triangular) + ${t1}$ :: A_true_sf_l(2,3), A_false_sf_l(2,3) !short and fat matrices + ${t1}$ :: A_true_ts_l(3,2), A_false_ts_l(3,2) !tall and skinny matrices + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + !upper triangular + A_true_s_u = reshape([1.,0.,3.,4.],[2,2]) + A_false_s_u = reshape([1.,2.,0.,4.],[2,2]) + A_true_sf_u = reshape([1.,0.,3.,4.,0.,6.],[2,3]) + A_false_sf_u = reshape([1.,2.,3.,4.,0.,6.],[2,3]) + A_true_ts_u = reshape([1.,0.,0.,4.,5.,0.],[3,2]) + A_false_ts_u = reshape([1.,0.,0.,4.,5.,6.],[3,2]) + !lower triangular + A_true_s_l = reshape([1.,2.,0.,4.],[2,2]) + A_false_s_l = reshape([1.,0.,3.,4.],[2,2]) + A_true_sf_l = reshape([1.,2.,0.,4.,0.,0.],[2,3]) + A_false_sf_l = reshape([1.,2.,3.,4.,0.,0.],[2,3]) + A_true_ts_l = reshape([1.,2.,3.,0.,5.,6.],[3,2]) + A_false_ts_l = reshape([1.,2.,3.,4.,5.,6.],[3,2]) + #:elif s1[0] == 'c' + !upper triangular + A_true_s_u = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(3.,1.),cmplx(4.,0.)],[2,2]) + A_false_s_u = reshape([cmplx(1.,1.),cmplx(2.,0.), & + cmplx(0.,0.),cmplx(4.,0.)],[2,2]) + A_true_sf_u = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(3.,1.),cmplx(4.,0.), & + cmplx(0.,0.),cmplx(6.,0.)],[2,3]) + A_false_sf_u = reshape([cmplx(1.,1.),cmplx(2.,0.), & + cmplx(3.,1.),cmplx(4.,0.), & + cmplx(0.,0.),cmplx(6.,0.)],[2,3]) + A_true_ts_u = reshape([cmplx(1.,1.),cmplx(0.,0.),cmplx(0.,0.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(0.,0.)],[3,2]) + A_false_ts_u = reshape([cmplx(1.,1.),cmplx(0.,0.),cmplx(0.,0.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.)],[3,2]) + !lower triangular + A_true_s_l = reshape([cmplx(1.,1.),cmplx(2.,0.), & + cmplx(0.,0.),cmplx(4.,0.)],[2,2]) + A_false_s_l = reshape([cmplx(1.,1.),cmplx(0.,0.), & + cmplx(3.,1.),cmplx(4.,0.)],[2,2]) + A_true_sf_l = reshape([cmplx(1.,1.),cmplx(2.,0.), & + cmplx(0.,0.),cmplx(4.,0.), & + cmplx(0.,0.),cmplx(0.,0.)],[2,3]) + A_false_sf_l = reshape([cmplx(1.,1.),cmplx(2.,0.), & + cmplx(3.,1.),cmplx(4.,0.), & + cmplx(0.,0.),cmplx(0.,0.)],[2,3]) + A_true_ts_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(0.,0.),cmplx(5.,1.),cmplx(6.,0.)],[3,2]) + A_false_ts_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.)],[3,2]) + #:elif s1[0] == 'i' + !upper triangular + A_true_s_u = reshape([1,0,3,4],[2,2]) + A_false_s_u = reshape([1,2,0,4],[2,2]) + A_true_sf_u = reshape([1,0,3,4,0,6],[2,3]) + A_false_sf_u = reshape([1,2,3,4,0,6],[2,3]) + A_true_ts_u = reshape([1,0,0,4,5,0],[3,2]) + A_false_ts_u = reshape([1,0,0,4,5,6],[3,2]) + !lower triangular + A_true_s_l = reshape([1,2,0,4],[2,2]) + A_false_s_l = reshape([1,0,3,4],[2,2]) + A_true_sf_l = reshape([1,2,0,4,0,0],[2,3]) + A_false_sf_l = reshape([1,2,3,4,0,0],[2,3]) + A_true_ts_l = reshape([1,2,3,0,5,6],[3,2]) + A_false_ts_l = reshape([1,2,3,4,5,6],[3,2]) + #:endif + + #! error check calls are type/kind independent + !upper triangular checks + call check(error, is_triangular(A_true_s_u,'u'), & + "is_triangular(A_true_s_u,'u') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_s_u,'u')), & + "(.not. is_triangular(A_false_s_u,'u')) failed.") + if (allocated(error)) return + call check(error, is_triangular(A_true_sf_u,'u'), & + "is_triangular(A_true_sf_u,'u') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_sf_u,'u')), & + "(.not. is_triangular(A_false_sf_u,'u')) failed.") + if (allocated(error)) return + call check(error, is_triangular(A_true_ts_u,'u'), & + "is_triangular(A_true_ts_u,'u') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_ts_u,'u')), & + "(.not. is_triangular(A_false_ts_u,'u')) failed.") + if (allocated(error)) return + !lower triangular checks + call check(error, is_triangular(A_true_s_l,'l'), & + "is_triangular(A_true_s_l,'l') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_s_l,'l')), & + "(.not. is_triangular(A_false_s_l,'l')) failed.") + if (allocated(error)) return + call check(error, is_triangular(A_true_sf_l,'l'), & + "is_triangular(A_true_sf_l,'l') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_sf_l,'l')), & + "(.not. is_triangular(A_false_sf_l,'l')) failed.") + if (allocated(error)) return + call check(error, is_triangular(A_true_ts_l,'l'), & + "is_triangular(A_true_ts_l,'l') failed.") + if (allocated(error)) return + call check(error, (.not. is_triangular(A_false_ts_l,'l')), & + "(.not. is_triangular(A_false_ts_l,'l')) failed.") + if (allocated(error)) return + end subroutine test_is_triangular_${s1}$ + #:endfor + + + !is_hessenberg + #:for k1, t1, s1 in RCI_KINDS_TYPES_SUFFIXES + subroutine test_is_hessenberg_${s1}$(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + #! variable sizes independent of type/kind + ${t1}$ :: A_true_s_u(3,3), A_false_s_u(3,3) !square matrices (upper hessenberg) + ${t1}$ :: A_true_sf_u(3,4), A_false_sf_u(3,4) !short and fat matrices + ${t1}$ :: A_true_ts_u(4,3), A_false_ts_u(4,3) !tall and skinny matrices + ${t1}$ :: A_true_s_l(3,3), A_false_s_l(3,3) !square matrices (lower hessenberg) + ${t1}$ :: A_true_sf_l(3,4), A_false_sf_l(3,4) !short and fat matrices + ${t1}$ :: A_true_ts_l(4,3), A_false_ts_l(4,3) !tall and skinny matrices + #! populate variables dependent on type/kind + #:if s1[0] == 'r' + !upper hessenberg + A_true_s_u = reshape([1.,2.,0.,4.,5.,6.,7.,8.,9.],[3,3]) + A_false_s_u = reshape([1.,2.,3.,4.,5.,6.,7.,8.,9.],[3,3]) + A_true_sf_u = reshape([1.,2.,0.,4.,5.,6.,7.,8.,9.,10.,11.,12.],[3,4]) + A_false_sf_u = reshape([1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.],[3,4]) + A_true_ts_u = reshape([1.,2.,0.,0.,5.,6.,7.,0.,9.,10.,11.,12.],[4,3]) + A_false_ts_u = reshape([1.,2.,3.,0.,5.,6.,7.,0.,9.,10.,11.,12.],[4,3]) + !lower hessenberg + A_true_s_l = reshape([1.,2.,3.,4.,5.,6.,0.,8.,9.],[3,3]) + A_false_s_l = reshape([1.,2.,3.,4.,5.,6.,7.,8.,9.],[3,3]) + A_true_sf_l = reshape([1.,2.,3.,4.,5.,6.,0.,8.,9.,0.,0.,12.],[3,4]) + A_false_sf_l = reshape([1.,2.,3.,4.,5.,6.,0.,8.,9.,0.,11.,12.],[3,4]) + A_true_ts_l = reshape([1.,2.,3.,4.,5.,6.,7.,8.,0.,10.,11.,12.],[4,3]) + A_false_ts_l = reshape([1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.],[4,3]) + #:elif s1[0] == 'c' + !upper hessenberg + A_true_s_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(0.,0.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(7.,1.),cmplx(8.,0.),cmplx(9.,1.)],[3,3]) + A_false_s_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(7.,1.),cmplx(8.,0.),cmplx(9.,1.)],[3,3]) + A_true_sf_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(0.,0.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(7.,1.),cmplx(8.,0.),cmplx(9.,1.), & + cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[3,4]) + A_false_sf_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(7.,1.),cmplx(8.,0.),cmplx(9.,1.), & + cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[3,4]) + A_true_ts_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(0.,0.),cmplx(0.,0.), & + cmplx(5.,1.),cmplx(6.,0.),cmplx(7.,1.),cmplx(0.,0.), & + cmplx(9.,1.),cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[4,3]) + A_false_ts_u = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.),cmplx(0.,0.), & + cmplx(5.,1.),cmplx(6.,0.),cmplx(7.,1.),cmplx(0.,0.), & + cmplx(9.,1.),cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[4,3]) + !lower hessenberg + A_true_s_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(0.,0.),cmplx(8.,0.),cmplx(9.,1.)],[3,3]) + A_false_s_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(7.,1.),cmplx(8.,0.),cmplx(9.,1.)],[3,3]) + A_true_sf_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(0.,0.),cmplx(8.,0.),cmplx(9.,1.), & + cmplx(0.,0.),cmplx(0.,0.),cmplx(12.,0.)],[3,4]) + A_false_sf_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.), & + cmplx(4.,0.),cmplx(5.,1.),cmplx(6.,0.), & + cmplx(0.,0.),cmplx(8.,0.),cmplx(9.,1.), & + cmplx(0.,0.),cmplx(11.,1.),cmplx(12.,0.)],[3,4]) + A_true_ts_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.),cmplx(4.,0.), & + cmplx(5.,1.),cmplx(6.,0.),cmplx(7.,1.),cmplx(8.,0.), & + cmplx(0.,0.),cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[4,3]) + A_false_ts_l = reshape([cmplx(1.,1.),cmplx(2.,0.),cmplx(3.,1.),cmplx(4.,0.), & + cmplx(5.,1.),cmplx(6.,0.),cmplx(7.,1.),cmplx(8.,0.), & + cmplx(9.,1.),cmplx(10.,0.),cmplx(11.,1.),cmplx(12.,0.)],[4,3]) + #:elif s1[0] == 'i' + !upper hessenberg + A_true_s_u = reshape([1,2,0,4,5,6,7,8,9],[3,3]) + A_false_s_u = reshape([1,2,3,4,5,6,7,8,9],[3,3]) + A_true_sf_u = reshape([1,2,0,4,5,6,7,8,9,10,11,12],[3,4]) + A_false_sf_u = reshape([1,2,3,4,5,6,7,8,9,10,11,12],[3,4]) + A_true_ts_u = reshape([1,2,0,0,5,6,7,0,9,10,11,12],[4,3]) + A_false_ts_u = reshape([1,2,3,0,5,6,7,0,9,10,11,12],[4,3]) + !lower hessenberg + A_true_s_l = reshape([1,2,3,4,5,6,0,8,9],[3,3]) + A_false_s_l = reshape([1,2,3,4,5,6,7,8,9],[3,3]) + A_true_sf_l = reshape([1,2,3,4,5,6,0,8,9,0,0,12],[3,4]) + A_false_sf_l = reshape([1,2,3,4,5,6,0,8,9,0,11,12],[3,4]) + A_true_ts_l = reshape([1,2,3,4,5,6,7,8,0,10,11,12],[4,3]) + A_false_ts_l = reshape([1,2,3,4,5,6,7,8,9,10,11,12],[4,3]) + #:endif + + #! error check calls are type/kind independent + !upper hessenberg checks + call check(error, is_hessenberg(A_true_s_u,'u'), & + "is_hessenberg(A_true_s_u,'u') failed.") + call check(error, (.not. is_hessenberg(A_false_s_u,'u')), & + "(.not. is_hessenberg(A_false_s_u,'u')) failed.") + call check(error, is_hessenberg(A_true_sf_u,'u'), & + "is_hessenberg(A_true_sf_u,'u') failed.") + call check(error, (.not. is_hessenberg(A_false_sf_u,'u')), & + "(.not. is_hessenberg(A_false_sf_u,'u')) failed.") + call check(error, is_hessenberg(A_true_ts_u,'u'), & + "is_hessenberg(A_true_ts_u,'u') failed.") + call check(error, (.not. is_hessenberg(A_false_ts_u,'u')), & + "(.not. is_hessenberg(A_false_ts_u,'u')) failed.") + !lower hessenberg checks + call check(error, is_hessenberg(A_true_s_l,'l'), & + "is_hessenberg(A_true_s_l,'l') failed.") + call check(error, (.not. is_hessenberg(A_false_s_l,'l')), & + "(.not. is_hessenberg(A_false_s_l,'l')) failed.") + call check(error, is_hessenberg(A_true_sf_l,'l'), & + "is_hessenberg(A_true_sf_l,'l') failed.") + call check(error, (.not. is_hessenberg(A_false_sf_l,'l')), & + "(.not. is_hessenberg(A_false_sf_l,'l')) failed.") + call check(error, is_hessenberg(A_true_ts_l,'l'), & + "is_hessenberg(A_true_ts_l,'l') failed.") + call check(error, (.not. is_hessenberg(A_false_ts_l,'l')), & + "(.not. is_hessenberg(A_false_ts_l,'l')) failed.") + end subroutine test_is_hessenberg_${s1}$ + #:endfor + +end module + + +program tester + use, intrinsic :: iso_fortran_env, only : error_unit + use testdrive, only : run_testsuite, new_testsuite, testsuite_type + use test_linalg_matrix_property_checks, only : collect_linalg_matrix_property_checks + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("linalg_matrix_property_checks", collect_linalg_matrix_property_checks) & + ] + + do is = 1, size(testsuites) + write(error_unit, fmt) "Testing:", testsuites(is)%name + call run_testsuite(testsuites(is)%collect, error_unit, stat) + end do + + if (stat > 0) then + write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" + error stop + end if +end program