/* cc -O cubes.c -lm -o cub 2>&1 | more
./cub -x 4 -y 3 > cub.out 2>&1
nohup nice ./cub -x 2 -y 2 -z 2 > cub.out 2>&1
#
#  Copyright (c) 2021 - 2022    David Albert Bagley, bagleyd AT verizon.net
#
#		   All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee is hereby granted,
#  provided that the above copyright notice appear in all copies and
#  that both that copyright notice and this permission notice appear in
#  supporting documentation, and that the name of the author not be
#  used in advertising or publicity pertaining to distribution of the
#  software without specific, written prior permission.
#
#  This program is distributed in the hope that it will be "usable",
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#  NOTE: you may THRASH your hard drive if you run out of RAM...
#  IF YOU HAVE LOTS OF DISK ACTIVITY AFTER EXECUTING THIS, TERMINATE IT!

https://kconrad.math.uconn.edu/blurbs/grouptheory/15puzzle.pdf

DO NOT try for order 16 i.e. 4x4 (aka. 15 puzzle) -x 4 -y 4, by the 27th move
it will eat all memory and freeze 16 Gig system.
DO NOT even try for order 14 i.e. 7x2 -x 7 -y 2, by the 42nd move it will eat
all memory and freeze 16 Gig system.
Highest I recommend (if you have this much memory (less if you do not)) is to
solve an order 12 puzzle that is: -x 4 -y 3 or -x 6 -y 2 or -x 3 -y 2 -z 2
-x 2 -y 2 -z 1 -> 6 moves
-x 3 -y 2 -z 1 -> diagonal 16, offset 15, reverse 14, offset reverse 15, furthest 21
-x 4 -y 2 -z 1 -> diagonal 20, offset 22, reverse 24, offset reverse 28, furthest 36
-x 5 -y 2 -z 1 -> diagonal 26, offset 31, reverse 44, offset reverse 45, furthest 55
-x 6 -y 2 -z 1 -> diagonal 42, offset 38, reverse 62, offset reverse 66, furthest 80
-x 3 -y 3 -z 1 -> diagonal 28, offset 22, reverse 30, offset reverse 31, furthest 31
-x 4 -y 3 -z 1 -> diagonal 32, offset 33, reverse 53, offset reverse 53, furthest 53
-x 2 -y 2 -z 2 -> diagonal 18, offset 17, reverse 19, offset reverse 19, furthest 19
-x 3 -y 2 -z 2 -> diagonal 20, offset 26, reverse 30, offset reverse 40, furthest 40

Option piecemeal -p may be able to extend this to find a good but probably
not best solution.
-p -x 5 -y 3 -z 1 -i 0 -> (diagonal) 68 moves
-p -x 5 -y 3 -z 1 -i 1 -> (offset) 48 moves
-p -x 4 -y 4 -z 1 -i 0 -> (diagonal) >28
-p -x 4 -y 4 -z 1 -i 1 -> (offset) 54 moves

head /proc/meminfo
  MemTotal:       16058756 kB
head /proc/cpuinfo
  cpu MHz
*/

/* Converted from panex.c						 */

/* This program calculates the cubes puzzle problem.			 */
/* lxmxn (usually l and m = 4 and n = 1) blocks to be exchanged .	 */

/* Input:								 */
/* # of blocks								 */
/* # of nodes you expect it to take to search for a solution.		 */
/* Start and Final positions hard coded					 */

/* Output:							 	 */
/* The optimum route to exchange the blocks, but must obey rule		 */
/* no block can go through another block (on 2d plane) or block in 3d.   */
/* As it  stands now a full solution will not be printed if number of	 */
/* greater than X.						 	 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define FILE_NAME_LENGTH 1024
#define FALSE		0
#define TRUE		1
#define TOP		0
#define RIGHT		1
#define BOTTOM		2
#define LEFT		3
#define INWARDS		4
#define OUTWARDS	5
#ifndef COORD
#if 0
#define COORD		4 /* half the space to store for 2D */
#else
#define COORD		6
#endif
#endif
#define POSSIBILITIES	COORD  /* maximum number of possible moves */
#define AREA		(sizeX * sizeY)
#define VOLUME		(AREA * sizeZ)
#define BLANK		0
#define CHAINS		3  /* for deleting nodes from hash table */
#define ARRAYTWEAKS	20 /* if <= blocks it will consult array for tweaks */
#define FREQBITS	22 /* sets frequency of what node & move its at now, for feedback */
#define NOFULLSOLUTION	10  /* lower this if you do not have the memory */

#define MINIMUM_MOVES 0
#define FURTHER 1
#define MODES 2

#define DIAGONAL_FLIP 0
#define OFFSET 1
#define REVERSE 2
#define OFFSET_REVERSE 3
#define STARTS 4

int debug = 0;
int mode = MINIMUM_MOVES;
int initial = DIAGONAL_FLIP;
int arch = 8;
#if (COORD < 6)
#define TO_COLUMN(pos) (pos % sizeX)
#define TO_ROW(pos) (pos / sizeX)
#define OPP(dir) ((dir + 2) % 4)
#else
#define TO_COLUMN(pos) ((pos % AREA) % sizeX)
#define TO_ROW(pos) ((pos % AREA) / sizeX)
#define OPP(dir) ((dir < 4) ? ((dir + 2) % 4) : (5 - dir + 4))
#endif
#define TO_STACK(pos) (pos / AREA)
#define MAX(i, j) (((i) > (j)) ? (i) : (j))
#define MIN(i, j) (((i) < (j)) ? (i) : (j))
#define CEIL(i) ((int) (i + 0.999999)) /* rough ceiling */
/* min bits needed to store a move */
#define MINBITS() CEIL(log2(VOLUME))
/* max blocks to store in a unit of memory */
#define MAXNUMINUNIT() ((int) (arch / MINBITS()))
/* min units to store all blocks */
#define MINUNITS() CEIL((double) VOLUME / MAXNUMINUNIT())

#ifdef ARCH128
#include <bitset>
using namespace std;
const int len = 128;
/* does not work past ? due to to_ulong() conversion, also slow */
#define TO_LONG(x) (x).to_ulong()
#define LONG bitset<len>
#else
#define TO_LONG(x) (x)
#define LONG unsigned long long
#endif

#define LOOKUP(x) ((units>1)?(TO_LONG(*x+((*(x+1))*253))%hashSize):\
	(TO_LONG(*x)%hashSize))

static int blocks;
static int size, hashSize;
static int bits, units, numInUnit, freqmask, loop;
static LONG bitmask, inputSize;

