Line data Source code
1 : !!****m* ABINIT/m_mergesort
2 : !!
3 : !! NAME
4 : !! m_mergesort
5 : !!
6 : !! FUNCTION
7 : !! Module for sorting integer arrays using merge sorting algorithm
8 : !!
9 : !!
10 : !! COPYRIGHT
11 : !! Copyright (C) 2010-2026 ABINIT group (hexu)
12 : !! This file is distributed under the terms of the
13 : !! GNU General Public Licence, see ~abinit/COPYING
14 : !! or http://www.gnu.org/copyleft/gpl.txt .
15 : !! For the initials of contributors, see ~abinit/doc/developers/contributors.txt .
16 : !!
17 : !! SOURCE
18 :
19 :
20 : #if defined HAVE_CONFIG_H
21 : #include "config.h"
22 : #endif
23 : #include "abi_common.h"
24 :
25 :
26 : module m_mergesort
27 : !!***
28 : use defs_basis
29 : use m_errors
30 : use m_abicore
31 : use m_mathfuncs, only: array_lessthan, array_morethan, array_le
32 : implicit none
33 : private
34 : public :: MergeSort2D
35 : public :: MergeSort
36 :
37 : interface MergeSort
38 : procedure MergeSort_dp
39 : procedure MergeSort_int
40 : end interface MergeSort
41 :
42 : contains
43 :
44 : !======== Merge sort algorithm for 1d array ===============
45 : ! Modified from https://rosettacode.org/wiki/Sorting_algorithms/Merge_sort#Fortran
46 : ! The original code is public domain.
47 :
48 0 : subroutine merge_int(A, B, C)
49 : ! The targe attribute is necessary, because A .or. B might overlap with C.
50 : integer, target, intent(in) :: A(:), B(:)
51 : integer, target, intent(inout) :: C(:)
52 : integer :: i, j, k
53 :
54 0 : if (size(A) + size(B) > size(C)) then
55 0 : stop (1)
56 : end if
57 :
58 0 : i = 1; j = 1
59 0 : do k = 1, size(C)
60 0 : if (i <= size(A) .and. j <= size(B)) then
61 0 : if (A(i) <= B(j)) then
62 0 : C(k) = A(i)
63 0 : i = i + 1
64 : else
65 0 : C(k) = B(j)
66 0 : j = j + 1
67 : end if
68 0 : else if (i <= size(A)) then
69 0 : C(k) = A(i)
70 0 : i = i + 1
71 0 : else if (j <= size(B)) then
72 0 : C(k) = B(j)
73 0 : j = j + 1
74 : end if
75 : end do
76 0 : end subroutine merge_int
77 :
78 0 : subroutine merge_with_order_int(A, B, C, orderA, orderB, orderC)
79 : ! The targe attribute is necessary, because A .or. B might overlap with C.
80 : integer, target, intent(in) :: A(:), B(:), orderA(:), orderB(:)
81 : integer, target, intent(inout) :: C(:), orderC(:)
82 : integer :: i, j, k
83 :
84 0 : if (size(A) + size(B) > size(C)) then
85 0 : stop (1)
86 : end if
87 :
88 0 : i = 1; j = 1
89 0 : do k = 1, size(C)
90 0 : if (i <= size(A) .and. j <= size(B)) then
91 0 : if (A(i) <= B(j)) then
92 0 : C(k) = A(i)
93 0 : orderC(k) = orderA(i)
94 0 : i = i + 1
95 : else
96 0 : C(k) = B(j)
97 0 : orderC(k) =orderB(j)
98 0 : j = j + 1
99 : end if
100 0 : else if (i <= size(A)) then
101 0 : C(k) = A(i)
102 0 : orderC(k) = orderA(i)
103 0 : i = i + 1
104 0 : else if (j <= size(B)) then
105 0 : C(k) = B(j)
106 0 : orderC(k) = orderB(j)
107 0 : j = j + 1
108 : end if
109 : end do
110 0 : end subroutine merge_with_order_int
111 :
112 :
113 12286 : subroutine swap_int(x, y)
114 : integer, intent(inout) :: x, y
115 : integer :: tmp
116 12286 : tmp = x; x = y; y = tmp
117 12286 : end subroutine swap_int
118 :
119 0 : recursive subroutine MergeSort_no_init_order_int(A, work, order, worder)
120 : integer, intent(inout) :: A(:)
121 : integer, intent(inout) :: work(:)
122 : integer, optional, intent(inout):: order(size(A)), worder(size(work))
123 : integer :: half, n
124 : logical :: ordered
125 0 : n=size(A)
126 0 : ordered=.False.
127 0 : if (present(order)) then
128 0 : ordered=.True.
129 : end if
130 :
131 0 : half = (size(A) + 1) / 2
132 0 : if (size(A) < 2) then
133 : continue
134 0 : else if (size(A) == 2) then
135 0 : if (A(1) > A(2)) then
136 0 : call swap_int(A(1), A(2))
137 0 : if(ordered) call swap_int(order(1), order(2))
138 : end if
139 : else
140 0 : if(ordered) then
141 0 : call MergeSort_no_init_order_int(A( : half), work, order(:half), worder)
142 0 : call MergeSort_no_init_order_int(A(half + 1 :), work, order(half+1:), worder)
143 0 : if (A(half) > A(half + 1)) then
144 0 : work(1 : half) = A(1 : half)
145 0 : worder(1:half) = order(1: half)
146 : call merge_with_order_int(work(1 : half), A(half + 1:), A &
147 0 : &, worder(1 : half), order(half + 1:), order)
148 : endif
149 : else
150 0 : call MergeSort_no_init_order_int(A( : half), work)
151 0 : call MergeSort_no_init_order_int(A(half + 1 :), work)
152 0 : if (A(half) > A(half + 1)) then
153 0 : work(1 : half) = A(1 : half)
154 0 : call merge_int(work(1 : half), A(half + 1:), A)
155 : endif
156 : endif
157 : end if
158 0 : end subroutine MergeSort_No_Init_Order_Int
159 :
160 0 : subroutine MergeSort_int(A, work, order, worder)
161 : integer, intent(inout) :: A(:)
162 : integer, intent(inout) :: work(:)
163 : integer, optional, intent(inout):: order(size(A)), worder(size(work))
164 : integer :: i
165 0 : if (present(order)) then
166 0 : do i =1 , size(A)
167 0 : order(i) = i
168 : end do
169 : end if
170 0 : call MergeSort_no_init_order_int(A, work, order, worder)
171 0 : end subroutine MergeSort_int
172 :
173 :
174 0 : subroutine merge_dp(A, B, C)
175 : ! The targe attribute is necessary, because A .or. B might overlap with C.
176 : real(dp), target, intent(in) :: A(:), B(:)
177 : real(dp), target, intent(inout) :: C(:)
178 : integer :: i, j, k
179 0 : if (size(A) + size(B) > size(C)) then
180 0 : stop (1)
181 : end if
182 0 : i = 1; j = 1
183 0 : do k = 1, size(C)
184 0 : if (i <= size(A) .and. j <= size(B)) then
185 0 : if (A(i) <= B(j)) then
186 0 : C(k) = A(i)
187 0 : i = i + 1
188 : else
189 0 : C(k) = B(j)
190 0 : j = j + 1
191 : end if
192 0 : else if (i <= size(A)) then
193 0 : C(k) = A(i)
194 0 : i = i + 1
195 0 : else if (j <= size(B)) then
196 0 : C(k) = B(j)
197 0 : j = j + 1
198 : end if
199 : end do
200 0 : end subroutine merge_dp
201 :
202 :
203 :
204 18216 : subroutine merge_with_order_dp(A, B, C, orderA, orderB, orderC)
205 : ! The targe attribute is necessary, because A .or. B might overlap with C.
206 : real(dp), target, intent(in) :: A(:), B(:)
207 : integer, target, intent(in) :: orderA(:), orderB(:)
208 : real(dp), target, intent(inout) :: C(:)
209 : integer, target, intent(inout) :: orderC(:)
210 : integer :: i, j, k
211 :
212 18216 : if (size(A) + size(B) > size(C)) then
213 0 : stop (1)
214 : end if
215 18216 : i = 1; j = 1
216 305330 : do k = 1, size(C)
217 305330 : if (i <= size(A) .and. j <= size(B)) then
218 247959 : if (A(i) <= B(j)) then
219 129364 : C(k) = A(i)
220 129364 : orderC(k) = orderA(i)
221 129364 : i = i + 1
222 : else
223 118595 : C(k) = B(j)
224 118595 : orderC(k) =orderB(j)
225 118595 : j = j + 1
226 : end if
227 39155 : else if (i <= size(A)) then
228 18775 : C(k) = A(i)
229 18775 : orderC(k) = orderA(i)
230 18775 : i = i + 1
231 20380 : else if (j <= size(B)) then
232 20380 : C(k) = B(j)
233 20380 : orderC(k) = orderB(j)
234 20380 : j = j + 1
235 : end if
236 : end do
237 18216 : end subroutine merge_with_order_dp
238 :
239 :
240 :
241 8944 : subroutine swap_dp(x, y)
242 : real(dp), intent(inout) :: x, y
243 : real(dp) :: tmp
244 8944 : tmp = x; x = y; y = tmp
245 : end subroutine swap_dp
246 :
247 :
248 130332 : recursive subroutine MergeSort_no_init_order_dp(A, work, order, worder)
249 : real(dp), intent(inout) :: A(:)
250 : real(dp), intent(inout) :: work(:)
251 : integer, optional, intent(inout):: order(size(A)), worder(size(work))
252 : integer :: half, n
253 : logical :: ordered
254 43444 : n=size(A)
255 43444 : ordered=.False.
256 43444 : if (present(order)) then
257 43444 : ordered=.True.
258 : end if
259 :
260 43444 : half = (size(A) + 1) / 2
261 43444 : if (size(A) < 2) then
262 : continue
263 39972 : else if (size(A) == 2) then
264 18336 : if (A(1) > A(2)) then
265 8944 : call swap_dp(A(1), A(2))
266 8944 : if(ordered) call swap_int(order(1), order(2))
267 : end if
268 : else
269 21636 : if(ordered) then
270 21636 : call MergeSort_no_init_order_dp(A( : half), work, order(:half), worder)
271 21636 : call MergeSort_no_init_order_dp(A(half + 1 :), work, order(half+1:), worder)
272 21636 : if (A(half) > A(half + 1)) then
273 166355 : work(1 : half) = A(1 : half)
274 166355 : worder(1:half) = order(1: half)
275 : call merge_with_order_dp(work(1 : half), A(half + 1:), A &
276 18216 : &, worder(1 : half), order(half + 1:), order)
277 : endif
278 : else
279 0 : call MergeSort_no_init_order_dp(A( : half), work)
280 0 : call MergeSort_no_init_order_dp(A(half + 1 :), work)
281 0 : if (A(half) > A(half + 1)) then
282 0 : work(1 : half) = A(1 : half)
283 0 : call merge_dp(work(1 : half), A(half + 1:), A)
284 : endif
285 : endif
286 : end if
287 86888 : end subroutine MergeSort_No_Init_Order_Dp
288 :
289 516 : subroutine MergeSort_dp(A, work, order, worder)
290 : real(dp), intent(inout) :: A(:)
291 : real(dp), intent(inout) :: work(:)
292 : integer, optional, intent(inout):: order(size(A)), worder(size(work))
293 : integer :: i
294 172 : if (present(order)) then
295 40316 : do i =1 , size(A)
296 40316 : order(i) = i
297 : end do
298 : end if
299 172 : call MergeSort_no_init_order_dp(A, work, order, worder)
300 344 : end subroutine MergeSort_dp
301 :
302 :
303 :
304 : !== Merge sort for 2D integer array=================================
305 :
306 :
307 0 : subroutine merge2D(A, B, C)
308 : ! The targe attribute is necessary, because A .or. B might overlap with C.
309 : integer, target, intent(in) :: A(:, :), B(:, :)
310 : integer, target, intent(inout) :: C(:,:)
311 : integer :: i, j, k
312 :
313 0 : if (size(A, 2) + size(B, 2) > size(C, 2)) then
314 0 : stop (1)
315 : end if
316 :
317 0 : i = 1; j = 1
318 0 : do k = 1, size(C, 2)
319 0 : if (i <= size(A, 2) .and. j <= size(B,2)) then
320 0 : if (array_le (A(:, i) , B(:,j), size(A, 1))) then
321 0 : C(:,k) = A(:,i)
322 0 : i = i + 1
323 : else
324 0 : C(:,k) = B(:, j)
325 0 : j = j + 1
326 : end if
327 0 : else if (i <= size(A, 2)) then
328 0 : C(:,k) = A(:, i)
329 0 : i = i + 1
330 0 : else if (j <= size(B, 2)) then
331 0 : C(:,k) = B(:,j)
332 0 : j = j + 1
333 : end if
334 : end do
335 0 : end subroutine merge2D
336 :
337 46137 : subroutine merge2D_with_order(A, B, C, orderA, orderB, orderC)
338 : ! The targe attribute is necessary, because A .or. B might overlap with C.
339 : integer, target, intent(in) :: A(:, :), B(:,:), orderA(:), orderB(:)
340 : integer, target, intent(inout) :: C(:,:), orderC(:)
341 : integer :: i, j, k
342 :
343 46137 : if (size(A, 2) + size(B,2) > size(C,2)) then
344 0 : stop (1)
345 : end if
346 :
347 15999535 : i = 1; j = 1
348 15999535 : do k = 1, size(C, 2)
349 15999535 : if (i <= size(A, 2) .and. j <= size(B, 2)) then
350 15325708 : if (array_le (A(:, i) , B(:,j), size(A, 1))) then
351 54500663 : C(:,k) = A(:,i)
352 7689063 : orderC(k) = orderA(i)
353 7689063 : i = i + 1
354 : else
355 54220061 : C(:,k) = B(:,j)
356 7636645 : orderC(k) =orderB(j)
357 15325708 : j = j + 1
358 : end if
359 627690 : else if (i <= size(A,2)) then
360 2104029 : C(:,k) = A(:,i)
361 296613 : orderC(k) = orderA(i)
362 296613 : i = i + 1
363 331077 : else if (j <= size(B, 2)) then
364 2255633 : C(:,k) = B(:,j)
365 331077 : orderC(k) = orderB(j)
366 331077 : j = j + 1
367 : end if
368 : end do
369 46137 : end subroutine merge2D_with_order
370 :
371 :
372 3342 : subroutine swap2D(x, y)
373 : integer, intent(inout) :: x(:), y(:)
374 : integer :: tmp, i
375 13804 : do i =1, size(x)
376 13804 : tmp = x(i); x(i) = y(i); y(i) = tmp
377 : end do
378 3342 : end subroutine swap2D
379 :
380 2607704 : recursive subroutine MergeSort2D_no_init_order(A, work, order, worder)
381 : integer, intent(inout) :: A(:, :)
382 : integer, intent(inout) :: work(:, :)
383 : integer, optional, intent(inout):: order(size(A, 2)), worder(size(work, 2))
384 : integer :: half, n
385 : logical :: ordered
386 1303852 : n=size(A,2)
387 1303852 : ordered=.False.
388 1303852 : if (present(order)) then
389 1303852 : ordered=.True.
390 : end if
391 :
392 1303852 : half = (n + 1) / 2
393 1303852 : if (n < 2) then
394 : continue
395 1278480 : else if (n == 2) then
396 626560 : if (array_morethan(A(:, 1) , A(:,2), size(A, 1))) then
397 3342 : call swap2D(A(:,1), A(:,2))
398 3342 : if(ordered) call swap_int(order(1), order(2))
399 : end if
400 : else
401 651920 : if(ordered) then
402 651920 : call MergeSort2D_no_init_order(A(:, : half), work, order(:half), worder)
403 651920 : call MergeSort2D_no_init_order(A(:, half + 1 :), work, order(half+1:), worder)
404 651920 : if (array_morethan(A(:,half) , A(:,half + 1), size(A, 1))) then
405 32341321 : work(:, 1 : half) = A(:, 1 : half)
406 8031813 : worder(1:half) = order(1: half)
407 : call merge2D_with_order(work(:, 1 : half), A(:, half + 1:), A &
408 46137 : &, worder(1 : half), order(half + 1:), order)
409 : endif
410 : else
411 0 : call MergeSort2D_no_init_order(A(:, : half), work)
412 0 : call MergeSort2D_no_init_order(A(:, half + 1 :), work)
413 0 : if (array_morethan(A(:,half) , A(:,half + 1), size(A, 1))) then
414 0 : work(:, 1 : half) = A(:,1 : half)
415 0 : call merge2D(work(:, 1 : half), A(:, half + 1:), A)
416 : endif
417 : endif
418 : end if
419 1303852 : end subroutine MergeSort2D_No_Init_Order
420 :
421 36 : subroutine MergeSort2D(A, work, order, worder)
422 : integer, intent(inout) :: A(:,:)
423 : integer, intent(inout) :: work(:, :)
424 : integer, optional, intent(inout):: order(size(A, 2)), worder(size(work, 2))
425 :
426 : integer :: i
427 12 : if(present(order)) then
428 1278504 : do i =1 , size(A, 2)
429 1278504 : order(i) = i
430 : end do
431 : end if
432 12 : call MergeSort2D_no_init_order(A, work, order, worder)
433 24 : end subroutine MergeSort2D
434 :
435 :
436 :
437 : end module m_mergesort
|