Skip to content

Commit 233d871

Browse files
authoredJul 20, 2021
Merge pull request #420 from ejovo13/lin_log_space
First implementation of real-valued linspace.
2 parents d0f13b0 + f51b67f commit 233d871

10 files changed

+1266
-7
lines changed
 

‎doc/specs/stdlib_math.md

+188-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ title: math
1919

2020
#### Description
2121

22-
Returns a value which lies in the given interval [`xmin`, `xmax`] (interval is `xmin` and `xmax` inclusive) and is closest to the input value `x`.
22+
Returns a value which lies in the given interval [`xmin`, `xmax`] (interval is `xmin` and `xmax` inclusive) and is closest to the input value `x`.
2323

2424
#### Syntax
2525

@@ -35,8 +35,8 @@ Elemental function.
3535

3636
#### Argument(s)
3737

38-
`x`: scalar of either `integer` or `real` type. This argument is `intent(in)`.
39-
`xmin`: scalar of either `integer` or `real` type. This argument is `intent(in)`.
38+
`x`: scalar of either `integer` or `real` type. This argument is `intent(in)`.
39+
`xmin`: scalar of either `integer` or `real` type. This argument is `intent(in)`.
4040
`xmax`: scalar of either `integer` or `real` type, which must be greater than or equal to `xmin`. This argument is `intent(in)`.
4141