#define numBlock(l) ((l >> 1) + ((l & 1) ? blocks : 0))
#define blockChar(l) ((l == BLANK) ? '-' : ((l < 10) ? l + '0' : l - 10 + 'a'))
#define blockDigit(l) ((l == BLANK) ? 0 : l)
#define SETBIT(t, p) (((LONG) (p)) << (bits * (t % numInUnit)))
#define setStart(t, p) startBlocks[t / numInUnit] |= SETBIT(t, p)
#define setFinish(t, p) finishBlocks[t / numInUnit] |= SETBIT(t, p)
#define setBlock(t, p) shrunkPositionOfBlock[t / numInUnit] |= SETBIT(t, p)

#define BITMASK ((1 << bits) - 1)
#define getStart(t) (((startBlocks[t / numInUnit]) >> (bits * (t % numInUnit))) & BITMASK)
#define getFinish(t) (((finishBlocks[t / numInUnit]) >> (bits * (t % numInUnit))) & BITMASK)
#define getBlock(t) (((shrunkPositionOfBlock[t / numInUnit]) >> (bits * (t % numInUnit))) & BITMASK)

/* move counts are for DIAGONAL_FLIP */
/* ones marked 128 are currently beyond reach for 16G */
static int moves1d[MODES][ARRAYTWEAKS] = { /* 1x1 - 19x1 */
  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
     11, 12, 13, 14, 15, 16, 17, 18, 19},
  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
     11, 12, 13, 14, 15, 16, 17, 18, 19}};
static int moves2dx2[MODES][ARRAYTWEAKS / 2 - 1] = { /* 2x2 - 10x2 */
  {6, 16, 20, 26, 42, 128, 128, 128, 128},
  {6, 21, 36, 55, 80, 128, 128, 128, 128}};
static int moves2dx3[MODES][ARRAYTWEAKS / 3 - 2] = { /* 3x3 - 6x3 */
  {28, 32, 128, 128},
  {31, 40, 128, 128}};
static int moves2dx4[MODES][ARRAYTWEAKS / 4 - 3] = { /* 4x4 - 5x4 */
  {128, 128},
  {128, 128}};
static int moves3dx2[MODES][ARRAYTWEAKS / 4 - 1] = { /* 2x2x2 - 5x2x2 */
  {12, 20, 128, 128},
  {15, 36, 128, 128}};
static int moves[MODES][ARRAYTWEAKS] = {
  {0, 1, 2, 6, 4, 16, 6, /*12 18*/ 20, 28, 26, 10,
     /*20 32*/ 42, 12, 128, 128, 128, 128, 128, 128, 128},
  {0, 1, 2, 6, 4, 21, 6, /*15 19*/ 36, 31, 55, 10,
     /*36 40*/ 80, 12, 128, 128, 128, 128, 128, 128, 128}};

static int hashSizes[ARRAYTWEAKS] = {31, 253, 1021, 65533, 131071,
  262141, 1048573, 8388607, 259000001, 259000001, 259000001,
  259000001, 259000001, 259000001, 259000001, 259000001,
  259000001, 259000001, 259000001, 259000001};

/* These values are maximum for FURTHER, as may be more than one way to get there */
static int moveSizes[ARRAYTWEAKS] = {
  1, 1, 1, 2, 1, 44, 1, 5136, 24047, 133107, 1,
  30068329, 1, 83000000, 83000000, 83000000, 43000000,
  83000000, 83000000, 83000000};
static long long minSizes[ARRAYTWEAKS] = { /* nodes */
  1, 2, 3, 12, 5, 360, 7, 20160, 181440, 1814400, 11,
  338300019, 13, 2675020213LL, 2675020213LL, 2675020213LL,
  2675020213LL, 2675020213LL, 2675020213LL, 2675020213LL};

static LONG *nodeLog = NULL;
static char *backPoss = NULL;
static int *move = NULL, *backPath = NULL;
static int newNode = 0, currNode = 0;

/* hashless, debug level 2 check */
/* slow but good to check the hashes are working */
static int newBottom = 0;
static int unitMem;

typedef struct _hashtype {
	int node;
	struct _hashtype *next;
} hashtype;

static hashtype **hash;
static hashtype *chain[CHAINS];
static int hashNewIndex;
static long long c;

static int sizeX = 0, sizeY = 0, sizeZ = 1;
static char *blockOfPosition = NULL; /* easier to see */
static char *startPosition = NULL, *finishPosition = NULL;
static char *positionOfBlock = NULL;
static int startSpacePosition, finishSpacePosition;
static LONG *startBlocks = NULL, *finishBlocks = NULL;
static LONG *shrunkPositionOfBlock = NULL;
static int blanks = 1;
static int frozenRows = 0, frozenColumns = 0;
static char startFile[FILE_NAME_LENGTH] = "";
static char finishFile[FILE_NAME_LENGTH] = "";
static int piecemeal = 0;
static int redirect = 0;
static LONG intMask = 0;
static LONG *rowMask = NULL;
static LONG *columnMask = NULL;

static char *
dirToString(int i)
{
	switch (i) {
	case TOP:
		return "UP";
	case RIGHT:
		return "RIGHT";
	case BOTTOM:
		return "DOWN";
	case LEFT:
		return "LEFT";
	case INWARDS:
		return "INWARDS";
	case OUTWARDS:
		return "OUTWARDS";
	default:
		return "?";
	}
}

static void
freeHash(int index) {
	hashtype **oldChain = &(chain[index]);
	hashtype *ctemp = *oldChain;
	hashtype **oldHashPt;
	hashtype *htemp, *temp;

	while (ctemp) {
		oldHashPt = &(hash[LOOKUP(&nodeLog[ctemp->node * units])]);
		htemp = *oldHashPt;

		if ((*oldHashPt)->node == ctemp->node) {
			*oldHashPt = htemp->next;
			(void) free((void *) htemp);
			/* fnc++; */
		} else {
			while (htemp->next->node != ctemp->node)
				htemp = htemp->next;
			temp = htemp->next;
			htemp->next = temp->next;
			(void) free((void *) temp);
			/* fc++; */
		}
		*oldChain = (*oldChain)->next;
		(void) free((void *) ctemp);
		ctemp = *oldChain;
	}
}

#ifdef DEBUG
static void
printHash() {
	int i;

	for (i = 0; i < hashSize; i++) {
		hashtype *temp = hash[i];

		while (temp != NULL) {
			(void) printf("%d: %d %lld\n", i, temp->node,
				nodeLog[temp->node * units]);
			temp = temp->next;
		}
	}
}
#endif

