#include "test-tool.h" #include "cache.h" #include "mergesort.h" struct line { char *text; struct line *next; }; static void *get_next(const void *a) { return ((const struct line *)a)->next; } static void set_next(void *a, void *b) { ((struct line *)a)->next = b; } static int compare_strings(const void *a, const void *b) { const struct line *x = a, *y = b; return strcmp(x->text, y->text); } static int sort_stdin(void) { struct line *line, *p = NULL, *lines = NULL; struct strbuf sb = STRBUF_INIT; while (!strbuf_getline(&sb, stdin)) { line = xmalloc(sizeof(struct line)); line->text = strbuf_detach(&sb, NULL); if (p) { line->next = p->next; p->next = line; } else { line->next = NULL; lines = line; } p = line; } lines = llist_mergesort(lines, get_next, set_next, compare_strings); while (lines) { puts(lines->text); lines = lines->next; } return 0; } static void dist_sawtooth(int *arr, int n, int m) { int i; for (i = 0; i < n; i++) arr[i] = i % m; } static void dist_rand(int *arr, int n, int m) { int i; for (i = 0; i < n; i++) arr[i] = rand() % m; } static void dist_stagger(int *arr, int n, int m) { int i; for (i = 0; i < n; i++) arr[i] = (i * m + i) % n; } static void dist_plateau(int *arr, int n, int m) { int i; for (i = 0; i < n; i++) arr[i] = (i < m) ? i : m; } static void dist_shuffle(int *arr, int n, int m) { int i, j, k; for (i = j = 0, k = 1; i < n; i++) arr[i] = (rand() % m) ? (j += 2) : (k += 2); } #define DIST(name) { #name, dist_##name } static struct dist { const char *name; void (*fn)(int *arr, int n, int m); } dist[] = { DIST(sawtooth), DIST(rand), DIST(stagger), DIST(plateau), DIST(shuffle), }; static void mode_copy(int *arr, int n) { /* nothing */ } static void mode_reverse(int *arr, int n) { int i, j; for (i = 0, j = n - 1; i < j; i++, j--) SWAP(arr[i], arr[j]); } static void mode_reverse_1st_half(int *arr, int n) { mode_reverse(arr, n / 2); } static void mode_reverse_2nd_half(int *arr, int n) { int half = n / 2; mode_reverse(arr + half, n - half); } static int compare_ints(const void *av, const void *bv) { const int *ap = av, *bp = bv; int a = *ap, b = *bp; return (a > b) - (a < b); } static void mode_sort(int *arr, int n) { QSORT(arr, n, compare_ints); } static void mode_dither(int *arr, int n) { int i; for (i = 0; i < n; i++) arr[i] += i % 5; } #define MODE(name) { #name, mode_##name } static struct mode { const char *name; void (*fn)(int *arr, int n); } mode[] = { MODE(copy), MODE(reverse), MODE(reverse_1st_half), MODE(reverse_2nd_half), MODE(sort), MODE(dither), }; static struct stats { int get_next, set_next, compare; } stats; struct number { int value, rank; struct number *next; }; static void *get_next_number(const void *a) { stats.get_next++; return ((const struct number *)a)->next; } static void set_next_number(void *a, void *b) { stats.set_next++; ((struct number *)a)->next = b; } static int compare_numbers(const void *av, const void *bv) { const struct number *an = av, *bn = bv; int a = an->value, b = bn->value; stats.compare++; return (a > b) - (a < b); } static void clear_numbers(struct number *list) { while (list) { struct number *next = list->next; free(list); list = next; } } static int test(const struct dist *dist, const struct mode *mode, int n, int m) { int *arr; size_t i; struct number *curr, *list, **tail; int is_sorted = 1; int is_stable = 1; const char *verdict; int result = -1; ALLOC_ARRAY(arr, n); dist->fn(arr, n, m); mode->fn(arr, n); for (i = 0, tail = &list; i < n; i++) { curr = xmalloc(sizeof(*curr)); curr->value = arr[i]; curr->rank = i; *tail = curr; tail = &curr->next; } *tail = NULL; stats.get_next = stats.set_next = stats.compare = 0; list = llist_mergesort(list, get_next_number, set_next_number, compare_numbers); QSORT(arr, n, compare_ints); for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) { if (arr[i] != curr->value) is_sorted = 0; if (curr->next && curr->value == curr->next->value && curr->rank >= curr->next->rank) is_stable = 0; } if (i < n) { verdict = "too short"; } else if (curr) { verdict = "too long"; } else if (!is_sorted) { verdict = "not sorted"; } else if (!is_stable) { verdict = "unstable"; } else { verdict = "OK"; result = 0; } printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n", dist->name, mode->name, n, m, stats.get_next, stats.set_next, stats.compare, verdict); clear_numbers(list); free(arr); return result; } /* * A version of the qsort certification program from "Engineering a Sort * Function" by Bentley and McIlroy, Software—Practice and Experience, * Volume 23, Issue 11, 1249–1265 (November 1993). */ static int run_tests(int argc, const char **argv) { const char *argv_default[] = { "100", "1023", "1024", "1025" }; if (!argc) return run_tests(ARRAY_SIZE(argv_default), argv_default); printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n", "distribut", "mode", "n", "m", "get_next", "set_next", "compare", "verdict"); while (argc--) { int i, j, m, n = strtol(*argv++, NULL, 10); for (i = 0; i < ARRAY_SIZE(dist); i++) { for (j = 0; j < ARRAY_SIZE(mode); j++) { for (m = 1; m < 2 * n; m *= 2) { if (test(&dist[i], &mode[j], n, m)) return 1; } } } } return 0; } int cmd__mergesort(int argc, const char **argv) { if (argc == 2 && !strcmp(argv[1], "sort")) return sort_stdin(); if (argc > 1 && !strcmp(argv[1], "test")) return run_tests(argc - 2, argv + 2); fprintf(stderr, "usage: test-tool mergesort sort\n"); fprintf(stderr, " or: test-tool mergesort test [...]\n"); return 129; }