From ae608ee1bb87b4824f29e3a942e909f2384207be Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Sun, 19 Dec 2021 12:39:15 +0100 Subject: [PATCH 1/6] Implement trueloc/falseloc --- CHANGELOG.md | 3 + doc/specs/index.md | 1 + doc/specs/stdlib_array.md | 81 +++++++++++++++ src/CMakeLists.txt | 1 + src/Makefile.manual | 1 + src/stdlib_array.f90 | 60 +++++++++++ src/tests/CMakeLists.txt | 1 + src/tests/Makefile.manual | 1 + src/tests/array/CMakeLists.txt | 1 + src/tests/array/Makefile.manual | 4 + src/tests/array/test_logicalloc.f90 | 154 ++++++++++++++++++++++++++++ 11 files changed, 308 insertions(+) create mode 100644 doc/specs/stdlib_array.md create mode 100644 src/stdlib_array.f90 create mode 100644 src/tests/array/CMakeLists.txt create mode 100644 src/tests/array/Makefile.manual create mode 100644 src/tests/array/test_logicalloc.f90 diff --git a/CHANGELOG.md b/CHANGELOG.md index 319e80071..071c90da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ Features available from the latest git source +- new module `stdlib_array` + [#603](https://github.com/fortran-lang/stdlib/pull/603) + - new procedures `trueloc`, `falseloc` - new module `stdlib_distribution_uniform` [#272](https://github.com/fortran-lang/stdlib/pull/272) - new module `stdlib_selection` diff --git a/doc/specs/index.md b/doc/specs/index.md index 7a7a6f143..fbe498b75 100644 --- a/doc/specs/index.md +++ b/doc/specs/index.md @@ -11,6 +11,7 @@ This is and index/directory of the specifications (specs) for each new module/fe ## Experimental Features & Modules + - [array](./stdlib_array.html) - Procedures for index manipulation and array handling - [ascii](./stdlib_ascii.html) - Procedures for handling ASCII characters - [bitsets](./stdlib_bitsets.html) - Bitset data types and procedures - [error](./stdlib_error.html) - Catching and handling errors diff --git a/doc/specs/stdlib_array.md b/doc/specs/stdlib_array.md new file mode 100644 index 000000000..6752e14b7 --- /dev/null +++ b/doc/specs/stdlib_array.md @@ -0,0 +1,81 @@ +--- +title: array +--- + +# The `stdlib_array` module + +[TOC] + +## Introduction + +Module for index manipulation and array handling tasks. + +## Procedures and methods provided + + +### `trueloc` + +#### Status + +Experimental + +#### Description + +Turn a logical mask into an index array by selecting all true values. + +#### Syntax + +`call [[trueloc(function)]] (array[, lbound])` + +#### Arguments + +`array`: List of default logical arrays. This argument is `intent(in)`. + +`lbound`: Lower bound of the array to index. This argument is `optional` and `intent(in)`. + +#### Examples + +```fortran +program demo + use stdlib_array, only : trueloc + implicit none + real, allocatable :: array(:) + allocate(array(500)) + call random_number(array) + array(trueloc(array > 0.5)) = 0.0 +end program demo +``` + + +### `falseloc` + +#### Status + +Experimental + +#### Description + +Turn a logical mask into an index array by selecting all false values. + +#### Syntax + +`call [[falseloc(function)]] (array[, lbound])` + +#### Arguments + +`array`: List of default logical arrays. This argument is `intent(in)`. + +`lbound`: Lower bound of the array to index. This argument is `optional` and `intent(in)`. + +#### Examples + +```fortran +program demo + use stdlib_array, only : falseloc + implicit none + real, allocatable :: array(:) + allocate(array(-200:200)) + call random_number(array) + array(falseloc(array < 0.5), lbound(array)) = 0.0 +end program demo +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d3a107e54..6c4774f63 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,6 +71,7 @@ list( fypp_f90("${fyppFlags}" "${fppFiles}" outFiles) set(SRC + stdlib_array.f90 stdlib_error.f90 stdlib_logger.f90 stdlib_system.F90 diff --git a/src/Makefile.manual b/src/Makefile.manual index f8e001377..1100021e4 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -46,6 +46,7 @@ SRCFYPP = \ stdlib_version.fypp SRC = f18estop.f90 \ + stdlib_array.f90 \ stdlib_error.f90 \ stdlib_specialfunctions.f90 \ stdlib_specialfunctions_legendre.f90 \ diff --git a/src/stdlib_array.f90 b/src/stdlib_array.f90 new file mode 100644 index 000000000..52de2bb5a --- /dev/null +++ b/src/stdlib_array.f90 @@ -0,0 +1,60 @@ +! SPDX-Identifier: MIT + +!> Module for index manipulation and general array handling +module stdlib_array + implicit none + private + + public :: trueloc, falseloc + +contains + + !> Return the positions of the true elements in array + pure function trueloc(array, lbound) result(loc) + !> Mask of logicals + logical, intent(in) :: array(:) + !> Lower bound of array to index + integer, intent(in), optional :: lbound + !> Locations of true elements + integer :: loc(count(array)) + + loc = logicalloc(array, .true., lbound) + end function trueloc + + !> Return the positions of the false elements in array + pure function falseloc(array, lbound) result(loc) + !> Mask of logicals + logical, intent(in) :: array(:) + !> Lower bound of array to index + integer, intent(in), optional :: lbound + !> Locations of false elements + integer :: loc(count(.not.array)) + + loc = logicalloc(array, .false., lbound) + end function falseloc + + !> Return the positions of the truthy elements in array + pure function logicalloc(array, truth, lbound) result(loc) + !> Mask of logicals + logical, intent(in) :: array(:) + !> Truthy value + logical, intent(in) :: truth + !> Lower bound of array to index + integer, intent(in), optional :: lbound + !> Locations of truthy elements + integer :: loc(count(array.eqv.truth)) + integer :: i, pos, offset + + offset = 0 + if (present(lbound)) offset = lbound - 1 + + i = 0 + do pos = 1, size(array) + if (array(pos).eqv.truth) then + i = i + 1 + loc(i) = pos + offset + end if + end do + end function logicalloc + +end module stdlib_array diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4e40b4f1b..1e824a69d 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -15,6 +15,7 @@ list( "-I${PROJECT_SOURCE_DIR}/src" ) +add_subdirectory(array) add_subdirectory(ascii) add_subdirectory(bitsets) add_subdirectory(io) diff --git a/src/tests/Makefile.manual b/src/tests/Makefile.manual index 83d93c992..7e60bd23a 100644 --- a/src/tests/Makefile.manual +++ b/src/tests/Makefile.manual @@ -12,6 +12,7 @@ testdrive.F90: $(FETCH) https://github.com/fortran-lang/test-drive/raw/v0.4.0/src/testdrive.F90 > $@ all test clean:: + $(MAKE) -f Makefile.manual --directory=array $@ $(MAKE) -f Makefile.manual --directory=ascii $@ $(MAKE) -f Makefile.manual --directory=bitsets $@ $(MAKE) -f Makefile.manual --directory=io $@ diff --git a/src/tests/array/CMakeLists.txt b/src/tests/array/CMakeLists.txt new file mode 100644 index 000000000..49e971e7a --- /dev/null +++ b/src/tests/array/CMakeLists.txt @@ -0,0 +1 @@ +ADDTEST(logicalloc) diff --git a/src/tests/array/Makefile.manual b/src/tests/array/Makefile.manual new file mode 100644 index 000000000..2a59ac3e0 --- /dev/null +++ b/src/tests/array/Makefile.manual @@ -0,0 +1,4 @@ +PROGS_SRC = test_logicalloc.f90 + + +include ../Makefile.manual.test.mk diff --git a/src/tests/array/test_logicalloc.f90 b/src/tests/array/test_logicalloc.f90 new file mode 100644 index 000000000..0715863f4 --- /dev/null +++ b/src/tests/array/test_logicalloc.f90 @@ -0,0 +1,154 @@ +! SPDX-Identifier: MIT + +module test_logicalloc + use stdlib_array, only : trueloc, falseloc + use stdlib_string_type, only : string_type, len + use testdrive, only : new_unittest, unittest_type, error_type, check + implicit none + private + + public :: collect_logicalloc + +contains + + !> Collect all exported unit tests + subroutine collect_logicalloc(testsuite) + !> Collection of tests + type(unittest_type), allocatable, intent(out) :: testsuite(:) + + testsuite = [ & + new_unittest("trueloc-where", test_trueloc_where), & + new_unittest("trueloc-merge", test_trueloc_merge), & + new_unittest("falseloc-where", test_falseloc_where), & + new_unittest("falseloc-merge", test_falseloc_merge) & + ] + end subroutine collect_logicalloc + + subroutine test_trueloc_where(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + bvec(trueloc(bvec > 0)) = 0.0 + + cvec = avec + where(cvec > 0) cvec = 0.0 + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + end subroutine test_trueloc_where + + subroutine test_trueloc_merge(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + bvec(trueloc(bvec > 0)) = 0.0 + + cvec = avec + cvec(:) = merge(0.0, cvec, cvec > 0) + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + end subroutine test_trueloc_merge + + subroutine test_falseloc_where(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + bvec(falseloc(bvec > 0)) = 0.0 + + cvec = avec + where(.not.(cvec > 0)) cvec = 0.0 + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + end subroutine test_falseloc_where + + subroutine test_falseloc_merge(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + bvec(falseloc(bvec > 0)) = 0.0 + + cvec = avec + cvec(:) = merge(cvec, 0.0, cvec > 0) + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + end subroutine test_falseloc_merge + +end module test_logicalloc + + +program tester + use, intrinsic :: iso_fortran_env, only : error_unit + use testdrive, only : run_testsuite, new_testsuite, testsuite_type + use test_logicalloc, only : collect_logicalloc + implicit none + integer :: stat, is + type(testsuite_type), allocatable :: testsuites(:) + character(len=*), parameter :: fmt = '("#", *(1x, a))' + + stat = 0 + + testsuites = [ & + new_testsuite("logicalloc", collect_logicalloc) & + ] + + 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 From 7b5fface9f0556d18248fef61d1e307a9247e0f8 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Mon, 20 Dec 2021 10:22:59 +0100 Subject: [PATCH 2/6] Add tests for empty and complete index list --- doc/specs/stdlib_array.md | 8 +++ src/tests/array/test_logicalloc.f90 | 86 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/doc/specs/stdlib_array.md b/doc/specs/stdlib_array.md index 6752e14b7..886046fe1 100644 --- a/doc/specs/stdlib_array.md +++ b/doc/specs/stdlib_array.md @@ -33,6 +33,10 @@ Turn a logical mask into an index array by selecting all true values. `lbound`: Lower bound of the array to index. This argument is `optional` and `intent(in)`. +#### Return value + +Returns an array of default integer size, with a maximum length of `size(array)` elements. + #### Examples ```fortran @@ -67,6 +71,10 @@ Turn a logical mask into an index array by selecting all false values. `lbound`: Lower bound of the array to index. This argument is `optional` and `intent(in)`. +#### Return value + +Returns an array of default integer size, with a maximum length of `size(array)` elements. + #### Examples ```fortran diff --git a/src/tests/array/test_logicalloc.f90 b/src/tests/array/test_logicalloc.f90 index 0715863f4..fce3a0dc5 100644 --- a/src/tests/array/test_logicalloc.f90 +++ b/src/tests/array/test_logicalloc.f90 @@ -17,13 +17,58 @@ subroutine collect_logicalloc(testsuite) type(unittest_type), allocatable, intent(out) :: testsuite(:) testsuite = [ & + new_unittest("trueloc-empty", test_trueloc_empty), & + new_unittest("trueloc-all", test_trueloc_all), & new_unittest("trueloc-where", test_trueloc_where), & new_unittest("trueloc-merge", test_trueloc_merge), & + new_unittest("falseloc-empty", test_falseloc_empty), & + new_unittest("falseloc-all", test_falseloc_all), & new_unittest("falseloc-where", test_falseloc_where), & new_unittest("falseloc-merge", test_falseloc_merge) & ] end subroutine collect_logicalloc + subroutine test_trueloc_empty(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + + bvec = avec + bvec(trueloc(bvec < 0)) = 0.0 + + call check(error, all(bvec == avec)) + deallocate(avec, bvec) + if (allocated(error)) exit + end do + end subroutine test_trueloc_empty + + subroutine test_trueloc_all(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:) + + do ndim = 100, 12000, 100 + allocate(avec(-ndim/2:ndim)) + + call random_number(avec) + + avec(trueloc(avec > 0, lbound(avec, 1))) = 0.0 + + call check(error, all(avec == 0.0)) + deallocate(avec) + if (allocated(error)) exit + end do + end subroutine test_trueloc_all + subroutine test_trueloc_where(error) !> Error handling type(error_type), allocatable, intent(out) :: error @@ -74,6 +119,47 @@ subroutine test_trueloc_merge(error) end do end subroutine test_trueloc_merge + subroutine test_falseloc_empty(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:) + + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + + bvec = avec + bvec(falseloc(bvec > 0)) = 0.0 + + call check(error, all(bvec == avec)) + deallocate(avec, bvec) + if (allocated(error)) exit + end do + end subroutine test_falseloc_empty + + subroutine test_falseloc_all(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:) + + do ndim = 100, 12000, 100 + allocate(avec(-ndim/2:ndim)) + + call random_number(avec) + + avec(falseloc(avec < 0, lbound(avec, 1))) = 0.0 + + call check(error, all(avec == 0.0)) + deallocate(avec) + if (allocated(error)) exit + end do + end subroutine test_falseloc_all + subroutine test_falseloc_where(error) !> Error handling type(error_type), allocatable, intent(out) :: error From 4a4ac226ae2e25ab0f705ce3cc8ad811eca690a2 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Mon, 20 Dec 2021 19:59:09 +0100 Subject: [PATCH 3/6] Use subroutine to implement logicalloc --- src/stdlib_array.f90 | 12 +++---- src/tests/array/test_logicalloc.f90 | 56 ++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/stdlib_array.f90 b/src/stdlib_array.f90 index 52de2bb5a..ed3b6faaa 100644 --- a/src/stdlib_array.f90 +++ b/src/stdlib_array.f90 @@ -18,7 +18,7 @@ pure function trueloc(array, lbound) result(loc) !> Locations of true elements integer :: loc(count(array)) - loc = logicalloc(array, .true., lbound) + call logicalloc(loc, array, .true., lbound) end function trueloc !> Return the positions of the false elements in array @@ -30,19 +30,19 @@ pure function falseloc(array, lbound) result(loc) !> Locations of false elements integer :: loc(count(.not.array)) - loc = logicalloc(array, .false., lbound) + call logicalloc(loc, array, .false., lbound) end function falseloc !> Return the positions of the truthy elements in array - pure function logicalloc(array, truth, lbound) result(loc) + pure subroutine logicalloc(loc, array, truth, lbound) + !> Locations of truthy elements + integer, intent(out) :: loc(:) !> Mask of logicals logical, intent(in) :: array(:) !> Truthy value logical, intent(in) :: truth !> Lower bound of array to index integer, intent(in), optional :: lbound - !> Locations of truthy elements - integer :: loc(count(array.eqv.truth)) integer :: i, pos, offset offset = 0 @@ -55,6 +55,6 @@ pure function logicalloc(array, truth, lbound) result(loc) loc(i) = pos + offset end if end do - end function logicalloc + end subroutine logicalloc end module stdlib_array diff --git a/src/tests/array/test_logicalloc.f90 b/src/tests/array/test_logicalloc.f90 index fce3a0dc5..999eb03d2 100644 --- a/src/tests/array/test_logicalloc.f90 +++ b/src/tests/array/test_logicalloc.f90 @@ -2,7 +2,8 @@ module test_logicalloc use stdlib_array, only : trueloc, falseloc - use stdlib_string_type, only : string_type, len + use stdlib_kinds, only : dp, i8 => int64 + use stdlib_strings, only : to_string use testdrive, only : new_unittest, unittest_type, error_type, check implicit none private @@ -75,7 +76,10 @@ subroutine test_trueloc_where(error) integer :: ndim real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tw + tl = 0.0_dp + tw = 0.0_dp do ndim = 100, 12000, 100 allocate(avec(ndim)) @@ -83,15 +87,20 @@ subroutine test_trueloc_where(error) avec(:) = avec - 0.5 bvec = avec + tl = tl - timing() bvec(trueloc(bvec > 0)) = 0.0 + tl = tl + timing() cvec = avec + tw = tw - timing() where(cvec > 0) cvec = 0.0 + tw = tw + timing() call check(error, all(bvec == cvec)) deallocate(avec, bvec, cvec) if (allocated(error)) exit end do + call report("trueloc", tl, "where", tw) end subroutine test_trueloc_where subroutine test_trueloc_merge(error) @@ -100,7 +109,10 @@ subroutine test_trueloc_merge(error) integer :: ndim real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tm + tl = 0.0_dp + tm = 0.0_dp do ndim = 100, 12000, 100 allocate(avec(ndim)) @@ -108,15 +120,20 @@ subroutine test_trueloc_merge(error) avec(:) = avec - 0.5 bvec = avec + tl = tl - timing() bvec(trueloc(bvec > 0)) = 0.0 + tl = tl + timing() cvec = avec + tm = tm - timing() cvec(:) = merge(0.0, cvec, cvec > 0) + tm = tm + timing() call check(error, all(bvec == cvec)) deallocate(avec, bvec, cvec) if (allocated(error)) exit end do + call report("trueloc", tl, "merge", tm) end subroutine test_trueloc_merge subroutine test_falseloc_empty(error) @@ -166,7 +183,10 @@ subroutine test_falseloc_where(error) integer :: ndim real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tw + tl = 0.0_dp + tw = 0.0_dp do ndim = 100, 12000, 100 allocate(avec(ndim)) @@ -174,15 +194,20 @@ subroutine test_falseloc_where(error) avec(:) = avec - 0.5 bvec = avec + tl = tl - timing() bvec(falseloc(bvec > 0)) = 0.0 + tl = tl + timing() cvec = avec + tw = tw - timing() where(.not.(cvec > 0)) cvec = 0.0 + tw = tw + timing() call check(error, all(bvec == cvec)) deallocate(avec, bvec, cvec) if (allocated(error)) exit end do + call report("falseloc", tl, "where", tw) end subroutine test_falseloc_where subroutine test_falseloc_merge(error) @@ -191,7 +216,10 @@ subroutine test_falseloc_merge(error) integer :: ndim real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tm + tl = 0.0_dp + tm = 0.0_dp do ndim = 100, 12000, 100 allocate(avec(ndim)) @@ -199,17 +227,43 @@ subroutine test_falseloc_merge(error) avec(:) = avec - 0.5 bvec = avec + tl = tl - timing() bvec(falseloc(bvec > 0)) = 0.0 + tl = tl + timing() cvec = avec + tm = tm - timing() cvec(:) = merge(cvec, 0.0, cvec > 0) + tm = tm + timing() call check(error, all(bvec == cvec)) deallocate(avec, bvec, cvec) if (allocated(error)) exit end do + call report("falseloc", tl, "merge", tm) end subroutine test_falseloc_merge + subroutine report(l1, t1, l2, t2) + character(len=*), intent(in) :: l1, l2 + real(dp), intent(in) :: t1, t2 + character(len=*), parameter :: fmt = "f6.4" + + !$omp critical + print '(2x, "[Timing]", *(1x, g0))', & + l1//":", to_string(t1, fmt)//"s", & + l2//":", to_string(t2, fmt)//"s", & + "ratio:", to_string(t1/t2, "f4.1") + !$omp end critical + end subroutine report + + function timing() result(time) + real(dp) :: time + + integer(i8) :: time_count, time_rate, time_max + call system_clock(time_count, time_rate, time_max) + time = real(time_count, dp)/real(time_rate, dp) + end function timing + end module test_logicalloc From 7ddcbccf3f9298264b4eeb2cb39d30a3c78eecb7 Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Mon, 20 Dec 2021 20:19:30 +0100 Subject: [PATCH 4/6] Add equivalent pack to testsuite --- src/tests/array/test_logicalloc.f90 | 76 ++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/tests/array/test_logicalloc.f90 b/src/tests/array/test_logicalloc.f90 index 999eb03d2..9be52b6f9 100644 --- a/src/tests/array/test_logicalloc.f90 +++ b/src/tests/array/test_logicalloc.f90 @@ -22,10 +22,12 @@ subroutine collect_logicalloc(testsuite) new_unittest("trueloc-all", test_trueloc_all), & new_unittest("trueloc-where", test_trueloc_where), & new_unittest("trueloc-merge", test_trueloc_merge), & + new_unittest("trueloc-pack", test_trueloc_pack), & new_unittest("falseloc-empty", test_falseloc_empty), & new_unittest("falseloc-all", test_falseloc_all), & new_unittest("falseloc-where", test_falseloc_where), & - new_unittest("falseloc-merge", test_falseloc_merge) & + new_unittest("falseloc-merge", test_falseloc_merge), & + new_unittest("falseloc-pack", test_falseloc_pack) & ] end subroutine collect_logicalloc @@ -136,6 +138,42 @@ subroutine test_trueloc_merge(error) call report("trueloc", tl, "merge", tm) end subroutine test_trueloc_merge + subroutine test_trueloc_pack(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tp + + tl = 0.0_dp + tp = 0.0_dp + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + tl = tl - timing() + bvec(trueloc(bvec > 0)) = 0.0 + tl = tl + timing() + + cvec = avec + tp = tp - timing() + block + integer :: i + cvec(pack([(i, i=1, size(cvec))], cvec > 0)) = 0.0 + end block + tp = tp + timing() + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + call report("trueloc", tl, "pack", tp) + end subroutine test_trueloc_pack + subroutine test_falseloc_empty(error) !> Error handling type(error_type), allocatable, intent(out) :: error @@ -243,6 +281,42 @@ subroutine test_falseloc_merge(error) call report("falseloc", tl, "merge", tm) end subroutine test_falseloc_merge + subroutine test_falseloc_pack(error) + !> Error handling + type(error_type), allocatable, intent(out) :: error + + integer :: ndim + real, allocatable :: avec(:), bvec(:), cvec(:) + real(dp) :: tl, tp + + tl = 0.0_dp + tp = 0.0_dp + do ndim = 100, 12000, 100 + allocate(avec(ndim)) + + call random_number(avec) + avec(:) = avec - 0.5 + + bvec = avec + tl = tl - timing() + bvec(falseloc(bvec > 0)) = 0.0 + tl = tl + timing() + + cvec = avec + tp = tp - timing() + block + integer :: i + cvec(pack([(i, i=1, size(cvec))], cvec < 0)) = 0.0 + end block + tp = tp + timing() + + call check(error, all(bvec == cvec)) + deallocate(avec, bvec, cvec) + if (allocated(error)) exit + end do + call report("falseloc", tl, "pack", tp) + end subroutine test_falseloc_pack + subroutine report(l1, t1, l2, t2) character(len=*), intent(in) :: l1, l2 real(dp), intent(in) :: t1, t2 From b015d9fdd7d61446770950bb21d8bd422129389e Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Mon, 20 Dec 2021 20:51:06 +0100 Subject: [PATCH 5/6] Update specification --- doc/specs/stdlib_array.md | 20 ++++++++++++++------ src/stdlib_array.f90 | 12 ++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/doc/specs/stdlib_array.md b/doc/specs/stdlib_array.md index 886046fe1..aae9689ba 100644 --- a/doc/specs/stdlib_array.md +++ b/doc/specs/stdlib_array.md @@ -25,7 +25,11 @@ Turn a logical mask into an index array by selecting all true values. #### Syntax -`call [[trueloc(function)]] (array[, lbound])` +`loc = [[trueloc(function)]] (array[, lbound])` + +#### Class + +Pure function. #### Arguments @@ -40,14 +44,14 @@ Returns an array of default integer size, with a maximum length of `size(array)` #### Examples ```fortran -program demo +program demo_trueloc use stdlib_array, only : trueloc implicit none real, allocatable :: array(:) allocate(array(500)) call random_number(array) array(trueloc(array > 0.5)) = 0.0 -end program demo +end program demo_trueloc ``` @@ -63,7 +67,11 @@ Turn a logical mask into an index array by selecting all false values. #### Syntax -`call [[falseloc(function)]] (array[, lbound])` +`loc = [[falseloc(function)]] (array[, lbound])` + +#### Class + +Pure function. #### Arguments @@ -78,12 +86,12 @@ Returns an array of default integer size, with a maximum length of `size(array)` #### Examples ```fortran -program demo +program demo_falseloc use stdlib_array, only : falseloc implicit none real, allocatable :: array(:) allocate(array(-200:200)) call random_number(array) array(falseloc(array < 0.5), lbound(array)) = 0.0 -end program demo +end program demo_falseloc ``` diff --git a/src/stdlib_array.f90 b/src/stdlib_array.f90 index ed3b6faaa..c5e4fa004 100644 --- a/src/stdlib_array.f90 +++ b/src/stdlib_array.f90 @@ -1,6 +1,8 @@ ! SPDX-Identifier: MIT !> Module for index manipulation and general array handling +!> +!> The specification of this module is available [here](../page/specs/stdlib_array.html). module stdlib_array implicit none private @@ -9,7 +11,10 @@ module stdlib_array contains - !> Return the positions of the true elements in array + !> Version: experimental + !> + !> Return the positions of the true elements in array. + !> [Specification](../page/specs/stdlib_array.html#trueloc) pure function trueloc(array, lbound) result(loc) !> Mask of logicals logical, intent(in) :: array(:) @@ -21,7 +26,10 @@ pure function trueloc(array, lbound) result(loc) call logicalloc(loc, array, .true., lbound) end function trueloc - !> Return the positions of the false elements in array + !> Version: experimental + !> + !> Return the positions of the false elements in array. + !> [Specification](../page/specs/stdlib_array.html#falseloc) pure function falseloc(array, lbound) result(loc) !> Mask of logicals logical, intent(in) :: array(:) From 13460663bc21346ea2fbdead460fe6a18014b88e Mon Sep 17 00:00:00 2001 From: Sebastian Ehlert <28669218+awvwgk@users.noreply.github.com> Date: Tue, 21 Dec 2021 23:48:41 +0100 Subject: [PATCH 6/6] Add relation of trueloc/falseloc with which/merge/pack --- doc/specs/stdlib_array.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/specs/stdlib_array.md b/doc/specs/stdlib_array.md index aae9689ba..8acaa20b3 100644 --- a/doc/specs/stdlib_array.md +++ b/doc/specs/stdlib_array.md @@ -22,6 +22,8 @@ Experimental #### Description Turn a logical mask into an index array by selecting all true values. +Provides similar functionality like the built-in `where` or the intrinsic procedures `merge` and `pack` when working with logical mask. +The built-in / intrinsics are usually preferable to `trueloc`, unless the access to the index array is required. #### Syntax @@ -64,6 +66,8 @@ Experimental #### Description Turn a logical mask into an index array by selecting all false values. +Provides similar functionality like the built-in `where` or the intrinsic procedures `merge` and `pack` when working with logical mask. +The built-in / intrinsics are usually preferable to `falseloc`, unless the access to the index array is required. #### Syntax