static int
inHash() {
	LONG *psPOB = shrunkPositionOfBlock;
	LONG *tempsPOB, *pnodeLog;
	hashtype *temp;
	int i;

	temp = hash[LOOKUP(psPOB)];
/*printf("temp %lld\n", temp);*/
	while (temp != NULL) {
		tempsPOB = psPOB;
		pnodeLog = &(nodeLog[temp->node * units]);
		for (i = 0; i < units; i++) {
/*printf("in here %lld %lld", *pnodeLog, *tempsPOB);*/
			if (*pnodeLog != *tempsPOB)
				goto HASHCONTINUE;
/*printf("in hash %lld %lld", *pnodeLog, *tempsPOB);*/
			pnodeLog++;
			tempsPOB++;
		}
		return TRUE;
HASHCONTINUE:   temp = temp->next;
	}
	return FALSE;
}

static void
memoryError(const char * string) {
	(void) fprintf(stderr, "Not enough memory for %s.\n", string);
	(void) fprintf(stderr, "Try a smaller number of nodes with -n.\n");
	(void) fprintf(stderr, "Exiting\n");
	exit(1);
}

static void
printBlocksDigit(char * inBlockOfPosition) {
	int position;
	char *pbOP = inBlockOfPosition;

	for (position = 0; position < VOLUME; position++) {
		if (position % sizeX == 0) {
			if (position > 0) {
				if (position % AREA == 0)
					(void) fprintf(stderr, "\n");
				(void) fprintf(stderr, "\n");
			}
		} else
			(void) fprintf(stderr, " ");
		(void) fprintf(stderr, "%d", blockDigit(*pbOP));
		pbOP++;
	}
	(void) fprintf(stderr, "\n");
}

static void
printBlocks(char * inBlockOfPosition) {
	int position;
	char *pbOP = inBlockOfPosition;

	for (position = 0; position < VOLUME; position++) {
		if (position % sizeX == 0) {
			if (position > 0) {
				if (position % AREA == 0)
					(void) printf("\n");
				(void) printf("\n");
			}
		} else
			(void) printf(" ");
		(void) printf("%c", blockChar(*pbOP));
		pbOP++;
	}
	(void) printf("\n");
}

static void
printPositions(char * inPositionOfBlock) {
	int block;
	char *ppOB = inPositionOfBlock;
	char *inBlockOfPosition;

	/*printf("print Positions\n");*/
	if (!(inBlockOfPosition = (char *) calloc(VOLUME,
			sizeof (char)))) {
		memoryError("printPositions");
	}
	for (block = 0; block < VOLUME; block++)
		inBlockOfPosition[inPositionOfBlock[block]] = block;
	printBlocks(inBlockOfPosition);
	if (inBlockOfPosition)
		(void) free((void *) inBlockOfPosition);
}

static void
putInHash(int index, int node) {
	hashtype **oldHashPt = &(hash[LOOKUP(&nodeLog[node * units])]);
	hashtype **oldChain = &(chain[index]);
	hashtype *htemp = *oldHashPt;
	hashtype *ctemp = *oldChain;

	if (!(htemp = (hashtype *) malloc(sizeof (hashtype)))) {
		memoryError("adding to hash table");
	}
	htemp->node = node;
	if (*oldHashPt == NULL) {
		*oldHashPt = htemp;
		htemp->next = NULL;
		/* nc++; */
	} else {
		htemp->next = *oldHashPt;
		*oldHashPt = htemp;
		c++;
	}
	if (!(ctemp = (hashtype *) malloc(sizeof (hashtype)))) {
		memoryError("adding to chain");
	}
	ctemp->node = node;
	ctemp->next = *oldChain;
	*oldChain = ctemp;
}

static void
putCurrent(int node) {
	LONG *pnodeLog = &(nodeLog[node * units]);
	LONG *psPOB = shrunkPositionOfBlock;
	int j;
	/*char *ppOB = positionOfBlock;
	int i, j, shift, n = 0;

	for (j = 0; j < units; j++)
		shrunkPositionOfBlock[j] = 0;
	for (i = 0; i < VOLUME; i++)
		setBlock(i, positionOfBlock[i]);
	for (;;) {
		shift = 0;
		*psPOB = 0;
		for (j = 0; j < numInUnit; j++) {
			*psPOB |= (((LONG) *ppOB) << shift);
			shift += bits;
			ppOB++;
			n++;
			if (n >= VOLUME) {
				*pnodeLog = *psPOB;
				return;
			}
		}*/
	for (j = 0; j < units; j++) {
		*pnodeLog = *psPOB;
		psPOB++;
		pnodeLog++;
	}
}

static void
getCurrent(int node) {
	int stack, i, j, shift, n = 0;
	LONG *pnodeLog = &(nodeLog[node * units]);
	LONG *psPOB = shrunkPositionOfBlock;
	char *pbOP = blockOfPosition;
	char value;

	for (;;) {
		shift = 0;
		*psPOB = *pnodeLog;
		for (j = 0; j < numInUnit; j++) {
			value = (char) TO_LONG((((LONG) *psPOB) >> shift) &
				bitmask);
			*pbOP = value;
			/*blockOfPosition[n] = value;*/
			positionOfBlock[value] = n;
			shift += bits;
			pbOP++;
			n++;
			if (n >= VOLUME) {
				return;
			}
		}
		pnodeLog++;
		psPOB++;
	}
}

static int
typeOfMix() {
	if (sizeX >= sizeZ && sizeY >= sizeZ) {
		return 1;
	} else if (sizeX >= sizeY && sizeZ >= sizeY) {
		return 2;
	} else if (sizeY >= sizeX && sizeZ >= sizeX) {
		return 3;
	}
	return -1;
}

static char
decideSwap() {
	if (initial == DIAGONAL_FLIP) {
		int mix = typeOfMix();

		if (mix == 1) { /* swap x/y */
			return (((sizeX >> 1) & 1) == 1
				&& ((sizeY >> 1) & 1) == 1
				&& (sizeZ & 1) == 1);
		} else if (mix == 2) { /* swap x/z */
			return (((sizeX >> 1) & 1) == 1
				&& ((sizeZ >> 1) & 1) == 1
				&& (sizeY & 1) == 1);
		} else if (mix == 3) { /* swap y/z */
			return (((sizeY >> 1) & 1) == 1
				&& ((sizeZ >> 1) & 1) == 1
				&& (sizeX & 1) == 1);
		}
		return FALSE;
	} else if (initial == OFFSET || initial == REVERSE) {
		return (((sizeX & 1) == 0 && (sizeY & 1) == 0 && (sizeZ & 1) == 1) ||
			((sizeX & 1) == 1 && (sizeY & 1) == 0 && (sizeZ & 1) == 0) ||
			((sizeX & 1) == 0 && (sizeY & 1) == 1 && (sizeZ & 1) == 0));
	} else if (initial == OFFSET_REVERSE) {
		return (((sizeX & 1) == 1 && (sizeY & 1) == 1 && (sizeZ & 1) == 1));
	}
}

