git/t/helper/test-mergesort.c
René Scharfe e031e9719d test-mergesort: add test subcommand
Adapt the qsort certification program from "Engineering a Sort Function"
by Bentley and McIlroy for testing our linked list sort function.  It
generates several lists with various distribution patterns and counts
the number of operations llist_mergesort() needs to order them.  It
compares the result to the output of a trusted sort function (qsort(1))
and also checks if the sort is stable.

Also add a test script that makes use of the new subcommand.

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-10-01 12:43:08 -07:00

289 lines
5.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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, 12491265 (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>...]\n");
return 129;
}