aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--auxiliary/CMakeLists.txt8
-rw-r--r--auxiliary/combi-test.c25
-rw-r--r--auxiliary/divvy-test.c103
-rw-r--r--auxiliary/latin-test.c110
-rw-r--r--auxiliary/matching.c397
-rw-r--r--auxiliary/obfusc.c (renamed from obfusc.c)0
-rw-r--r--auxiliary/penrose-test.c95
-rw-r--r--auxiliary/sort-test.c70
-rw-r--r--auxiliary/tree234-test.c746
-rw-r--r--combi.c32
-rw-r--r--divvy.c122
-rw-r--r--latin.c117
-rw-r--r--matching.c427
-rw-r--r--matching.h28
-rw-r--r--penrose.c128
-rw-r--r--puzzles.h2
-rw-r--r--sort.c71
-rw-r--r--tree234.c770
-rw-r--r--tree234.h24
20 files changed, 1613 insertions, 1672 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a70532c..481294f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -266,16 +266,6 @@ puzzle(untangle
add_subdirectory(unfinished)
add_subdirectory(auxiliary)
-cliprogram(obfusc obfusc.c)
-cliprogram(latincheck latin.c COMPILE_DEFINITIONS STANDALONE_LATIN_TEST)
-cliprogram(matching matching.c COMPILE_DEFINITIONS STANDALONE_MATCHING_TEST)
-cliprogram(combi combi.c COMPILE_DEFINITIONS STANDALONE_COMBI_TEST)
-cliprogram(divvy divvy.c COMPILE_DEFINITIONS TESTMODE)
-cliprogram(penrose-test penrose.c COMPILE_DEFINITIONS TEST_PENROSE)
-cliprogram(penrose-vector-test penrose.c COMPILE_DEFINITIONS TEST_VECTORS)
-cliprogram(sort-test sort.c COMPILE_DEFINITIONS SORT_TEST)
-cliprogram(tree234-test tree234.c COMPILE_DEFINITIONS TEST)
-
if(build_cli_programs)
write_generated_games_header()
include(CheckFunctionExists)
diff --git a/auxiliary/CMakeLists.txt b/auxiliary/CMakeLists.txt
index d78b60a..f475812 100644
--- a/auxiliary/CMakeLists.txt
+++ b/auxiliary/CMakeLists.txt
@@ -1,2 +1,10 @@
+cliprogram(combi-test combi-test.c)
+cliprogram(divvy-test divvy-test.c)
cliprogram(hatgen hatgen.c COMPILE_DEFINITIONS TEST_HAT)
cliprogram(hat-test hat-test.c)
+cliprogram(latin-test latin-test.c)
+cliprogram(matching matching.c)
+cliprogram(obfusc obfusc.c)
+cliprogram(penrose-test penrose-test.c)
+cliprogram(sort-test sort-test.c)
+cliprogram(tree234-test tree234-test.c)
diff --git a/auxiliary/combi-test.c b/auxiliary/combi-test.c
new file mode 100644
index 0000000..3db46e3
--- /dev/null
+++ b/auxiliary/combi-test.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include "puzzles.h"
+
+int main(int argc, char *argv[])
+{
+ combi_ctx *c;
+ int i, r, n;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: combi R N\n");
+ exit(1);
+ }
+
+ r = atoi(argv[1]); n = atoi(argv[2]);
+ c = new_combi(r, n);
+ printf("combi %d of %d, %d elements.\n", c->r, c->n, c->total);
+
+ while (next_combi(c)) {
+ for (i = 0; i < c->r; i++) {
+ printf("%d ", c->a[i]);
+ }
+ printf("\n");
+ }
+ free_combi(c);
+}
diff --git a/auxiliary/divvy-test.c b/auxiliary/divvy-test.c
new file mode 100644
index 0000000..b6c9584
--- /dev/null
+++ b/auxiliary/divvy-test.c
@@ -0,0 +1,103 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "puzzles.h"
+
+int main(int argc, char **argv)
+{
+ int *dsf;
+ int i;
+ int w = 9, h = 4, k = 6, tries = 100;
+ random_state *rs;
+ int fail_counter = 0;
+
+ rs = random_new("123456", 6);
+
+ if (argc > 1)
+ w = atoi(argv[1]);
+ if (argc > 2)
+ h = atoi(argv[2]);
+ if (argc > 3)
+ k = atoi(argv[3]);
+ if (argc > 4)
+ tries = atoi(argv[4]);
+
+ for (i = 0; i < tries; i++) {
+ int x, y;
+
+ while ((dsf = divvy_rectangle_attempt(w, h, k, rs)) == NULL)
+ fail_counter++;
+
+ for (y = 0; y <= 2*h; y++) {
+ for (x = 0; x <= 2*w; x++) {
+ int miny = y/2 - 1 /*, maxy = y/2 */;
+ int minx = x/2 - 1 /*, maxx = x/2 */;
+ int classes[4], tx, ty;
+ for (ty = 0; ty < 2; ty++)
+ for (tx = 0; tx < 2; tx++) {
+ int cx = minx+tx, cy = miny+ty;
+ if (cx < 0 || cx >= w || cy < 0 || cy >= h)
+ classes[ty*2+tx] = -1;
+ else
+ classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
+ }
+ switch (y%2 * 2 + x%2) {
+ case 0: /* corner */
+ /*
+ * Cases for the corner:
+ *
+ * - if all four surrounding squares belong
+ * to the same omino, we print a space.
+ *
+ * - if the top two are the same and the
+ * bottom two are the same, we print a
+ * horizontal line.
+ *
+ * - if the left two are the same and the
+ * right two are the same, we print a
+ * vertical line.
+ *
+ * - otherwise, we print a cross.
+ */
+ if (classes[0] == classes[1] &&
+ classes[1] == classes[2] &&
+ classes[2] == classes[3])
+ printf(" ");
+ else if (classes[0] == classes[1] &&
+ classes[2] == classes[3])
+ printf("-");
+ else if (classes[0] == classes[2] &&
+ classes[1] == classes[3])
+ printf("|");
+ else
+ printf("+");
+ break;
+ case 1: /* horiz edge */
+ if (classes[1] == classes[3])
+ printf(" ");
+ else
+ printf("--");
+ break;
+ case 2: /* vert edge */
+ if (classes[2] == classes[3])
+ printf(" ");
+ else
+ printf("|");
+ break;
+ case 3: /* square centre */
+ printf(" ");
+ break;
+ }
+ }
+ printf("\n");
+ }
+ printf("\n");
+ sfree(dsf);
+ }
+
+ printf("%d retries needed for %d successes\n", fail_counter, tries);
+
+ return 0;
+}
diff --git a/auxiliary/latin-test.c b/auxiliary/latin-test.c
new file mode 100644
index 0000000..e467bf2
--- /dev/null
+++ b/auxiliary/latin-test.c
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "puzzles.h"
+#include "latin.h"
+
+static const char *quis;
+
+#define ELT(sq,x,y) (sq[((y)*order)+(x)])
+
+static void latin_print(digit *sq, int order)
+{
+ int x, y;
+
+ for (y = 0; y < order; y++) {
+ for (x = 0; x < order; x++) {
+ printf("%2u ", ELT(sq, x, y));
+ }
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void gen(int order, random_state *rs, int debug)
+{
+ digit *sq;
+
+ sq = latin_generate(order, rs);
+ latin_print(sq, order);
+ if (latin_check(sq, order)) {
+ fprintf(stderr, "Square is not a latin square!");
+ exit(1);
+ }
+
+ sfree(sq);
+}
+
+static void test_soak(int order, random_state *rs)
+{
+ digit *sq;
+ int n = 0;
+ time_t tt_start, tt_now, tt_last;
+
+ tt_now = tt_start = time(NULL);
+
+ while(1) {
+ sq = latin_generate(order, rs);
+ sfree(sq);
+ n++;
+
+ tt_last = time(NULL);
+ if (tt_last > tt_now) {
+ tt_now = tt_last;
+ printf("%d total, %3.1f/s\n", n,
+ (double)n / (double)(tt_now - tt_start));
+ }
+ }
+}
+
+static void usage_exit(const char *msg)
+{
+ if (msg)
+ fprintf(stderr, "%s: %s\n", quis, msg);
+ fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, soak = 0;
+ random_state *rs;
+ time_t seed = time(NULL);
+
+ quis = argv[0];
+ while (--argc > 0) {
+ const char *p = *++argv;
+ if (!strcmp(p, "--soak"))
+ soak = 1;
+ else if (!strcmp(p, "--seed")) {
+ if (argc == 0)
+ usage_exit("--seed needs an argument");
+ seed = (time_t)atoi(*++argv);
+ argc--;
+ } else if (*p == '-')
+ usage_exit("unrecognised option");
+ else
+ break; /* finished options */
+ }
+
+ rs = random_new((void*)&seed, sizeof(time_t));
+
+ if (soak == 1) {
+ if (argc != 1) usage_exit("only one argument for --soak");
+ test_soak(atoi(*argv), rs);
+ } else {
+ if (argc > 0) {
+ for (i = 0; i < argc; i++) {
+ gen(atoi(*argv++), rs, 1);
+ }
+ } else {
+ while (1) {
+ i = random_upto(rs, 20) + 1;
+ gen(i, rs, 0);
+ }
+ }
+ }
+ random_free(rs);
+ return 0;
+}
diff --git a/auxiliary/matching.c b/auxiliary/matching.c
new file mode 100644
index 0000000..8157455
--- /dev/null
+++ b/auxiliary/matching.c
@@ -0,0 +1,397 @@
+/*
+ * Standalone tool to run the matching algorithm.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#include "puzzles.h"
+#include "matching.h"
+#include "tree234.h"
+
+static int nl, nr, count;
+static int **adjlists, *adjsizes;
+static int *adjdata, *outl, *outr, *witness;
+static void *scratch;
+static random_state *rs;
+
+static void allocate(int nl_, int nr_, int maxedges)
+{
+ nl = nl_;
+ nr = nr_;
+ adjdata = snewn(maxedges, int);
+ adjlists = snewn(nl, int *);
+ adjsizes = snewn(nl, int);
+ outl = snewn(nl, int);
+ outr = snewn(nr, int);
+ witness = snewn(nl+nr, int);
+ scratch = smalloc(matching_scratch_size(nl, nr));
+}
+
+static void deallocate(void)
+{
+ sfree(adjlists);
+ sfree(adjsizes);
+ sfree(adjdata);
+ sfree(outl);
+ sfree(outr);
+ sfree(witness);
+ sfree(scratch);
+}
+
+static void find_and_check_matching(void)
+{
+ int i, j, k;
+
+ count = matching_with_scratch(scratch, nl, nr, adjlists, adjsizes,
+ rs, outl, outr);
+ matching_witness(scratch, nl, nr, witness);
+
+ for (i = j = 0; i < nl; i++) {
+ if (outl[i] != -1) {
+ assert(0 <= outl[i] && outl[i] < nr);
+ assert(outr[outl[i]] == i);
+ j++;
+
+ for (k = 0; k < adjsizes[i]; k++)
+ if (adjlists[i][k] == outl[i])
+ break;
+ assert(k < adjsizes[i]);
+ }
+ }
+ assert(j == count);
+
+ for (i = j = 0; i < nr; i++) {
+ if (outr[i] != -1) {
+ assert(0 <= outr[i] && outr[i] < nl);
+ assert(outl[outr[i]] == i);
+ j++;
+ }
+ }
+ assert(j == count);
+
+ for (i = 0; i < nl; i++) {
+ if (outl[i] == -1)
+ assert(witness[i] == 0);
+ }
+ for (i = 0; i < nr; i++) {
+ if (outr[i] == -1)
+ assert(witness[nl+i] == 1);
+ }
+ for (i = 0; i < nl; i++) {
+ for (j = 0; j < adjsizes[i]; j++) {
+ k = adjlists[i][j];
+
+ if (outl[i] == k)
+ assert(!(witness[i] == 1 && witness[nl+k] == 0));
+ else
+ assert(!(witness[i] == 0 && witness[nl+k] == 1));
+ }
+ }
+}
+
+struct nodename {
+ const char *name;
+ int index;
+};
+
+static int compare_nodes(void *av, void *bv)
+{
+ const struct nodename *a = (const struct nodename *)av;
+ const struct nodename *b = (const struct nodename *)bv;
+ return strcmp(a->name, b->name);
+}
+
+static int node_index(tree234 *n2i, tree234 *i2n, const char *name)
+{
+ struct nodename *nn, *nn_prev;
+ char *namedup = dupstr(name);
+
+ nn = snew(struct nodename);
+ nn->name = namedup;
+ nn->index = count234(n2i);
+
+ nn_prev = add234(n2i, nn);
+ if (nn_prev != nn) {
+ sfree(nn);
+ sfree(namedup);
+ } else {
+ addpos234(i2n, nn, nn->index);
+ }
+
+ return nn_prev->index;
+}
+
+struct edge {
+ int L, R;
+};
+
+static int compare_edges(void *av, void *bv)
+{
+ const struct edge *a = (const struct edge *)av;
+ const struct edge *b = (const struct edge *)bv;
+ if (a->L < b->L) return -1;
+ if (a->L > b->L) return +1;
+ if (a->R < b->R) return -1;
+ if (a->R > b->R) return +1;
+ return 0;
+}
+
+static void matching_from_user_input(FILE *fp, const char *filename)
+{
+ tree234 *Ln2i, *Li2n, *Rn2i, *Ri2n, *edges;
+ char *line = NULL;
+ struct edge *e;
+ int i, lineno = 0;
+ int *adjptr;
+
+ Ln2i = newtree234(compare_nodes);
+ Rn2i = newtree234(compare_nodes);
+ Li2n = newtree234(NULL);
+ Ri2n = newtree234(NULL);
+ edges = newtree234(compare_edges);
+
+ while (sfree(line), lineno++, (line = fgetline(fp)) != NULL) {
+ char *p, *Lname, *Rname;
+
+ p = line;
+ while (*p && isspace((unsigned char)*p)) p++;
+ if (!*p)
+ continue;
+
+ Lname = p;
+ while (*p && !isspace((unsigned char)*p)) p++;
+ if (*p)
+ *p++ = '\0';
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ if (!*p) {
+ fprintf(stderr, "%s:%d: expected 2 words, found 1\n",
+ filename, lineno);
+ exit(1);
+ }
+
+ Rname = p;
+ while (*p && !isspace((unsigned char)*p)) p++;
+ if (*p)
+ *p++ = '\0';
+ while (*p && isspace((unsigned char)*p)) p++;
+
+ if (*p) {
+ fprintf(stderr, "%s:%d: expected 2 words, found more\n",
+ filename, lineno);
+ exit(1);
+ }
+
+ e = snew(struct edge);
+ e->L = node_index(Ln2i, Li2n, Lname);
+ e->R = node_index(Rn2i, Ri2n, Rname);
+ if (add234(edges, e) != e) {
+ fprintf(stderr, "%s:%d: duplicate edge\n",
+ filename, lineno);
+ exit(1);
+ }
+ }
+
+ allocate(count234(Ln2i), count234(Rn2i), count234(edges));
+
+ adjptr = adjdata;
+ for (i = 0; i < nl; i++)
+ adjlists[i] = NULL;
+ for (i = 0; (e = index234(edges, i)) != NULL; i++) {
+ if (!adjlists[e->L])
+ adjlists[e->L] = adjptr;
+ *adjptr++ = e->R;
+ adjsizes[e->L] = adjptr - adjlists[e->L];
+ }
+
+ find_and_check_matching();
+
+ for (i = 0; i < nl; i++) {
+ if (outl[i] != -1) {
+ struct nodename *Lnn = index234(Li2n, i);
+ struct nodename *Rnn = index234(Ri2n, outl[i]);
+ printf("%s %s\n", Lnn->name, Rnn->name);
+ }
+ }
+ deallocate();
+}
+
+static void test_subsets(void)
+{
+ int b = 8;
+ int n = 1 << b;
+ int i, j, nruns, expected_size;
+ int *adjptr;
+ int *edgecounts;
+ struct stats {
+ int min, max;
+ double n, sx, sxx;
+ } *stats;
+ static const char seed[] = "fixed random seed for repeatability";
+
+ /*
+ * Generate a graph in which every subset of [b] = {1,...,b}
+ * (represented as a b-bit integer 0 <= i < n) has an edge going
+ * to every subset obtained by removing exactly one element.
+ *
+ * This graph is the disjoint union of the corresponding graph for
+ * each layer (collection of same-sized subset) of the power set
+ * of [b]. Each of those graphs has a matching of size equal to
+ * the smaller of its vertex sets. So we expect the overall size
+ * of the output matching to be less than n by the size of the
+ * largest layer, that is, to be n - binomial(n, floor(n/2)).
+ *
+ * We run the generation repeatedly, randomising it every time,
+ * and we expect to see every possible edge appear sooner or
+ * later.
+ */
+
+ rs = random_new(seed, strlen(seed));
+
+ allocate(n, n, n*b);
+ adjptr = adjdata;
+ expected_size = 0;
+ for (i = 0; i < n; i++) {
+ adjlists[i] = adjptr;
+ for (j = 0; j < b; j++) {
+ if (i & (1 << j))
+ *adjptr++ = i & ~(1 << j);
+ }
+ adjsizes[i] = adjptr - adjlists[i];
+ if (adjsizes[i] != b/2)
+ expected_size++;
+ }
+
+ edgecounts = snewn(n*b, int);
+ for (i = 0; i < n*b; i++)
+ edgecounts[i] = 0;
+
+ stats = snewn(b, struct stats);
+
+ nruns = 0;
+ while (nruns < 10000) {
+ nruns++;
+ find_and_check_matching();
+ assert(count == expected_size);
+
+ for (i = 0; i < n; i++)
+ for (j = 0; j < b; j++)
+ if ((i ^ outl[i]) == (1 << j))
+ edgecounts[b*i+j]++;
+
+ if (nruns % 1000 == 0) {
+ for (i = 0; i < b; i++) {
+ struct stats *st = &stats[i];
+ st->min = st->max = -1;
+ st->n = st->sx = st->sxx = 0;
+ }
+
+ for (i = 0; i < n; i++) {
+ int pop = 0;
+ for (j = 0; j < b; j++)
+ if (i & (1 << j))
+ pop++;
+ pop--;
+
+ for (j = 0; j < b; j++) {
+ if (i & (1 << j)) {
+ struct stats *st = &stats[pop];
+ int x = edgecounts[b*i+j];
+ if (st->max == -1 || st->max < x)
+ st->max = x;
+ if (st->min == -1 || st->min > x)
+ st->min = x;
+ st->n++;
+ st->sx += x;
+ st->sxx += (double)x*x;
+ } else {
+ assert(edgecounts[b*i+j] == 0);
+ }
+ }
+ }
+ }
+ }
+
+ printf("after %d runs:\n", nruns);
+ for (j = 0; j < b; j++) {
+ struct stats *st = &stats[j];
+ printf("edges between layers %d,%d:"
+ " min=%d max=%d mean=%f variance=%f\n",
+ j, j+1, st->min, st->max, st->sx/st->n,
+ (st->sxx - st->sx*st->sx/st->n) / st->n);
+ }
+ sfree(edgecounts);
+ deallocate();
+}
+
+int main(int argc, char **argv)
+{
+ static const char stdin_identifier[] = "<standard input>";
+ const char *infile = NULL;
+ bool doing_opts = true;
+ enum { USER_INPUT, AUTOTEST } mode = USER_INPUT;
+
+ while (--argc > 0) {
+ const char *arg = *++argv;
+
+ if (doing_opts && arg[0] == '-' && arg[1]) {
+ if (!strcmp(arg, "--")) {
+ doing_opts = false;
+ } else if (!strcmp(arg, "--random")) {
+ char buf[64];
+ int len = sprintf(buf, "%lu", (unsigned long)time(NULL));
+ rs = random_new(buf, len);
+ } else if (!strcmp(arg, "--autotest")) {
+ mode = AUTOTEST;
+ } else {
+ fprintf(stderr, "matching: unrecognised option '%s'\n", arg);
+ return 1;
+ }
+ } else {
+ if (!infile) {
+ infile = (!strcmp(arg, "-") ? stdin_identifier : arg);
+ } else {
+ fprintf(stderr, "matching: too many arguments\n");
+ return 1;
+ }
+ }
+ }
+
+ if (mode == USER_INPUT) {
+ FILE *fp;
+
+ if (!infile)
+ infile = stdin_identifier;
+
+ if (infile != stdin_identifier) {
+ fp = fopen(infile, "r");
+ if (!fp) {
+ fprintf(stderr, "matching: could not open input file '%s'\n",
+ infile);
+ return 1;
+ }
+ } else {
+ fp = stdin;
+ }
+
+ matching_from_user_input(fp, infile);
+
+ if (infile != stdin_identifier)
+ fclose(fp);
+ }
+
+ if (mode == AUTOTEST) {
+ if (infile) {
+ fprintf(stderr, "matching: expected no filename argument "
+ "with --autotest\n");
+ return 1;
+ }
+
+ test_subsets();
+ }
+
+ return 0;
+}
diff --git a/obfusc.c b/auxiliary/obfusc.c
index c62189c..c62189c 100644
--- a/obfusc.c
+++ b/auxiliary/obfusc.c
diff --git a/auxiliary/penrose-test.c b/auxiliary/penrose-test.c
new file mode 100644
index 0000000..468997d
--- /dev/null
+++ b/auxiliary/penrose-test.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "puzzles.h"
+#include "penrose.h"
+
+static int show_recursion = 0;
+static int ntiles, nfinal;
+
+static int test_cb(penrose_state *state, vector *vs, int n, int depth)
+{
+ int i, xoff = 0, yoff = 0;
+ double l = penrose_side_length(state->start_size, depth);
+ double rball = l / 10.0;
+ const char *col;
+
+ ntiles++;
+ if (state->max_depth == depth) {
+ col = n == 4 ? "black" : "green";
+ nfinal++;
+ } else {
+ if (!show_recursion)
+ return 0;
+ col = n == 4 ? "red" : "blue";
+ }
+ if (n != 4) yoff = state->start_size;
+
+ printf("<polygon points=\"");
+ for (i = 0; i < n; i++) {
+ printf("%s%f,%f", (i == 0) ? "" : " ",
+ v_x(vs, i) + xoff, v_y(vs, i) + yoff);
+ }
+ printf("\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", col, col);
+ printf("<ellipse cx=\"%f\" cy=\"%f\" rx=\"%f\" ry=\"%f\" fill=\"%s\" />",
+ v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col);
+
+ return 0;
+}
+
+static void usage_exit(void)
+{
+ fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ penrose_state ps;
+ int which = 0;
+
+ while (--argc > 0) {
+ char *p = *++argv;
+ if (!strcmp(p, "-h") || !strcmp(p, "--help")) {
+ usage_exit();
+ } else if (!strcmp(p, "--recursion")) {
+ show_recursion = 1;
+ } else if (*p == '-') {
+ fprintf(stderr, "Unrecognised option '%s'\n", p);
+ exit(1);
+ } else {
+ break;
+ }
+ }
+
+ if (argc < 3) usage_exit();
+
+ if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2;
+ else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3;
+ else usage_exit();
+
+ ps.start_size = atoi(argv[1]);
+ ps.max_depth = atoi(argv[2]);
+ ps.new_tile = test_cb;
+
+ ntiles = nfinal = 0;
+
+ printf("\
+<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
+<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\
+\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\
+\n\
+<svg xmlns=\"http://www.w3.org/2000/svg\"\n\
+xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
+
+ printf("<g>\n");
+ penrose(&ps, which, 0);
+ printf("</g>\n");
+
+ printf("<!-- %d tiles and %d leaf tiles total -->\n",
+ ntiles, nfinal);
+
+ printf("</svg>");
+
+ return 0;
+}
diff --git a/auxiliary/sort-test.c b/auxiliary/sort-test.c
new file mode 100644
index 0000000..7ff0e1e
--- /dev/null
+++ b/auxiliary/sort-test.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "puzzles.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;
+}
diff --git a/auxiliary/tree234-test.c b/auxiliary/tree234-test.c
new file mode 100644
index 0000000..66ef274
--- /dev/null
+++ b/auxiliary/tree234-test.c
@@ -0,0 +1,746 @@
+/*
+ * Test code for the 2-3-4 tree. This code maintains an alternative
+ * representation of the data in the tree, in an array (using the
+ * obvious and slow insert and delete functions). After each tree
+ * operation, the verify() function is called, which ensures all
+ * the tree properties are preserved:
+ * - node->child->parent always equals node
+ * - tree->root->parent always equals NULL
+ * - number of kids == 0 or number of elements + 1;
+ * - tree has the same depth everywhere
+ * - every node has at least one element
+ * - subtree element counts are accurate
+ * - any NULL kid pointer is accompanied by a zero count
+ * - in a sorted tree: ordering property between elements of a
+ * node and elements of its children is preserved
+ * and also ensures the list represented by the tree is the same
+ * list it should be. (This last check also doubly verifies the
+ * ordering properties, because the `same list it should be' is by
+ * definition correctly ordered. It also ensures all nodes are
+ * distinct, because the enum functions would get caught in a loop
+ * if not.)
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "puzzles.h"
+
+#define TREE234_INTERNALS
+#include "tree234.h"
+
+/*
+ * Error reporting function.
+ */
+static void error(const char *fmt, ...) {
+ va_list ap;
+ printf("ERROR: ");
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+/* The array representation of the data. */
+static void **array;
+static int arraylen, arraysize;
+static cmpfn234 cmp;
+
+/* The tree representation of the same data. */
+static tree234 *tree;
+
+/*
+ * Routines to provide a diagnostic printout of a tree. Currently
+ * relies on every element in the tree being a one-character string
+ * :-)
+ */
+typedef struct {
+ char **levels;
+} dispctx;
+
+static int dispnode(node234 *n, int level, dispctx *ctx) {
+ if (level == 0) {
+ int xpos = strlen(ctx->levels[0]);
+ int len;
+
+ if (n->elems[2])
+ len = sprintf(ctx->levels[0]+xpos, " %s%s%s",
+ (char *)n->elems[0], (char *)n->elems[1],
+ (char *)n->elems[2]);
+ else if (n->elems[1])
+ len = sprintf(ctx->levels[0]+xpos, " %s%s",
+ (char *)n->elems[0], (char *)n->elems[1]);
+ else
+ len = sprintf(ctx->levels[0]+xpos, " %s",
+ (char *)n->elems[0]);
+ return xpos + 1 + (len-1) / 2;
+ } else {
+ int xpos[4], nkids;
+ int nodelen, mypos, myleft, x, i;
+
+ xpos[0] = dispnode(n->kids[0], level-3, ctx);
+ xpos[1] = dispnode(n->kids[1], level-3, ctx);
+ nkids = 2;
+ if (n->kids[2]) {
+ xpos[2] = dispnode(n->kids[2], level-3, ctx);
+ nkids = 3;
+ }
+ if (n->kids[3]) {
+ xpos[3] = dispnode(n->kids[3], level-3, ctx);
+ nkids = 4;
+ }
+
+ if (nkids == 4)
+ mypos = (xpos[1] + xpos[2]) / 2;
+ else if (nkids == 3)
+ mypos = xpos[1];
+ else
+ mypos = (xpos[0] + xpos[1]) / 2;
+ nodelen = nkids * 2 - 1;
+ myleft = mypos - ((nodelen-1)/2);
+ assert(myleft >= xpos[0]);
+ assert(myleft + nodelen-1 <= xpos[nkids-1]);
+
+ x = strlen(ctx->levels[level]);
+ while (x <= xpos[0] && x < myleft)
+ ctx->levels[level][x++] = ' ';
+ while (x < myleft)
+ ctx->levels[level][x++] = '_';
+ if (nkids==4)
+ x += sprintf(ctx->levels[level]+x, ".%s.%s.%s.",
+ (char *)n->elems[0], (char *)n->elems[1],
+ (char *)n->elems[2]);
+ else if (nkids==3)
+ x += sprintf(ctx->levels[level]+x, ".%s.%s.",
+ (char *)n->elems[0], (char *)n->elems[1]);
+ else
+ x += sprintf(ctx->levels[level]+x, ".%s.",
+ (char *)n->elems[0]);
+ while (x < xpos[nkids-1])
+ ctx->levels[level][x++] = '_';
+ ctx->levels[level][x] = '\0';
+
+ x = strlen(ctx->levels[level-1]);
+ for (i = 0; i < nkids; i++) {
+ int rpos, pos;
+ rpos = xpos[i];
+ if (i > 0 && i < nkids-1)
+ pos = myleft + 2*i;
+ else
+ pos = rpos;
+ if (rpos < pos)
+ rpos++;
+ while (x < pos && x < rpos)
+ ctx->levels[level-1][x++] = ' ';
+ if (x == pos)
+ ctx->levels[level-1][x++] = '|';
+ while (x < pos || x < rpos)
+ ctx->levels[level-1][x++] = '_';
+ if (x == pos)
+ ctx->levels[level-1][x++] = '|';
+ }
+ ctx->levels[level-1][x] = '\0';
+
+ x = strlen(ctx->levels[level-2]);
+ for (i = 0; i < nkids; i++) {
+ int rpos = xpos[i];
+
+ while (x < rpos)
+ ctx->levels[level-2][x++] = ' ';
+ ctx->levels[level-2][x++] = '|';
+ }
+ ctx->levels[level-2][x] = '\0';
+
+ return mypos;
+ }
+}
+
+static void disptree(tree234 *t) {
+ dispctx ctx;
+ char *leveldata;
+ int width = count234(t);
+ int ht = height234(t) * 3 - 2;
+ int i;
+
+ if (!t->root) {
+ printf("[empty tree]\n");
+ }
+
+ leveldata = smalloc(ht * (width+2));
+ ctx.levels = smalloc(ht * sizeof(char *));
+ for (i = 0; i < ht; i++) {
+ ctx.levels[i] = leveldata + i * (width+2);
+ ctx.levels[i][0] = '\0';
+ }
+
+ (void) dispnode(t->root, ht-1, &ctx);
+
+ for (i = ht; i-- ;)
+ printf("%s\n", ctx.levels[i]);
+
+ sfree(ctx.levels);
+ sfree(leveldata);
+}
+
+typedef struct {
+ int treedepth;
+ int elemcount;
+} chkctx;
+
+static int chknode(chkctx *ctx, int level, node234 *node,
+ void *lowbound, void *highbound) {
+ int nkids, nelems;
+ int i;
+ int count;
+
+ /* Count the non-NULL kids. */
+ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);
+ /* Ensure no kids beyond the first NULL are non-NULL. */
+ for (i = nkids; i < 4; i++)
+ if (node->kids[i]) {
+ error("node %p: nkids=%d but kids[%d] non-NULL",
+ node, nkids, i);
+ } else if (node->counts[i]) {
+ error("node %p: kids[%d] NULL but count[%d]=%d nonzero",
+ node, i, i, node->counts[i]);
+ }
+
+ /* Count the non-NULL elements. */
+ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);
+ /* Ensure no elements beyond the first NULL are non-NULL. */
+ for (i = nelems; i < 3; i++)
+ if (node->elems[i]) {
+ error("node %p: nelems=%d but elems[%d] non-NULL",
+ node, nelems, i);
+ }
+
+ if (nkids == 0) {
+ /*
+ * If nkids==0, this is a leaf node; verify that the tree
+ * depth is the same everywhere.
+ */
+ if (ctx->treedepth < 0)
+ ctx->treedepth = level; /* we didn't know the depth yet */
+ else if (ctx->treedepth != level)
+ error("node %p: leaf at depth %d, previously seen depth %d",
+ node, level, ctx->treedepth);
+ } else {
+ /*
+ * If nkids != 0, then it should be nelems+1, unless nelems
+ * is 0 in which case nkids should also be 0 (and so we
+ * shouldn't be in this condition at all).
+ */
+ int shouldkids = (nelems ? nelems+1 : 0);
+ if (nkids != shouldkids) {
+ error("node %p: %d elems should mean %d kids but has %d",
+ node, nelems, shouldkids, nkids);
+ }
+ }
+
+ /*
+ * nelems should be at least 1.
+ */
+ if (nelems == 0) {
+ error("node %p: no elems", node, nkids);
+ }
+
+ /*
+ * Add nelems to the running element count of the whole tree.
+ */
+ ctx->elemcount += nelems;
+
+ /*
+ * Check ordering property: all elements should be strictly >
+ * lowbound, strictly < highbound, and strictly < each other in
+ * sequence. (lowbound and highbound are NULL at edges of tree
+ * - both NULL at root node - and NULL is considered to be <
+ * everything and > everything. IYSWIM.)
+ */
+ if (cmp) {
+ for (i = -1; i < nelems; i++) {
+ void *lower = (i == -1 ? lowbound : node->elems[i]);
+ void *higher = (i+1 == nelems ? highbound : node->elems[i+1]);
+ if (lower && higher && cmp(lower, higher) >= 0) {
+ error("node %p: kid comparison [%d=%s,%d=%s] failed",
+ node, i, lower, i+1, higher);
+ }
+ }
+ }
+
+ /*
+ * Check parent pointers: all non-NULL kids should have a
+ * parent pointer coming back to this node.
+ */
+ for (i = 0; i < nkids; i++)
+ if (node->kids[i]->parent != node) {
+ error("node %p kid %d: parent ptr is %p not %p",
+ node, i, node->kids[i]->parent, node);
+ }
+
+
+ /*
+ * Now (finally!) recurse into subtrees.
+ */
+ count = nelems;
+
+ for (i = 0; i < nkids; i++) {
+ void *lower = (i == 0 ? lowbound : node->elems[i-1]);
+ void *higher = (i >= nelems ? highbound : node->elems[i]);
+ int subcount = chknode(ctx, level+1, node->kids[i], lower, higher);
+ if (node->counts[i] != subcount) {
+ error("node %p kid %d: count says %d, subtree really has %d",
+ node, i, node->counts[i], subcount);
+ }
+ count += subcount;
+ }
+
+ return count;
+}
+
+static void verifytree(tree234 *tree, void **array, int arraylen) {
+ chkctx ctx;
+ int i;
+ void *p;
+
+ ctx.treedepth = -1; /* depth unknown yet */
+ ctx.elemcount = 0; /* no elements seen yet */
+ /*
+ * Verify validity of tree properties.
+ */
+ if (tree->root) {
+ if (tree->root->parent != NULL)
+ error("root->parent is %p should be null", tree->root->parent);
+ chknode(&ctx, 0, tree->root, NULL, NULL);
+ }
+ printf("tree depth: %d\n", ctx.treedepth);
+ /*
+ * Enumerate the tree and ensure it matches up to the array.
+ */
+ for (i = 0; NULL != (p = index234(tree, i)); i++) {
+ if (i >= arraylen)
+ error("tree contains more than %d elements", arraylen);
+ if (array[i] != p)
+ error("enum at position %d: array says %s, tree says %s",
+ i, array[i], p);
+ }
+ if (ctx.elemcount != i) {
+ error("tree really contains %d elements, enum gave %d",
+ ctx.elemcount, i);
+ }
+ if (i < arraylen) {
+ error("enum gave only %d elements, array has %d", i, arraylen);
+ }
+ i = count234(tree);
+ if (ctx.elemcount != i) {
+ error("tree really contains %d elements, count234 gave %d",
+ ctx.elemcount, i);
+ }
+}
+static void verify(void) { verifytree(tree, array, arraylen); }
+
+static void internal_addtest(void *elem, int index, void *realret) {
+ int i, j;
+ void *retval;
+
+ if (arraysize < arraylen+1) {
+ arraysize = arraylen+1+256;
+ array = (array == NULL ? smalloc(arraysize*sizeof(*array)) :
+ srealloc(array, arraysize*sizeof(*array)));
+ }
+
+ i = index;
+ /* now i points to the first element >= elem */
+ retval = elem; /* expect elem returned (success) */
+ for (j = arraylen; j > i; j--)
+ array[j] = array[j-1];
+ array[i] = elem; /* add elem to array */
+ arraylen++;
+
+ if (realret != retval) {
+ error("add: retval was %p expected %p", realret, retval);
+ }
+
+ verify();
+}
+
+static void addtest(void *elem) {
+ int i;
+ void *realret;
+
+ realret = add234(tree, elem);
+
+ i = 0;
+ while (i < arraylen && cmp(elem, array[i]) > 0)
+ i++;
+ if (i < arraylen && !cmp(elem, array[i])) {
+ void *retval = array[i]; /* expect that returned not elem */
+ if (realret != retval) {
+ error("add: retval was %p expected %p", realret, retval);
+ }
+ } else
+ internal_addtest(elem, i, realret);
+}
+
+static void addpostest(void *elem, int i) {
+ void *realret;
+
+ realret = addpos234(tree, elem, i);
+
+ internal_addtest(elem, i, realret);
+}
+
+static void delpostest(int i) {
+ int index = i;
+ void *elem = array[i], *ret;
+
+ /* i points to the right element */
+ while (i < arraylen-1) {
+ array[i] = array[i+1];
+ i++;
+ }
+ arraylen--; /* delete elem from array */
+
+ if (tree->cmp)
+ ret = del234(tree, elem);
+ else
+ ret = delpos234(tree, index);
+
+ if (ret != elem) {
+ error("del returned %p, expected %p", ret, elem);
+ }
+
+ verify();
+}
+
+static void deltest(void *elem) {
+ int i;
+
+ i = 0;
+ while (i < arraylen && cmp(elem, array[i]) > 0)
+ i++;
+ if (i >= arraylen || cmp(elem, array[i]) != 0)
+ return; /* don't do it! */
+ delpostest(i);
+}
+
+/* A sample data set and test utility. Designed for pseudo-randomness,
+ * and yet repeatability. */
+
+/*
+ * This random number generator uses the `portable implementation'
+ * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;
+ * change it if not.
+ */
+static int randomnumber(unsigned *seed) {
+ *seed *= 1103515245;
+ *seed += 12345;
+ return ((*seed) / 65536) % 32768;
+}
+
+static int mycmp(void *av, void *bv) {
+ char const *a = (char const *)av;
+ char const *b = (char const *)bv;
+ return strcmp(a, b);
+}
+
+static const char *const strings_init[] = {
+ "0", "2", "3", "I", "K", "d", "H", "J", "Q", "N", "n", "q", "j", "i",
+ "7", "G", "F", "D", "b", "x", "g", "B", "e", "v", "V", "T", "f", "E",
+ "S", "8", "A", "k", "X", "p", "C", "R", "a", "o", "r", "O", "Z", "u",
+ "6", "1", "w", "L", "P", "M", "c", "U", "h", "9", "t", "5", "W", "Y",
+ "m", "s", "l", "4",
+#if 0
+ "a", "ab", "absque", "coram", "de",
+ "palam", "clam", "cum", "ex", "e",
+ "sine", "tenus", "pro", "prae",
+ "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",
+ "penguin", "blancmange", "pangolin", "whale", "hedgehog",
+ "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",
+ "murfl", "spoo", "breen", "flarn", "octothorpe",
+ "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",
+ "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",
+ "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",
+ "wand", "ring", "amulet"
+#endif
+};
+
+#define NSTR lenof(strings_init)
+static char *strings[NSTR];
+
+static void findtest(void) {
+ static const int rels[] = {
+ REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT
+ };
+ static const char *const relnames[] = {
+ "EQ", "GE", "LE", "LT", "GT"
+ };
+ int i, j, rel, index;
+ char *p, *ret, *realret, *realret2;
+ int lo, hi, mid, c;
+
+ for (i = 0; i < (int)NSTR; i++) {
+ p = strings[i];
+ for (j = 0; j < (int)(sizeof(rels)/sizeof(*rels)); j++) {
+ rel = rels[j];
+
+ lo = 0; hi = arraylen-1;
+ while (lo <= hi) {
+ mid = (lo + hi) / 2;
+ c = strcmp(p, array[mid]);
+ if (c < 0)
+ hi = mid-1;
+ else if (c > 0)
+ lo = mid+1;
+ else
+ break;
+ }
+
+ if (c == 0) {
+ if (rel == REL234_LT)
+ ret = (mid > 0 ? array[--mid] : NULL);
+ else if (rel == REL234_GT)
+ ret = (mid < arraylen-1 ? array[++mid] : NULL);
+ else
+ ret = array[mid];
+ } else {
+ assert(lo == hi+1);
+ if (rel == REL234_LT || rel == REL234_LE) {
+ mid = hi;
+ ret = (hi >= 0 ? array[hi] : NULL);
+ } else if (rel == REL234_GT || rel == REL234_GE) {
+ mid = lo;
+ ret = (lo < arraylen ? array[lo] : NULL);
+ } else
+ ret = NULL;
+ }
+
+ realret = findrelpos234(tree, p, NULL, rel, &index);
+ if (realret != ret) {
+ error("find(\"%s\",%s) gave %s should be %s",
+ p, relnames[j], realret, ret);
+ }
+ if (realret && index != mid) {
+ error("find(\"%s\",%s) gave %d should be %d",
+ p, relnames[j], index, mid);
+ }
+ if (realret && rel == REL234_EQ) {
+ realret2 = index234(tree, index);
+ if (realret2 != realret) {
+ error("find(\"%s\",%s) gave %s(%d) but %d -> %s",
+ p, relnames[j], realret, index, index, realret2);
+ }
+ }
+#if 0
+ printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],
+ realret, index);
+#endif
+ }
+ }
+
+ realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);
+ if (arraylen && (realret != array[0] || index != 0)) {
+ error("find(NULL,GT) gave %s(%d) should be %s(0)",
+ realret, index, array[0]);
+ } else if (!arraylen && (realret != NULL)) {
+ error("find(NULL,GT) gave %s(%d) should be NULL",
+ realret, index);
+ }
+
+ realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);
+ if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) {
+ error("find(NULL,LT) gave %s(%d) should be %s(0)",
+ realret, index, array[arraylen-1]);
+ } else if (!arraylen && (realret != NULL)) {
+ error("find(NULL,LT) gave %s(%d) should be NULL",
+ realret, index);
+ }
+}
+
+static void splittest(tree234 *tree, void **array, int arraylen) {
+ int i;
+ tree234 *tree3, *tree4;
+ for (i = 0; i <= arraylen; i++) {
+ tree3 = copytree234(tree, NULL, NULL);
+ tree4 = splitpos234(tree3, i, false);
+ verifytree(tree3, array, i);
+ verifytree(tree4, array+i, arraylen-i);
+ join234(tree3, tree4);
+ freetree234(tree4); /* left empty by join */
+ verifytree(tree3, array, arraylen);
+ freetree234(tree3);
+ }
+}
+
+int main(void) {
+ int in[NSTR];
+ int i, j, k;
+ int tworoot, tmplen;
+ unsigned seed = 0;
+ tree234 *tree2, *tree3, *tree4;
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ for (i = 0; i < (int)NSTR; i++)
+ strings[i] = dupstr(strings_init[i]);
+
+ for (i = 0; i < (int)NSTR; i++) in[i] = 0;
+ array = NULL;
+ arraylen = arraysize = 0;
+ tree = newtree234(mycmp);
+ cmp = mycmp;
+
+ verify();
+ for (i = 0; i < 10000; i++) {
+ j = randomnumber(&seed);
+ j %= NSTR;
+ printf("trial: %d\n", i);
+ if (in[j]) {
+ printf("deleting %s (%d)\n", strings[j], j);
+ deltest(strings[j]);
+ in[j] = 0;
+ } else {
+ printf("adding %s (%d)\n", strings[j], j);
+ addtest(strings[j]);
+ in[j] = 1;
+ }
+ disptree(tree);
+ findtest();
+ }
+
+ while (arraylen > 0) {
+ j = randomnumber(&seed);
+ j %= arraylen;
+ deltest(array[j]);
+ }
+
+ freetree234(tree);
+
+ /*
+ * Now try an unsorted tree. We don't really need to test
+ * delpos234 because we know del234 is based on it, so it's
+ * already been tested in the above sorted-tree code; but for
+ * completeness we'll use it to tear down our unsorted tree
+ * once we've built it.
+ */
+ tree = newtree234(NULL);
+ cmp = NULL;
+ verify();
+ for (i = 0; i < 1000; i++) {
+ printf("trial: %d\n", i);
+ j = randomnumber(&seed);
+ j %= NSTR;
+ k = randomnumber(&seed);
+ k %= count234(tree)+1;
+ printf("adding string %s at index %d\n", strings[j], k);
+ addpostest(strings[j], k);
+ }
+
+ /*
+ * While we have this tree in its full form, we'll take a copy
+ * of it to use in split and join testing.
+ */
+ tree2 = copytree234(tree, NULL, NULL);
+ verifytree(tree2, array, arraylen);/* check the copy is accurate */
+ /*
+ * Split tests. Split the tree at every possible point and
+ * check the resulting subtrees.
+ */
+ tworoot = (!tree2->root->elems[1]);/* see if it has a 2-root */
+ splittest(tree2, array, arraylen);
+ /*
+ * Now do the split test again, but on a tree that has a 2-root
+ * (if the previous one didn't) or doesn't (if the previous one
+ * did).
+ */
+ tmplen = arraylen;
+ while ((!tree2->root->elems[1]) == tworoot) {
+ delpos234(tree2, --tmplen);
+ }
+ printf("now trying splits on second tree\n");
+ splittest(tree2, array, tmplen);
+ freetree234(tree2);
+
+ /*
+ * Back to the main testing of uncounted trees.
+ */
+ while (count234(tree) > 0) {
+ printf("cleanup: tree size %d\n", count234(tree));
+ j = randomnumber(&seed);
+ j %= count234(tree);
+ printf("deleting string %s from index %d\n", (char *)array[j], j);
+ delpostest(j);
+ }
+ freetree234(tree);
+
+ /*
+ * Finally, do some testing on split/join on _sorted_ trees. At
+ * the same time, we'll be testing split on very small trees.
+ */
+ tree = newtree234(mycmp);
+ cmp = mycmp;
+ arraylen = 0;
+ for (i = 0; i < 17; i++) {
+ tree2 = copytree234(tree, NULL, NULL);
+ splittest(tree2, array, arraylen);
+ freetree234(tree2);
+ if (i < 16)
+ addtest(strings[i]);
+ }
+ freetree234(tree);
+
+ /*
+ * Test silly cases of join: join(emptytree, emptytree), and
+ * also ensure join correctly spots when sorted trees fail the
+ * ordering constraint.
+ */
+ tree = newtree234(mycmp);
+ tree2 = newtree234(mycmp);
+ tree3 = newtree234(mycmp);
+ tree4 = newtree234(mycmp);
+ assert(mycmp(strings[0], strings[1]) < 0); /* just in case :-) */
+ add234(tree2, strings[1]);
+ add234(tree4, strings[0]);
+ array[0] = strings[0];
+ array[1] = strings[1];
+ verifytree(tree, array, 0);
+ verifytree(tree2, array+1, 1);
+ verifytree(tree3, array, 0);
+ verifytree(tree4, array, 1);
+
+ /*
+ * So:
+ * - join(tree,tree3) should leave both tree and tree3 unchanged.
+ * - joinr(tree,tree2) should leave both tree and tree2 unchanged.
+ * - join(tree4,tree3) should leave both tree3 and tree4 unchanged.
+ * - join(tree, tree2) should move the element from tree2 to tree.
+ * - joinr(tree4, tree3) should move the element from tree4 to tree3.
+ * - join(tree,tree3) should return NULL and leave both unchanged.
+ * - join(tree3,tree) should work and create a bigger tree in tree3.
+ */
+ assert(tree == join234(tree, tree3));
+ verifytree(tree, array, 0);
+ verifytree(tree3, array, 0);
+ assert(tree2 == join234r(tree, tree2));
+ verifytree(tree, array, 0);
+ verifytree(tree2, array+1, 1);
+ assert(tree4 == join234(tree4, tree3));
+ verifytree(tree3, array, 0);
+ verifytree(tree4, array, 1);
+ assert(tree == join234(tree, tree2));
+ verifytree(tree, array+1, 1);
+ verifytree(tree2, array, 0);
+ assert(tree3 == join234r(tree4, tree3));
+ verifytree(tree3, array, 1);
+ verifytree(tree4, array, 0);
+ assert(NULL == join234(tree, tree3));
+ verifytree(tree, array+1, 1);
+ verifytree(tree3, array, 1);
+ assert(tree3 == join234(tree3, tree));
+ verifytree(tree3, array, 2);
+ verifytree(tree, array, 0);
+
+ return 0;
+}
diff --git a/combi.c b/combi.c
index deb0213..132d740 100644
--- a/combi.c
+++ b/combi.c
@@ -71,35 +71,3 @@ void free_combi(combi_ctx *combi)
sfree(combi->a);
sfree(combi);
}
-
-/* compile this with:
- * gcc -o combi.exe -DSTANDALONE_COMBI_TEST combi.c malloc.c
- */
-#ifdef STANDALONE_COMBI_TEST
-
-#include <stdio.h>
-
-int main(int argc, char *argv[])
-{
- combi_ctx *c;
- int i, r, n;
-
- if (argc < 3) {
- fprintf(stderr, "Usage: combi R N\n");
- exit(1);
- }
-
- r = atoi(argv[1]); n = atoi(argv[2]);
- c = new_combi(r, n);
- printf("combi %d of %d, %d elements.\n", c->r, c->n, c->total);
-
- while (next_combi(c)) {
- for (i = 0; i < c->r; i++) {
- printf("%d ", c->a[i]);
- }
- printf("\n");
- }
- free_combi(c);
-}
-
-#endif
diff --git a/divvy.c b/divvy.c
index 5b8ee27..021956f 100644
--- a/divvy.c
+++ b/divvy.c
@@ -260,7 +260,7 @@ static bool addremcommon(int w, int h, int x, int y, int *own, int val)
* In both of the above suggested use cases, the user would
* probably want w==h==k, but that isn't a requirement.
*/
-static int *divvy_internal(int w, int h, int k, random_state *rs)
+int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs)
{
int *order, *queue, *tmp, *own, *sizes, *addable, *retdsf;
bool *removable;
@@ -652,131 +652,13 @@ static int *divvy_internal(int w, int h, int k, random_state *rs)
return retdsf;
}
-#ifdef TESTMODE
-static int fail_counter = 0;
-#endif
-
int *divvy_rectangle(int w, int h, int k, random_state *rs)
{
int *ret;
do {
- ret = divvy_internal(w, h, k, rs);
-
-#ifdef TESTMODE
- if (!ret)
- fail_counter++;
-#endif
-
+ ret = divvy_rectangle_attempt(w, h, k, rs);
} while (!ret);
return ret;
}
-
-#ifdef TESTMODE
-
-/*
- * gcc -g -O0 -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
- *
- * or to debug
- *
- * gcc -g -O0 -DDIVVY_DIAGNOSTICS -DTESTMODE -I.. -o divvy divvy.c ../random.c ../malloc.c ../dsf.c ../misc.c ../nullfe.c
- */
-
-int main(int argc, char **argv)
-{
- int *dsf;
- int i;
- int w = 9, h = 4, k = 6, tries = 100;
- random_state *rs;
-
- rs = random_new("123456", 6);
-
- if (argc > 1)
- w = atoi(argv[1]);
- if (argc > 2)
- h = atoi(argv[2]);
- if (argc > 3)
- k = atoi(argv[3]);
- if (argc > 4)
- tries = atoi(argv[4]);
-
- for (i = 0; i < tries; i++) {
- int x, y;
-
- dsf = divvy_rectangle(w, h, k, rs);
- assert(dsf);
-
- for (y = 0; y <= 2*h; y++) {
- for (x = 0; x <= 2*w; x++) {
- int miny = y/2 - 1 /*, maxy = y/2 */;
- int minx = x/2 - 1 /*, maxx = x/2 */;
- int classes[4], tx, ty;
- for (ty = 0; ty < 2; ty++)
- for (tx = 0; tx < 2; tx++) {
- int cx = minx+tx, cy = miny+ty;
- if (cx < 0 || cx >= w || cy < 0 || cy >= h)
- classes[ty*2+tx] = -1;
- else
- classes[ty*2+tx] = dsf_canonify(dsf, cy*w+cx);
- }
- switch (y%2 * 2 + x%2) {
- case 0: /* corner */
- /*
- * Cases for the corner:
- *
- * - if all four surrounding squares belong
- * to the same omino, we print a space.
- *
- * - if the top two are the same and the
- * bottom two are the same, we print a
- * horizontal line.
- *
- * - if the left two are the same and the
- * right two are the same, we print a
- * vertical line.
- *
- * - otherwise, we print a cross.
- */
- if (classes[0] == classes[1] &&
- classes[1] == classes[2] &&
- classes[2] == classes[3])
- printf(" ");
- else if (classes[0] == classes[1] &&
- classes[2] == classes[3])
- printf("-");
- else if (classes[0] == classes[2] &&
- classes[1] == classes[3])
- printf("|");
- else
- printf("+");
- break;
- case 1: /* horiz edge */
- if (classes[1] == classes[3])
- printf(" ");
- else
- printf("--");
- break;
- case 2: /* vert edge */
- if (classes[2] == classes[3])
- printf(" ");
- else
- printf("|");
- break;
- case 3: /* square centre */
- printf(" ");
- break;
- }
- }
- printf("\n");
- }
- printf("\n");
- sfree(dsf);
- }
-
- printf("%d retries needed for %d successes\n", fail_counter, tries);
-
- return 0;
-}
-
-#endif
diff --git a/latin.c b/latin.c
index 454e55c..a2d5713 100644
--- a/latin.c
+++ b/latin.c
@@ -1311,121 +1311,4 @@ bool latin_check(digit *sq, int order)
return ret;
}
-
-/* --------------------------------------------------------
- * Testing (and printing).
- */
-
-#ifdef STANDALONE_LATIN_TEST
-
-#include <stdio.h>
-#include <time.h>
-
-static const char *quis;
-
-static void latin_print(digit *sq, int order)
-{
- int x, y;
-
- for (y = 0; y < order; y++) {
- for (x = 0; x < order; x++) {
- printf("%2u ", ELT(sq, x, y));
- }
- printf("\n");
- }
- printf("\n");
-}
-
-static void gen(int order, random_state *rs, int debug)
-{
- digit *sq;
-
- solver_show_working = debug;
-
- sq = latin_generate(order, rs);
- latin_print(sq, order);
- if (latin_check(sq, order)) {
- fprintf(stderr, "Square is not a latin square!");
- exit(1);
- }
-
- sfree(sq);
-}
-
-static void test_soak(int order, random_state *rs)
-{
- digit *sq;
- int n = 0;
- time_t tt_start, tt_now, tt_last;
-
- solver_show_working = 0;
- tt_now = tt_start = time(NULL);
-
- while(1) {
- sq = latin_generate(order, rs);
- sfree(sq);
- n++;
-
- tt_last = time(NULL);
- if (tt_last > tt_now) {
- tt_now = tt_last;
- printf("%d total, %3.1f/s\n", n,
- (double)n / (double)(tt_now - tt_start));
- }
- }
-}
-
-static void usage_exit(const char *msg)
-{
- if (msg)
- fprintf(stderr, "%s: %s\n", quis, msg);
- fprintf(stderr, "Usage: %s [--seed SEED] --soak <params> | [game_id [game_id ...]]\n", quis);
- exit(1);
-}
-
-int main(int argc, char *argv[])
-{
- int i, soak = 0;
- random_state *rs;
- time_t seed = time(NULL);
-
- quis = argv[0];
- while (--argc > 0) {
- const char *p = *++argv;
- if (!strcmp(p, "--soak"))
- soak = 1;
- else if (!strcmp(p, "--seed")) {
- if (argc == 0)
- usage_exit("--seed needs an argument");
- seed = (time_t)atoi(*++argv);
- argc--;
- } else if (*p == '-')
- usage_exit("unrecognised option");
- else
- break; /* finished options */
- }
-
- rs = random_new((void*)&seed, sizeof(time_t));
-
- if (soak == 1) {
- if (argc != 1) usage_exit("only one argument for --soak");
- test_soak(atoi(*argv), rs);
- } else {
- if (argc > 0) {
- for (i = 0; i < argc; i++) {
- gen(atoi(*argv++), rs, 1);
- }
- } else {
- while (1) {
- i = random_upto(rs, 20) + 1;
- gen(i, rs, 0);
- }
- }
- }
- random_free(rs);
- return 0;
-}
-
-#endif
-
/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/matching.c b/matching.c
index 27b288f..aabdae0 100644
--- a/matching.c
+++ b/matching.c
@@ -319,35 +319,7 @@ int matching(int nl, int nr, int **adjlists, int *adjsizes,
return ret;
}
-#ifdef STANDALONE_MATCHING_TEST
-
-/*
- * Diagnostic routine used in testing this algorithm. It is passed a
- * pointer to a piece of scratch space that's just been used by
- * matching_with_scratch, and extracts from it a labelling of the
- * input graph that acts as a 'witness' to the maximality of the
- * returned matching.
- *
- * The output parameter 'witness' should be an array of (nl+nr)
- * integers, indexed such that witness[L] corresponds to an L-vertex (for
- * L=0,1,...,nl-1) and witness[nl+R] corresponds to an R-vertex (for
- * R=0,1,...,nr-1). On return, this array will assign each vertex a
- * label which is either 0 or 1, and the following properties will
- * hold:
- *
- * + all vertices not paired up by the matching are type L0 or R1
- * + every L0->R1 edge is used by the matching
- * + no L1->R0 edge is used by the matching.
- *
- * The mere existence of such a labelling is enough to prove the
- * maximality of the matching, because if there is any larger matching
- * then its symmetric difference with this one must include at least
- * one 'augmenting path', which starts at a free L-vertex and ends at
- * a free R-vertex, traversing only unused L->R edges and only used
- * R->L edges. But that would mean it starts at an L0, ends at an R1,
- * and never follows an edge that can get from an 0 to a 1.
- */
-static void matching_witness(void *scratchv, int nl, int nr, int *witness)
+void matching_witness(void *scratchv, int nl, int nr, int *witness)
{
struct scratch *s = (struct scratch *)scratchv;
int i, j;
@@ -357,400 +329,3 @@ static void matching_witness(void *scratchv, int nl, int nr, int *witness)
for (j = 0; j < nr; j++)
witness[nl + j] = s->Rlayer[j] == -1;
}
-
-/*
- * Standalone tool to run the matching algorithm.
- */
-
-#include <string.h>
-#include <ctype.h>
-#include <time.h>
-
-#include "tree234.h"
-
-static int nl, nr, count;
-static int **adjlists, *adjsizes;
-static int *adjdata, *outl, *outr, *witness;
-static void *scratch;
-static random_state *rs;
-
-static void allocate(int nl_, int nr_, int maxedges)
-{
- nl = nl_;
- nr = nr_;
- adjdata = snewn(maxedges, int);
- adjlists = snewn(nl, int *);
- adjsizes = snewn(nl, int);
- outl = snewn(nl, int);
- outr = snewn(nr, int);
- witness = snewn(nl+nr, int);
- scratch = smalloc(matching_scratch_size(nl, nr));
-}
-
-static void deallocate(void)
-{
- sfree(adjlists);
- sfree(adjsizes);
- sfree(adjdata);
- sfree(outl);
- sfree(outr);
- sfree(witness);
- sfree(scratch);
-}
-
-static void find_and_check_matching(void)
-{
- int i, j, k;
-
- count = matching_with_scratch(scratch, nl, nr, adjlists, adjsizes,
- rs, outl, outr);
- matching_witness(scratch, nl, nr, witness);
-
- for (i = j = 0; i < nl; i++) {
- if (outl[i] != -1) {
- assert(0 <= outl[i] && outl[i] < nr);
- assert(outr[outl[i]] == i);
- j++;
-
- for (k = 0; k < adjsizes[i]; k++)
- if (adjlists[i][k] == outl[i])
- break;
- assert(k < adjsizes[i]);
- }
- }
- assert(j == count);
-
- for (i = j = 0; i < nr; i++) {
- if (outr[i] != -1) {
- assert(0 <= outr[i] && outr[i] < nl);
- assert(outl[outr[i]] == i);
- j++;
- }
- }
- assert(j == count);
-
- for (i = 0; i < nl; i++) {
- if (outl[i] == -1)
- assert(witness[i] == 0);
- }
- for (i = 0; i < nr; i++) {
- if (outr[i] == -1)
- assert(witness[nl+i] == 1);
- }
- for (i = 0; i < nl; i++) {
- for (j = 0; j < adjsizes[i]; j++) {
- k = adjlists[i][j];
-
- if (outl[i] == k)
- assert(!(witness[i] == 1 && witness[nl+k] == 0));
- else
- assert(!(witness[i] == 0 && witness[nl+k] == 1));
- }
- }
-}
-
-struct nodename {
- const char *name;
- int index;
-};
-
-static int compare_nodes(void *av, void *bv)
-{
- const struct nodename *a = (const struct nodename *)av;
- const struct nodename *b = (const struct nodename *)bv;
- return strcmp(a->name, b->name);
-}
-
-static int node_index(tree234 *n2i, tree234 *i2n, const char *name)
-{
- struct nodename *nn, *nn_prev;
- char *namedup = dupstr(name);
-
- nn = snew(struct nodename);
- nn->name = namedup;
- nn->index = count234(n2i);
-
- nn_prev = add234(n2i, nn);
- if (nn_prev != nn) {
- sfree(nn);
- sfree(namedup);
- } else {
- addpos234(i2n, nn, nn->index);
- }
-
- return nn_prev->index;
-}
-
-struct edge {
- int L, R;
-};
-
-static int compare_edges(void *av, void *bv)
-{
- const struct edge *a = (const struct edge *)av;
- const struct edge *b = (const struct edge *)bv;
- if (a->L < b->L) return -1;
- if (a->L > b->L) return +1;
- if (a->R < b->R) return -1;
- if (a->R > b->R) return +1;
- return 0;
-}
-
-static void matching_from_user_input(FILE *fp, const char *filename)
-{
- tree234 *Ln2i, *Li2n, *Rn2i, *Ri2n, *edges;
- char *line = NULL;
- struct edge *e;
- int i, lineno = 0;
- int *adjptr;
-
- Ln2i = newtree234(compare_nodes);
- Rn2i = newtree234(compare_nodes);
- Li2n = newtree234(NULL);
- Ri2n = newtree234(NULL);
- edges = newtree234(compare_edges);
-
- while (sfree(line), lineno++, (line = fgetline(fp)) != NULL) {
- char *p, *Lname, *Rname;
-
- p = line;
- while (*p && isspace((unsigned char)*p)) p++;
- if (!*p)
- continue;
-
- Lname = p;
- while (*p && !isspace((unsigned char)*p)) p++;
- if (*p)
- *p++ = '\0';
- while (*p && isspace((unsigned char)*p)) p++;
-
- if (!*p) {
- fprintf(stderr, "%s:%d: expected 2 words, found 1\n",
- filename, lineno);
- exit(1);
- }
-
- Rname = p;
- while (*p && !isspace((unsigned char)*p)) p++;
- if (*p)
- *p++ = '\0';
- while (*p && isspace((unsigned char)*p)) p++;
-
- if (*p) {
- fprintf(stderr, "%s:%d: expected 2 words, found more\n",
- filename, lineno);
- exit(1);
- }
-
- e = snew(struct edge);
- e->L = node_index(Ln2i, Li2n, Lname);
- e->R = node_index(Rn2i, Ri2n, Rname);
- if (add234(edges, e) != e) {
- fprintf(stderr, "%s:%d: duplicate edge\n",
- filename, lineno);
- exit(1);
- }
- }
-
- allocate(count234(Ln2i), count234(Rn2i), count234(edges));
-
- adjptr = adjdata;
- for (i = 0; i < nl; i++)
- adjlists[i] = NULL;
- for (i = 0; (e = index234(edges, i)) != NULL; i++) {
- if (!adjlists[e->L])
- adjlists[e->L] = adjptr;
- *adjptr++ = e->R;
- adjsizes[e->L] = adjptr - adjlists[e->L];
- }
-
- find_and_check_matching();
-
- for (i = 0; i < nl; i++) {
- if (outl[i] != -1) {
- struct nodename *Lnn = index234(Li2n, i);
- struct nodename *Rnn = index234(Ri2n, outl[i]);
- printf("%s %s\n", Lnn->name, Rnn->name);
- }
- }
- deallocate();
-}
-
-static void test_subsets(void)
-{
- int b = 8;
- int n = 1 << b;
- int i, j, nruns, expected_size;
- int *adjptr;
- int *edgecounts;
- struct stats {
- int min, max;
- double n, sx, sxx;
- } *stats;
- static const char seed[] = "fixed random seed for repeatability";
-
- /*
- * Generate a graph in which every subset of [b] = {1,...,b}
- * (represented as a b-bit integer 0 <= i < n) has an edge going
- * to every subset obtained by removing exactly one element.
- *
- * This graph is the disjoint union of the corresponding graph for
- * each layer (collection of same-sized subset) of the power set
- * of [b]. Each of those graphs has a matching of size equal to
- * the smaller of its vertex sets. So we expect the overall size
- * of the output matching to be less than n by the size of the
- * largest layer, that is, to be n - binomial(n, floor(n/2)).
- *
- * We run the generation repeatedly, randomising it every time,
- * and we expect to see every possible edge appear sooner or
- * later.
- */
-
- rs = random_new(seed, strlen(seed));
-
- allocate(n, n, n*b);
- adjptr = adjdata;
- expected_size = 0;
- for (i = 0; i < n; i++) {
- adjlists[i] = adjptr;
- for (j = 0; j < b; j++) {
- if (i & (1 << j))
- *adjptr++ = i & ~(1 << j);
- }
- adjsizes[i] = adjptr - adjlists[i];
- if (adjsizes[i] != b/2)
- expected_size++;
- }
-
- edgecounts = snewn(n*b, int);
- for (i = 0; i < n*b; i++)
- edgecounts[i] = 0;
-
- stats = snewn(b, struct stats);
-
- nruns = 0;
- while (nruns < 10000) {
- nruns++;
- find_and_check_matching();
- assert(count == expected_size);
-
- for (i = 0; i < n; i++)
- for (j = 0; j < b; j++)
- if ((i ^ outl[i]) == (1 << j))
- edgecounts[b*i+j]++;
-
- if (nruns % 1000 == 0) {
- for (i = 0; i < b; i++) {
- struct stats *st = &stats[i];
- st->min = st->max = -1;
- st->n = st->sx = st->sxx = 0;
- }
-
- for (i = 0; i < n; i++) {
- int pop = 0;
- for (j = 0; j < b; j++)
- if (i & (1 << j))
- pop++;
- pop--;
-
- for (j = 0; j < b; j++) {
- if (i & (1 << j)) {
- struct stats *st = &stats[pop];
- int x = edgecounts[b*i+j];
- if (st->max == -1 || st->max < x)
- st->max = x;
- if (st->min == -1 || st->min > x)
- st->min = x;
- st->n++;
- st->sx += x;
- st->sxx += (double)x*x;
- } else {
- assert(edgecounts[b*i+j] == 0);
- }
- }
- }
- }
- }
-
- printf("after %d runs:\n", nruns);
- for (j = 0; j < b; j++) {
- struct stats *st = &stats[j];
- printf("edges between layers %d,%d:"
- " min=%d max=%d mean=%f variance=%f\n",
- j, j+1, st->min, st->max, st->sx/st->n,
- (st->sxx - st->sx*st->sx/st->n) / st->n);
- }
- sfree(edgecounts);
- deallocate();
-}
-
-int main(int argc, char **argv)
-{
- static const char stdin_identifier[] = "<standard input>";
- const char *infile = NULL;
- bool doing_opts = true;
- enum { USER_INPUT, AUTOTEST } mode = USER_INPUT;
-
- while (--argc > 0) {
- const char *arg = *++argv;
-
- if (doing_opts && arg[0] == '-' && arg[1]) {
- if (!strcmp(arg, "--")) {
- doing_opts = false;
- } else if (!strcmp(arg, "--random")) {
- char buf[64];
- int len = sprintf(buf, "%lu", (unsigned long)time(NULL));
- rs = random_new(buf, len);
- } else if (!strcmp(arg, "--autotest")) {
- mode = AUTOTEST;
- } else {
- fprintf(stderr, "matching: unrecognised option '%s'\n", arg);
- return 1;
- }
- } else {
- if (!infile) {
- infile = (!strcmp(arg, "-") ? stdin_identifier : arg);
- } else {
- fprintf(stderr, "matching: too many arguments\n");
- return 1;
- }
- }
- }
-
- if (mode == USER_INPUT) {
- FILE *fp;
-
- if (!infile)
- infile = stdin_identifier;
-
- if (infile != stdin_identifier) {
- fp = fopen(infile, "r");
- if (!fp) {
- fprintf(stderr, "matching: could not open input file '%s'\n",
- infile);
- return 1;
- }
- } else {
- fp = stdin;
- }
-
- matching_from_user_input(fp, infile);
-
- if (infile != stdin_identifier)
- fclose(fp);
- }
-
- if (mode == AUTOTEST) {
- if (infile) {
- fprintf(stderr, "matching: expected no filename argument "
- "with --autotest\n");
- return 1;
- }
-
- test_subsets();
- }
-
- return 0;
-}
-
-#endif /* STANDALONE_MATCHING_TEST */
diff --git a/matching.h b/matching.h
index a4d3098..dbf424d 100644
--- a/matching.h
+++ b/matching.h
@@ -77,4 +77,32 @@ size_t matching_scratch_size(int nl, int nr);
int matching(int nl, int nr, int **adjlists, int *adjsizes,
random_state *rs, int *outl, int *outr);
+/*
+ * Diagnostic routine used in testing this algorithm. It is passed a
+ * pointer to a piece of scratch space that's just been used by
+ * matching_with_scratch, and extracts from it a labelling of the
+ * input graph that acts as a 'witness' to the maximality of the
+ * returned matching.
+ *
+ * The output parameter 'witness' should be an array of (nl+nr)
+ * integers, indexed such that witness[L] corresponds to an L-vertex (for
+ * L=0,1,...,nl-1) and witness[nl+R] corresponds to an R-vertex (for
+ * R=0,1,...,nr-1). On return, this array will assign each vertex a
+ * label which is either 0 or 1, and the following properties will
+ * hold:
+ *
+ * + all vertices not paired up by the matching are type L0 or R1
+ * + every L0->R1 edge is used by the matching
+ * + no L1->R0 edge is used by the matching.
+ *
+ * The mere existence of such a labelling is enough to prove the
+ * maximality of the matching, because if there is any larger matching
+ * then its symmetric difference with this one must include at least
+ * one 'augmenting path', which starts at a free L-vertex and ends at
+ * a free R-vertex, traversing only unused L->R edges and only used
+ * R->L edges. But that would mean it starts at an L0, ends at an R1,
+ * and never follows an edge that can get from an 0 to a 1.
+ */
+void matching_witness(void *scratch, int nl, int nr, int *witness);
+
#endif /* MATCHING_MATCHING_H */
diff --git a/penrose.c b/penrose.c
index 1318109..457b3fd 100644
--- a/penrose.c
+++ b/penrose.c
@@ -499,131 +499,3 @@ void penrose_calculate_size(int which, int tilesize, int w, int h,
*depth = n;
*required_radius = rradius;
}
-
-/* -------------------------------------------------------
- * Test code.
- */
-
-#ifdef TEST_PENROSE
-
-#include <stdio.h>
-#include <string.h>
-
-static int show_recursion = 0;
-static int ntiles, nfinal;
-
-static int test_cb(penrose_state *state, vector *vs, int n, int depth)
-{
- int i, xoff = 0, yoff = 0;
- double l = penrose_side_length(state->start_size, depth);
- double rball = l / 10.0;
- const char *col;
-
- ntiles++;
- if (state->max_depth == depth) {
- col = n == 4 ? "black" : "green";
- nfinal++;
- } else {
- if (!show_recursion)
- return 0;
- col = n == 4 ? "red" : "blue";
- }
- if (n != 4) yoff = state->start_size;
-
- printf("<polygon points=\"");
- for (i = 0; i < n; i++) {
- printf("%s%f,%f", (i == 0) ? "" : " ",
- v_x(vs, i) + xoff, v_y(vs, i) + yoff);
- }
- printf("\" style=\"fill: %s; fill-opacity: 0.2; stroke: %s\" />\n", col, col);
- printf("<ellipse cx=\"%f\" cy=\"%f\" rx=\"%f\" ry=\"%f\" fill=\"%s\" />",
- v_x(vs, 0) + xoff, v_y(vs, 0) + yoff, rball, rball, col);
-
- return 0;
-}
-
-static void usage_exit(void)
-{
- fprintf(stderr, "Usage: penrose-test [--recursion] P2|P3 SIZE DEPTH\n");
- exit(1);
-}
-
-int main(int argc, char *argv[])
-{
- penrose_state ps;
- int which = 0;
-
- while (--argc > 0) {
- char *p = *++argv;
- if (!strcmp(p, "-h") || !strcmp(p, "--help")) {
- usage_exit();
- } else if (!strcmp(p, "--recursion")) {
- show_recursion = 1;
- } else if (*p == '-') {
- fprintf(stderr, "Unrecognised option '%s'\n", p);
- exit(1);
- } else {
- break;
- }
- }
-
- if (argc < 3) usage_exit();
-
- if (strcmp(argv[0], "P2") == 0) which = PENROSE_P2;
- else if (strcmp(argv[0], "P3") == 0) which = PENROSE_P3;
- else usage_exit();
-
- ps.start_size = atoi(argv[1]);
- ps.max_depth = atoi(argv[2]);
- ps.new_tile = test_cb;
-
- ntiles = nfinal = 0;
-
- printf("\
-<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
-<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n\
-\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n\
-\n\
-<svg xmlns=\"http://www.w3.org/2000/svg\"\n\
-xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n");
-
- printf("<g>\n");
- penrose(&ps, which, 0);
- printf("</g>\n");
-
- printf("<!-- %d tiles and %d leaf tiles total -->\n",
- ntiles, nfinal);
-
- printf("</svg>");
-
- return 0;
-}
-
-#endif
-
-#ifdef TEST_VECTORS
-
-static void dbgv(const char *msg, vector v)
-{
- printf("%s: %s\n", msg, v_debug(v));
-}
-
-int main(int argc, const char *argv[])
-{
- vector v = v_unit();
-
- dbgv("unit vector", v);
- v = v_rotate(v, 36);
- dbgv("rotated 36", v);
- v = v_scale(v, 2);
- dbgv("scaled x2", v);
- v = v_shrinkphi(v);
- dbgv("shrunk phi", v);
- v = v_rotate(v, -36);
- dbgv("rotated -36", v);
-
- return 0;
-}
-
-#endif
-/* vim: set shiftwidth=4 tabstop=8: */
diff --git a/puzzles.h b/puzzles.h
index 36ba2a9..6b5ff6e 100644
--- a/puzzles.h
+++ b/puzzles.h
@@ -563,6 +563,8 @@ void free_combi(combi_ctx *combi);
*/
/* divides w*h rectangle into pieces of size k. Returns w*h dsf. */
int *divvy_rectangle(int w, int h, int k, random_state *rs);
+/* Same, but only tries once, and may fail. (Exposed for test program.) */
+int *divvy_rectangle_attempt(int w, int h, int k, random_state *rs);
/*
* findloop.c
diff --git a/sort.c b/sort.c
index 2e20cac..6dff92f 100644
--- a/sort.c
+++ b/sort.c
@@ -87,74 +87,3 @@ void arraysort_fn(void *array, size_t nmemb, size_t size,
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 */
diff --git a/tree234.c b/tree234.c
index 1d33be8..ef2c3ff 100644
--- a/tree234.c
+++ b/tree234.c
@@ -29,11 +29,12 @@
#include <stdlib.h>
#include <assert.h>
+#define TREE234_INTERNALS
#include "tree234.h"
#include "puzzles.h" /* for smalloc/sfree */
-#ifdef TEST
+#ifdef DEBUG_TREE234
#include <stdarg.h>
static void logprintf(const char *fmt, ...)
{
@@ -47,20 +48,6 @@ static void logprintf(const char *fmt, ...)
#define LOG(x)
#endif
-typedef struct node234_Tag node234;
-
-struct tree234_Tag {
- node234 *root;
- cmpfn234 cmp;
-};
-
-struct node234_Tag {
- node234 *parent;
- node234 *kids[4];
- int counts[4];
- void *elems[3];
-};
-
/*
* Create a 2-3-4 tree.
*/
@@ -1109,7 +1096,7 @@ static node234 *join234_internal(node234 *left, void *sep,
return root;
}
-static int height234(tree234 *t) {
+int height234(tree234 *t) {
int level = 0;
node234 *n = t->root;
while (n) {
@@ -1457,754 +1444,3 @@ tree234 *copytree234(tree234 *t, copyfn234 copyfn, void *copyfnstate) {
return t2;
}
-
-#ifdef TEST
-
-/*
- * Test code for the 2-3-4 tree. This code maintains an alternative
- * representation of the data in the tree, in an array (using the
- * obvious and slow insert and delete functions). After each tree
- * operation, the verify() function is called, which ensures all
- * the tree properties are preserved:
- * - node->child->parent always equals node
- * - tree->root->parent always equals NULL
- * - number of kids == 0 or number of elements + 1;
- * - tree has the same depth everywhere
- * - every node has at least one element
- * - subtree element counts are accurate
- * - any NULL kid pointer is accompanied by a zero count
- * - in a sorted tree: ordering property between elements of a
- * node and elements of its children is preserved
- * and also ensures the list represented by the tree is the same
- * list it should be. (This last check also doubly verifies the
- * ordering properties, because the `same list it should be' is by
- * definition correctly ordered. It also ensures all nodes are
- * distinct, because the enum functions would get caught in a loop
- * if not.)
- */
-
-#include <string.h>
-#include <stdarg.h>
-
-#define srealloc realloc
-
-/*
- * Error reporting function.
- */
-static void error(const char *fmt, ...) {
- va_list ap;
- printf("ERROR: ");
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- va_end(ap);
- printf("\n");
-}
-
-/* The array representation of the data. */
-static void **array;
-static int arraylen, arraysize;
-static cmpfn234 cmp;
-
-/* The tree representation of the same data. */
-static tree234 *tree;
-
-/*
- * Routines to provide a diagnostic printout of a tree. Currently
- * relies on every element in the tree being a one-character string
- * :-)
- */
-typedef struct {
- char **levels;
-} dispctx;
-
-static int dispnode(node234 *n, int level, dispctx *ctx) {
- if (level == 0) {
- int xpos = strlen(ctx->levels[0]);
- int len;
-
- if (n->elems[2])
- len = sprintf(ctx->levels[0]+xpos, " %s%s%s",
- (char *)n->elems[0], (char *)n->elems[1],
- (char *)n->elems[2]);
- else if (n->elems[1])
- len = sprintf(ctx->levels[0]+xpos, " %s%s",
- (char *)n->elems[0], (char *)n->elems[1]);
- else
- len = sprintf(ctx->levels[0]+xpos, " %s",
- (char *)n->elems[0]);
- return xpos + 1 + (len-1) / 2;
- } else {
- int xpos[4], nkids;
- int nodelen, mypos, myleft, x, i;
-
- xpos[0] = dispnode(n->kids[0], level-3, ctx);
- xpos[1] = dispnode(n->kids[1], level-3, ctx);
- nkids = 2;
- if (n->kids[2]) {
- xpos[2] = dispnode(n->kids[2], level-3, ctx);
- nkids = 3;
- }
- if (n->kids[3]) {
- xpos[3] = dispnode(n->kids[3], level-3, ctx);
- nkids = 4;
- }
-
- if (nkids == 4)
- mypos = (xpos[1] + xpos[2]) / 2;
- else if (nkids == 3)
- mypos = xpos[1];
- else
- mypos = (xpos[0] + xpos[1]) / 2;
- nodelen = nkids * 2 - 1;
- myleft = mypos - ((nodelen-1)/2);
- assert(myleft >= xpos[0]);
- assert(myleft + nodelen-1 <= xpos[nkids-1]);
-
- x = strlen(ctx->levels[level]);
- while (x <= xpos[0] && x < myleft)
- ctx->levels[level][x++] = ' ';
- while (x < myleft)
- ctx->levels[level][x++] = '_';
- if (nkids==4)
- x += sprintf(ctx->levels[level]+x, ".%s.%s.%s.",
- (char *)n->elems[0], (char *)n->elems[1],
- (char *)n->elems[2]);
- else if (nkids==3)
- x += sprintf(ctx->levels[level]+x, ".%s.%s.",
- (char *)n->elems[0], (char *)n->elems[1]);
- else
- x += sprintf(ctx->levels[level]+x, ".%s.",
- (char *)n->elems[0]);
- while (x < xpos[nkids-1])
- ctx->levels[level][x++] = '_';
- ctx->levels[level][x] = '\0';
-
- x = strlen(ctx->levels[level-1]);
- for (i = 0; i < nkids; i++) {
- int rpos, pos;
- rpos = xpos[i];
- if (i > 0 && i < nkids-1)
- pos = myleft + 2*i;
- else
- pos = rpos;
- if (rpos < pos)
- rpos++;
- while (x < pos && x < rpos)
- ctx->levels[level-1][x++] = ' ';
- if (x == pos)
- ctx->levels[level-1][x++] = '|';
- while (x < pos || x < rpos)
- ctx->levels[level-1][x++] = '_';
- if (x == pos)
- ctx->levels[level-1][x++] = '|';
- }
- ctx->levels[level-1][x] = '\0';
-
- x = strlen(ctx->levels[level-2]);
- for (i = 0; i < nkids; i++) {
- int rpos = xpos[i];
-
- while (x < rpos)
- ctx->levels[level-2][x++] = ' ';
- ctx->levels[level-2][x++] = '|';
- }
- ctx->levels[level-2][x] = '\0';
-
- return mypos;
- }
-}
-
-static void disptree(tree234 *t) {
- dispctx ctx;
- char *leveldata;
- int width = count234(t);
- int ht = height234(t) * 3 - 2;
- int i;
-
- if (!t->root) {
- printf("[empty tree]\n");
- }
-
- leveldata = smalloc(ht * (width+2));
- ctx.levels = smalloc(ht * sizeof(char *));
- for (i = 0; i < ht; i++) {
- ctx.levels[i] = leveldata + i * (width+2);
- ctx.levels[i][0] = '\0';
- }
-
- (void) dispnode(t->root, ht-1, &ctx);
-
- for (i = ht; i-- ;)
- printf("%s\n", ctx.levels[i]);
-
- sfree(ctx.levels);
- sfree(leveldata);
-}
-
-typedef struct {
- int treedepth;
- int elemcount;
-} chkctx;
-
-static int chknode(chkctx *ctx, int level, node234 *node,
- void *lowbound, void *highbound) {
- int nkids, nelems;
- int i;
- int count;
-
- /* Count the non-NULL kids. */
- for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++);
- /* Ensure no kids beyond the first NULL are non-NULL. */
- for (i = nkids; i < 4; i++)
- if (node->kids[i]) {
- error("node %p: nkids=%d but kids[%d] non-NULL",
- node, nkids, i);
- } else if (node->counts[i]) {
- error("node %p: kids[%d] NULL but count[%d]=%d nonzero",
- node, i, i, node->counts[i]);
- }
-
- /* Count the non-NULL elements. */
- for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++);
- /* Ensure no elements beyond the first NULL are non-NULL. */
- for (i = nelems; i < 3; i++)
- if (node->elems[i]) {
- error("node %p: nelems=%d but elems[%d] non-NULL",
- node, nelems, i);
- }
-
- if (nkids == 0) {
- /*
- * If nkids==0, this is a leaf node; verify that the tree
- * depth is the same everywhere.
- */
- if (ctx->treedepth < 0)
- ctx->treedepth = level; /* we didn't know the depth yet */
- else if (ctx->treedepth != level)
- error("node %p: leaf at depth %d, previously seen depth %d",
- node, level, ctx->treedepth);
- } else {
- /*
- * If nkids != 0, then it should be nelems+1, unless nelems
- * is 0 in which case nkids should also be 0 (and so we
- * shouldn't be in this condition at all).
- */
- int shouldkids = (nelems ? nelems+1 : 0);
- if (nkids != shouldkids) {
- error("node %p: %d elems should mean %d kids but has %d",
- node, nelems, shouldkids, nkids);
- }
- }
-
- /*
- * nelems should be at least 1.
- */
- if (nelems == 0) {
- error("node %p: no elems", node, nkids);
- }
-
- /*
- * Add nelems to the running element count of the whole tree.
- */
- ctx->elemcount += nelems;
-
- /*
- * Check ordering property: all elements should be strictly >
- * lowbound, strictly < highbound, and strictly < each other in
- * sequence. (lowbound and highbound are NULL at edges of tree
- * - both NULL at root node - and NULL is considered to be <
- * everything and > everything. IYSWIM.)
- */
- if (cmp) {
- for (i = -1; i < nelems; i++) {
- void *lower = (i == -1 ? lowbound : node->elems[i]);
- void *higher = (i+1 == nelems ? highbound : node->elems[i+1]);
- if (lower && higher && cmp(lower, higher) >= 0) {
- error("node %p: kid comparison [%d=%s,%d=%s] failed",
- node, i, lower, i+1, higher);
- }
- }
- }
-
- /*
- * Check parent pointers: all non-NULL kids should have a
- * parent pointer coming back to this node.
- */
- for (i = 0; i < nkids; i++)
- if (node->kids[i]->parent != node) {
- error("node %p kid %d: parent ptr is %p not %p",
- node, i, node->kids[i]->parent, node);
- }
-
-
- /*
- * Now (finally!) recurse into subtrees.
- */
- count = nelems;
-
- for (i = 0; i < nkids; i++) {
- void *lower = (i == 0 ? lowbound : node->elems[i-1]);
- void *higher = (i >= nelems ? highbound : node->elems[i]);
- int subcount = chknode(ctx, level+1, node->kids[i], lower, higher);
- if (node->counts[i] != subcount) {
- error("node %p kid %d: count says %d, subtree really has %d",
- node, i, node->counts[i], subcount);
- }
- count += subcount;
- }
-
- return count;
-}
-
-static void verifytree(tree234 *tree, void **array, int arraylen) {
- chkctx ctx;
- int i;
- void *p;
-
- ctx.treedepth = -1; /* depth unknown yet */
- ctx.elemcount = 0; /* no elements seen yet */
- /*
- * Verify validity of tree properties.
- */
- if (tree->root) {
- if (tree->root->parent != NULL)
- error("root->parent is %p should be null", tree->root->parent);
- chknode(&ctx, 0, tree->root, NULL, NULL);
- }
- printf("tree depth: %d\n", ctx.treedepth);
- /*
- * Enumerate the tree and ensure it matches up to the array.
- */
- for (i = 0; NULL != (p = index234(tree, i)); i++) {
- if (i >= arraylen)
- error("tree contains more than %d elements", arraylen);
- if (array[i] != p)
- error("enum at position %d: array says %s, tree says %s",
- i, array[i], p);
- }
- if (ctx.elemcount != i) {
- error("tree really contains %d elements, enum gave %d",
- ctx.elemcount, i);
- }
- if (i < arraylen) {
- error("enum gave only %d elements, array has %d", i, arraylen);
- }
- i = count234(tree);
- if (ctx.elemcount != i) {
- error("tree really contains %d elements, count234 gave %d",
- ctx.elemcount, i);
- }
-}
-static void verify(void) { verifytree(tree, array, arraylen); }
-
-static void internal_addtest(void *elem, int index, void *realret) {
- int i, j;
- void *retval;
-
- if (arraysize < arraylen+1) {
- arraysize = arraylen+1+256;
- array = (array == NULL ? smalloc(arraysize*sizeof(*array)) :
- srealloc(array, arraysize*sizeof(*array)));
- }
-
- i = index;
- /* now i points to the first element >= elem */
- retval = elem; /* expect elem returned (success) */
- for (j = arraylen; j > i; j--)
- array[j] = array[j-1];
- array[i] = elem; /* add elem to array */
- arraylen++;
-
- if (realret != retval) {
- error("add: retval was %p expected %p", realret, retval);
- }
-
- verify();
-}
-
-static void addtest(void *elem) {
- int i;
- void *realret;
-
- realret = add234(tree, elem);
-
- i = 0;
- while (i < arraylen && cmp(elem, array[i]) > 0)
- i++;
- if (i < arraylen && !cmp(elem, array[i])) {
- void *retval = array[i]; /* expect that returned not elem */
- if (realret != retval) {
- error("add: retval was %p expected %p", realret, retval);
- }
- } else
- internal_addtest(elem, i, realret);
-}
-
-static void addpostest(void *elem, int i) {
- void *realret;
-
- realret = addpos234(tree, elem, i);
-
- internal_addtest(elem, i, realret);
-}
-
-static void delpostest(int i) {
- int index = i;
- void *elem = array[i], *ret;
-
- /* i points to the right element */
- while (i < arraylen-1) {
- array[i] = array[i+1];
- i++;
- }
- arraylen--; /* delete elem from array */
-
- if (tree->cmp)
- ret = del234(tree, elem);
- else
- ret = delpos234(tree, index);
-
- if (ret != elem) {
- error("del returned %p, expected %p", ret, elem);
- }
-
- verify();
-}
-
-static void deltest(void *elem) {
- int i;
-
- i = 0;
- while (i < arraylen && cmp(elem, array[i]) > 0)
- i++;
- if (i >= arraylen || cmp(elem, array[i]) != 0)
- return; /* don't do it! */
- delpostest(i);
-}
-
-/* A sample data set and test utility. Designed for pseudo-randomness,
- * and yet repeatability. */
-
-/*
- * This random number generator uses the `portable implementation'
- * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits;
- * change it if not.
- */
-static int randomnumber(unsigned *seed) {
- *seed *= 1103515245;
- *seed += 12345;
- return ((*seed) / 65536) % 32768;
-}
-
-static int mycmp(void *av, void *bv) {
- char const *a = (char const *)av;
- char const *b = (char const *)bv;
- return strcmp(a, b);
-}
-
-static const char *const strings_init[] = {
- "0", "2", "3", "I", "K", "d", "H", "J", "Q", "N", "n", "q", "j", "i",
- "7", "G", "F", "D", "b", "x", "g", "B", "e", "v", "V", "T", "f", "E",
- "S", "8", "A", "k", "X", "p", "C", "R", "a", "o", "r", "O", "Z", "u",
- "6", "1", "w", "L", "P", "M", "c", "U", "h", "9", "t", "5", "W", "Y",
- "m", "s", "l", "4",
-#if 0
- "a", "ab", "absque", "coram", "de",
- "palam", "clam", "cum", "ex", "e",
- "sine", "tenus", "pro", "prae",
- "banana", "carrot", "cabbage", "broccoli", "onion", "zebra",
- "penguin", "blancmange", "pangolin", "whale", "hedgehog",
- "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux",
- "murfl", "spoo", "breen", "flarn", "octothorpe",
- "snail", "tiger", "elephant", "octopus", "warthog", "armadillo",
- "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin",
- "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper",
- "wand", "ring", "amulet"
-#endif
-};
-
-#define NSTR lenof(strings_init)
-static char *strings[NSTR];
-
-static void findtest(void) {
- static const int rels[] = {
- REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT
- };
- static const char *const relnames[] = {
- "EQ", "GE", "LE", "LT", "GT"
- };
- int i, j, rel, index;
- char *p, *ret, *realret, *realret2;
- int lo, hi, mid, c;
-
- for (i = 0; i < (int)NSTR; i++) {
- p = strings[i];
- for (j = 0; j < (int)(sizeof(rels)/sizeof(*rels)); j++) {
- rel = rels[j];
-
- lo = 0; hi = arraylen-1;
- while (lo <= hi) {
- mid = (lo + hi) / 2;
- c = strcmp(p, array[mid]);
- if (c < 0)
- hi = mid-1;
- else if (c > 0)
- lo = mid+1;
- else
- break;
- }
-
- if (c == 0) {
- if (rel == REL234_LT)
- ret = (mid > 0 ? array[--mid] : NULL);
- else if (rel == REL234_GT)
- ret = (mid < arraylen-1 ? array[++mid] : NULL);
- else
- ret = array[mid];
- } else {
- assert(lo == hi+1);
- if (rel == REL234_LT || rel == REL234_LE) {
- mid = hi;
- ret = (hi >= 0 ? array[hi] : NULL);
- } else if (rel == REL234_GT || rel == REL234_GE) {
- mid = lo;
- ret = (lo < arraylen ? array[lo] : NULL);
- } else
- ret = NULL;
- }
-
- realret = findrelpos234(tree, p, NULL, rel, &index);
- if (realret != ret) {
- error("find(\"%s\",%s) gave %s should be %s",
- p, relnames[j], realret, ret);
- }
- if (realret && index != mid) {
- error("find(\"%s\",%s) gave %d should be %d",
- p, relnames[j], index, mid);
- }
- if (realret && rel == REL234_EQ) {
- realret2 = index234(tree, index);
- if (realret2 != realret) {
- error("find(\"%s\",%s) gave %s(%d) but %d -> %s",
- p, relnames[j], realret, index, index, realret2);
- }
- }
-#if 0
- printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j],
- realret, index);
-#endif
- }
- }
-
- realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index);
- if (arraylen && (realret != array[0] || index != 0)) {
- error("find(NULL,GT) gave %s(%d) should be %s(0)",
- realret, index, array[0]);
- } else if (!arraylen && (realret != NULL)) {
- error("find(NULL,GT) gave %s(%d) should be NULL",
- realret, index);
- }
-
- realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index);
- if (arraylen && (realret != array[arraylen-1] || index != arraylen-1)) {
- error("find(NULL,LT) gave %s(%d) should be %s(0)",
- realret, index, array[arraylen-1]);
- } else if (!arraylen && (realret != NULL)) {
- error("find(NULL,LT) gave %s(%d) should be NULL",
- realret, index);
- }
-}
-
-static void splittest(tree234 *tree, void **array, int arraylen) {
- int i;
- tree234 *tree3, *tree4;
- for (i = 0; i <= arraylen; i++) {
- tree3 = copytree234(tree, NULL, NULL);
- tree4 = splitpos234(tree3, i, false);
- verifytree(tree3, array, i);
- verifytree(tree4, array+i, arraylen-i);
- join234(tree3, tree4);
- freetree234(tree4); /* left empty by join */
- verifytree(tree3, array, arraylen);
- freetree234(tree3);
- }
-}
-
-int main(void) {
- int in[NSTR];
- int i, j, k;
- int tworoot, tmplen;
- unsigned seed = 0;
- tree234 *tree2, *tree3, *tree4;
-
- setvbuf(stdout, NULL, _IOLBF, 0);
-
- for (i = 0; i < (int)NSTR; i++)
- strings[i] = dupstr(strings_init[i]);
-
- for (i = 0; i < (int)NSTR; i++) in[i] = 0;
- array = NULL;
- arraylen = arraysize = 0;
- tree = newtree234(mycmp);
- cmp = mycmp;
-
- verify();
- for (i = 0; i < 10000; i++) {
- j = randomnumber(&seed);
- j %= NSTR;
- printf("trial: %d\n", i);
- if (in[j]) {
- printf("deleting %s (%d)\n", strings[j], j);
- deltest(strings[j]);
- in[j] = 0;
- } else {
- printf("adding %s (%d)\n", strings[j], j);
- addtest(strings[j]);
- in[j] = 1;
- }
- disptree(tree);
- findtest();
- }
-
- while (arraylen > 0) {
- j = randomnumber(&seed);
- j %= arraylen;
- deltest(array[j]);
- }
-
- freetree234(tree);
-
- /*
- * Now try an unsorted tree. We don't really need to test
- * delpos234 because we know del234 is based on it, so it's
- * already been tested in the above sorted-tree code; but for
- * completeness we'll use it to tear down our unsorted tree
- * once we've built it.
- */
- tree = newtree234(NULL);
- cmp = NULL;
- verify();
- for (i = 0; i < 1000; i++) {
- printf("trial: %d\n", i);
- j = randomnumber(&seed);
- j %= NSTR;
- k = randomnumber(&seed);
- k %= count234(tree)+1;
- printf("adding string %s at index %d\n", strings[j], k);
- addpostest(strings[j], k);
- }
-
- /*
- * While we have this tree in its full form, we'll take a copy
- * of it to use in split and join testing.
- */
- tree2 = copytree234(tree, NULL, NULL);
- verifytree(tree2, array, arraylen);/* check the copy is accurate */
- /*
- * Split tests. Split the tree at every possible point and
- * check the resulting subtrees.
- */
- tworoot = (!tree2->root->elems[1]);/* see if it has a 2-root */
- splittest(tree2, array, arraylen);
- /*
- * Now do the split test again, but on a tree that has a 2-root
- * (if the previous one didn't) or doesn't (if the previous one
- * did).
- */
- tmplen = arraylen;
- while ((!tree2->root->elems[1]) == tworoot) {
- delpos234(tree2, --tmplen);
- }
- printf("now trying splits on second tree\n");
- splittest(tree2, array, tmplen);
- freetree234(tree2);
-
- /*
- * Back to the main testing of uncounted trees.
- */
- while (count234(tree) > 0) {
- printf("cleanup: tree size %d\n", count234(tree));
- j = randomnumber(&seed);
- j %= count234(tree);
- printf("deleting string %s from index %d\n", (char *)array[j], j);
- delpostest(j);
- }
- freetree234(tree);
-
- /*
- * Finally, do some testing on split/join on _sorted_ trees. At
- * the same time, we'll be testing split on very small trees.
- */
- tree = newtree234(mycmp);
- cmp = mycmp;
- arraylen = 0;
- for (i = 0; i < 17; i++) {
- tree2 = copytree234(tree, NULL, NULL);
- splittest(tree2, array, arraylen);
- freetree234(tree2);
- if (i < 16)
- addtest(strings[i]);
- }
- freetree234(tree);
-
- /*
- * Test silly cases of join: join(emptytree, emptytree), and
- * also ensure join correctly spots when sorted trees fail the
- * ordering constraint.
- */
- tree = newtree234(mycmp);
- tree2 = newtree234(mycmp);
- tree3 = newtree234(mycmp);
- tree4 = newtree234(mycmp);
- assert(mycmp(strings[0], strings[1]) < 0); /* just in case :-) */
- add234(tree2, strings[1]);
- add234(tree4, strings[0]);
- array[0] = strings[0];
- array[1] = strings[1];
- verifytree(tree, array, 0);
- verifytree(tree2, array+1, 1);
- verifytree(tree3, array, 0);
- verifytree(tree4, array, 1);
-
- /*
- * So:
- * - join(tree,tree3) should leave both tree and tree3 unchanged.
- * - joinr(tree,tree2) should leave both tree and tree2 unchanged.
- * - join(tree4,tree3) should leave both tree3 and tree4 unchanged.
- * - join(tree, tree2) should move the element from tree2 to tree.
- * - joinr(tree4, tree3) should move the element from tree4 to tree3.
- * - join(tree,tree3) should return NULL and leave both unchanged.
- * - join(tree3,tree) should work and create a bigger tree in tree3.
- */
- assert(tree == join234(tree, tree3));
- verifytree(tree, array, 0);
- verifytree(tree3, array, 0);
- assert(tree2 == join234r(tree, tree2));
- verifytree(tree, array, 0);
- verifytree(tree2, array+1, 1);
- assert(tree4 == join234(tree4, tree3));
- verifytree(tree3, array, 0);
- verifytree(tree4, array, 1);
- assert(tree == join234(tree, tree2));
- verifytree(tree, array+1, 1);
- verifytree(tree2, array, 0);
- assert(tree3 == join234r(tree4, tree3));
- verifytree(tree3, array, 1);
- verifytree(tree4, array, 0);
- assert(NULL == join234(tree, tree3));
- verifytree(tree, array+1, 1);
- verifytree(tree3, array, 1);
- assert(tree3 == join234(tree3, tree));
- verifytree(tree3, array, 2);
- verifytree(tree, array, 0);
-
- return 0;
-}
-
-#endif
-
-#if 0 /* sorted list of strings might be useful */
-{
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
-}
-#endif
diff --git a/tree234.h b/tree234.h
index ccd943d..b58f939 100644
--- a/tree234.h
+++ b/tree234.h
@@ -31,7 +31,11 @@
#include <stdbool.h>
/*
- * This typedef is opaque outside tree234.c itself.
+ * This typedef is typically opaque outside tree234.c itself. But you
+ * can define TREE234_INTERNALS to get a definition of it and its
+ * subsidiary node structure, as long as you're prepared to commit to
+ * responding to changes in the internals (which probably means you're
+ * tree234.c itself or tree234-test.c).
*/
typedef struct tree234_Tag tree234;
@@ -39,6 +43,24 @@ typedef int (*cmpfn234)(void *, void *);
typedef void *(*copyfn234)(void *state, void *element);
+#ifdef TREE234_INTERNALS
+typedef struct node234_Tag node234;
+
+struct tree234_Tag {
+ node234 *root;
+ cmpfn234 cmp;
+};
+
+struct node234_Tag {
+ node234 *parent;
+ node234 *kids[4];
+ int counts[4];
+ void *elems[3];
+};
+
+int height234(tree234 *t);
+#endif
+
/*
* Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and
* lookups by key will fail: you can only look things up by numeric