Skip to content

Commit 6957436

Browse files
authored
Merge pull request #605 from zoziha/add_diff
[stdlib_math] Add function `diff`
2 parents 084f3c4 + cc2344e commit 6957436

File tree

7 files changed

+354
-2
lines changed

7 files changed

+354
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ Features available from the latest git source
3636
[#488](https://github.com/fortran-lang/stdlib/pull/488)
3737
- new procedures `arg`, `argd` and `argpi`
3838
[#498](https://github.com/fortran-lang/stdlib/pull/498)
39+
- new procedure `diff`
40+
[#605](https://github.com/fortran-lang/stdlib/pull/605)
3941

4042
Changes to existing modules
4143

doc/specs/stdlib_math.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,3 +649,88 @@ program demo_math_all_close
649649
650650
end program demo_math_all_close
651651
```
652+
653+
### `diff`
654+
655+
#### Description
656+
657+
Computes differences between adjacent elements of an array.
658+
659+
#### Syntax
660+
661+
For a rank-1 array
662+
```fortran
663+
y = [[stdlib_math(module):diff(interface)]](x [, n, prepend, append])
664+
```
665+
and for a rank-2 array
666+
```fortran
667+
y = [[stdlib_math(module):diff(interface)]](x [, n, dim, prepend, append])
668+
```
669+
670+
#### Status
671+
672+
Experimental.
673+
674+
#### Class
675+
676+
Pure function.
677+
678+
#### Arguments
679+
680+
`x`: The array to take a difference of.
681+
Shall be a `real/integer` and `rank-1/rank-2` array.
682+
This argument is `intent(in)`.
683+
684+
`n`: How many times to iteratively calculate the difference.
685+
Shall be an `integer` scalar.
686+
This argument is `intent(in)` and `optional`, and has value of `1` by default.
687+
688+
`dim`: The dimension of the input array along which to calculate the difference.
689+
Its value must be between `1` and `rank(x)`.
690+
Shall be an `integer` scalar.
691+
This argument is `intent(in)` and `optional` and has a value of `1` by default.
692+
693+
`prepend`, `append`: Arrays to prepend or append to a along axis prior to performing the difference.
694+
The dimension and shape must match a except along axis.
695+
Shall be a `real/integer` and `rank-1/rank-2` array.
696+
This argument is `intent(in)` and `optional`, which is no value by default.
697+
698+
Note:
699+
- The `x`, `prepend` and `append` arguments must have the same `type`, `kind` and `rank`.
700+
- If the value of `n` is less than or equal to `0` (which is not recommended), the return value of `diff` is `x`.
701+
- If the value of `dim` is not equal to `1` or `2` (which is not recommended),
702+
`1` will be used by the internal process of `diff`.
703+
704+
705+
#### Result value
706+
707+
Returns the finite difference of the input array.
708+
Shall be a `real/integer` and `rank-1/rank-2` array.
709+
When both `prepend` and `append` are not present, the result `y` has one fewer element than `x` alongside the dimension `dim`.
710+
711+
#### Example
712+
713+
```fortran
714+
program demo_diff
715+
716+
use stdlib_math, only: diff
717+
implicit none
718+
719+
integer :: i(7) = [1, 1, 2, 3, 5, 8, 13]
720+
real :: x(6) = [0, 5, 15, 30, 50, 75]
721+
integer :: A(3, 3) = reshape([1, 7, 17, 3, 11, 19, 5, 13, 23], [3, 3])
722+
integer :: Y(3, 2)
723+
724+
print *, diff(i) ! [0, 1, 1, 2, 3, 5]
725+
print *, diff(x, 2) ! [5.0, 5.0, 5.0, 5.0]
726+
727+
Y = diff(A, n=1, dim=2)
728+
print *, Y(1, :) ! [2, 2]
729+
print *, Y(2, :) ! [4, 2]
730+
print *, Y(3, :) ! [2, 4]
731+
732+
print *, diff(i, prepend=[0]) ! [1, 0, 1, 1, 2, 3, 5]
733+
print *, diff(i, append=[21]) ! [0, 1, 1, 2, 3, 5, 8]
734+
735+
end program demo_diff
736+
```

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ set(fppFiles
5151
stdlib_math_arange.fypp
5252
stdlib_math_is_close.fypp
5353
stdlib_math_all_close.fypp
54+
stdlib_math_diff.fypp
5455
stdlib_string_type.fypp
5556
stdlib_string_type_constructor.fypp
5657
stdlib_strings_to_string.fypp

src/Makefile.manual

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ SRCFYPP = \
4848
stdlib_math_logspace.fypp \
4949
stdlib_math_is_close.fypp \
5050
stdlib_math_all_close.fypp \
51+
stdlib_math_diff.fypp \
5152
stdlib_string_type.fypp \
5253
stdlib_string_type_constructor.fypp \
5354
stdlib_strings.fypp \
@@ -238,6 +239,7 @@ stdlib_math_is_close.o: \
238239
stdlib_math_all_close.o: \
239240
stdlib_math.o \
240241
stdlib_math_is_close.o
242+
stdlib_math_diff.o: stdlib_math.o
241243
stdlib_stringlist_type.o: stdlib_string_type.o \
242244
stdlib_math.o \
243245
stdlib_optval.o

src/stdlib_math.fypp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module stdlib_math
1414
public :: EULERS_NUMBER_QP
1515
#:endif
1616
public :: DEFAULT_LINSPACE_LENGTH, DEFAULT_LOGSPACE_BASE, DEFAULT_LOGSPACE_LENGTH
17-
public :: arange, arg, argd, argpi, is_close, all_close
17+
public :: arange, arg, argd, argpi, is_close, all_close, diff
1818

1919
integer, parameter :: DEFAULT_LINSPACE_LENGTH = 100
2020
integer, parameter :: DEFAULT_LOGSPACE_LENGTH = 50
@@ -359,6 +359,28 @@ module stdlib_math
359359
#:endfor
360360
#:endfor
361361
end interface all_close
362+
363+
!> Version: experimental
364+
!>
365+
!> Computes differences between adjacent elements of an array.
366+
!> ([Specification](../page/specs/stdlib_math.html#diff))
367+
interface diff
368+
#:set RI_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES
369+
#:for k1, t1 in RI_KINDS_TYPES
370+
pure module function diff_1_${k1}$(x, n, prepend, append) result(y)
371+
${t1}$, intent(in) :: x(:)
372+
integer, intent(in), optional :: n
373+
${t1}$, intent(in), optional :: prepend(:), append(:)
374+
${t1}$, allocatable :: y(:)
375+
end function diff_1_${k1}$
376+
pure module function diff_2_${k1}$(X, n, dim, prepend, append) result(y)
377+
${t1}$, intent(in) :: x(:, :)
378+
integer, intent(in), optional :: n, dim
379+
${t1}$, intent(in), optional :: prepend(:, :), append(:, :)
380+
${t1}$, allocatable :: y(:, :)
381+
end function diff_2_${k1}$
382+
#:endfor
383+
end interface diff
362384

363385
contains
364386

src/stdlib_math_diff.fypp

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
!> Inspired by original code (MIT license) written in 2016 by Keurfon Luu (keurfonluu@outlook.com)
2+
!> https://github.com/keurfonluu/Forlab
3+
4+
#:include "common.fypp"
5+
#:set RI_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES
6+
submodule (stdlib_math) stdlib_math_diff
7+
8+
implicit none
9+
10+
contains
11+
12+
!> `diff` computes differences of adjacent elements of an array.
13+
14+
#:for k1, t1 in RI_KINDS_TYPES
15+
pure module function diff_1_${k1}$(x, n, prepend, append) result(y)
16+
${t1}$, intent(in) :: x(:)
17+
integer, intent(in), optional :: n
18+
${t1}$, intent(in), optional :: prepend(:), append(:)
19+
${t1}$, allocatable :: y(:)
20+
integer :: size_prepend, size_append, size_x, size_work
21+
integer :: n_, i
22+
23+
n_ = optval(n, 1)
24+
if (n_ <= 0) then
25+
y = x
26+
return
27+
end if
28+
29+
size_prepend = 0
30+
size_append = 0
31+
if (present(prepend)) size_prepend = size(prepend)
32+
if (present(append)) size_append = size(append)
33+
size_x = size(x)
34+
size_work = size_x + size_prepend + size_append
35+
36+
if (size_work <= n_) then
37+
allocate(y(0))
38+
return
39+
end if
40+
41+
!> Use a quick exit for the common case, to avoid memory allocation.
42+
if (size_prepend == 0 .and. size_append == 0 .and. n_ == 1) then
43+
y = x(2:) - x(1:size_x-1)
44+
return
45+
end if
46+
47+
block
48+
${t1}$ :: work(size_work)
49+
if (size_prepend > 0) work(:size_prepend) = prepend
50+
work(size_prepend+1:size_prepend+size_x) = x
51+
if (size_append > 0) work(size_prepend+size_x+1:) = append
52+
53+
do i = 1, n_
54+
work(1:size_work-i) = work(2:size_work-i+1) - work(1:size_work-i)
55+
end do
56+
57+
y = work(1:size_work-n_)
58+
end block
59+
60+
end function diff_1_${k1}$
61+
62+
pure module function diff_2_${k1}$(x, n, dim, prepend, append) result(y)
63+
${t1}$, intent(in) :: x(:, :)
64+
integer, intent(in), optional :: n, dim
65+
${t1}$, intent(in), optional :: prepend(:, :), append(:, :)
66+
${t1}$, allocatable :: y(:, :)
67+
integer :: size_prepend, size_append, size_x, size_work
68+
integer :: n_, dim_, i
69+
70+
n_ = optval(n, 1)
71+
if (n_ <= 0) then
72+
y = x
73+
return
74+
end if
75+
76+
size_prepend = 0
77+
size_append = 0
78+
if (present(dim)) then
79+
if (dim == 1 .or. dim == 2) then
80+
dim_ = dim
81+
else
82+
dim_ = 1
83+
end if
84+
else
85+
dim_ = 1
86+
end if
87+
88+
if (present(prepend)) size_prepend = size(prepend, dim_)
89+
if (present(append)) size_append = size(append, dim_)
90+
size_x = size(x, dim_)
91+
size_work = size_x + size_prepend + size_append
92+
93+
if (size_work <= n_) then
94+
allocate(y(0, 0))
95+
return
96+
end if
97+
98+
!> Use a quick exit for the common case, to avoid memory allocation.
99+
if (size_prepend == 0 .and. size_append == 0 .and. n_ == 1) then
100+
if (dim_ == 1) then
101+
y = x(2:, :) - x(1:size_x-1, :)
102+
elseif (dim_ == 2) then
103+
y = x(:, 2:) - x(:, 1:size_x-1)
104+
end if
105+
return
106+
end if
107+
108+
if (dim_ == 1) then
109+
block
110+
${t1}$ :: work(size_work, size(x, 2))
111+
if (size_prepend > 0) work(1:size_prepend, :) = prepend
112+
work(size_prepend+1:size_x+size_prepend, :) = x
113+
if (size_append > 0) work(size_x+size_prepend+1:, :) = append
114+
do i = 1, n_
115+
work(1:size_work-i, :) = work(2:size_work-i+1, :) - work(1:size_work-i, :)
116+
end do
117+
118+
y = work(1:size_work-n_, :)
119+
end block
120+
121+
elseif (dim_ == 2) then
122+
block
123+
${t1}$ :: work(size(x, 1), size_work)
124+
if (size_prepend > 0) work(:, 1:size_prepend) = prepend
125+
work(:, size_prepend+1:size_x+size_prepend) = x
126+
if (size_append > 0) work(:, size_x+size_prepend+1:) = append
127+
do i = 1, n_
128+
work(:, 1:size_work-i) = work(:, 2:size_work-i+1) - work(:, 1:size_work-i)
129+
end do
130+
131+
y = work(:, 1:size_work-n_)
132+
end block
133+
134+
end if
135+
136+
end function diff_2_${k1}$
137+
#:endfor
138+
139+
end submodule stdlib_math_diff

0 commit comments

Comments
 (0)