static int
toInvPosition(int column, int row, int stack) {
	int mix = typeOfMix();

	if (mix == 1) { /* swap x/y */
		return (row + sizeY * column + AREA * stack);
	} else if (mix == 2 || mix == 3) { /* swap x/z */
		return (stack + sizeZ * column + sizeX * sizeZ * row);
	}
	return -1;
}

static void
setStartPosition() {
	int i, j, switch1, switch2;

	if ((sizeX > 1 && sizeY == 1 && sizeZ == 1) ||
			(sizeX == 1 && sizeY > 1 && sizeZ == 1) ||
			(sizeX == 1 && sizeY == 1 && sizeZ > 1)) {
		int max = (sizeX > sizeY) ? sizeX :
			((sizeY > sizeZ) ? sizeY : sizeZ);

		startSpacePosition = 0;
		startPosition[startSpacePosition] = 0;
		for (i = 1; i < max; i++)
			startPosition[i] = i;
		return;
	}
	if (initial == DIAGONAL_FLIP) {
		if ((sizeX == 2 && sizeY == 2 && sizeZ == 1) ||
				(sizeX == 2 && sizeY == 1 && sizeZ == 2) ||
				(sizeX == 1 && sizeY == 2 && sizeZ == 2)) {
			startSpacePosition = 0;
			startPosition[startSpacePosition] = 0;
			startPosition[1] = 3;
			startPosition[2] = 2;
			startPosition[3] = 1;
			return;
	}
		for (i = 0; i < VOLUME; i++) {
			j = toInvPosition(TO_COLUMN(i), TO_ROW(i), TO_STACK(i)) + 1;
			startPosition[i] = j;
			if (j == VOLUME - 2)
				switch1 = i;
			else if (j == VOLUME - 1)
				switch2 = i;
		}
		startSpacePosition = VOLUME - 1;
		startPosition[VOLUME - 1] = 0;
		if (decideSwap()) {
			printf("need swap\n");
			i = startPosition[switch1];
			startPosition[switch1] =
				startPosition[switch2];
			startPosition[switch2] = i;
		}
	} else if (initial == OFFSET) {
		for (i = 0; i < VOLUME; i++) {
			j = i;
			startPosition[i] = j;
			if (j == VOLUME - 2)
				switch1 = i;
			else if (j == VOLUME - 1)
				switch2 = i;
		}
		startSpacePosition = 0;
		startPosition[startSpacePosition] = 0;
		if (decideSwap()) {
			printf("need swap\n");
			i = startPosition[switch1];
			startPosition[switch1] =
				startPosition[switch2];
			startPosition[switch2] = i;
		}
	} else if (initial == REVERSE) {
		for (i = 0; i < VOLUME - 1; i++) {
			j = VOLUME - i - 1;
			startPosition[i] = j;
			if (j == VOLUME - 2)
				switch1 = i;
			else if (j == VOLUME - 1)
				switch2 = i;
		}
		startSpacePosition = VOLUME - 1;
		startPosition[startSpacePosition] = 0;
		if (decideSwap()) {
			printf("need swap\n");
			i = startPosition[switch1];
			startPosition[switch1] =
				startPosition[switch2];
			startPosition[switch2] = i;
		}
	} else if (initial == OFFSET_REVERSE) {
		for (i = 0; i < VOLUME; i++) {
			j = VOLUME - i;
			startPosition[i] = j;
			if (j == VOLUME - 2)
				switch1 = i;
			else if (j == VOLUME - 1)
				switch2 = i;
		}
		startSpacePosition = 0;
		startPosition[startSpacePosition] = 0;
		if (decideSwap()) {
			printf("need swap\n");
			i = startPosition[switch1];
			startPosition[switch1] =
				startPosition[switch2];
			startPosition[switch2] = i;
		}
	}
}

static void
setFinishPosition() {
	int i;

	for (i = 0; i < VOLUME - 1; i++)
		finishPosition[i] = i + 1;
	finishSpacePosition = VOLUME - 1;
	finishPosition[finishSpacePosition] = 0;
}

static char
checkSolvedRowFromTop(int row)
{
	int i, start;

	start = row * sizeX;
	for (i = 0; i < sizeX; i++) {
		if (blockOfPosition[start + i - 1] != start + i)
			return FALSE;
	}
	return TRUE;
}

static char
checkSolvedColumnFromLeft(int column)
{
	int i, start;

	start = column;
	for (i = 0; i < sizeY; i++) {
		if (blockOfPosition[start + i * sizeX - 1] != start + i * sizeX)
			return FALSE;
	}
	return TRUE;
}

static char
checkSolvedRowFromBottom(int row)
{
	int i, start;

	start = (sizeY - 1 + row) * sizeX;
	for (i = 0; i < sizeX; i++) {
		if (blockOfPosition[start + i - 1] != start + i)
			return FALSE;
	}
	return TRUE;
}

static char
checkSolvedColumnFromRight(int column)
{
	int i, start;

	start = column + sizeX - 1;
	for (i = 0; i < sizeY; i++) {
		if (blockOfPosition[start + i * sizeX - 1] != start + i * sizeX)
			return FALSE;
	}
	return TRUE;
}

static char
checkSolved()
{
	int i;

	for (i = 1; i < VOLUME; i++) {
		if (blockOfPosition[i - 1] != i)
			return FALSE;
	}
	return TRUE;
}

static void
setRowMaskFromTop(int row)
{
	int i, j, start, shift = 0;
	int modUnit, divUnit;

	for (i = 0; i < MINUNITS(); i++)
		rowMask[i] = 0;
	for (j = 0; j <= row; j++) {
		start = j * bits * sizeX;
		shift = 0;
		for (i = 0; i < sizeX; i++) {
			modUnit = (start + shift) % arch;
			divUnit = (start + shift) / arch;
			rowMask[divUnit] |= intMask << modUnit;
			shift += bits;
		}
	}
}

