(git:34ef472)
cp_array_sort.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 
8 ! **************************************************************************************************
9 !> \brief Routine for sorting an array
10 !> \note
11 !> CP2K:
12 !> Please use the interface defined in util.F for calling sort().
13 !>
14 !> DBCSR:
15 !> Please use the interface defined in dbcsr_toollib.F for calling sort().
16 !> \par History
17 !> 12.2012 first version [ole]
18 !> \author Ole Schuett
19 ! **************************************************************************************************
21 
22  USE kinds, ONLY: sp, dp, int_4, int_8
23 
24 #include "../base/base_uses.f90"
25 
26 
27  IMPLICIT NONE
28  PRIVATE
29 
31 
32 CONTAINS
33 
34 
35 ! **************************************************************************************************
36 !> \brief Sorts an array inplace using a combination of merge- and bubble-sort.
37 !> It also returns the indices, which the elements had before the sort.
38 !> \param arr the array to sort
39 !> \param n length of array
40 !> \param indices returns elements-indices before the sort
41 !> \par History
42 !> 12.2012 created [ole]
43 !> \author Ole Schuett
44 ! **************************************************************************************************
45  SUBROUTINE cp_1d_s_sort(arr, n, indices)
46  INTEGER, INTENT(IN) :: n
47  REAL(kind=sp), DIMENSION(1:n), INTENT(INOUT) :: arr
48  integer, DIMENSION(1:n), INTENT(INOUT) :: indices
49 
50  INTEGER :: i
51  REAL(kind=sp), ALLOCATABLE :: tmp_arr(:)
52  INTEGER, ALLOCATABLE :: tmp_idx(:)
53 
54  IF (n > 1) THEN
55  ! scratch space used during the merge step
56  ALLOCATE (tmp_arr((n + 1)/2), tmp_idx((n + 1)/2))
57 
58  indices = (/(i, i=1, n)/)
59 
60  CALL cp_1d_s_sort_low(arr(1:n), indices, tmp_arr, tmp_idx)
61 
62  DEALLOCATE (tmp_arr, tmp_idx)
63  ELSE IF (n > 0) THEN ! can be a frequent case in cp2k
64  indices(1) = 1
65  END IF
66 
67  END SUBROUTINE cp_1d_s_sort
68 
69 ! **************************************************************************************************
70 !> \brief The actual sort routing. Only cp_1d_s_sort and itself should call this.
71 !> \param arr the array to sort
72 !> \param indices elements-indices before the sort
73 !> \param tmp_arr scratch space
74 !> \param tmp_idx scratch space
75 !> \par History
76 !> 12.2012 created [ole]
77 !> \author Ole Schuett
78 ! **************************************************************************************************
79  RECURSIVE SUBROUTINE cp_1d_s_sort_low(arr, indices, tmp_arr, tmp_idx)
80  REAL(kind=sp), DIMENSION(:), INTENT(INOUT) :: arr
81  INTEGER, DIMENSION(size(arr)), INTENT(INOUT) :: indices
82  REAL(kind=sp), DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_arr
83  INTEGER, DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_idx
84  REAL(kind=sp) :: a
85  INTEGER :: t, m, i, j, k
86  LOGICAL :: swapped
87  ! a,t: used during swaping of elements in arr and indices
88 
89  swapped = .true.
90 
91  ! If only a few elements are left we switch to bubble-sort for efficiency.
92  IF (size(arr) <= 7) THEN ! 7 seems to be a good choice for the moment
93  DO j = size(arr) - 1, 1, -1
94  swapped = .false.
95  DO i = 1, j
96  IF (cp_1d_s_less_than(arr(i + 1), arr(i))) THEN
97  ! swap arr(i) with arr(i+1)
98  a = arr(i)
99  arr(i) = arr(i + 1)
100  arr(i + 1) = a
101  ! swap indices(i) with indices(i+1)
102  t = indices(i)
103  indices(i) = indices(i + 1)
104  indices(i + 1) = t
105  swapped = .true.
106  END IF
107  END DO
108  IF (.NOT. swapped) EXIT
109  END DO
110  RETURN
111  END IF
112 
113  ! split list in half and recursively sort both sublists
114  m = (size(arr) + 1)/2 ! index where we're going to divide the list in two
115  CALL cp_1d_s_sort_low(arr(1:m), indices(1:m), tmp_arr, tmp_idx)
116  CALL cp_1d_s_sort_low(arr(m + 1:), indices(m + 1:), tmp_arr, tmp_idx)
117 
118  ! Check for a special case: Can we just concate the two sorted sublists?
119  ! This leads to O(n) scaling if the input is already sorted.
120  IF (cp_1d_s_less_than(arr(m + 1), arr(m))) THEN
121  ! ...no - let's merge the two sorted sublists arr(:m) and arr(m+1:)
122  ! Merge will be performed directly in arr. Need backup of first sublist.
123  tmp_arr(1:m) = arr(1:m)
124  tmp_idx(1:m) = indices(1:m)
125  i = 1; ! number of elemens consumed from 1st sublist
126  j = 1; ! number of elemens consumed from 2nd sublist
127  k = 1; ! number of elemens already merged
128 
129  DO WHILE (i <= m .and. j <= size(arr) - m)
130  IF (cp_1d_s_less_than(arr(m + j), tmp_arr(i))) THEN
131  arr(k) = arr(m + j)
132  indices(k) = indices(m + j)
133  j = j + 1
134  ELSE
135  arr(k) = tmp_arr(i)
136  indices(k) = tmp_idx(i)
137  i = i + 1
138  END IF
139  k = k + 1
140  END DO
141 
142  ! One of the two sublist is now empty.
143  ! Copy possibly remaining tail of 1st sublist
144  DO WHILE (i <= m)
145  arr(k) = tmp_arr(i)
146  indices(k) = tmp_idx(i)
147  i = i + 1
148  k = k + 1
149  END DO
150  ! The possibly remaining tail of 2nd sublist is already at the right spot.
151  END IF
152 
153  END SUBROUTINE cp_1d_s_sort_low
154 
155 
156  PURE FUNCTION cp_1d_s_less_than(a, b) RESULT(res)
157  REAL(kind=sp), INTENT(IN) :: a, b
158  LOGICAL :: res
159  res = a < b
160  END FUNCTION cp_1d_s_less_than
161 
162 
163 ! **************************************************************************************************
164 !> \brief Sorts an array inplace using a combination of merge- and bubble-sort.
165 !> It also returns the indices, which the elements had before the sort.
166 !> \param arr the array to sort
167 !> \param n length of array
168 !> \param indices returns elements-indices before the sort
169 !> \par History
170 !> 12.2012 created [ole]
171 !> \author Ole Schuett
172 ! **************************************************************************************************
173  SUBROUTINE cp_1d_r_sort(arr, n, indices)
174  INTEGER, INTENT(IN) :: n
175  REAL(kind=dp), DIMENSION(1:n), INTENT(INOUT) :: arr
176  integer, DIMENSION(1:n), INTENT(INOUT) :: indices
177 
178  INTEGER :: i
179  REAL(kind=dp), ALLOCATABLE :: tmp_arr(:)
180  INTEGER, ALLOCATABLE :: tmp_idx(:)
181 
182  IF (n > 1) THEN
183  ! scratch space used during the merge step
184  ALLOCATE (tmp_arr((n + 1)/2), tmp_idx((n + 1)/2))
185 
186  indices = (/(i, i=1, n)/)
187 
188  CALL cp_1d_r_sort_low(arr(1:n), indices, tmp_arr, tmp_idx)
189 
190  DEALLOCATE (tmp_arr, tmp_idx)
191  ELSE IF (n > 0) THEN ! can be a frequent case in cp2k
192  indices(1) = 1
193  END IF
194 
195  END SUBROUTINE cp_1d_r_sort
196 
197 ! **************************************************************************************************
198 !> \brief The actual sort routing. Only cp_1d_r_sort and itself should call this.
199 !> \param arr the array to sort
200 !> \param indices elements-indices before the sort
201 !> \param tmp_arr scratch space
202 !> \param tmp_idx scratch space
203 !> \par History
204 !> 12.2012 created [ole]
205 !> \author Ole Schuett
206 ! **************************************************************************************************
207  RECURSIVE SUBROUTINE cp_1d_r_sort_low(arr, indices, tmp_arr, tmp_idx)
208  REAL(kind=dp), DIMENSION(:), INTENT(INOUT) :: arr
209  INTEGER, DIMENSION(size(arr)), INTENT(INOUT) :: indices
210  REAL(kind=dp), DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_arr
211  INTEGER, DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_idx
212  REAL(kind=dp) :: a
213  INTEGER :: t, m, i, j, k
214  LOGICAL :: swapped
215  ! a,t: used during swaping of elements in arr and indices
216 
217  swapped = .true.
218 
219  ! If only a few elements are left we switch to bubble-sort for efficiency.
220  IF (size(arr) <= 7) THEN ! 7 seems to be a good choice for the moment
221  DO j = size(arr) - 1, 1, -1
222  swapped = .false.
223  DO i = 1, j
224  IF (cp_1d_r_less_than(arr(i + 1), arr(i))) THEN
225  ! swap arr(i) with arr(i+1)
226  a = arr(i)
227  arr(i) = arr(i + 1)
228  arr(i + 1) = a
229  ! swap indices(i) with indices(i+1)
230  t = indices(i)
231  indices(i) = indices(i + 1)
232  indices(i + 1) = t
233  swapped = .true.
234  END IF
235  END DO
236  IF (.NOT. swapped) EXIT
237  END DO
238  RETURN
239  END IF
240 
241  ! split list in half and recursively sort both sublists
242  m = (size(arr) + 1)/2 ! index where we're going to divide the list in two
243  CALL cp_1d_r_sort_low(arr(1:m), indices(1:m), tmp_arr, tmp_idx)
244  CALL cp_1d_r_sort_low(arr(m + 1:), indices(m + 1:), tmp_arr, tmp_idx)
245 
246  ! Check for a special case: Can we just concate the two sorted sublists?
247  ! This leads to O(n) scaling if the input is already sorted.
248  IF (cp_1d_r_less_than(arr(m + 1), arr(m))) THEN
249  ! ...no - let's merge the two sorted sublists arr(:m) and arr(m+1:)
250  ! Merge will be performed directly in arr. Need backup of first sublist.
251  tmp_arr(1:m) = arr(1:m)
252  tmp_idx(1:m) = indices(1:m)
253  i = 1; ! number of elemens consumed from 1st sublist
254  j = 1; ! number of elemens consumed from 2nd sublist
255  k = 1; ! number of elemens already merged
256 
257  DO WHILE (i <= m .and. j <= size(arr) - m)
258  IF (cp_1d_r_less_than(arr(m + j), tmp_arr(i))) THEN
259  arr(k) = arr(m + j)
260  indices(k) = indices(m + j)
261  j = j + 1
262  ELSE
263  arr(k) = tmp_arr(i)
264  indices(k) = tmp_idx(i)
265  i = i + 1
266  END IF
267  k = k + 1
268  END DO
269 
270  ! One of the two sublist is now empty.
271  ! Copy possibly remaining tail of 1st sublist
272  DO WHILE (i <= m)
273  arr(k) = tmp_arr(i)
274  indices(k) = tmp_idx(i)
275  i = i + 1
276  k = k + 1
277  END DO
278  ! The possibly remaining tail of 2nd sublist is already at the right spot.
279  END IF
280 
281  END SUBROUTINE cp_1d_r_sort_low
282 
283 
284  PURE FUNCTION cp_1d_r_less_than(a, b) RESULT(res)
285  REAL(kind=dp), INTENT(IN) :: a, b
286  LOGICAL :: res
287  res = a < b
288  END FUNCTION cp_1d_r_less_than
289 
290 
291 ! **************************************************************************************************
292 !> \brief Sorts an array inplace using a combination of merge- and bubble-sort.
293 !> It also returns the indices, which the elements had before the sort.
294 !> \param arr the array to sort
295 !> \param n length of array
296 !> \param indices returns elements-indices before the sort
297 !> \par History
298 !> 12.2012 created [ole]
299 !> \author Ole Schuett
300 ! **************************************************************************************************
301  SUBROUTINE cp_1d_i4_sort(arr, n, indices)
302  INTEGER, INTENT(IN) :: n
303  INTEGER(kind=int_4), DIMENSION(1:n), INTENT(INOUT) :: arr
304  integer, DIMENSION(1:n), INTENT(INOUT) :: indices
305 
306  INTEGER :: i
307  INTEGER(kind=int_4), ALLOCATABLE :: tmp_arr(:)
308  INTEGER, ALLOCATABLE :: tmp_idx(:)
309 
310  IF (n > 1) THEN
311  ! scratch space used during the merge step
312  ALLOCATE (tmp_arr((n + 1)/2), tmp_idx((n + 1)/2))
313 
314  indices = (/(i, i=1, n)/)
315 
316  CALL cp_1d_i4_sort_low(arr(1:n), indices, tmp_arr, tmp_idx)
317 
318  DEALLOCATE (tmp_arr, tmp_idx)
319  ELSE IF (n > 0) THEN ! can be a frequent case in cp2k
320  indices(1) = 1
321  END IF
322 
323  END SUBROUTINE cp_1d_i4_sort
324 
325 ! **************************************************************************************************
326 !> \brief The actual sort routing. Only cp_1d_i4_sort and itself should call this.
327 !> \param arr the array to sort
328 !> \param indices elements-indices before the sort
329 !> \param tmp_arr scratch space
330 !> \param tmp_idx scratch space
331 !> \par History
332 !> 12.2012 created [ole]
333 !> \author Ole Schuett
334 ! **************************************************************************************************
335  RECURSIVE SUBROUTINE cp_1d_i4_sort_low(arr, indices, tmp_arr, tmp_idx)
336  INTEGER(kind=int_4), DIMENSION(:), INTENT(INOUT) :: arr
337  INTEGER, DIMENSION(size(arr)), INTENT(INOUT) :: indices
338  INTEGER(kind=int_4), DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_arr
339  INTEGER, DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_idx
340  INTEGER(kind=int_4) :: a
341  INTEGER :: t, m, i, j, k
342  LOGICAL :: swapped
343  ! a,t: used during swaping of elements in arr and indices
344 
345  swapped = .true.
346 
347  ! If only a few elements are left we switch to bubble-sort for efficiency.
348  IF (size(arr) <= 7) THEN ! 7 seems to be a good choice for the moment
349  DO j = size(arr) - 1, 1, -1
350  swapped = .false.
351  DO i = 1, j
352  IF (cp_1d_i4_less_than(arr(i + 1), arr(i))) THEN
353  ! swap arr(i) with arr(i+1)
354  a = arr(i)
355  arr(i) = arr(i + 1)
356  arr(i + 1) = a
357  ! swap indices(i) with indices(i+1)
358  t = indices(i)
359  indices(i) = indices(i + 1)
360  indices(i + 1) = t
361  swapped = .true.
362  END IF
363  END DO
364  IF (.NOT. swapped) EXIT
365  END DO
366  RETURN
367  END IF
368 
369  ! split list in half and recursively sort both sublists
370  m = (size(arr) + 1)/2 ! index where we're going to divide the list in two
371  CALL cp_1d_i4_sort_low(arr(1:m), indices(1:m), tmp_arr, tmp_idx)
372  CALL cp_1d_i4_sort_low(arr(m + 1:), indices(m + 1:), tmp_arr, tmp_idx)
373 
374  ! Check for a special case: Can we just concate the two sorted sublists?
375  ! This leads to O(n) scaling if the input is already sorted.
376  IF (cp_1d_i4_less_than(arr(m + 1), arr(m))) THEN
377  ! ...no - let's merge the two sorted sublists arr(:m) and arr(m+1:)
378  ! Merge will be performed directly in arr. Need backup of first sublist.
379  tmp_arr(1:m) = arr(1:m)
380  tmp_idx(1:m) = indices(1:m)
381  i = 1; ! number of elemens consumed from 1st sublist
382  j = 1; ! number of elemens consumed from 2nd sublist
383  k = 1; ! number of elemens already merged
384 
385  DO WHILE (i <= m .and. j <= size(arr) - m)
386  IF (cp_1d_i4_less_than(arr(m + j), tmp_arr(i))) THEN
387  arr(k) = arr(m + j)
388  indices(k) = indices(m + j)
389  j = j + 1
390  ELSE
391  arr(k) = tmp_arr(i)
392  indices(k) = tmp_idx(i)
393  i = i + 1
394  END IF
395  k = k + 1
396  END DO
397 
398  ! One of the two sublist is now empty.
399  ! Copy possibly remaining tail of 1st sublist
400  DO WHILE (i <= m)
401  arr(k) = tmp_arr(i)
402  indices(k) = tmp_idx(i)
403  i = i + 1
404  k = k + 1
405  END DO
406  ! The possibly remaining tail of 2nd sublist is already at the right spot.
407  END IF
408 
409  END SUBROUTINE cp_1d_i4_sort_low
410 
411 
412  PURE FUNCTION cp_1d_i4_less_than(a, b) RESULT(res)
413  INTEGER(kind=int_4), INTENT(IN) :: a, b
414  LOGICAL :: res
415  res = a < b
416  END FUNCTION cp_1d_i4_less_than
417 
418 
419 ! **************************************************************************************************
420 !> \brief Sorts an array inplace using a combination of merge- and bubble-sort.
421 !> It also returns the indices, which the elements had before the sort.
422 !> \param arr the array to sort
423 !> \param n length of array
424 !> \param indices returns elements-indices before the sort
425 !> \par History
426 !> 12.2012 created [ole]
427 !> \author Ole Schuett
428 ! **************************************************************************************************
429  SUBROUTINE cp_1d_i8_sort(arr, n, indices)
430  INTEGER, INTENT(IN) :: n
431  INTEGER(kind=int_8), DIMENSION(1:n), INTENT(INOUT) :: arr
432  integer, DIMENSION(1:n), INTENT(INOUT) :: indices
433 
434  INTEGER :: i
435  INTEGER(kind=int_8), ALLOCATABLE :: tmp_arr(:)
436  INTEGER, ALLOCATABLE :: tmp_idx(:)
437 
438  IF (n > 1) THEN
439  ! scratch space used during the merge step
440  ALLOCATE (tmp_arr((n + 1)/2), tmp_idx((n + 1)/2))
441 
442  indices = (/(i, i=1, n)/)
443 
444  CALL cp_1d_i8_sort_low(arr(1:n), indices, tmp_arr, tmp_idx)
445 
446  DEALLOCATE (tmp_arr, tmp_idx)
447  ELSE IF (n > 0) THEN ! can be a frequent case in cp2k
448  indices(1) = 1
449  END IF
450 
451  END SUBROUTINE cp_1d_i8_sort
452 
453 ! **************************************************************************************************
454 !> \brief The actual sort routing. Only cp_1d_i8_sort and itself should call this.
455 !> \param arr the array to sort
456 !> \param indices elements-indices before the sort
457 !> \param tmp_arr scratch space
458 !> \param tmp_idx scratch space
459 !> \par History
460 !> 12.2012 created [ole]
461 !> \author Ole Schuett
462 ! **************************************************************************************************
463  RECURSIVE SUBROUTINE cp_1d_i8_sort_low(arr, indices, tmp_arr, tmp_idx)
464  INTEGER(kind=int_8), DIMENSION(:), INTENT(INOUT) :: arr
465  INTEGER, DIMENSION(size(arr)), INTENT(INOUT) :: indices
466  INTEGER(kind=int_8), DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_arr
467  INTEGER, DIMENSION((size(arr) + 1)/2), INTENT(INOUT) :: tmp_idx
468  INTEGER(kind=int_8) :: a
469  INTEGER :: t, m, i, j, k
470  LOGICAL :: swapped
471  ! a,t: used during swaping of elements in arr and indices
472 
473  swapped = .true.
474 
475  ! If only a few elements are left we switch to bubble-sort for efficiency.
476  IF (size(arr) <= 7) THEN ! 7 seems to be a good choice for the moment
477  DO j = size(arr) - 1, 1, -1
478  swapped = .false.
479  DO i = 1, j
480  IF (cp_1d_i8_less_than(arr(i + 1), arr(i))) THEN
481  ! swap arr(i) with arr(i+1)
482  a = arr(i)
483  arr(i) = arr(i + 1)
484  arr(i + 1) = a
485  ! swap indices(i) with indices(i+1)
486  t = indices(i)
487  indices(i) = indices(i + 1)
488  indices(i + 1) = t
489  swapped = .true.
490  END IF
491  END DO
492  IF (.NOT. swapped) EXIT
493  END DO
494  RETURN
495  END IF
496 
497  ! split list in half and recursively sort both sublists
498  m = (size(arr) + 1)/2 ! index where we're going to divide the list in two
499  CALL cp_1d_i8_sort_low(arr(1:m), indices(1:m), tmp_arr, tmp_idx)
500  CALL cp_1d_i8_sort_low(arr(m + 1:), indices(m + 1:), tmp_arr, tmp_idx)
501 
502  ! Check for a special case: Can we just concate the two sorted sublists?
503  ! This leads to O(n) scaling if the input is already sorted.
504  IF (cp_1d_i8_less_than(arr(m + 1), arr(m))) THEN
505  ! ...no - let's merge the two sorted sublists arr(:m) and arr(m+1:)
506  ! Merge will be performed directly in arr. Need backup of first sublist.
507  tmp_arr(1:m) = arr(1:m)
508  tmp_idx(1:m) = indices(1:m)
509  i = 1; ! number of elemens consumed from 1st sublist
510  j = 1; ! number of elemens consumed from 2nd sublist
511  k = 1; ! number of elemens already merged
512 
513  DO WHILE (i <= m .and. j <= size(arr) - m)
514  IF (cp_1d_i8_less_than(arr(m + j), tmp_arr(i))) THEN
515  arr(k) = arr(m + j)
516  indices(k) = indices(m + j)
517  j = j + 1
518  ELSE
519  arr(k) = tmp_arr(i)
520  indices(k) = tmp_idx(i)
521  i = i + 1
522  END IF
523  k = k + 1
524  END DO
525 
526  ! One of the two sublist is now empty.
527  ! Copy possibly remaining tail of 1st sublist
528  DO WHILE (i <= m)
529  arr(k) = tmp_arr(i)
530  indices(k) = tmp_idx(i)
531  i = i + 1
532  k = k + 1
533  END DO
534  ! The possibly remaining tail of 2nd sublist is already at the right spot.
535  END IF
536 
537  END SUBROUTINE cp_1d_i8_sort_low
538 
539 
540  PURE FUNCTION cp_1d_i8_less_than(a, b) RESULT(res)
541  INTEGER(kind=int_8), INTENT(IN) :: a, b
542  LOGICAL :: res
543  res = a < b
544  END FUNCTION cp_1d_i8_less_than
545 
546 END MODULE cp_array_sort
Routine for sorting an array.
Definition: cp_array_sort.F:20
subroutine, public cp_1d_i4_sort(arr, n, indices)
Sorts an array inplace using a combination of merge- and bubble-sort. It also returns the indices,...
subroutine, public cp_1d_i8_sort(arr, n, indices)
Sorts an array inplace using a combination of merge- and bubble-sort. It also returns the indices,...
subroutine, public cp_1d_s_sort(arr, n, indices)
Sorts an array inplace using a combination of merge- and bubble-sort. It also returns the indices,...
Definition: cp_array_sort.F:46
subroutine, public cp_1d_r_sort(arr, n, indices)
Sorts an array inplace using a combination of merge- and bubble-sort. It also returns the indices,...
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public int_8
Definition: kinds.F:54
integer, parameter, public dp
Definition: kinds.F:34
integer, parameter, public sp
Definition: kinds.F:33
integer, parameter, public int_4
Definition: kinds.F:51