/src/libunistring/lib/array-mergesort.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* Stable-sorting of an array using mergesort. |
2 | | Copyright (C) 2009-2022 Free Software Foundation, Inc. |
3 | | Written by Bruno Haible <bruno@clisp.org>, 2009. |
4 | | |
5 | | This file is free software: you can redistribute it and/or modify |
6 | | it under the terms of the GNU Lesser General Public License as |
7 | | published by the Free Software Foundation; either version 2.1 of the |
8 | | License, or (at your option) any later version. |
9 | | |
10 | | This file is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU Lesser General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU Lesser General Public License |
16 | | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
17 | | |
18 | | /* This file implements stable sorting of an array, using the mergesort |
19 | | algorithm. |
20 | | Worst-case running time for an array of length N is O(N log N). |
21 | | Unlike the mpsort module, the algorithm here attempts to minimize not |
22 | | only the number of comparisons, but also the number of copying operations. |
23 | | |
24 | | Before including this file, you need to define |
25 | | ELEMENT The type of every array element. |
26 | | COMPARE A two-argument macro that takes two 'const ELEMENT *' |
27 | | pointers and returns a negative, zero, or positive 'int' |
28 | | value if the element pointed to by the first argument is, |
29 | | respectively, less, equal, or greater than the element |
30 | | pointed to by the second argument. |
31 | | STATIC The storage class of the functions being defined. |
32 | | STATIC_FROMTO (Optional.) Overrides STATIC for the 'merge_sort_fromto' |
33 | | function. |
34 | | Before including this file, you also need to include: |
35 | | #include <stddef.h> |
36 | | */ |
37 | | |
38 | | /* Merge the sorted arrays src1[0..n1-1] and src2[0..n2-1] into |
39 | | dst[0..n1+n2-1]. In case of ambiguity, put the elements of src1 |
40 | | before the elements of src2. |
41 | | n1 and n2 must be > 0. |
42 | | The arrays src1 and src2 must not overlap the dst array, except that |
43 | | src1 may be dst[n2..n1+n2-1], or src2 may be dst[n1..n1+n2-1]. */ |
44 | | static void |
45 | | merge (const ELEMENT *src1, size_t n1, |
46 | | const ELEMENT *src2, size_t n2, |
47 | | ELEMENT *dst) |
48 | | { |
49 | | for (;;) /* while (n1 > 0 && n2 > 0) */ |
50 | | { |
51 | | if (COMPARE (src1, src2) <= 0) |
52 | | { |
53 | | *dst++ = *src1++; |
54 | | n1--; |
55 | | if (n1 == 0) |
56 | | break; |
57 | | } |
58 | | else |
59 | | { |
60 | | *dst++ = *src2++; |
61 | | n2--; |
62 | | if (n2 == 0) |
63 | | break; |
64 | | } |
65 | | } |
66 | | /* Here n1 == 0 || n2 == 0 but also n1 > 0 || n2 > 0. */ |
67 | | if (n1 > 0) |
68 | | { |
69 | | if (dst != src1) |
70 | | do |
71 | | { |
72 | | *dst++ = *src1++; |
73 | | n1--; |
74 | | } |
75 | | while (n1 > 0); |
76 | | } |
77 | | else /* n2 > 0 */ |
78 | | { |
79 | | if (dst != src2) |
80 | | do |
81 | | { |
82 | | *dst++ = *src2++; |
83 | | n2--; |
84 | | } |
85 | | while (n2 > 0); |
86 | | } |
87 | | } |
88 | | |
89 | | /* Sort src[0..n-1] into dst[0..n-1], using tmp[0..n/2-1] as temporary |
90 | | (scratch) storage. |
91 | | The arrays src, dst, tmp must not overlap. */ |
92 | | #ifdef STATIC_FROMTO |
93 | | STATIC_FROMTO |
94 | | #else |
95 | | STATIC |
96 | | #endif |
97 | | void |
98 | | merge_sort_fromto (const ELEMENT *src, ELEMENT *dst, size_t n, ELEMENT *tmp) |
99 | | { |
100 | | switch (n) |
101 | | { |
102 | | case 0: |
103 | | return; |
104 | | case 1: |
105 | | /* Nothing to do. */ |
106 | | dst[0] = src[0]; |
107 | | return; |
108 | | case 2: |
109 | | /* Trivial case. */ |
110 | | if (COMPARE (&src[0], &src[1]) <= 0) |
111 | | { |
112 | | /* src[0] <= src[1] */ |
113 | | dst[0] = src[0]; |
114 | | dst[1] = src[1]; |
115 | | } |
116 | | else |
117 | | { |
118 | | dst[0] = src[1]; |
119 | | dst[1] = src[0]; |
120 | | } |
121 | | break; |
122 | | case 3: |
123 | | /* Simple case. */ |
124 | | if (COMPARE (&src[0], &src[1]) <= 0) |
125 | | { |
126 | | if (COMPARE (&src[1], &src[2]) <= 0) |
127 | | { |
128 | | /* src[0] <= src[1] <= src[2] */ |
129 | | dst[0] = src[0]; |
130 | | dst[1] = src[1]; |
131 | | dst[2] = src[2]; |
132 | | } |
133 | | else if (COMPARE (&src[0], &src[2]) <= 0) |
134 | | { |
135 | | /* src[0] <= src[2] < src[1] */ |
136 | | dst[0] = src[0]; |
137 | | dst[1] = src[2]; |
138 | | dst[2] = src[1]; |
139 | | } |
140 | | else |
141 | | { |
142 | | /* src[2] < src[0] <= src[1] */ |
143 | | dst[0] = src[2]; |
144 | | dst[1] = src[0]; |
145 | | dst[2] = src[1]; |
146 | | } |
147 | | } |
148 | | else |
149 | | { |
150 | | if (COMPARE (&src[0], &src[2]) <= 0) |
151 | | { |
152 | | /* src[1] < src[0] <= src[2] */ |
153 | | dst[0] = src[1]; |
154 | | dst[1] = src[0]; |
155 | | dst[2] = src[2]; |
156 | | } |
157 | | else if (COMPARE (&src[1], &src[2]) <= 0) |
158 | | { |
159 | | /* src[1] <= src[2] < src[0] */ |
160 | | dst[0] = src[1]; |
161 | | dst[1] = src[2]; |
162 | | dst[2] = src[0]; |
163 | | } |
164 | | else |
165 | | { |
166 | | /* src[2] < src[1] < src[0] */ |
167 | | dst[0] = src[2]; |
168 | | dst[1] = src[1]; |
169 | | dst[2] = src[0]; |
170 | | } |
171 | | } |
172 | | break; |
173 | | default: |
174 | | { |
175 | | size_t n1 = n / 2; |
176 | | size_t n2 = (n + 1) / 2; |
177 | | /* Note: n1 + n2 = n, n1 <= n2. */ |
178 | | /* Sort src[n1..n-1] into dst[n1..n-1], scratching tmp[0..n2/2-1]. */ |
179 | | merge_sort_fromto (src + n1, dst + n1, n2, tmp); |
180 | | /* Sort src[0..n1-1] into tmp[0..n1-1], scratching dst[0..n1-1]. */ |
181 | | merge_sort_fromto (src, tmp, n1, dst); |
182 | | /* Merge the two half results. */ |
183 | | merge (tmp, n1, dst + n1, n2, dst); |
184 | | } |
185 | | break; |
186 | | } |
187 | | } |
188 | | |
189 | | /* Sort src[0..n-1], using tmp[0..n-1] as temporary (scratch) storage. |
190 | | The arrays src, tmp must not overlap. */ |
191 | | STATIC void |
192 | | merge_sort_inplace (ELEMENT *src, size_t n, ELEMENT *tmp) |
193 | 0 | { |
194 | 0 | switch (n) |
195 | 0 | { |
196 | 0 | case 0: |
197 | 0 | case 1: |
198 | | /* Nothing to do. */ |
199 | 0 | return; |
200 | 0 | case 2: |
201 | | /* Trivial case. */ |
202 | 0 | if (COMPARE (&src[0], &src[1]) <= 0) |
203 | 0 | { |
204 | | /* src[0] <= src[1] */ |
205 | 0 | } |
206 | 0 | else |
207 | 0 | { |
208 | 0 | ELEMENT t = src[0]; |
209 | 0 | src[0] = src[1]; |
210 | 0 | src[1] = t; |
211 | 0 | } |
212 | 0 | break; |
213 | 0 | case 3: |
214 | | /* Simple case. */ |
215 | 0 | if (COMPARE (&src[0], &src[1]) <= 0) |
216 | 0 | { |
217 | 0 | if (COMPARE (&src[1], &src[2]) <= 0) |
218 | 0 | { |
219 | | /* src[0] <= src[1] <= src[2] */ |
220 | 0 | } |
221 | 0 | else if (COMPARE (&src[0], &src[2]) <= 0) |
222 | 0 | { |
223 | | /* src[0] <= src[2] < src[1] */ |
224 | 0 | ELEMENT t = src[1]; |
225 | 0 | src[1] = src[2]; |
226 | 0 | src[2] = t; |
227 | 0 | } |
228 | 0 | else |
229 | 0 | { |
230 | | /* src[2] < src[0] <= src[1] */ |
231 | 0 | ELEMENT t = src[0]; |
232 | 0 | src[0] = src[2]; |
233 | 0 | src[2] = src[1]; |
234 | 0 | src[1] = t; |
235 | 0 | } |
236 | 0 | } |
237 | 0 | else |
238 | 0 | { |
239 | 0 | if (COMPARE (&src[0], &src[2]) <= 0) |
240 | 0 | { |
241 | | /* src[1] < src[0] <= src[2] */ |
242 | 0 | ELEMENT t = src[0]; |
243 | 0 | src[0] = src[1]; |
244 | 0 | src[1] = t; |
245 | 0 | } |
246 | 0 | else if (COMPARE (&src[1], &src[2]) <= 0) |
247 | 0 | { |
248 | | /* src[1] <= src[2] < src[0] */ |
249 | 0 | ELEMENT t = src[0]; |
250 | 0 | src[0] = src[1]; |
251 | 0 | src[1] = src[2]; |
252 | 0 | src[2] = t; |
253 | 0 | } |
254 | 0 | else |
255 | 0 | { |
256 | | /* src[2] < src[1] < src[0] */ |
257 | 0 | ELEMENT t = src[0]; |
258 | 0 | src[0] = src[2]; |
259 | 0 | src[2] = t; |
260 | 0 | } |
261 | 0 | } |
262 | 0 | break; |
263 | 0 | default: |
264 | 0 | { |
265 | 0 | size_t n1 = n / 2; |
266 | 0 | size_t n2 = (n + 1) / 2; |
267 | | /* Note: n1 + n2 = n, n1 <= n2. */ |
268 | | /* Sort src[n1..n-1], scratching tmp[0..n2-1]. */ |
269 | 0 | merge_sort_inplace (src + n1, n2, tmp); |
270 | | /* Sort src[0..n1-1] into tmp[0..n1-1], scratching tmp[n1..2*n1-1]. */ |
271 | 0 | merge_sort_fromto (src, tmp, n1, tmp + n1); |
272 | | /* Merge the two half results. */ |
273 | 0 | merge (tmp, n1, src + n1, n2, src); |
274 | 0 | } |
275 | 0 | break; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | #undef ELEMENT |
280 | | #undef COMPARE |
281 | | #undef STATIC |