static void
setRowMaskFromBottom(int row)
{
	int i, j, start, shift = 0;
	int modUnit, divUnit;

	for (i = 0; i < MINUNITS(); i++)
		rowMask[i] = 0;
	for (j = sizeY - 1; j >= sizeY - 1 - row; j--) {
		start = j * bits * sizeX;
		shift = 0;
		for (i = 0; i < sizeX; i++) {
			modUnit = (start + shift) % arch;
			divUnit = (start + shift) / arch;
			rowMask[divUnit] |= intMask << modUnit;
			shift += bits;
		}
	}
}

static void
setColumnMaskFromLeft(int column)
{
	int i, j, start, shift = 0;
	int modUnit, divUnit;

	for (i = 0; i < MINUNITS(); i++)
		columnMask[i] = 0;
	for (j = 0; j <= column; j++) {
		start = j * bits;
		shift = 0;
		for (i = 0; i < sizeY; i++) {
			modUnit = (start + shift) % arch;
			divUnit = (start + shift) / arch;
			columnMask[divUnit] |= intMask << modUnit;
			shift += sizeX * bits;
		}
	}
}

static void
setColumnMaskFromRight(int column)
{
	int i, j, start, shift = 0;
	int modUnit, divUnit;

	for (i = 0; i < MINUNITS(); i++)
		columnMask[i] = 0;
	for (j = sizeX - 1; j >= sizeX - 1 - column; j--) {
		start = j * bits;
		shift = 0;
		for (i = 0; i < sizeY; i++) {
			modUnit = (start + shift) % arch;
			divUnit = (start + shift) / arch;
			columnMask[divUnit] |= intMask << modUnit;
			shift += sizeX * bits;
		}
	}
}

static char
checkSolvedByUnitRow(int row)
{
	int i;

	setRowMaskFromTop(row);
	for (i = 0; i < MINUNITS(); i++)
		if ((shrunkPositionOfBlock[i] & rowMask[i])
				!= (finishBlocks[i] & rowMask[i]))
			return FALSE;
/*printf("rowMask %lld\n", rowMask[0]);
printf("%lld %lld\n", shrunkPositionOfBlock[0], finishBlocks[0]);
printf("%lld %lld\n", shrunkPositionOfBlock[0] & rowMask[0], finishBlocks[0] & rowMask[0]);*/
	return TRUE;
}

static char
checkSolvedByUnitColumn(int column)
{
	int i;

	setColumnMaskFromLeft(column);
	for (i = 0; i < MINUNITS(); i++)
		if ((shrunkPositionOfBlock[i] & columnMask[i])
				!= (finishBlocks[i] & columnMask[i]))
			return FALSE;
/*printf("columnMask %lld\n", columnMask[0]);
printf("%lld %lld\n", shrunkPositionOfBlock[0], finishBlocks[0]);
printf("%lld %lld\n", shrunkPositionOfBlock[0] & columnMask[0], finishBlocks[0] & columnMask[0]);*/
	return TRUE;
}

static char
checkSolvedByUnit()
{
	int i;

	for (i = 0; i < units; i++)
		if (shrunkPositionOfBlock[i] != finishBlocks[i])
			return FALSE;
	return TRUE;
}

static char
partiallySolveRow(int row)
{
	if (sizeZ == 1 && sizeX > 1 && sizeY - row >= sizeX
			&& (sizeY - row > 3 || sizeX == 3 && sizeY - row == 3)
			&& piecemeal)
		return TRUE;
	return FALSE;
}

static char
partiallySolveColumn(int column)
{
	if (sizeZ == 1 && sizeY > 1 && sizeX - column >= sizeY
			&& (sizeX - column > 3 || sizeY == 3 && sizeX - column == 3)
			&& piecemeal)
		return TRUE;
	return FALSE;
}

static int
scanStartPosition(FILE *fp) {
	int i, num, c;

	for (i = 0; i < VOLUME; i++) {
		if (fscanf(fp, "%d ", &num) != 1) {
			(void) fprintf(stderr,
				"corrupt start record, expecting an integer for %d\n", i);
			return FALSE;
		}
		startPosition[i] = num;
		if (!num)
			startSpacePosition = i;
	}
	return TRUE;
}

static int
scanFinishPosition(FILE *fp) {
	int i, num, c;

	for (i = 0; i < VOLUME; i++) {
		if (fscanf(fp, "%d ", &num) != 1) {
			(void) fprintf(stderr,
				"corrupt start record, expecting an integer for %d\n", i);
			return FALSE;
		}
		finishPosition[i] = num;
		if (!num)
			finishSpacePosition = i;
	}
	return TRUE;
}

static void
setBlocksToStart() {
	int i, j;

	/* Need one more for start position */
	if (strcmp(startFile, "") != 0) {
		FILE *fp;
		if ((fp = fopen(startFile, "r")) != NULL) {
			scanStartPosition(fp);
		} else {
			fprintf(stderr,
				"cannot open %s\n", startFile);
			exit(1);
		}
	} else {
		setStartPosition();
	}
	for (i = 0; i < VOLUME; i++) {
		j = startPosition[i];
		blockOfPosition[j] = i;
		positionOfBlock[i] = j;
	}
	if (blockOfPosition[0] != startSpacePosition) {
		printf("fix start!!! blockOfPosition0=%d, startSpacePosition=%d\n",
			blockOfPosition[0], startSpacePosition);
	}
	printf("start:\n");
	printBlocks(positionOfBlock);
	for (i = 0; i < VOLUME; i++)
		setStart(i, startPosition[i]);
	if (debug) {
		printf("check start\n");
		/*for (i = 0; i < VOLUME; i++)
			startPosition[i] = getStart(i);*/
		for (i = 0; i < VOLUME; i++) {
			j = startPosition[i];
			blockOfPosition[j] = i;
			positionOfBlock[i] = j;
		}
		/*printBlocks(positionOfBlock);*/
	}
	for (j = 0; j < units; j++)
		shrunkPositionOfBlock[j] = 0;
	for (i = 0; i < VOLUME; i++)
		setBlock(i, positionOfBlock[i]);
	if (checkSolvedByUnit())
		printf("solution FAILED!!! (start)\n");
}

