(git:374b731)
Loading...
Searching...
No Matches
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
32CONTAINS
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
546END MODULE cp_array_sort
Routine for sorting an array.
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,...
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