| #include "test-tool.h" | 
 | #include "cache.h" | 
 | #include "mergesort.h" | 
 |  | 
 | static uint32_t minstd_rand(uint32_t *state) | 
 | { | 
 | 	*state = (uint64_t)*state * 48271 % 2147483647; | 
 | 	return *state; | 
 | } | 
 |  | 
 | 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; | 
 | 	uint32_t seed = 1; | 
 | 	for (i = 0; i < n; i++) | 
 | 		arr[i] = minstd_rand(&seed) % 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; | 
 | 	uint32_t seed = 1; | 
 | 	for (i = j = 0, k = 1; i < n; i++) | 
 | 		arr[i] = minstd_rand(&seed) % 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 const struct dist *get_dist_by_name(const char *name) | 
 | { | 
 | 	int i; | 
 | 	for (i = 0; i < ARRAY_SIZE(dist); i++) { | 
 | 	       if (!strcmp(dist[i].name, name)) | 
 | 		       return &dist[i]; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | static void unriffle(int *arr, int n, int *tmp) | 
 | { | 
 | 	int i, j; | 
 | 	COPY_ARRAY(tmp, arr, n); | 
 | 	for (i = j = 0; i < n; i += 2) | 
 | 		arr[j++] = tmp[i]; | 
 | 	for (i = 1; i < n; i += 2) | 
 | 		arr[j++] = tmp[i]; | 
 | } | 
 |  | 
 | static void unriffle_recursively(int *arr, int n, int *tmp) | 
 | { | 
 | 	if (n > 1) { | 
 | 		int half = n / 2; | 
 | 		unriffle(arr, n, tmp); | 
 | 		unriffle_recursively(arr, half, tmp); | 
 | 		unriffle_recursively(arr + half, n - half, tmp); | 
 | 	} | 
 | } | 
 |  | 
 | static void mode_unriffle(int *arr, int n) | 
 | { | 
 | 	int *tmp; | 
 | 	ALLOC_ARRAY(tmp, n); | 
 | 	unriffle_recursively(arr, n, tmp); | 
 | 	free(tmp); | 
 | } | 
 |  | 
 | static unsigned int prev_pow2(unsigned int n) | 
 | { | 
 | 	unsigned int pow2 = 1; | 
 | 	while (pow2 * 2 < n) | 
 | 		pow2 *= 2; | 
 | 	return pow2; | 
 | } | 
 |  | 
 | static void unriffle_recursively_skewed(int *arr, int n, int *tmp) | 
 | { | 
 | 	if (n > 1) { | 
 | 		int pow2 = prev_pow2(n); | 
 | 		int rest = n - pow2; | 
 | 		unriffle(arr + pow2 - rest, rest * 2, tmp); | 
 | 		unriffle_recursively_skewed(arr, pow2, tmp); | 
 | 		unriffle_recursively_skewed(arr + pow2, rest, tmp); | 
 | 	} | 
 | } | 
 |  | 
 | static void mode_unriffle_skewed(int *arr, int n) | 
 | { | 
 | 	int *tmp; | 
 | 	ALLOC_ARRAY(tmp, n); | 
 | 	unriffle_recursively_skewed(arr, n, tmp); | 
 | 	free(tmp); | 
 | } | 
 |  | 
 | #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), | 
 | 	MODE(unriffle), | 
 | 	MODE(unriffle_skewed), | 
 | }; | 
 |  | 
 | static const struct mode *get_mode_by_name(const char *name) | 
 | { | 
 | 	int i; | 
 | 	for (i = 0; i < ARRAY_SIZE(mode); i++) { | 
 | 	       if (!strcmp(mode[i].name, name)) | 
 | 		       return &mode[i]; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int generate(int argc, const char **argv) | 
 | { | 
 | 	const struct dist *dist = NULL; | 
 | 	const struct mode *mode = NULL; | 
 | 	int i, n, m, *arr; | 
 |  | 
 | 	if (argc != 4) | 
 | 		return 1; | 
 |  | 
 | 	dist = get_dist_by_name(argv[0]); | 
 | 	mode = get_mode_by_name(argv[1]); | 
 | 	n = strtol(argv[2], NULL, 10); | 
 | 	m = strtol(argv[3], NULL, 10); | 
 | 	if (!dist || !mode) | 
 | 		return 1; | 
 |  | 
 | 	ALLOC_ARRAY(arr, n); | 
 | 	dist->fn(arr, n, m); | 
 | 	mode->fn(arr, n); | 
 | 	for (i = 0; i < n; i++) | 
 | 		printf("%08x\n", arr[i]); | 
 | 	free(arr); | 
 | 	return 0; | 
 | } | 
 |  | 
 | 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) | 
 | { | 
 | 	int i; | 
 | 	const char *sep; | 
 |  | 
 | 	if (argc == 6 && !strcmp(argv[1], "generate")) | 
 | 		return generate(argc - 2, argv + 2); | 
 | 	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 generate <distribution> <mode> <n> <m>\n"); | 
 | 	fprintf(stderr, "   or: test-tool mergesort sort\n"); | 
 | 	fprintf(stderr, "   or: test-tool mergesort test [<n>...]\n"); | 
 | 	fprintf(stderr, "\n"); | 
 | 	for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ") | 
 | 		fprintf(stderr, "%s%s", sep, dist[i].name); | 
 | 	fprintf(stderr, "\n"); | 
 | 	for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ") | 
 | 		fprintf(stderr, "%s%s", sep, mode[i].name); | 
 | 	fprintf(stderr, "\n"); | 
 | 	return 129; | 
 | } |