static void
setBlocksToFinish() {
	int i, j;

	if (strcmp(finishFile, "") != 0) {
		FILE *fp;
		if ((fp = fopen(finishFile, "r")) != NULL) {
			scanFinishPosition(fp);
		} else {
			fprintf(stderr,
				"cannot open %s\n", finishFile);
			exit(1);
		}
	} else {
		setFinishPosition();
	}
	for (i = 0; i < VOLUME; i++) {
		j = finishPosition[i];
		blockOfPosition[j] = i;
		positionOfBlock[i] = j;
	}
	if (blockOfPosition[0] != finishSpacePosition) {
		printf("fix finish!!! blockOfPosition0=%d, finishSpacePosition=%d\n",
			blockOfPosition[0], finishSpacePosition);
	}
	printf("finish:\n");
	printBlocks(positionOfBlock);
	for (i = 0; i < VOLUME; i++)
		setFinish(i, finishPosition[i]);
	if (debug) {
		/*for (i = 0; i < VOLUME; i++)
			finishPosition[i] = getFinish(i);*/
		for (i = 0; i < VOLUME; i++) {
			j = finishPosition[i];
			blockOfPosition[j] = i;
			positionOfBlock[i] = j;
		}
		/*printPositions(blockOfPosition);*/
	}
	for (j = 0; j < units; j++)
		shrunkPositionOfBlock[j] = 0;
	for (i = 0; i < VOLUME; i++)
		setBlock(i, positionOfBlock[i]);
	if (!checkSolvedByUnit())
		printf("solution FAILED!!! (finish)\n");
}

static void
resetBlocks() {
	int i;

	if (hash)
		(void) free((void *) hash);
	if (!(hash = (hashtype **) calloc(hashSize,
			sizeof (hashtype *)))) {
		memoryError("hash table");
	}

	if (nodeLog)
		(void) free((void *) nodeLog);
	if (!(nodeLog = (LONG *) calloc(size * units,
			sizeof (LONG)))) {
		memoryError("nodeLog");
	}
	if (move)
		(void) free((void *) move);
	if (!(move = (int *) calloc(size, sizeof (int)))) {
		memoryError("move");
	}
	if (backPoss)
		(void) free((void *) backPoss);
	if (!(backPoss = (char *) calloc(size, sizeof (char)))) {
		memoryError("backPoss");
	}
	if (backPath)
		(void) free((void *) backPath);
	if (!(backPath = (int *) calloc(size, sizeof (int)))) {
		memoryError("backPath");
	}

	if (blockOfPosition)
		(void) free((void *) blockOfPosition);
	if (!(blockOfPosition = (char *) calloc(VOLUME,
			sizeof (char)))) {
		memoryError("blockOfPosition");
	}

	if (positionOfBlock)
		(void) free((void *) positionOfBlock);
	if (!(positionOfBlock = (char *) calloc(VOLUME,
			sizeof (char)))) {
		memoryError("positionOfBlock");
	}
	if (shrunkPositionOfBlock)
		(void) free((void *) shrunkPositionOfBlock);
	if (!(shrunkPositionOfBlock = (LONG *) calloc(units,
			sizeof (LONG)))) {
		memoryError("shrunkPositionOfBlock");
	}

	if (rowMask)
		(void) free((void *) rowMask);
	if (!(rowMask = (LONG *) calloc(units,
			sizeof (LONG)))) {
		memoryError("rowMask");
	}

	if (columnMask)
		(void) free((void *) columnMask);
	if (!(columnMask = (LONG *) calloc(units,
			sizeof (LONG)))) {
		memoryError("columnMask");
	}

	if (startPosition)
		(void) free((void *) startPosition);
	if (!(startPosition = (char *) calloc(VOLUME,
			sizeof (char)))) {
		memoryError("startPosition");
	}

	if (finishPosition)
		(void) free((void *) finishPosition);
	if (!(finishPosition = (char *) calloc(VOLUME,
			sizeof (char)))) {
		memoryError("finishPosition");
	}
	if (startBlocks)
		(void) free((void *) startBlocks);
	if (!(startBlocks = (LONG *) calloc(units,
			sizeof (LONG)))) {
		memoryError("startBlocks");
	}
	if (finishBlocks)
		(void) free((void *) finishBlocks);
	if (!(finishBlocks = (LONG *) calloc(units,
			sizeof (LONG)))) {
		memoryError("finishBlocks");
	}
	setBlocksToFinish();
	setBlocksToStart();
	putCurrent(currNode);
	getCurrent(currNode);
	if (debug) {
		printf("printBlocks\n");
		printBlocks(blockOfPosition);
		printf("printPositions\n");
		printPositions(positionOfBlock);
	}
	move[0] = 0;
	backPoss[0] = -1; /* needed for first pass */
	loop = 0;
	hashNewIndex = 0;
	newNode = 0;
	currNode = 0;
	c = 0;
	for (i = 0; i < bits; i++) {
		intMask |= 1 << i;
	}
}

static int
moveBlocks(int fromPosition)
{
	int fromBlock = blockOfPosition[fromPosition];
	int spacePosition = positionOfBlock[0];
	int i, j;

	positionOfBlock[fromBlock] = spacePosition;
	positionOfBlock[0] = fromPosition;

	blockOfPosition[fromPosition] = blockOfPosition[spacePosition];
	blockOfPosition[spacePosition] = fromBlock;
	for (j = 0; j < units; j++)
		shrunkPositionOfBlock[j] = 0;
	for (i = 0; i < VOLUME; i++)
		setBlock(i, blockOfPosition[i]);
}

static char
moveSpace(int direction, int spacePosition)
{
	int row;
	switch (direction) {
	case TOP:
		if (TO_ROW(spacePosition) < sizeY - 1) {
			moveBlocks(spacePosition + sizeX);
			return TRUE;
		}
		break;
	case RIGHT:
		if (TO_COLUMN(spacePosition) > frozenColumns) {
			moveBlocks(spacePosition - 1);
			return TRUE;
		}
		break;
	case BOTTOM:
		if (TO_ROW(spacePosition) > frozenRows) {
			moveBlocks(spacePosition - sizeX);
			return TRUE;
		}
		break;
	case LEFT:
		if (TO_COLUMN(spacePosition) < sizeX - 1) {
			moveBlocks(spacePosition + 1);
			return TRUE;
		}
		break;
	case INWARDS:
		if (TO_STACK(spacePosition) < sizeZ - 1) {
			moveBlocks(spacePosition + AREA);
			return TRUE;
		}
		break;
	case OUTWARDS:
		if (TO_STACK(spacePosition) > 0) {
			moveBlocks(spacePosition - AREA);
			return TRUE;
		}
		break;
	default:
		(void) printf("dir? %d\n", direction);
	}
	return FALSE;

}