4242
Note: All arguments must have same `type` and same `kind`.
@@ -90,3 +90,188 @@ program demo_clip_real
9090
! clipped_value <- 3.02500010
9191
end program demo_clip_real
9292
```
93+
94+
### `linspace` - Create a linearly spaced rank one array
95+
96+
#### Description
97+
98+
Returns a linearly spaced rank 1 array from [`start`, `end`]. Optionally, you can specify the length of the returned array by passing `n`.
99+
100+
#### Syntax
101+
102+
`res = [[stdlib_math(module):linspace(interface)]] (start, end [, n])`
103+
104+
#### Status
105+
106+
Experimental
107+
108+
#### Class
109+
110+
Function.
111+
112+
#### Argument(s)
113+
114+
`start`: Shall be scalar of any numeric type or kind. This argument is `intent(in)`.
115+
`end`: Shall be the same `type` and `kind` as `start`. This argument is `intent(in)`.
116+
`n`: Shall be an integer specifying the length of the output. This argument is `optional` and `intent(in)`.
117+
118+
#### Output value or Result value
119+
120+
The output is a rank 1 array whose length is either 100 (default value) or `n`.
121+
122+
If `n` == 1, return a rank 1 array whose only element is `end`.
123+
If `n` <= 0, return a rank 1 array with length 0.
124+
125+
If `start`/`end` are `real` or `complex` types, the `result` will be of the same type and kind as `start`/`end`.
126+
If `start`/`end` are `integer` types, the `result` will default to a `real(dp)` array.
127+
128+
#### Examples
129+
130+
##### Example 1:
131+
132+
Here inputs are of type `complex` and kind `dp`
133+
```fortran
134+
program demo_linspace_complex
135+
use stdlib_math, only: linspace
136+
use stdlib_kinds, only: dp
137+
implicit none
138+
139+
complex(dp) :: start = complex(10.0_dp, 5.0_dp)
140+
complex(dp) :: end = complex(-10.0_dp, 15.0_dp)
141+
142+
complex(dp) :: z(11)
143+
144+
z = linspace(start, end, 11)
145+
end program demo_linspace_complex
146+
```
147+
148+
##### Example 2:
149+
150+
Here inputs are of type `integer` and kind `int16`, with the result defaulting to `real(dp)`.
151+
```fortran
152+
program demo_linspace_int16
153+
use stdlib_math, only: linspace
154+
use stdlib_kinds, only: int16, dp
155+
implicit none
156+
157+
integer(int16) :: start = 10_int16
158+
integer(int16) :: end = 23_int16
159+
160+
real(dp) :: r(15)
161+
162+
r = linspace(start, end, 15)
163+
end program demo_linspace_int16
164+
```
165+
166+
### `logspace` - Create a logarithmically spaced rank one array
167+
168+
#### Description
169+
170+
Returns a logarithmically spaced rank 1 array from [`base`^`start`, `base`^`end`]. The default size of the array is 50. Optionally, you can specify the length of the returned array by passing `n`. You can also specify the `base` used to compute the range (default 10).
171+
172+
#### Syntax
173+
174+
`res = [[stdlib_math(module):logspace(interface)]] (start, end [, n [, base]])`
175+
176+
#### Status
177+
178+
Experimental
179+
180+
#### Class
181+
182+
Function.
183+
184+
#### Argument(s)
185+
186+
`start`: Shall be a scalar of any numeric type. All kinds are supported for real and complex arguments. For integers, only the default kind is currently implemented. This argument is `intent(in)`.
187+
`end`: Shall be the same `type` and `kind` as `start`. This argument is `intent(in)`.
188+
`n`: Shall be an integer specifying the length of the output. This argument is `optional` and `intent(in)`.
189+
`base` : Shall be a scalar of any numeric type. All kinds are supported for real and complex arguments. For integers, only the default kind is currently implemented. This argument is `optional` and `intent(in)`.
190+
191+
#### Output value or Result value
192+
193+
The output is a rank 1 array whose length is either 50 (default value) or `n`.
194+
195+
If `n` == 1, return a rank 1 array whose only element is `base`^`end`.
196+
If `n` <= 0, return a rank 1 array with length 0
197+
198+
The `type` and `kind` of the output is dependent on the `type` and `kind` of the passed parameters.
199+
200+
For function calls where the `base` is not specified: `logspace(start, end)`/`logspace(start, end, n)`, the `type` and `kind` of
201+
the output follows the same scheme as above for `linspace`.
202+
>If `start`/`end` are `real` or `complex` types, the `result` will be the same type and kind as `start`/`end`.
203+
>If `start`/`end` are integer types, the `result` will default to a `real(dp)` array.
204+
205+
For function calls where the `base` is specified, the `type` and `kind` of the result is in accordance with the following table:
206+
207+
| `start`/`end` | `n` | `base` | `output` |
208+
| ------------- | --- | ------ | -------- |
209+
| `real(KIND)` | `Integer` | `real(KIND)` | `real(KIND)` |
210+
| " " | " " | `complex(KIND)` | `complex(KIND)` |
211+
| " " | " " | `Integer` | `real(KIND)` |
212+
| `complex(KIND)` | " " | `real(KIND)` | `complex(KIND)` |
213+
| " " | " " | `complex(KIND)` | `complex(KIND)` |
214+
| " " | " " | `Integer` | `complex(KIND)` |
215+
| `Integer` | " " | `real(KIND)` | `real(KIND)` |
216+
| " " | " " | `complex(KIND)` | `complex(KIND)` |
217+
| " " | " " | `Integer` | `Integer` |
218+
219+
#### Examples
220+
221+
##### Example 1:
222+
223+
Here inputs are of type `complex` and kind `dp`. `n` and `base` is not specified and thus default to 50 and 10, respectively.
224+
```fortran
225+
program demo_logspace_complex
226+
use stdlib_math, only: logspace
227+
use stdlib_kinds, only: dp
228+
implicit none
229+
230+
complex(dp) :: start = (10.0_dp, 5.0_dp)
231+
complex(dp) :: end = (-10.0_dp, 15.0_dp)
232+
233+
complex(dp) :: z(11) ! Complex values raised to complex powers results in complex values
234+
235+
z = logspace(start, end, 11)
236+
end program demo_logspace_complex
237+
```
238+
239+
##### Example 2:
240+
241+
Here inputs are of type `integer` and default kind. `base` is not specified and thus defaults to 10.
242+
```fortran
243+
program demo_logspace_int
244+
use stdlib_math, only: logspace
245+
use stdlib_kinds, only: dp
246+
implicit none
247+
248+
integer :: start = 10
249+
integer :: end = 23
250+
integer :: n = 15
251+
252+
real(dp) :: r(n) ! Integer values raised to real powers results in real values
253+
254+
r = logspace(start, end, n)
255+
end program demo_logspace_int
256+
```
257+
258+
##### Example 3:
259+
260+
Here `start`/`end` are of type `real` and double precision. `base` is type `complex` and also double precision.
261+
```fortran
262+
program demo_logspace_rstart_cbase
263+
use stdlib_math, only: logspace
264+
use stdlib_kinds, only: dp
265+
implicit none
266+
267+
real(dp) :: start = 0.0_dp
268+
real(dp) :: end = 3.0_dp
269+
integer :: n = 4
270+
complex(dp) :: base = (0.0_dp, 1.0_dp)
271+
272+
complex(dp) :: z(n) ! complex values raised to real powers result in complex values
273+
274+
z = logspace(start, end, n, base)
275+
276+
end program demo_logspace_rstart_cbase
277+
```

‎src/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ set(fppFiles
2929
stdlib_quadrature_simps.fypp
3030
stdlib_stats_distribution_PRNG.fypp
3131
stdlib_math.fypp
32+
stdlib_math_linspace.fypp
33+
stdlib_math_logspace.fypp
3234
stdlib_string_type.fypp
3335
)
3436

‎src/Makefile.manual

+7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ SRCFYPP =\
2525
stdlib_stats_moment_scalar.fypp \
2626
stdlib_stats_var.fypp \
2727
stdlib_math.fypp \
28+
stdlib_math_linspace.fypp \
29+
stdlib_math_logspace.fypp \
2830
stdlib_stats_distribution_PRNG.fypp \
2931
stdlib_string_type.fypp
3032

@@ -73,6 +75,7 @@ stdlib_error.o: stdlib_optval.o
7375
stdlib_specialfunctions.o: stdlib_kinds.o
7476
stdlib_specialfunctions_legendre.o: stdlib_kinds.o stdlib_specialfunctions.o
7577
stdlib_io.o: \
78+
stdlib_ascii.o \
7679
stdlib_error.o \
7780
stdlib_optval.o \
7881
stdlib_kinds.o
@@ -141,4 +144,8 @@ stdlib_strings.o: stdlib_ascii.o \
141144
stdlib_string_type.o \
142145
stdlib_optval.o
143146
stdlib_math.o: stdlib_kinds.o
147+
stdlib_math_linspace.o: \
148+
stdlib_math.o
149+
stdlib_math_logspace.o: \
150+
stdlib_math_linspace.o
144151
stdlib_linalg_outer_product.o: stdlib_linalg.o

‎src/stdlib_math.fypp

+250-3
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,268 @@
11
#:include "common.fypp"
22
#:set IR_KINDS_TYPES = INT_KINDS_TYPES + REAL_KINDS_TYPES
3+
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
34

45
module stdlib_math
56
use stdlib_kinds, only: int8, int16, int32, int64, sp, dp, qp
67

78
implicit none
89
private
9-
public :: clip
10+
public :: clip, linspace, logspace
11+
public :: EULERS_NUMBER_SP, EULERS_NUMBER_DP, EULERS_NUMBER_QP
12+
public :: DEFAULT_LINSPACE_LENGTH, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH
13+
14+
integer, parameter :: DEFAULT_LINSPACE_LENGTH = 100
15+
integer, parameter :: DEFAULT_LOGSPACE_LENGTH = 50
16+
integer, parameter :: DEFAULT_LOGSPACE_BASE = 10
17+
18+
! Useful constants for lnspace
19+
real(sp), parameter :: EULERS_NUMBER_SP = exp(1.0_sp)
20+
real(dp), parameter :: EULERS_NUMBER_DP = exp(1.0_dp)
21+
real(qp), parameter :: EULERS_NUMBER_QP = exp(1.0_qp)
1022

1123
interface clip
1224
#:for k1, t1 in IR_KINDS_TYPES
1325
module procedure clip_${k1}$
1426
#:endfor
1527
end interface clip
1628

29+
interface linspace
30+
!! Version: Experimental
31+
!!
32+
!! Create rank 1 array of linearly spaced elements
33+
!! If the number of elements is not specified, create an array with size 100. If n is a negative value,
34+
!! return an array with size 0. If n = 1, return an array whose only element is end
35+
!!([Specification](../page/specs/stdlib_math.html#linspace-create-a-linearly-spaced-rank-one-array))
36+
#:for k1, t1 in RC_KINDS_TYPES
37+
#:set RName = rname("linspace_default", 1, t1, k1)
38+
module function ${RName}$(start, end) result(res)
39+
${t1}$, intent(in) :: start
40+
${t1}$, intent(in) :: end
41+
42+
${t1}$ :: res(DEFAULT_LINSPACE_LENGTH)
43+
end function ${RName}$
44+
#:endfor
45+
46+
#:for k1, t1 in RC_KINDS_TYPES
47+
#:set RName = rname("linspace_n", 1, t1, k1)
48+
module function ${RName}$(start, end, n) result(res)
49+
${t1}$, intent(in) :: start
50+
${t1}$, intent(in) :: end
51+
integer, intent(in) :: n
52+
53+
${t1}$ :: res(n)
54+
end function ${RName}$
55+
#:endfor
56+
57+
58+
! Add support for integer linspace
59+
!!
60+
!! When dealing with integers as the `start` and `end` parameters, the return type is always a `real(dp)`.
61+
#:for k1, t1 in INT_KINDS_TYPES
62+
#:set RName = rname("linspace_default", 1, t1, k1)
63+
#! The interface for INT_KINDS_TYPES cannot be combined with RC_KINDS_TYPES
64+
#! because the output for integer types is always a real with dp.
65+
module function ${RName}$(start, end) result(res)
66+
${t1}$, intent(in) :: start
67+
${t1}$, intent(in) :: end
68+
69+
real(dp) :: res(DEFAULT_LINSPACE_LENGTH)
70+
end function ${RName}$
71+
#:endfor
72+
73+
#:for k1, t1 in INT_KINDS_TYPES
74+
#:set RName = rname("linspace_n", 1, t1, k1)
75+
module function ${RName}$(start, end, n) result(res)
76+
${t1}$, intent(in) :: start
77+
${t1}$, intent(in) :: end
78+
integer, intent(in) :: n
79+
80+
real(dp) :: res(n)
81+
end function ${RName}$
82+
#:endfor
83+
84+
end interface
85+
86+
interface logspace
87+
!! Version: Experimental
88+
!!
89+
!! Create rank 1 array of logarithmically spaced elements from base**start to base**end.
90+
!! If the number of elements is not specified, create an array with size 50. If n is a negative value,
91+
!! return an array with size 0. If n = 1, return an array whose only element is base**end. If no base
92+
!! is specified, logspace will default to using a base of 10
93+
!!
94+
!!([Specification](../page/specs/stdlib_math.html#logspace-create-a-logarithmically-spaced-rank-one-array))
95+
#!=========================================================
96+
#!= logspace(start, end) =
97+
#!=========================================================
98+
#:for k1, t1 in RC_KINDS_TYPES
99+
#:set RName = rname("logspace", 1, t1, k1, "default")
100+
module function ${RName}$(start, end) result(res)
101+
102+
${t1}$, intent(in) :: start
103+
${t1}$, intent(in) :: end
104+
105+
${t1}$ :: res(DEFAULT_LOGSPACE_LENGTH)
106+
107+
end function ${RName}$
108+
#:endfor
109+
#! Integer support
110+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "default")
111+
module function ${RName}$(start, end) result(res)
112+
113+
integer, intent(in) :: start
114+
integer, intent(in) :: end
115+
116+
real(dp) :: res(DEFAULT_LOGSPACE_LENGTH)
117+
118+
end function ${RName}$
119+
120+
#!=========================================================
121+
#!= logspace(start, end, n) =
122+
#!=========================================================
123+
#:for k1, t1 in RC_KINDS_TYPES
124+
#:set RName = rname("logspace", 1, t1, k1, "n")
125+
module function ${RName}$(start, end, n) result(res)
126+
${t1}$, intent(in) :: start
127+
${t1}$, intent(in) :: end
128+
integer, intent(in) :: n
129+
130+
${t1}$ :: res(n)
131+
end function ${RName}$
132+
#:endfor
133+
#! Integer support
134+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n")
135+
module function ${RName}$(start, end, n) result(res)
136+
integer, intent(in) :: start
137+
integer, intent(in) :: end
138+
integer, intent(in) :: n
139+
140+
real(dp) :: res(n)
141+
end function ${RName}$
142+
143+
#!=========================================================
144+
#!= logspace(start, end, n, base) =
145+
#!=========================================================
146+
#! Need another function where base is not optional,
147+
#! otherwise the compiler can not differentiate between
148+
#! generic calls to logspace_n where a base is not present
149+
#! ========================================================
150+
#:for k1, t1 in REAL_KINDS_TYPES
151+
! Generate logarithmically spaced sequence from ${k1}$ base to the powers
152+
! of ${k1}$ start and end. [base^start, ... , base^end]
153+
! Different combinations of parameter types will lead to different result types.
154+
! Those combinations are indicated in the body of each function.
155+
#:set RName = rname("logspace", 1, t1, k1, "n_rbase")
156+
module function ${RName}$(start, end, n, base) result(res)
157+
${t1}$, intent(in) :: start
158+
${t1}$, intent(in) :: end
159+
integer, intent(in) :: n
160+
${t1}$, intent(in) :: base
161+
! real(${k1}$) endpoints + real(${k1}$) base = real(${k1}$) result
162+
${t1}$ :: res(n)
163+
end function ${RName}$
164+
165+
#:set RName = rname("logspace", 1, t1, k1, "n_cbase")
166+
module function ${RName}$(start, end, n, base) result(res)
167+
${t1}$, intent(in) :: start
168+
${t1}$, intent(in) :: end
169+
integer, intent(in) :: n
170+
complex(${k1}$), intent(in) :: base
171+
! real(${k1}$) endpoints + complex(${k1}$) base = complex(${k1}$) result
172+
${t1}$ :: res(n)
173+
end function ${RName}$
174+
175+
#:set RName = rname("logspace", 1, t1, k1, "n_ibase")
176+
module function ${RName}$(start, end, n, base) result(res)
177+
${t1}$, intent(in) :: start
178+
${t1}$, intent(in) :: end
179+
integer, intent(in) :: n
180+
integer, intent(in) :: base
181+
! real(${k1}$) endpoints + integer base = real(${k1}$) result
182+
${t1}$ :: res(n)
183+
end function ${RName}$
184+
#:endfor
185+
#! ========================================================
186+
#! ========================================================
187+
#:for k1, t1 in CMPLX_KINDS_TYPES
188+
! Generate logarithmically spaced sequence from ${k1}$ base to the powers
189+
! of ${k1}$ start and end. [base^start, ... , base^end]
190+
! Different combinations of parameter types will lead to different result types.
191+
! Those combinations are indicated in the body of each function.
192+
#:set RName = rname("logspace", 1, t1, k1, "n_rbase")
193+
module function ${RName}$(start, end, n, base) result(res)
194+
${t1}$, intent(in) :: start
195+
${t1}$, intent(in) :: end
196+
integer, intent(in) :: n
197+
real(${k1}$), intent(in) :: base
198+
! complex(${k1}$) endpoints + real(${k1}$) base = complex(${k1}$) result
199+
${t1}$ :: res(n)
200+
end function ${RName}$
201+
202+
#:set RName = rname("logspace", 1, t1, k1, "n_cbase")
203+
module function ${RName}$(start, end, n, base) result(res)
204+
${t1}$, intent(in) :: start
205+
${t1}$, intent(in) :: end
206+
integer, intent(in) :: n
207+
complex(${k1}$), intent(in) :: base
208+
! complex(${k1}$) endpoints + complex(${k1}$) base = complex(${k1}$) result
209+
${t1}$ :: res(n)
210+
end function ${RName}$
211+
212+
#:set RName = rname("logspace", 1, t1, k1, "n_ibase")
213+
module function ${RName}$(start, end, n, base) result(res)
214+
${t1}$, intent(in) :: start
215+
${t1}$, intent(in) :: end
216+
integer, intent(in) :: n
217+
integer, intent(in) :: base
218+
! complex(${k1}$) endpoints + integer base = complex(${k1}$) result
219+
${t1}$ :: res(n)
220+
end function ${RName}$
221+
#:endfor
222+
#! ========================================================
223+
#! ========================================================
224+
#! Provide support for Integer start/endpoints
225+
! Generate logarithmically spaced sequence from ${k1}$ base to the powers
226+
! of ${k1}$ start and end. [base^start, ... , base^end]
227+
! Different combinations of parameter types will lead to different result types.
228+
! Those combinations are indicated in the body of each function.
229+
#:for k1 in REAL_KINDS
230+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_r" + str(k1) + "base")
231+
module function ${RName}$(start, end, n, base) result(res)
232+
integer, intent(in) :: start
233+
integer, intent(in) :: end
234+
integer, intent(in) :: n
235+
real(${k1}$), intent(in) :: base
236+
! integer endpoints + real(${k1}$) base = real(${k1}$) result
237+
real(${k1}$) :: res(n)
238+
end function ${RName}$
239+
240+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_c" + str(k1) + "base")
241+
module function ${RName}$(start, end, n, base) result(res)
242+
integer, intent(in) :: start
243+
integer, intent(in) :: end
244+
integer, intent(in) :: n
245+
complex(${k1}$), intent(in) :: base
246+
! integer endpoints + complex(${k1}$) base = complex(${k1}$) result
247+
complex(${k1}$) :: res(n)
248+
end function ${RName}$
249+
#:endfor
250+
251+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_ibase")
252+
module function ${RName}$(start, end, n, base) result(res)
253+
integer, intent(in) :: start
254+
integer, intent(in) :: end
255+
integer, intent(in) :: n
256+
integer, intent(in) :: base
257+
! integer endpoints + integer base = integer result
258+
integer :: res(n)
259+
end function ${RName}$
260+
261+
262+
end interface
263+
17264
contains
18-
265+
19266
#:for k1, t1 in IR_KINDS_TYPES
20267
elemental function clip_${k1}$(x, xmin, xmax) result(res)
21268
${t1}$, intent(in) :: x
@@ -25,6 +272,6 @@ contains
25272

26273
res = max(min(x, xmax), xmin)
27274
end function clip_${k1}$
28-
275+
29276
#:endfor
30277
end module stdlib_math

‎src/stdlib_math_linspace.fypp

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#:include "common.fypp"
2+
submodule (stdlib_math) stdlib_math_linspace
3+
4+
implicit none
5+
6+
contains
7+
8+
#:for k1, t1 in REAL_KINDS_TYPES
9+
#:set RName = rname("linspace_default", 1, t1, k1)
10+
module function ${RName}$(start, end) result(res)
11+
${t1}$, intent(in) :: start
12+
${t1}$, intent(in) :: end
13+
14+
${t1}$ :: res(DEFAULT_LINSPACE_LENGTH)
15+
16+
res = linspace(start, end, DEFAULT_LINSPACE_LENGTH)
17+
18+
end function ${RName}$
19+
#:endfor
20+
21+
#:for k1, t1 in REAL_KINDS_TYPES
22+
#:set RName = rname("linspace_n", 1, t1, k1)
23+
module function ${RName}$(start, end, n) result(res)
24+
${t1}$, intent(in) :: start
25+
${t1}$, intent(in) :: end
26+
integer, intent(in) :: n
27+
28+
${t1}$ :: res(n)
29+
30+
integer :: i ! Looping index
31+
${t1}$ :: interval ! Difference between adjacent elements
32+
33+
34+
if(n <= 0) return ! If passed length is less than or equal to 0, return an empty (allocated with length 0) array
35+
if(n == 1) then
36+
res(1) = end
37+
return
38+
end if
39+
40+
interval = (end - start) / real((n - 1), ${k1}$)
41+
42+
res(1) = start
43+
res(n) = end
44+
45+
do i = 2, n - 1
46+
47+
res(i) = real((i-1), ${k1}$) * interval + start
48+
49+
end do
50+
51+
end function ${RName}$
52+
#:endfor
53+
54+
55+
#:for k1, t1 in CMPLX_KINDS_TYPES
56+
#:set RName = rname("linspace_default", 1, t1, k1)
57+
module procedure ${RName}$
58+
59+
res = linspace(start, end, DEFAULT_LINSPACE_LENGTH)
60+
61+
end procedure ${RName}$
62+
#:endfor
63+
64+
#:for k1, t1 in CMPLX_KINDS_TYPES
65+
#:set RName = rname("linspace_n", 1, t1, k1)
66+
module procedure ${RName}$
67+
68+
real(${k1}$) :: x(n) ! array of the real part of complex number
69+
real(${k1}$) :: y(n) ! array of the imaginary part of the complex number
70+
71+
x = linspace(start%re, end%re, n)
72+
y = linspace(start%im, end%im, n)
73+
74+
res = cmplx(x, y, kind=${k1}$)
75+
76+
end procedure ${RName}$
77+
#:endfor
78+
79+
#:for k1, t1 in INT_KINDS_TYPES
80+
#:set RName = rname("linspace_default", 1, t1, k1)
81+
module procedure ${RName}$
82+
83+
res = linspace(real(start, kind=dp), real(end, kind=dp), DEFAULT_LINSPACE_LENGTH)
84+
85+
end procedure ${RName}$
86+
#:endfor
87+
88+
#:for k1, t1 in INT_KINDS_TYPES
89+
#:set RName = rname("linspace_n", 1, t1, k1)
90+
module procedure ${RName}$
91+
92+
res = linspace(real(start, kind=dp), real(end, kind=dp), n)
93+
94+
end procedure ${RName}$
95+
#:endfor
96+
97+
end submodule

‎src/stdlib_math_logspace.fypp

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#:include "common.fypp"
2+
#:set RC_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES
3+
4+
submodule (stdlib_math) stdlib_math_logspace
5+
6+
implicit none
7+
8+
contains
9+
10+
#!=========================================================
11+
#!= logspace(start, end) =
12+
#!=========================================================
13+
#:for k1, t1 in RC_KINDS_TYPES
14+
#:set RName = rname("logspace", 1, t1, k1, "default")
15+
module procedure ${RName}$
16+
res = logspace(start, end, DEFAULT_LOGSPACE_LENGTH, real(DEFAULT_LOGSPACE_BASE, ${k1}$))
17+
end procedure
18+
#:endfor
19+
#! Integer support
20+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "default")
21+
module procedure ${RName}$
22+
res = logspace(start, end, DEFAULT_LOGSPACE_LENGTH, DEFAULT_LOGSPACE_BASE)
23+
end procedure
24+
25+
#!=========================================================
26+
#!= logspace(start, end, n) =
27+
#!=========================================================
28+
#:for k1, t1 in RC_KINDS_TYPES
29+
#:set RName = rname("logspace", 1, t1, k1, "n")
30+
module procedure ${RName}$
31+
res = logspace(start, end, n, real(DEFAULT_LOGSPACE_BASE, ${k1}$))
32+
end procedure
33+
#:endfor
34+
#! Integer support
35+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n")
36+
module procedure ${RName}$
37+
res = logspace(start, end, n, DEFAULT_LOGSPACE_BASE)
38+
end procedure
39+
40+
#!=========================================================
41+
#!= logspace(start, end, n, base) =
42+
#!=========================================================
43+
#:for k1, t1 in RC_KINDS_TYPES
44+
#:set RName = rname("logspace", 1, t1, k1, "n_rbase")
45+
module procedure ${RName}$
46+
${t1}$ :: exponents(n)
47+
exponents = linspace(start, end, n)
48+
res = base ** exponents
49+
end procedure
50+
51+
#:set RName = rname("logspace", 1, t1, k1, "n_cbase")
52+
module procedure ${RName}$
53+
${t1}$ :: exponents(n)
54+
exponents = linspace(start, end, n)
55+
res = base ** exponents
56+
end procedure
57+
58+
#:set RName = rname("logspace", 1, t1, k1, "n_ibase")
59+
module procedure ${RName}$
60+
${t1}$ :: exponents(n)
61+
exponents = linspace(start, end, n)
62+
res = base ** exponents
63+
end procedure
64+
#:endfor
65+
#! Integer support:
66+
! Generate logarithmically spaced sequence from ${k1}$ base to the powers
67+
! of ${k1}$ start and end. [base^start, ... , base^end]
68+
! RName = ${RName}$
69+
#:for k1 in REAL_KINDS
70+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_r" + str(k1) + "base")
71+
module procedure ${RName}$
72+
integer :: exponents(n)
73+
exponents = linspace(start, end, n)
74+
res = base ** exponents
75+
end procedure
76+
77+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_c" + str(k1) + "base")
78+
module procedure ${RName}$
79+
integer :: exponents(n)
80+
exponents = linspace(start, end, n)
81+
res = base ** exponents
82+
end procedure
83+
#:endfor
84+
85+
#:set RName = rname("logspace", 1, "integer(int32)", "int32", "n_ibase")
86+
module procedure ${RName}$
87+
integer :: exponents(n)
88+
exponents = linspace(start, end, n)
89+
res = base ** exponents
90+
end procedure
91+
92+
93+
end submodule

‎src/tests/math/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
ADDTEST(stdlib_math)
2+
ADDTEST(linspace)
3+
ADDTEST(logspace)

‎src/tests/math/Makefile.manual

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
PROGS_SRC = test_stdlib_math.f90
1+
PROGS_SRC = test_stdlib_math.f90 test_linspace.f90 test_logspace.f90
22

33

44
include ../Makefile.manual.test.mk

‎src/tests/math/test_linspace.f90

+383
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
program test_linspace
2+
use stdlib_error, only: check
3+
use stdlib_kinds, only: sp, dp, int8, int16
4+
use stdlib_math, only: linspace, DEFAULT_LINSPACE_LENGTH
5+
6+
implicit none
7+
8+
integer :: iunit
9+
logical :: warn = .false.
10+
real(sp), parameter :: TOLERANCE_SP = 1000 * epsilon(1.0_sp)
11+
real(dp), parameter :: TOLERANCE_DP = 1000 * epsilon(1.0_dp) ! Percentage of the range for which the actual gap must not exceed
12+
13+
! Testing linspace.
14+
!
15+
! For single and double precision, check if the beginning and end values are properly recorded
16+
! and make sure that the size of the result array is as expected.
17+
!
18+
! This testing suite makes use of the a repeated section of code that will check to make
19+
! sure that every element is linearly spaced (i.e., call check(|array(i+1) - array(i)| < |expected_value| * TOLERANCE)).
20+
! I would convert this repeated code into a subroutine but that would require the implementation of a
21+
! generic procedure given that each linear space will have a different expected_value type and kind.
22+
23+
open(newunit=iunit, file="test_linspace_log.txt", status="unknown") ! Log the results of the functions
24+
25+
write(iunit,*) "Writing to unit #: ", iunit
26+
27+
call test_linspace_sp
28+
call test_linspace_dp
29+
call test_linspace_neg_index ! Make sure that when passed a negative index the result is an empty array
30+
call test_linspace_cmplx
31+
call test_linspace_cmplx_2
32+
call test_linspace_cmplx_3
33+
call test_linspace_cmplx_sp
34+
call test_linspace_cmplx_sp_2
35+
call test_linspace_int16
36+
call test_linspace_int8
37+
38+
close(unit=iunit)
39+
40+
contains
41+
42+
subroutine test_linspace_sp
43+
44+
integer, parameter :: n = 20
45+
real(sp) :: start = 1.0_sp
46+
real(sp) :: end = 10.0_sp
47+
real(sp) :: expected_interval
48+
real(sp) :: true_difference
49+
50+
integer :: i
51+
real(sp), allocatable :: x(:)
52+
53+
x = linspace(start, end, n)
54+
55+
expected_interval =( end - start ) / real(( n - 1 ), sp)
56+
57+
call check(x(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
58+
call check(x(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
59+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
60+
61+
print *, "Made it through first round of tests"
62+
63+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
64+
do i = 1, n-1
65+
66+
true_difference = x(i + 1) - x(i)
67+
call check(abs(true_difference - expected_interval) < abs(expected_interval) * TOLERANCE_SP)
68+
69+
end do
70+
71+
end subroutine
72+
73+
subroutine test_linspace_dp
74+
75+
real(dp) :: start = 1.0_dp
76+
real(dp) :: end = 10.0_dp
77+
integer, parameter :: n = DEFAULT_LINSPACE_LENGTH
78+
real(dp) :: expected_interval
79+
real(dp) :: true_difference
80+
81+
real(dp), allocatable :: x(:)
82+
integer :: i
83+
84+
x = linspace(start, end)
85+
86+
expected_interval =( end - start ) / ( n - 1 )
87+
88+
call check(x(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
89+
call check(x(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
90+
call check(size(x) == n, msg="Array not allocated to default size", warn=warn)
91+
92+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
93+
do i = 1, n-1
94+
95+
true_difference = x(i + 1) - x(i)
96+
call check(abs(true_difference - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
97+
98+
end do
99+
100+
end subroutine
101+
102+
subroutine test_linspace_neg_index
103+
104+
real(dp) :: start = 1.0_dp
105+
real(dp) :: end = 10.0_dp
106+
107+
real(dp), allocatable :: x(:)
108+
109+
x = linspace(start, end, -15)
110+
111+
call check(size(x) == 0, msg="Allocated array is not empty", warn=warn)
112+
113+
end subroutine
114+
115+
subroutine test_linspace_cmplx
116+
117+
complex(dp) :: start = (0.0_dp, 10.0_dp)
118+
complex(dp) :: end = (1.0_dp, 0.0_dp)
119+
complex(dp) :: expected_interval
120+
integer, parameter :: n = 10
121+
122+
complex(dp), allocatable :: z(:)
123+
124+
integer :: i
125+
126+
z = linspace(start, end, n)
127+
128+
expected_interval =( end - start ) / ( n - 1 )
129+
130+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
131+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
132+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
133+
134+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
135+
do i = 1, n-1
136+
137+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
138+
139+
end do
140+
141+
write(unit=iunit, fmt=*) "linspace((0.0_dp, 10.0_dp), (1.0_dp, 0.0_dp), 10): "
142+
write(unit=iunit,fmt='(70("="))')
143+
do i = 1, n
144+
write(unit=iunit,fmt=*) z(i)
145+
end do
146+
write(iunit,*)
147+
write(iunit,*)
148+
149+
end subroutine
150+
151+
subroutine test_linspace_cmplx_2
152+
153+
complex(dp) :: start = (10.0_dp, 10.0_dp)
154+
complex(dp) :: end = (1.0_dp, 1.0_dp)
155+
complex(dp) :: expected_interval
156+
157+
integer, parameter :: n = 5
158+
159+
complex(dp), allocatable :: z(:)
160+
161+
integer :: i
162+
163+
z = linspace(start, end, n)
164+
165+
expected_interval =( end - start ) / ( n - 1 )
166+
167+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
168+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
169+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
170+
171+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
172+
do i = 1, n-1
173+
174+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
175+
176+
end do
177+
178+
write(unit=iunit, fmt=*) "linspace((10.0_dp, 10.0_dp), (1.0_dp, 1.0_dp), 5): "
179+
write(unit=iunit,fmt='(70("="))')
180+
do i = 1, n
181+
write(unit=iunit,fmt=*) z(i)
182+
end do
183+
write(iunit,*)
184+
write(iunit,*)
185+
186+
end subroutine
187+
188+
subroutine test_linspace_cmplx_3
189+
190+
complex(dp) :: start = (-5.0_dp, 100.0_dp)
191+
complex(dp) :: end = (20.0_dp, 13.0_dp)
192+
complex(dp) :: expected_interval
193+
194+
integer, parameter :: n = 20
195+
196+
complex(dp), allocatable :: z(:)
197+
198+
integer :: i
199+
200+
z = linspace(start, end, n)
201+
202+
expected_interval = ( end - start ) / ( n - 1 )
203+
204+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
205+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
206+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
207+
208+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
209+
do i = 1, n-1
210+
211+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
212+
213+
end do
214+
215+
write(unit=iunit, fmt=*) "linspace((-5.0_dp, 100.0_dp), (20.0_dp, 13.0_dp), 20): "
216+
write(unit=iunit,fmt='(70("="))')
217+
do i = 1, n
218+
write(unit=iunit,fmt=*) z(i)
219+
end do
220+
write(iunit,*)
221+
write(iunit,*)
222+
223+
end subroutine
224+
225+
subroutine test_linspace_cmplx_sp
226+
227+
complex(sp) :: start = (0.5_sp, 5.0_sp)
228+
complex(sp) :: end = (1.0_sp, -30.0_sp)
229+
complex(sp) :: expected_interval
230+
231+
integer, parameter :: n = 10
232+
233+
complex(sp), allocatable :: z(:)
234+
235+
integer :: i
236+
237+
z = linspace(start, end, n)
238+
239+
expected_interval =( end - start ) / ( n - 1 )
240+
241+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
242+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
243+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
244+
245+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
246+
do i = 1, n-1
247+
248+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_SP)
249+
250+
end do
251+
252+
write(unit=iunit, fmt=*) "linspace((0.5_sp, 5.0_sp), (1.0_sp, -30.0_sp), 10): "
253+
write(unit=iunit,fmt='(70("="))')
254+
do i = 1, n
255+
write(unit=iunit,fmt=*) z(i)
256+
end do
257+
write(iunit,*)
258+
write(iunit,*)
259+
260+
end subroutine
261+
262+
subroutine test_linspace_cmplx_sp_2
263+
264+
complex(sp) :: start = (50.0_sp, 500.0_sp)
265+
complex(sp) :: end = (-100.0_sp, 2000.0_sp)
266+
complex(sp) :: expected_interval
267+
complex(sp) :: true_interval
268+
real(sp) :: offset
269+
270+
integer, parameter :: n = DEFAULT_LINSPACE_LENGTH
271+
272+
complex(sp), allocatable :: z(:)
273+
274+
integer :: i
275+
276+
z = linspace(start, end)
277+
278+
expected_interval =( end - start ) / ( n - 1 )
279+
280+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
281+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
282+
call check(size(z) == n, msg="Array not allocated to default size", warn=warn)
283+
284+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
285+
do i = 1, n-1
286+
287+
true_interval = (z(i + 1) - z(i))
288+
offset = abs(true_interval - expected_interval)
289+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_SP)
290+
! print *, i
291+
292+
end do
293+
294+
write(unit=iunit, fmt=*) "linspace((50.0_sp, 500.0_sp), (-100.0_sp, 2000.0_sp)): "
295+
write(unit=iunit,fmt='(70("="))')
296+
do i = 1, n
297+
write(unit=iunit,fmt=*) z(i)
298+
end do
299+
write(iunit,*)
300+
write(iunit,*)
301+
302+
end subroutine
303+
304+
subroutine test_linspace_int16
305+
306+
integer(int16) :: start = 5
307+
integer(int16) :: end = 10
308+
real(dp) :: expected_interval
309+
310+
integer, parameter :: n = 6
311+
312+
integer(int16), allocatable :: z(:)
313+
314+
integer :: i
315+
316+
z = linspace(start, end, n)
317+
318+
expected_interval =( end - start ) / ( n - 1 )
319+
320+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
321+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
322+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
323+
324+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
325+
do i = 1, n-1
326+
327+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
328+
329+
end do
330+
331+
write(unit=iunit, fmt=*) "linspace(5_int16, 10_int16, 10): "
332+
write(unit=iunit,fmt='(70("="))')
333+
do i = 1, n
334+
write(unit=iunit,fmt=*) z(i)
335+
end do
336+
write(iunit,*)
337+
write(iunit,*)
338+
339+
end subroutine
340+
341+
subroutine test_linspace_int8
342+
343+
integer(int8) :: start = 20
344+
integer(int8) :: end = 50
345+
346+
real(dp) :: expected_interval
347+
348+
integer, parameter :: n = 10
349+
350+
real(dp), allocatable :: z(:)
351+
integer(int8) :: z_int(n)
352+
353+
integer :: i
354+
355+
z = linspace(start, end, n)
356+
z_int = linspace(start, end, n)
357+
358+
expected_interval =real( end - start, dp ) / ( n - 1 )
359+
360+
call check(z(1) == start, msg="Initial value of array is not equal to the passed start parameter", warn=warn)
361+
call check(z(n) == end, msg="Final array value is not equal to end parameter", warn=warn)
362+
call check(size(z) == n, msg="Array not allocated to correct size", warn=warn)
363+
364+
! Due to roundoff error, it is possible that the jump from x(n-1) to x(n) is slightly different than the expected interval
365+
do i = 1, n-1
366+
367+
call check(abs( ( z(i + 1) - z(i) ) - expected_interval) < abs(expected_interval) * TOLERANCE_DP)
368+
369+
end do
370+
371+
write(unit=iunit, fmt=*) "linspace(5_int16, 10_int16, 10): "
372+
write(unit=iunit,fmt='(70("="))')
373+
do i = 1, n
374+
write(unit=iunit,fmt=*) z(i)
375+
end do
376+
write(iunit,*)
377+
write(iunit,*)
378+
379+
end subroutine
380+
381+
382+
383+
end program

‎src/tests/math/test_logspace.f90

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
program test_logspace
2+
3+
use stdlib_error, only: check
4+
use stdlib_kinds, only: sp, dp, int8, int16, int32, int64
5+
use stdlib_math, only: logspace, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH
6+
7+
implicit none
8+
9+
logical :: warn = .false.
10+
11+
integer :: iunit
12+
13+
! Testing logspace
14+
!
15+
! logspace should return a rank 1 array of values equally logarithmically spaced
16+
! from the base**start to base**end, using 10 as the base. If no length
17+
! is specified, return a rank 1 array with 50 elements.
18+
!
19+
! Also test to verify that the proportion between adjacent elements is constant within
20+
! a certain tolerance
21+
22+
real(sp), parameter :: TOLERANCE_SP = 1000 * epsilon(1.0_sp)
23+
real(dp), parameter :: TOLERANCE_DP = 1000 * epsilon(1.0_dp) ! Percentage of the range for which the actual gap must not exceed
24+
25+
open(newunit=iunit, file="test_logspace_log.txt", status="unknown") ! Log the results of the function
26+
27+
write(iunit,*) "Writing to unit #: ", iunit
28+
29+
call test_logspace_sp
30+
call test_logspace_dp
31+
call test_logspace_default
32+
call test_logspace_base_2
33+
call test_logspace_base_2_cmplx_start
34+
call test_logspace_base_i_int_start
35+
36+
close(unit=iunit)
37+
38+
contains
39+
40+
subroutine test_logspace_sp
41+
42+
integer :: n = 20
43+
real(sp) :: start = 0.0_sp
44+
real(sp) :: end = 2.0_sp
45+
46+
real(sp) :: expected_proportion
47+
integer :: i = 1
48+
49+
real(sp), allocatable :: x(:)
50+
51+
x = logspace(start, end, n)
52+
53+
expected_proportion = 10 ** ( ( end - start ) / ( n - 1 ) )
54+
55+
call check(x(1) == DEFAULT_LOGSPACE_BASE ** start, msg="Initial value of array is not equal to 10^start", warn=warn)
56+
call check(x(n) == DEFAULT_LOGSPACE_BASE ** end, msg="Final value of array is not equal to 10^end", warn=warn)
57+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
58+
59+
do i = 1, n-1
60+
61+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_SP)
62+
63+
end do
64+
65+
66+
write(unit=iunit, fmt=*) "logspace(0.0_sp, 2.0_sp, 20): "
67+
write(unit=iunit,fmt='(70("="))')
68+
write(unit=iunit,fmt="(20(F7.3, 2X))") x
69+
write(iunit,*)
70+
write(iunit,*)
71+
72+
end subroutine
73+
74+
subroutine test_logspace_dp
75+
76+
integer :: n = 10
77+
real(dp) :: start = 1.0_dp
78+
real(dp) :: end = 0.0_dp
79+
real(dp) :: expected_proportion
80+
integer :: i = 1
81+
82+
real(dp), allocatable :: x(:)
83+
84+
x = logspace(start, end, n)
85+
86+
expected_proportion = 10 ** ( ( end - start ) / ( n - 1 ) )
87+
88+
89+
call check(x(1) == DEFAULT_LOGSPACE_BASE ** start, msg="Initial value of array is not equal to 10^start", warn=warn)
90+
call check(x(n) == DEFAULT_LOGSPACE_BASE ** end, msg="Final value of array is not equal to 10^end", warn=warn)
91+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
92+
93+
do i = 1, n-1
94+
95+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_DP)
96+
97+
end do
98+
99+
write(unit=iunit, fmt=*) "logspace(1.0_dp, 0.0_dp, 10): "
100+
write(unit=iunit,fmt=99)
101+
write(unit=iunit,fmt="(10(F7.3, 2X))") x
102+
write(iunit,*)
103+
write(iunit,*)
104+
105+
99 format(70("="))
106+
107+
end subroutine
108+
109+
subroutine test_logspace_default
110+
111+
real(dp) :: start = 0.0_dp
112+
real(dp) :: end = 1.0_dp
113+
integer :: n = DEFAULT_LOGSPACE_LENGTH
114+
real(dp) :: expected_proportion
115+
integer :: i
116+
117+
real(dp), allocatable :: x(:)
118+
119+
x = logspace(start, end)
120+
121+
expected_proportion = 10 ** ( ( end - start ) / ( n - 1 ) )
122+
123+
124+
call check(x(1) == DEFAULT_LOGSPACE_BASE ** start, msg="Initial value of array is not equal to 10^start", warn=warn)
125+
call check(x(n) == DEFAULT_LOGSPACE_BASE ** end, msg="Final value of array is not equal to 10^end", warn=warn)
126+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
127+
128+
do i = 1, n-1
129+
130+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_DP)
131+
132+
end do
133+
134+
write(unit=iunit, fmt=*) "logspace(0.0_dp, 1.0_dp): "
135+
write(unit=iunit,fmt='(70("="))')
136+
write(unit=iunit,fmt="(50(F7.3, 2X))") x
137+
write(iunit,*)
138+
write(iunit,*)
139+
140+
end subroutine
141+
142+
subroutine test_logspace_base_2
143+
144+
integer :: n = 10
145+
real(dp) :: start = 1.0_dp
146+
real(dp) :: end = 10.0_dp
147+
integer :: base = 2
148+
integer :: i
149+
real(dp) :: expected_proportion
150+
151+
real(dp), allocatable :: x(:)
152+
153+
x = logspace(start, end, n, base)
154+
155+
expected_proportion = 2 ** ( ( end - start ) / ( n - 1 ) )
156+
157+
call check(x(1) == base ** start, msg="Initial value of array is not equal to 2^start", warn=warn)
158+
call check(x(n) == base ** end, msg="Final value of array is not equal to 2^end", warn=warn)
159+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
160+
161+
do i = 1, n-1
162+
163+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_DP)
164+
165+
end do
166+
167+
write(unit=iunit, fmt=*) "logspace(1.0_dp, 10.0_dp, 10, 2): "
168+
write(unit=iunit,fmt='(70("="))')
169+
write(unit=iunit,fmt="(10(F9.3, 2X))") x
170+
write(iunit,*)
171+
write(iunit,*)
172+
173+
end subroutine
174+
175+
subroutine test_logspace_base_2_cmplx_start
176+
177+
integer :: n = 10
178+
complex(dp) :: start = (1, 0)
179+
complex(dp) :: end = (0, 1)
180+
integer :: base = 2
181+
complex(dp) :: expected_proportion
182+
integer :: i
183+
184+
complex(dp), allocatable :: x(:)
185+
186+
x = logspace(start, end, n, base)
187+
188+
expected_proportion = 2 ** ( ( end - start ) / ( n - 1 ) )
189+
190+
191+
call check(x(1) == base ** start, msg="Initial value of array is not equal to 2^start", warn=warn)
192+
call check(x(n) == base ** end, msg="Final value of array is not equal to 2^end", warn=warn)
193+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
194+
195+
do i = 1, n-1
196+
197+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_DP)
198+
199+
end do
200+
201+
write(unit=iunit, fmt=*) "logspace(1, i, 10, 2): "
202+
write(unit=iunit,fmt='(70("="))')
203+
write(unit=iunit,fmt="(10('(', F6.3, ',', 1X, F6.3, ')', 2X))") x
204+
write(iunit,*)
205+
write(iunit,*)
206+
207+
end subroutine
208+
209+
subroutine test_logspace_base_i_int_start
210+
211+
integer :: n = 5
212+
integer :: start = 1
213+
integer :: end = 5
214+
complex(dp) :: base = (0, 1) ! i
215+
complex(dp) :: expected_proportion
216+
integer :: i = 1
217+
218+
complex(dp), allocatable :: x(:)
219+
220+
x = logspace(start, end, n, base)
221+
222+
expected_proportion = base ** ( ( end - start ) / ( n - 1 ) )
223+
224+
call check(x(1) == base ** start, msg="Initial value of array is not equal to 2^start", warn=warn)
225+
call check(x(n) == base ** end, msg="Final value of array is not equal to 2^end", warn=warn)
226+
call check(size(x) == n, msg="Array not allocated to appropriate size", warn=warn)
227+
228+
do i = 1, n-1
229+
230+
call check(abs(x(i + 1) / x(i) - expected_proportion) < abs(expected_proportion) * TOLERANCE_DP)
231+
232+
end do
233+
234+
write(unit=iunit, fmt=*) "logspace(1, 5, 5, i): "
235+
write(unit=iunit,fmt='(70("="))')
236+
write(unit=iunit,fmt="(10('(', F6.3, ',', 1X, F6.3, ')', 2X))") x
237+
write(iunit,*)
238+
write(iunit,*)
239+
240+
end subroutine
241+
242+
243+
end program

0 commit comments

Comments
 (0)
Please sign in to comment.