mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-20 23:51:29 -07:00
Move other test main()s out of library source files.
Having stated the principle in the previous commit, I should apply it consistently. A source file linked into the Puzzles library of common support code should not also define a main() under ifdef. This commit only goes as far as the _library_ support modules. It would be a much bigger job to do the same for all the actual _puzzles_ that have test main()s or standalone-solver main()s. And it's not necessary, because modifying one of those source files only triggers a rebuild of _one_ puzzle, not absolutely everything. (Not to mention that it's quite likely the puzzle and the test main() will need to be modified in conjunction anyway.) As in the previous commit, this has required exposing a few internal API functions as global, and maybe editing them a bit. In particular, the one-shot internal function that divvy_rectangle() loops on until it succeeds is now exposed as divvy_rectangle_attempt(), which means the test program doesn't have to condition a failure counter into the real function. I've thrown away penrose-vector-test completely, because that didn't look like a test program with any ongoing use at all - it was surely vestigial, while James was getting the vector representation up and running in the first place.
This commit is contained in:
@ -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)
|
||||
|
25
auxiliary/combi-test.c
Normal file
25
auxiliary/combi-test.c
Normal file
@ -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);
|
||||
}
|
103
auxiliary/divvy-test.c
Normal file
103
auxiliary/divvy-test.c
Normal file
@ -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;
|
||||
}
|
110
auxiliary/latin-test.c
Normal file
110
auxiliary/latin-test.c
Normal file
@ -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;
|
||||
}
|
397
auxiliary/matching.c
Normal file
397
auxiliary/matching.c
Normal file
@ -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;
|
||||
}
|
130
auxiliary/obfusc.c
Normal file
130
auxiliary/obfusc.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Stand-alone tool to access the Puzzles obfuscation algorithm.
|
||||
*
|
||||
* To deobfuscate, use "obfusc -d":
|
||||
*
|
||||
* obfusc -d reads binary data from stdin, writes to stdout
|
||||
* obfusc -d <hex string> works on the given hex string instead of stdin
|
||||
* obfusc -d -h writes a hex string instead of binary to stdout
|
||||
*
|
||||
* To obfuscate, "obfusc -e":
|
||||
*
|
||||
* obfusc -e reads binary from stdin, writes hex to stdout
|
||||
* obfusc -e <hex string> works on the given hex string instead of stdin
|
||||
* obfusc -e -b writes binary instead of text to stdout
|
||||
*
|
||||
* The default output format is hex for -e and binary for -d
|
||||
* because that's the way obfuscation is generally used in
|
||||
* Puzzles. Either of -b and -h can always be specified to set it
|
||||
* explicitly.
|
||||
*
|
||||
* Data read from standard input is assumed always to be binary;
|
||||
* data provided on the command line is taken to be hex.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "puzzles.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
enum { BINARY, DEFAULT, HEX } outputmode = DEFAULT;
|
||||
char *inhex = NULL;
|
||||
unsigned char *data;
|
||||
int datalen;
|
||||
enum { UNKNOWN, DECODE, ENCODE } mode = UNKNOWN;
|
||||
bool doing_opts = true;
|
||||
|
||||
while (--argc > 0) {
|
||||
char *p = *++argv;
|
||||
|
||||
if (doing_opts && *p == '-') {
|
||||
if (!strcmp(p, "--")) {
|
||||
doing_opts = 0;
|
||||
continue;
|
||||
}
|
||||
p++;
|
||||
while (*p) {
|
||||
switch (*p) {
|
||||
case 'e':
|
||||
mode = ENCODE;
|
||||
break;
|
||||
case 'd':
|
||||
mode = DECODE;
|
||||
break;
|
||||
case 'b':
|
||||
outputmode = BINARY;
|
||||
break;
|
||||
case 'h':
|
||||
outputmode = HEX;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "obfusc: unrecognised option '-%c'\n",
|
||||
*p);
|
||||
return 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
} else {
|
||||
if (!inhex) {
|
||||
inhex = p;
|
||||
} else {
|
||||
fprintf(stderr, "obfusc: expected at most one argument\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == UNKNOWN) {
|
||||
fprintf(stderr, "usage: obfusc < -e | -d > [ -b | -h ] [hex data]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (outputmode == DEFAULT)
|
||||
outputmode = (mode == DECODE ? BINARY : HEX);
|
||||
|
||||
if (inhex) {
|
||||
datalen = strlen(inhex) / 2;
|
||||
data = hex2bin(inhex, datalen);
|
||||
} else {
|
||||
int datasize = 4096;
|
||||
datalen = 0;
|
||||
data = snewn(datasize, unsigned char);
|
||||
while (1) {
|
||||
int ret = fread(data + datalen, 1, datasize - datalen, stdin);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "obfusc: read: %s\n", strerror(errno));
|
||||
return 1;
|
||||
} else if (ret == 0) {
|
||||
break;
|
||||
} else {
|
||||
datalen += ret;
|
||||
if (datasize - datalen < 4096) {
|
||||
datasize = datalen * 5 / 4 + 4096;
|
||||
data = sresize(data, datasize, unsigned char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obfuscate_bitmap(data, datalen * 8, mode == DECODE);
|
||||
|
||||
if (outputmode == BINARY) {
|
||||
int ret = fwrite(data, 1, datalen, stdout);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "obfusc: write: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < datalen; i++)
|
||||
printf("%02x", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
95
auxiliary/penrose-test.c
Normal file
95
auxiliary/penrose-test.c
Normal file
@ -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;
|
||||
}
|
70
auxiliary/sort-test.c
Normal file
70
auxiliary/sort-test.c
Normal file
@ -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;
|
||||
}
|
746
auxiliary/tree234-test.c
Normal file
746
auxiliary/tree234-test.c
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user