/* FURTHER */
static void
printLastData(int node) {
	if (backPath[node] > node || (node != 0 && backPath[node] == node)) {
		return;
	}
	getCurrent(node);
	if (node != 0) {
		(void) printf("%3d: %s\n", move[node],
			dirToString((int) backPoss[node]));
	}
	printPositions(positionOfBlock);
}

/* FURTHER */
static void
printLastHash(int index) {
	hashtype **oldChain = &(chain[index]);
	hashtype *ctemp = *oldChain;
	while (ctemp != NULL) {
		printLastData(ctemp->node);
		ctemp = ctemp->next;
	}
}

static void
printDataMove(int node) {
	if (backPath[node] > node || (node != 0 && backPath[node] == node)) {
		return;
	}
	if (node != 0) {
		printDataMove(backPath[node]);
	}
	getCurrent(node);
	if (node != 0) {
		(void) fprintf(stderr, "%d: %d\n", move[node],
			backPoss[node]);
	}
}

static void
printData(int node) {
	if (backPath[node] > node || (node != 0 && backPath[node] == node)) {
		return;
	}
	if (node != 0) {
		printData(backPath[node]);
	}
	getCurrent(node);
	if (node != 0) {
		(void) printf("%3d: %s\n", move[node],
			dirToString((int) backPoss[node]));
	}
	printPositions(positionOfBlock);
}

static int
getnum(char * message) {
	char buff[80];

	(void) printf("%s: ", message);
	return((int) strtol(fgets(buff, 80, stdin), 0, 0));
}

static void
readData(int nodes) {
#ifdef ARCH128
	arch = 128;
#elif defined( ARCH64 )
	arch = 64;
	if (debug)
		(void) printf("%d bit architecture.\n", arch);
#else
	arch = 8 * sizeof( bitmask );
	(void) printf("Assuming %d bit architecture.\n", arch);
#endif
	while (sizeX < 1)
		sizeX = getnum((char *) "Enter: # of sizeX >");
	while (sizeY < 1)
		sizeY = getnum((char *) "Enter: # of sizeY >");
	while (sizeZ < 1)
		sizeZ = getnum((char *) "Enter: # of sizeZ >");
	blocks = VOLUME - 1;
	if (blocks > ARRAYTWEAKS) {
		(void) printf("Be careful, you need lots of memory.\n");
		inputSize = getnum((char *)
			"Enter: # of nodes to keep in memory >");
		hashSize = getnum((char *)
			"Enter: size of hash >");
	} else {
		if (blocks < NOFULLSOLUTION && nodes == 0)
			inputSize = minSizes[blocks];
		else {
			/* 3 moves must be kept in memory to progress */
			int minMem = 3 * moveSizes[blocks];

			if (nodes == 0)
				inputSize = minMem;
			else
				inputSize = nodes;
			if (inputSize < minMem) {
				(void) printf("Nodes %lld < %d, changing\n",
					inputSize, minMem);
				inputSize = minMem;
			}
			if (inputSize < minSizes[blocks])
				(void) printf(
					"Full path may not be printed\n");
		}
		hashSize = hashSizes[blocks];
	}
	size = inputSize;
	if (debug)
		(void) printf("size %d\n", size);
	bits = MINBITS();
	if (bits == 0) {
		(void) printf("no moves needed\n");
		exit(0);
	}
	units = MINUNITS();
	numInUnit = MAXNUMINUNIT();
	if (debug)
		(void) printf("bits %d, units %d, numInUnit %d\n",
			bits, units, numInUnit);
	bitmask = (1 << bits) - 1;
	freqmask = (int) (~((1 << FREQBITS) - 1));
	if (units > 2)
		(void) printf("Key for hash may not be efficient.\n");
	if (debug > 1) /* hashless check */
		unitMem = (sizeof (char)) * units;
	resetBlocks();
}

static int
isNewNode() {
	int unit, node;
	LONG *psPOB, *pnodeLog;
	LONG *pnodeLogFirst = &(nodeLog[newNode * units]);

	for (node = newNode; node >= newBottom; node--) {
		/* check if already done */
		pnodeLog = pnodeLogFirst;
		psPOB = shrunkPositionOfBlock;
		for (unit = 0; unit < units; unit++) {
			if (*pnodeLog != *psPOB)
				goto CONTINUE; /* I hate gotos */
			pnodeLog++;
			psPOB++;
		}
		return FALSE;
CONTINUE:	pnodeLogFirst -= unitMem;
	}
	return TRUE;
}

