Files
puzzles/sort.c
Ben Harris 0186d78da9 Mark many more function (and some objects) static
I noticed commit db3b531e2cab765a00475054d2e9046c9d0437d3 in the history
where Simon added a bunch of "static" qualifiers.  That suggested that
consistently marking internal functions "static" is desirable, so I
tried a build using GCC's -Wmissing-declarations, which requires prior
declaration (presumed to be in a header file) of all global functions.

This commit makes the GTK build clean under GCC's
-Wmissing-declarations.  I've also adding "static" to a few obviously
internal objects, but GCC doesn't complain about those so I certainly
haven't got them all.
2023-02-18 00:13:15 +00:00

161 lines
4.1 KiB
C

/*
* Implement arraysort() defined in puzzles.h.
*
* Strategy: heapsort.
*/
#include <stddef.h>
#include <string.h>
#include "puzzles.h"
static void memswap(void *av, void *bv, size_t size)
{
char t[4096];
char *a = (char *)av, *b = (char *)bv;
while (size > 0) {
size_t thissize = size < sizeof(t) ? size : sizeof(t);
memcpy(t, a, thissize);
memcpy(a, b, thissize);
memcpy(b, t, thissize);
size -= thissize;
a += thissize;
b += thissize;
}
}
#define PTR(i) ((char *)array + size * (i))
#define SWAP(i,j) memswap(PTR(i), PTR(j), size)
#define CMP(i,j) cmp(PTR(i), PTR(j), ctx)
#define LCHILD(i) (2*(i)+1)
#define RCHILD(i) (2*(i)+2)
#define PARENT(i) (((i)-1)/2)
static void downheap(void *array, size_t nmemb, size_t size,
arraysort_cmpfn_t cmp, void *ctx, size_t i)
{
while (LCHILD(i) < nmemb) {
/* Identify the smallest element out of i and its children. */
size_t j = i;
if (CMP(j, LCHILD(i)) < 0)
j = LCHILD(i);
if (RCHILD(i) < nmemb &&
CMP(j, RCHILD(i)) < 0)
j = RCHILD(i);
if (j == i)
return; /* smallest element is already where it should be */
SWAP(j, i);
i = j;
}
}
void arraysort_fn(void *array, size_t nmemb, size_t size,
arraysort_cmpfn_t cmp, void *ctx)
{
size_t i;
if (nmemb < 2)
return; /* trivial */
/*
* Stage 1: build the heap.
*
* Linear-time if we do it by downheaping the elements in
* decreasing order of index, instead of the more obvious approach
* of upheaping in increasing order. (Also, it means we don't need
* the upheap function at all.)
*
* We don't need to downheap anything in the second half of the
* array, because it can't have any children to swap with anyway.
*/
for (i = PARENT(nmemb-1) + 1; i-- > 0 ;)
downheap(array, nmemb, size, cmp, ctx, i);
/*
* Stage 2: dismantle the heap by repeatedly swapping the root
* element (at index 0) into the last position and then
* downheaping the new root.
*/
for (i = nmemb-1; i > 0; i--) {
SWAP(0, i);
downheap(array, i, size, cmp, ctx, 0);
}
}
#ifdef SORT_TEST
#include <stdlib.h>
#include <time.h>
static int testcmp(const void *av, const void *bv, void *ctx)
{
int a = *(const int *)av, b = *(const int *)bv;
const int *keys = (const int *)ctx;
return keys[a] < keys[b] ? -1 : keys[a] > keys[b] ? +1 : 0;
}
static int resetcmp(const void *av, const void *bv)
{
int a = *(const int *)av, b = *(const int *)bv;
return a < b ? -1 : a > b ? +1 : 0;
}
int main(int argc, char **argv)
{
typedef int Array[3723];
Array data, keys;
int iteration;
unsigned seed;
seed = (argc > 1 ? strtoul(argv[1], NULL, 0) : time(NULL));
printf("Random seed = %u\n", seed);
srand(seed);
for (iteration = 0; iteration < 10000; iteration++) {
int j;
const char *fail = NULL;
for (j = 0; j < lenof(data); j++) {
data[j] = j;
keys[j] = rand();
}
arraysort(data, lenof(data), testcmp, keys);
for (j = 1; j < lenof(data); j++) {
if (keys[data[j]] < keys[data[j-1]])
fail = "output misordered";
}
if (!fail) {
Array reset;
memcpy(reset, data, sizeof(data));
qsort(reset, lenof(reset), sizeof(*reset), resetcmp);
for (j = 0; j < lenof(reset); j++)
if (reset[j] != j)
fail = "output not permuted";
}
if (fail) {
printf("Failed at iteration %d: %s\n", iteration, fail);
printf("Key values:\n");
for (j = 0; j < lenof(keys); j++)
printf(" [%2d] %10d\n", j, keys[j]);
printf("Output sorted order:\n");
for (j = 0; j < lenof(data); j++)
printf(" [%2d] %10d\n", data[j], keys[data[j]]);
return 1;
}
}
printf("OK\n");
return 0;
}
#endif /* SORT_TEST */