Raw
1 #define DISABLE_SIGN_COMPARE_WARNINGS
2
3 #include "test-tool.h"
4 #include "mem-pool.h"
5 #include "mergesort.h"
6 #include "strbuf.h"
7
8 static uint32_t minstd_rand(uint32_t *state)
9 {
10 *state = (uint64_t)*state * 48271 % 2147483647;
11 return *state;
12 }
13
14 struct line {
15 char *text;
16 struct line *next;
17 };
18
19 DEFINE_LIST_SORT(static, sort_lines, struct line, next);
20
21 static int compare_strings(const struct line *x, const struct line *y)
22 {
23 return strcmp(x->text, y->text);
24 }
25
26 static int sort_stdin(void)
27 {
28 struct line *lines;
29 struct line **tail = &lines;
30 struct strbuf sb = STRBUF_INIT;
31 struct mem_pool lines_pool;
32 char *p;
33
34 strbuf_read(&sb, 0, 0);
35
36 /*
37 * Split by newline, but don't create an item
38 * for the empty string after the last separator.
39 */
40 if (sb.len && sb.buf[sb.len - 1] == '\n')
41 strbuf_setlen(&sb, sb.len - 1);
42
43 mem_pool_init(&lines_pool, 0);
44 p = sb.buf;
45 for (;;) {
46 char *eol = strchr(p, '\n');
47 struct line *line = mem_pool_alloc(&lines_pool, sizeof(*line));
48 line->text = p;
49 *tail = line;
50 tail = &line->next;
51 if (!eol)
52 break;
53 *eol = '\0';
54 p = eol + 1;
55 }
56 *tail = NULL;
57
58 sort_lines(&lines, compare_strings);
59
60 while (lines) {
61 puts(lines->text);
62 lines = lines->next;
63 }
64 return 0;
65 }
66
67 static void dist_sawtooth(int *arr, int n, int m)
68 {
69 int i;
70 for (i = 0; i < n; i++)
71 arr[i] = i % m;
72 }
73
74 static void dist_rand(int *arr, int n, int m)
75 {
76 int i;
77 uint32_t seed = 1;
78 for (i = 0; i < n; i++)
79 arr[i] = minstd_rand(&seed) % m;
80 }
81
82 static void dist_stagger(int *arr, int n, int m)
83 {
84 int i;
85 for (i = 0; i < n; i++)
86 arr[i] = (i * m + i) % n;
87 }
88
89 static void dist_plateau(int *arr, int n, int m)
90 {
91 int i;
92 for (i = 0; i < n; i++)
93 arr[i] = (i < m) ? i : m;
94 }
95
96 static void dist_shuffle(int *arr, int n, int m)
97 {
98 int i, j, k;
99 uint32_t seed = 1;
100 for (i = j = 0, k = 1; i < n; i++)
101 arr[i] = minstd_rand(&seed) % m ? (j += 2) : (k += 2);
102 }
103
104 #define DIST(name) { #name, dist_##name }
105
106 static struct dist {
107 const char *name;
108 void (*fn)(int *arr, int n, int m);
109 } dist[] = {
110 DIST(sawtooth),
111 DIST(rand),
112 DIST(stagger),
113 DIST(plateau),
114 DIST(shuffle),
115 };
116
117 static const struct dist *get_dist_by_name(const char *name)
118 {
119 int i;
120 for (i = 0; i < ARRAY_SIZE(dist); i++) {
121 if (!strcmp(dist[i].name, name))
122 return &dist[i];
123 }
124 return NULL;
125 }
126
127 static void mode_copy(int *arr UNUSED, int n UNUSED)
128 {
129 /* nothing */
130 }
131
132 static void mode_reverse(int *arr, int n)
133 {
134 int i, j;
135 for (i = 0, j = n - 1; i < j; i++, j--)
136 SWAP(arr[i], arr[j]);
137 }
138
139 static void mode_reverse_1st_half(int *arr, int n)
140 {
141 mode_reverse(arr, n / 2);
142 }
143
144 static void mode_reverse_2nd_half(int *arr, int n)
145 {
146 int half = n / 2;
147 mode_reverse(arr + half, n - half);
148 }
149
150 static int compare_ints(const void *av, const void *bv)
151 {
152 const int *ap = av, *bp = bv;
153 int a = *ap, b = *bp;
154 return (a > b) - (a < b);
155 }
156
157 static void mode_sort(int *arr, int n)
158 {
159 QSORT(arr, n, compare_ints);
160 }
161
162 static void mode_dither(int *arr, int n)
163 {
164 int i;
165 for (i = 0; i < n; i++)
166 arr[i] += i % 5;
167 }
168
169 static void unriffle(int *arr, int n, int *tmp)
170 {
171 int i, j;
172 COPY_ARRAY(tmp, arr, n);
173 for (i = j = 0; i < n; i += 2)
174 arr[j++] = tmp[i];
175 for (i = 1; i < n; i += 2)
176 arr[j++] = tmp[i];
177 }
178
179 static void unriffle_recursively(int *arr, int n, int *tmp)
180 {
181 if (n > 1) {
182 int half = n / 2;
183 unriffle(arr, n, tmp);
184 unriffle_recursively(arr, half, tmp);
185 unriffle_recursively(arr + half, n - half, tmp);
186 }
187 }
188
189 static void mode_unriffle(int *arr, int n)
190 {
191 int *tmp;
192 ALLOC_ARRAY(tmp, n);
193 unriffle_recursively(arr, n, tmp);
194 free(tmp);
195 }
196
197 static unsigned int prev_pow2(unsigned int n)
198 {
199 unsigned int pow2 = 1;
200 while (pow2 * 2 < n)
201 pow2 *= 2;
202 return pow2;
203 }
204
205 static void unriffle_recursively_skewed(int *arr, int n, int *tmp)
206 {
207 if (n > 1) {
208 int pow2 = prev_pow2(n);
209 int rest = n - pow2;
210 unriffle(arr + pow2 - rest, rest * 2, tmp);
211 unriffle_recursively_skewed(arr, pow2, tmp);
212 unriffle_recursively_skewed(arr + pow2, rest, tmp);
213 }
214 }
215
216 static void mode_unriffle_skewed(int *arr, int n)
217 {
218 int *tmp;
219 ALLOC_ARRAY(tmp, n);
220 unriffle_recursively_skewed(arr, n, tmp);
221 free(tmp);
222 }
223
224 #define MODE(name) { #name, mode_##name }
225
226 static struct mode {
227 const char *name;
228 void (*fn)(int *arr, int n);
229 } mode[] = {
230 MODE(copy),
231 MODE(reverse),
232 MODE(reverse_1st_half),
233 MODE(reverse_2nd_half),
234 MODE(sort),
235 MODE(dither),
236 MODE(unriffle),
237 MODE(unriffle_skewed),
238 };
239
240 static const struct mode *get_mode_by_name(const char *name)
241 {
242 int i;
243 for (i = 0; i < ARRAY_SIZE(mode); i++) {
244 if (!strcmp(mode[i].name, name))
245 return &mode[i];
246 }
247 return NULL;
248 }
249
250 static int generate(int argc, const char **argv)
251 {
252 const struct dist *dist = NULL;
253 const struct mode *mode = NULL;
254 int i, n, m, *arr;
255
256 if (argc != 4)
257 return 1;
258
259 dist = get_dist_by_name(argv[0]);
260 mode = get_mode_by_name(argv[1]);
261 n = strtol(argv[2], NULL, 10);
262 m = strtol(argv[3], NULL, 10);
263 if (!dist || !mode)
264 return 1;
265
266 ALLOC_ARRAY(arr, n);
267 dist->fn(arr, n, m);
268 mode->fn(arr, n);
269 for (i = 0; i < n; i++)
270 printf("%08x\n", arr[i]);
271 free(arr);
272 return 0;
273 }
274
275 static struct stats {
276 int get_next, set_next, compare;
277 } stats;
278
279 struct number {
280 int value, rank;
281 struct number *next;
282 };
283
284 DEFINE_LIST_SORT_DEBUG(static, sort_numbers, struct number, next,
285 stats.get_next++, stats.set_next++);
286
287 static int compare_numbers(const struct number *an, const struct number *bn)
288 {
289 int a = an->value, b = bn->value;
290 stats.compare++;
291 return (a > b) - (a < b);
292 }
293
294 static void clear_numbers(struct number *list)
295 {
296 while (list) {
297 struct number *next = list->next;
298 free(list);
299 list = next;
300 }
301 }
302
303 static int test(const struct dist *dist, const struct mode *mode, int n, int m)
304 {
305 int *arr;
306 size_t i;
307 struct number *curr, *list, **tail;
308 int is_sorted = 1;
309 int is_stable = 1;
310 const char *verdict;
311 int result = -1;
312
313 ALLOC_ARRAY(arr, n);
314 dist->fn(arr, n, m);
315 mode->fn(arr, n);
316 for (i = 0, tail = &list; i < n; i++) {
317 curr = xmalloc(sizeof(*curr));
318 curr->value = arr[i];
319 curr->rank = i;
320 *tail = curr;
321 tail = &curr->next;
322 }
323 *tail = NULL;
324
325 stats.get_next = stats.set_next = stats.compare = 0;
326 sort_numbers(&list, compare_numbers);
327
328 QSORT(arr, n, compare_ints);
329 for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) {
330 if (arr[i] != curr->value)
331 is_sorted = 0;
332 if (curr->next && curr->value == curr->next->value &&
333 curr->rank >= curr->next->rank)
334 is_stable = 0;
335 }
336 if (i < n) {
337 verdict = "too short";
338 } else if (curr) {
339 verdict = "too long";
340 } else if (!is_sorted) {
341 verdict = "not sorted";
342 } else if (!is_stable) {
343 verdict = "unstable";
344 } else {
345 verdict = "OK";
346 result = 0;
347 }
348
349 printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n",
350 dist->name, mode->name, n, m, stats.get_next, stats.set_next,
351 stats.compare, verdict);
352
353 clear_numbers(list);
354 free(arr);
355
356 return result;
357 }
358
359 /*
360 * A version of the qsort certification program from "Engineering a Sort
361 * Function" by Bentley and McIlroy, Software—Practice and Experience,
362 * Volume 23, Issue 11, 1249–1265 (November 1993).
363 */
364 static int run_tests(int argc, const char **argv)
365 {
366 const char *argv_default[] = { "100", "1023", "1024", "1025" };
367 if (!argc)
368 return run_tests(ARRAY_SIZE(argv_default), argv_default);
369 printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n",
370 "distribut", "mode", "n", "m", "get_next", "set_next",
371 "compare", "verdict");
372 while (argc--) {
373 int i, j, m, n = strtol(*argv++, NULL, 10);
374 for (i = 0; i < ARRAY_SIZE(dist); i++) {
375 for (j = 0; j < ARRAY_SIZE(mode); j++) {
376 for (m = 1; m < 2 * n; m *= 2) {
377 if (test(&dist[i], &mode[j], n, m))
378 return 1;
379 }
380 }
381 }
382 }
383 return 0;
384 }
385
386 int cmd__mergesort(int argc, const char **argv)
387 {
388 int i;
389 const char *sep;
390
391 if (argc == 6 && !strcmp(argv[1], "generate"))
392 return generate(argc - 2, argv + 2);
393 if (argc == 2 && !strcmp(argv[1], "sort"))
394 return sort_stdin();
395 if (argc > 1 && !strcmp(argv[1], "test"))
396 return run_tests(argc - 2, argv + 2);
397 fprintf(stderr, "usage: test-tool mergesort generate <distribution> <mode> <n> <m>\n");
398 fprintf(stderr, " or: test-tool mergesort sort\n");
399 fprintf(stderr, " or: test-tool mergesort test [<n>...]\n");
400 fprintf(stderr, "\n");
401 for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ")
402 fprintf(stderr, "%s%s", sep, dist[i].name);
403 fprintf(stderr, "\n");
404 for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ")
405 fprintf(stderr, "%s%s", sep, mode[i].name);
406 fprintf(stderr, "\n");
407 return 129;
408 }