static void
searchBreadthFirst() {
	int localMoveSize = 0, moveSize = 0;
	char m;
	int foundSolution = 0;
	char *ppOB = positionOfBlock;

    for (;;) {
LOOP:
	for (m = 0; m < (char) POSSIBILITIES; m++) {
		if (backPoss[currNode] == OPP(m)) {
			/*printf("not doing %d\n", m);*/
			/* undoing previous move */
			continue; /* move could be shorter, so ignore */
		}
		if (moveSpace((int) m, ppOB[0])) {
			if (debug) {
				(void) printf("breadth dir %s, last dir %s\n", dirToString(m), dirToString(backPoss[currNode]));
				printPositions(ppOB);
			}
			if (!(inHash())) {
				/*printf("not in hash\n");*/
				if (debug > 1 && !isNewNode()) {
					/* hashless check */
					(void) printf("got node:%d %lld\n",
						newNode + 1,
						*shrunkPositionOfBlock);
				}
				newNode++;
				if (newNode >= size) {
					newNode = 0;
				}
				if ((newNode & freqmask) == newNode) {
					if (newNode == 0)
						(void) printf("loop:%d; ",
							loop + 1);
					(void) printf("node:%d; move:%d; collisions:%lld\n",
						newNode, move[currNode] + 1, c);
				}
				backPoss[newNode] = m;
				backPath[newNode] = currNode;
				move[newNode] = move[currNode] + 1;
				localMoveSize++;
				putCurrent(newNode); /* save node */
				putInHash(hashNewIndex, newNode);
				if (partiallySolveRow(frozenRows) &&
						checkSolvedByUnitRow(frozenRows)) {
					frozenRows += 1;
					printf("freezing row %d at move %d\n", frozenRows, move[newNode]);
					printData(newNode);
					currNode = newNode;
					goto LOOP;
				}
				if (partiallySolveColumn(frozenColumns) &&
						checkSolvedByUnitColumn(frozenColumns)) {
					frozenColumns += 1;
					printf("freezing column %d at move %d\n", frozenColumns, move[newNode]);
					printData(newNode);
					currNode = newNode;
					goto LOOP;
				}
				if (checkSolvedByUnit()) {
					(void) printf("loops: %d, size: %d, nodes: %d; moves: %d\n",
						loop, size, newNode + 1, move[newNode]);
					if (redirect) {
						(void) fprintf(stderr, "blocks.x: %d\n", sizeX);
						(void) fprintf(stderr, "blocks.y: %d\n", sizeY);
						(void) fprintf(stderr, "blocks.z: %d\n", sizeZ);
						(void) fprintf(stderr, "moves: %d\n", move[newNode]);
						(void) fprintf(stderr, "\nstartingPosition:\n");
						printBlocksDigit(startPosition);
						(void) fprintf(stderr, "\nmoves direction\n");
						printDataMove(newNode);
					}
					printData(newNode);
					(void) printf("total nodes: %d * %d + %d\n",
						loop, size, newNode + 1);
					(void) printf("total nodes when found solution: %lld\n",
						(long long) loop * size + newNode + 1);
					(void) printf("moveSize: %d\n",
						moveSize);
					(void) printf("collisions: %lld\n",
						c);
					(void) printf("total moves: %d\n",
						move[newNode]);
					foundSolution = move[newNode];
					if (mode == MINIMUM_MOVES)
						exit(0);
					/* Do not exit as may find a better solution */
					/* Done when number of moves for mode progresses */
				}
			}
			getCurrent(currNode); /* backout */
		}
	}
	if (newNode != currNode) {
		if (move[currNode] < move[(currNode + 1) % size]) {
			if (mode != FURTHER && foundSolution) {
				(void) printf("total nodes when complete: %lld\n",
					(long long) loop * size + newNode + 1);
				exit(0);
			}
			if (debug > 1) {
				printLastHash(hashNewIndex);
			}
			if (debug > 1) { /* hashless check */
				newBottom = move[(currNode + 1) % size];
			}
			if (localMoveSize > moveSize) {
				moveSize = localMoveSize;
			}
			localMoveSize = 0;
			hashNewIndex = (hashNewIndex + 1) % CHAINS;
			freeHash(hashNewIndex);
		}
		currNode++;
		if (currNode >= size) {
			currNode = 0;
			loop++;
		}
		getCurrent(currNode);
	} else {
		if (mode == FURTHER)
			(void) printf("Farthest point\n");
		else if (!foundSolution)
			(void) printf("No solution\n");
		(void) printf("loops: %d, size: %d, nodes: %d; moves: %d\n",
			loop, size, newNode + 1, move[newNode]);
		if (mode == FURTHER)
			printData(newNode);
		if (size < minSizes[blocks - 1])
			(void) printf("total nodes: %d\n",
				loop * size + newNode + 1);
		else
			(void) printf("total nodes: %d * %d + %d\n",
				loop, size, newNode + 1);
		(void) printf("moveSize: %d\n", moveSize);
		(void) printf("collisions: %lld\n", c);
		(void) printf("total moves: %d\n", move[newNode]);
		if (mode == FURTHER) {
			printLastHash((hashNewIndex + CHAINS - 1) % CHAINS);
		}
		exit(0);
	}
    }
}

static void
usage()
{
	(void) fprintf(stderr,
		"usage: \"cubes [-d debugLevel] [-i initial] [-s start_file] [-f finish_file] [-m mode] [-n nodesNumber] [-p] [-r] [-x X] [-y Y] [-z Z]\"\n");
	(void) fprintf(stderr, "\tinitial:\t0 diagonal flip (default)\n");
	(void) fprintf(stderr, "\t\t1 offset\n");
	(void) fprintf(stderr, "\t\t2 reverse\n");
	(void) fprintf(stderr, "\t\t3 offset reverse\n");
	(void) fprintf(stderr, "\tstart:\tstart positions (s)\n");
	(void) fprintf(stderr, "\tfinish:\tfinish positions (f)\n");
	(void) fprintf(stderr, "\tmode:\t0 match finish point (default)\n");
	(void) fprintf(stderr, "\t\t1 find furthest point\n");
	(void) fprintf(stderr, "\tpiecemeal:\tallow piecemeal solution (p)\n");
	(void) fprintf(stderr, "\tredirect:\toutput log to stderr (r)\n");
	exit(1);
}

int
main(int argc, char **argv) {
	int nodes = 0;

	argc--;
	argv++;
	while (argc) {
		if (argv[0][0] == '-') {
			switch (argv[0][1]) {
			case '\0':
				usage('a');
				break;
			case 'd':
				if (argc > 1) {
					argc--;
					argv++;
					debug = atoi(argv[0]);
					break;
				} else {
					usage('d');
				}
			case 'i':
				if (argc > 1) {
					argc--;
					argv++;
					initial = atoi(argv[0]);
					if (initial < 0 || initial >= STARTS)
						initial = DIAGONAL_FLIP;
					break;
				} else {
					usage('i');
				}
			case 'f':
				argc--;
				argv++;
				strncpy(finishFile, argv[0], FILE_NAME_LENGTH);
				break;
			case 'm':
				if (argc > 1) {
					argc--;
					argv++;
					mode = atoi(argv[0]);
					if (mode < 0 || mode >= MODES)
						mode = MINIMUM_MOVES;
					break;
				} else {
					usage('m');
				}
			case 'n':
				if (argc > 1) {
					argc--;
					argv++;
					nodes = atoi(argv[0]);
					break;
				} else {
					usage('n');
				}
			case 'p':
				piecemeal = TRUE;
				break;
			case 'r':
				redirect = TRUE;
				break;
			case 's':
				argc--;
				argv++;
				strncpy(startFile, argv[0], FILE_NAME_LENGTH);
				break;
			case 'x':
				if (argc > 1) {
					argc--;
					argv++;
					sizeX = atoi(argv[0]);
					break;
				} else {
					usage('x');
				}
			case 'y':
				if (argc > 1) {
					argc--;
					argv++;
					sizeY = atoi(argv[0]);
					break;
				} else {
					usage('y');
				}
			case 'z':
				if (argc > 1) {
					argc--;
					argv++;
					sizeZ = atoi(argv[0]);
					break;
				} else {
					usage('z');
				}
			default:
				usage('b');
			}
		}
		argc--;
		argv++;
	}
	readData(nodes);

#if !defined( __MSDOS__ ) && !defined( __CYGWIN__ )
	/* This gets rid of the unwanted buffering in UNIX */
	(void) setlinebuf(stdout);
#endif

	searchBreadthFirst();

#ifdef VMS
	return 1;
#else
	return 0;
#endif
}
