diff --git a/doc/specs/stdlib_math.md b/doc/specs/stdlib_math.md index 2e09796de..8d7fb516b 100644 --- a/doc/specs/stdlib_math.md +++ b/doc/specs/stdlib_math.md @@ -275,3 +275,71 @@ program demo_logspace_rstart_cbase end program demo_logspace_rstart_cbase ``` +## `arange` + +### Status + +Experimental + +### Class + +Pure function. + +### Description + +Creates a one-dimensional `array` of the `integer/real` type with fixed-spaced values of given spacing, within a given interval. + +### Syntax + +`result = [[stdlib_math(module):arange(interface)]](start [, end, step])` + +### Arguments + +All arguments should be the same type and kind. + +`start`: Shall be an `integer/real` scalar. +This is an `intent(in)` argument. +The default `start` value is `1`. + +`end`: Shall be an `integer/real` scalar. +This is an `intent(in)` and `optional` argument. +The default `end` value is the inputted `start` value. + +`step`: Shall be an `integer/real` scalar and large than `0`. +This is an `intent(in)` and `optional` argument. +The default `step` value is `1`. + +#### Warning +If `step = 0`, the `step` argument will be corrected to `1/1.0` by the internal process of the `arange` function. +If `step < 0`, the `step` argument will be corrected to `abs(step)` by the internal process of the `arange` function. + +### Return value + +Returns a one-dimensional `array` of fixed-spaced values. + +For `integer` type arguments, the length of the result vector is `(end - start)/step + 1`. +For `real` type arguments, the length of the result vector is `floor((end - start)/step) + 1`. + +### Example + +```fortran +program demo_math_arange + use stdlib_math, only: arange + + print *, arange(3) !! [1,2,3] + print *, arange(-1) !! [1,0,-1] + print *, arange(0,2) !! [0,1,2] + print *, arange(1,-1) !! [1,0,-1] + print *, arange(0, 2, 2) !! [0,2] + + print *, arange(3.0) !! [1.0,2.0,3.0] + print *, arange(0.0,5.0) !! [0.0,1.0,2.0,3.0,4.0,5.0] + print *, arange(0.0,6.0,2.5) !! [0.0,2.5,5.0] + + print *, (1.0,1.0)*arange(3) !! [(1.0,1.0),(2.0,2.0),[3.0,3.0]] + + print *, arange(0.0,2.0,-2.0) !! [0.0,2.0]. Not recommended: `step` argument is negative! + print *, arange(0.0,2.0,0.0) !! [0.0,1.0,2.0]. Not recommended: `step` argument is zero! + +end program demo_math_arange +``` \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c4f6d76e7..c4c6cf858 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,7 @@ set(fppFiles stdlib_math.fypp stdlib_math_linspace.fypp stdlib_math_logspace.fypp + stdlib_math_arange.fypp stdlib_string_type.fypp ) diff --git a/src/Makefile.manual b/src/Makefile.manual index a12f81255..9e78df5d8 100644 --- a/src/Makefile.manual +++ b/src/Makefile.manual @@ -7,6 +7,7 @@ SRCFYPP =\ stdlib_linalg.fypp \ stdlib_linalg_diag.fypp \ stdlib_linalg_outer_product.fypp \ + stdlib_math_arange.fypp \ stdlib_optval.fypp \ stdlib_quadrature.fypp \ stdlib_quadrature_trapz.fypp \ @@ -149,9 +150,12 @@ stdlib_string_type.o: stdlib_ascii.o \ stdlib_strings.o: stdlib_ascii.o \ stdlib_string_type.o \ stdlib_optval.o -stdlib_math.o: stdlib_kinds.o +stdlib_math.o: stdlib_kinds.o \ + stdlib_optval.o stdlib_math_linspace.o: \ stdlib_math.o stdlib_math_logspace.o: \ stdlib_math_linspace.o +stdlib_math_arange.o: \ + stdlib_math.o stdlib_linalg_outer_product.o: stdlib_linalg.o diff --git a/src/stdlib_math.fypp b/src/stdlib_math.fypp index 7a3296158..2ca0f543d 100644 --- a/src/stdlib_math.fypp +++ b/src/stdlib_math.fypp @@ -4,12 +4,14 @@ module stdlib_math use stdlib_kinds, only: int8, int16, int32, int64, sp, dp, qp + use stdlib_optval, only: optval implicit none private public :: clip, linspace, logspace public :: EULERS_NUMBER_SP, EULERS_NUMBER_DP, EULERS_NUMBER_QP public :: DEFAULT_LINSPACE_LENGTH, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH + public :: arange integer, parameter :: DEFAULT_LINSPACE_LENGTH = 100 integer, parameter :: DEFAULT_LOGSPACE_LENGTH = 50 @@ -261,6 +263,22 @@ module stdlib_math end interface + !> Version: experimental + !> + !> `arange` creates a one-dimensional `array` of the `integer/real` type + !> with fixed-spaced values of given spacing, within a given interval. + !> ([Specification](../page/specs/stdlib_math.html#arange)) + interface arange + #:set RI_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + #:for k1, t1 in RI_KINDS_TYPES + pure module function arange_${t1[0]}$_${k1}$(start, end, step) result(result) + ${t1}$, intent(in) :: start + ${t1}$, intent(in), optional :: end, step + ${t1}$, allocatable :: result(:) + end function arange_${t1[0]}$_${k1}$ + #:endfor + end interface arange + contains #:for k1, t1 in IR_KINDS_TYPES diff --git a/src/stdlib_math_arange.fypp b/src/stdlib_math_arange.fypp new file mode 100644 index 000000000..075d76f7a --- /dev/null +++ b/src/stdlib_math_arange.fypp @@ -0,0 +1,54 @@ +#:include "common.fypp" +submodule(stdlib_math) stdlib_math_arange + +contains + + #:for k1, t1 in REAL_KINDS_TYPES + !> `arange` creates a vector of the `${t1}$` type + !> with evenly spaced values within a given interval. + pure module function arange_${t1[0]}$_${k1}$(start, end, step) result(result) + + ${t1}$, intent(in) :: start + ${t1}$, intent(in), optional :: end, step + ${t1}$, allocatable :: result(:) + + ${t1}$ :: start_, end_, step_ + integer :: i + + start_ = merge(start, 1.0_${k1}$, present(end)) + end_ = optval(end, start) + step_ = optval(step, 1.0_${k1}$) + step_ = sign(merge(step_, 1.0_${k1}$, step_ /= 0.0_${k1}$), end_ - start_) + + allocate(result(floor((end_ - start_)/step_) + 1)) + + result = [(start_ + (i - 1)*step_, i=1, size(result), 1)] + + end function arange_${t1[0]}$_${k1}$ + #:endfor + + #:for k1, t1 in INT_KINDS_TYPES + !> `arange` creates a vector of the `${t1}$` type + !> with evenly spaced values within a given interval. + pure module function arange_${t1[0]}$_${k1}$(start, end, step) result(result) + + ${t1}$, intent(in) :: start + ${t1}$, intent(in), optional :: end, step + ${t1}$, allocatable :: result(:) + + ${t1}$ :: start_, end_, step_ + ${t1}$ :: i + + start_ = merge(start, 1_${k1}$, present(end)) + end_ = optval(end, start) + step_ = optval(step, 1_${k1}$) + step_ = sign(merge(step_, 1_${k1}$, step_ /= 0_${k1}$), end_ - start_) + + allocate(result((end_ - start_)/step_ + 1)) + + result = [(i, i=start_, end_, step_)] + + end function arange_${t1[0]}$_${k1}$ + #:endfor + +end submodule stdlib_math_arange diff --git a/src/tests/math/CMakeLists.txt b/src/tests/math/CMakeLists.txt index c4335e9ce..0bcaf1e1a 100644 --- a/src/tests/math/CMakeLists.txt +++ b/src/tests/math/CMakeLists.txt @@ -1,3 +1,4 @@ ADDTEST(stdlib_math) ADDTEST(linspace) -ADDTEST(logspace) \ No newline at end of file +ADDTEST(logspace) +ADDTEST(math_arange) \ No newline at end of file diff --git a/src/tests/math/Makefile.manual b/src/tests/math/Makefile.manual index 209990732..f11cbf7a4 100644 --- a/src/tests/math/Makefile.manual +++ b/src/tests/math/Makefile.manual @@ -1,4 +1,5 @@ -PROGS_SRC = test_stdlib_math.f90 test_linspace.f90 test_logspace.f90 +PROGS_SRC = test_stdlib_math.f90 test_linspace.f90 test_logspace.f90 \ + test_math_arange.f90 include ../Makefile.manual.test.mk diff --git a/src/tests/math/test_math_arange.f90 b/src/tests/math/test_math_arange.f90 new file mode 100644 index 000000000..0da565afe --- /dev/null +++ b/src/tests/math/test_math_arange.f90 @@ -0,0 +1,53 @@ +!> SPDX-Identifier: MIT +module test_math_arange + + use stdlib_error, only: check + use stdlib_math, only: arange + implicit none + + logical, private :: warn = .false. + +contains + + subroutine test_math_arange_real + !> Normal + call check(all(arange(3.0) == [1.0, 2.0, 3.0]), msg="all(arange(3.0) == [1.0,2.0,3.0]) failed.", warn=warn) + call check(all(arange(-1.0) == [1.0, 0.0, -1.0]), msg="all(arange(-1.0) == [1.0,0.0,-1.0]) failed.", warn=warn) + call check(all(arange(0.0, 2.0) == [0.0, 1.0, 2.0]), msg="all(arange(0.0,2.0) == [0.0,1.0,2.0]) failed.", warn=warn) + call check(all(arange(1.0, -1.0) == [1.0, 0.0, -1.0]), msg="all(arange(1.0,-1.0) == [1.0,0.0,-1.0]) failed.", warn=warn) + call check(all(arange(1.0, 1.0) == [1.0]), msg="all(arange(1.0,1.0) == [1.0]) failed.", warn=warn) + call check(all(arange(0.0, 2.0, 2.0) == [0.0, 2.0]), msg="all(arange(0.0,2.0,2.0) == [0.0,2.0]) failed.", warn=warn) + call check(all(arange(1.0, -1.0, 2.0) == [1.0, -1.0]), msg="all(arange(1.0,-1.0,2.0) == [1.0,-1.0]) failed.", warn=warn) + !> Not recommended + call check(all(arange(0.0, 2.0, -2.0) == [0.0, 2.0]), msg="all(arange(0.0,2.0,-2.0) == [0.0,2.0]) failed.", warn=warn) + call check(all(arange(1.0, -1.0, -2.0) == [1.0, -1.0]),msg="all(arange(1.0,-1.0,-2.0) == [1.0,-1.0]) failed.", warn=warn) + call check(all(arange(0.0, 2.0, 0.0) == [0.0,1.0,2.0]),msg="all(arange(0.0, 2.0, 0.0) == [0.0,1.0,2.0]) failed.", warn=warn) + end subroutine test_math_arange_real + + subroutine test_math_arange_integer + !> Normal + call check(all(arange(3) == [1, 2, 3]), msg="all(arange(3) == [1,2,3]) failed.", warn=warn) + call check(all(arange(-1) == [1, 0, -1]), msg="all(arange(-1) == [1,0,-1]) failed.", warn=warn) + call check(all(arange(0, 2) == [0, 1, 2]), msg="all(arange(0,2) == [0,1,2]) failed.", warn=warn) + call check(all(arange(1, -1) == [1, 0, -1]), msg="all(arange(1,-1) == [1,0,-1]) failed.", warn=warn) + call check(all(arange(1, 1) == [1]), msg="all(arange(1,1) == [1]) failed.", warn=warn) + call check(all(arange(0, 2, 2) == [0, 2]), msg="all(arange(0,2,2) == [0,2]) failed.", warn=warn) + call check(all(arange(1, -1, 2) == [1, -1]), msg="all(arange(1,-1,2) == [1,-1]) failed.", warn=warn) + !> Not recommended + call check(all(arange(0, 2, -2) == [0, 2]), msg="all(arange(0,2,-2) == [0,2]) failed.", warn=warn) + call check(all(arange(1, -1, -2) == [1, -1]), msg="all(arange(1,-1,-2) == [1,-1]) failed.", warn=warn) + call check(all(arange(0, 2, 0) == [0,1,2]), msg="all(arange(0, 2, 0) == [0,1,2]) failed.", warn=warn) + end subroutine test_math_arange_integer + +end module test_math_arange + +program tester + + use test_math_arange + + call test_math_arange_real + call test_math_arange_integer + + print *, "All tests in `test_math_arange` passed." + +end program tester