/*
 * @(#)RubikS.c
 *
 * Taken from code originally written by
 * Michael B. Martin <martinm@sps1.phys.vt.edu>
 * From cubist10.c-- for IBM PC.
 * Used by permission.
 * Taken from the algorithm in the Ideal Solution book.
 * Break ability taken from the X puzzle by Don Bennett, HP Labs
 * Revenge stuff taken from Rubik's Revenge Puzzle The Ideal Solution
 * book 1982.
 *
 * Copyright 1994 - 2021  David A. 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 "useful",
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

/* For break code */
/* Puzzle - (C) Copyright 1987, 1988 Don Bennett.
 *
 * 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.
 */

/* Solver file for Rubik */

#define JMP
#ifdef JMP
#include <setjmp.h> /* longjmp ... interrupt */
#endif
#include "RubikP.h"

#define TOP_FACE 0
#define LEFT_FACE 1
#define FRONT_FACE 2
#define RIGHT_FACE 3
#define BOTTOM_FACE 4
#define BACK_FACE 5

/* Mappings of Ideal notation to my General nxnxn cube notation */
#define rotateLeft(w)	movePuzzlePiece(w,FRONT_FACE,0,LEFT,TRUE)
#define rotateRight(w)	movePuzzlePiece(w,FRONT_FACE,0,RIGHT,TRUE)
#define rotateUp(w)	movePuzzlePiece(w,FRONT_FACE,0,TOP,TRUE)
#define rotateDown(w)	movePuzzlePiece(w,FRONT_FACE,0,BOTTOM,TRUE)
#define rotateCw(w)	movePuzzlePiece(w,TOP_FACE,0,RIGHT,TRUE)
#define rotateCcw(w)	movePuzzlePiece(w,TOP_FACE,0,LEFT,TRUE)

#define LAST (w->rubik.sizeX-1)
#define rotateTopLeft(w)	movePuzzlePiece(w,FRONT_FACE,0,LEFT,FALSE)
#define rotateCenterLeft(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX*(w->rubik.sizeX>>1),LEFT,FALSE)
#define rotateBottomLeft(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX*LAST,LEFT,FALSE)
#define rotateTopRight(w)	movePuzzlePiece(w,FRONT_FACE,0,RIGHT,FALSE)
#define rotateCenterRight(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX*(w->rubik.sizeX>>1),RIGHT,FALSE)
#define rotateBottomRight(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX*LAST,RIGHT,FALSE)
#define rotateLeftUp(w)		movePuzzlePiece(w,FRONT_FACE,0,TOP,FALSE)
#define rotateCenterUp(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX>>1,TOP,FALSE)
#define rotateRightUp(w)	movePuzzlePiece(w,FRONT_FACE,LAST,TOP,FALSE)
#define rotateLeftDown(w)	movePuzzlePiece(w,FRONT_FACE,0,BOTTOM,FALSE)
#define rotateCenterDown(w)	movePuzzlePiece(w,FRONT_FACE,w->rubik.sizeX>>1,BOTTOM,FALSE)
#define rotateRightDown(w)	movePuzzlePiece(w,FRONT_FACE,LAST,BOTTOM,FALSE)
#define rotateFrontCw(w)	movePuzzlePiece(w,TOP_FACE,w->rubik.sizeX*LAST,RIGHT,FALSE)
#define rotateFrontCcw(w)	movePuzzlePiece(w,TOP_FACE,w->rubik.sizeX*LAST,LEFT,FALSE)
#define rotateBackCw(w)		movePuzzlePiece(w,TOP_FACE,0,LEFT,FALSE)
#define rotateBackCcw(w)	movePuzzlePiece(w,TOP_FACE,0,RIGHT,FALSE)

static int otherFaceFromEdge[MAX_FACES][MAX_ORIENT] =
{
	{5, 3, 2, 1},
	{0, 2, 4, 5},
	{0, 3, 4, 1},
	{0, 5, 4, 2},
	{2, 3, 5, 1},
	{4, 3, 0, 1}
};

static int otherEdgeFromEdge[MAX_FACES][2 * MAX_ORIENT] =
{
	{5, 4, 1, 0, 1, 0, 1, 0},
	{7, 6, 7, 6, 7, 6, 7, 6},
	{5, 4, 7, 6, 1, 0, 3, 2},
	{3, 2, 3, 2, 3, 2, 3, 2},
	{5, 4, 5, 4, 1, 0, 5, 4},
	{5, 4, 3, 2, 1, 0, 7, 6}
};

/* clock is a little distorted */
static int clockEdgeToPosition[MAX_ORIENT * 2] =
{1, 2, 7, 11, 14, 13, 8, 4};

static int positionToClockEdge[16] =
{  -1, 0, 1, -1,
   7, -1, -1, 2,
   6, -1, -1, 3,
   -1, 5, 4, -1
};

static int clockCenterToPosition[MAX_ORIENT] =
{5, 6, 10, 9};

#if 0
static int positionToClockCenter[16] =
{  -1, -1, -1, -1,
   -1, 0, 1, -1,
   -1, 3, 2, -1,
   -1, -1, -1, -1
};
#endif

static int oppositeFace[MAX_FACES] =
{BOTTOM_FACE, RIGHT_FACE, BACK_FACE, LEFT_FACE, TOP_FACE, FRONT_FACE};

static int bestOrder[MAX_ORIENT] =
{0, 1, 3, 2};

static int lastColumn[MAX_ORIENT] =
{
	BACK_FACE, BOTTOM_FACE, FRONT_FACE, TOP_FACE
};

static int offsetCenters[MAX_FACES] =
{3, 0, 2, 3, 0, 0};

#if 0
static int mapGeneralToIdeal[MAX_FACES];
#endif

typedef struct _RubikFE {
	int face, edge;
} RubikFE;

#if 0
typedef struct _RubikFP {
	int face, x, y;
} RubikFP;

static void printFP(RubikFP fp) {
	(void) printf("  fp:face=%d position=(%d, %d)\n",
		fp.face, fp.x, fp.y);
}

static void printFEB(RubikFE feb) {
	(void) printf("  feb:face=%d edge=%d\n",
		feb.face, feb.edge);
}

static void printClockEdges(RubikWidget w) {
	int face, j;

	for (face = 0; face < MAX_FACES; face++) {
		(void) printf("face %d\n", face);
		for (j = 0; j < 2 * MAX_ORIENT; j++) {
			int position, otherPosition, otherFace, k;

			position = clockEdgeToPosition[j];
			otherFace = otherFaceFromEdge[face][j >> 1];
			k = otherEdgeFromEdge[face][j];
			otherPosition = clockEdgeToPosition[k];
			(void) printf(" edge %d: %d %d\n", j,
				w->rubik.cubeLoc[face][position].face,
				w->rubik.cubeLoc[otherFace][otherPosition].face);
		}
	}
}
#endif

#ifdef DEBUG
static int mapGeneralToIdeal[MAX_FACES] =
{5, 4, 1, 2, 6, 3};
#endif

static int mapIdealToGeneral[MAX_FACES] =
{2, 3, 5, 1, 0, 4};		/* Remember to subtract 1 */

static Boolean solvingFlag = False;

#ifdef JMP
static Boolean abortSolvingFlag = False;
static jmp_buf solve_env;

static void
abortSolving(void)
{
	if (solvingFlag)
		abortSolvingFlag = True;
}

#ifdef WINVER
static Boolean
processMessage(UINT msg)
{
	switch (msg) {
	case WM_KEYDOWN:
	case WM_CLOSE:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		abortSolving();
		return True;
	default:
		return False;
	}
}
#else
static void
processButton(void /*XButtonEvent *event*/)
{
	abortSolving();
}

static void
processVisibility(XVisibilityEvent *event)
{
	if (event->state != VisibilityUnobscured)
		abortSolving();
}

static void
getNextEvent(RubikWidget w, XEvent *event)
{
	if (!XCheckMaskEvent(XtDisplay(w), VisibilityChangeMask, event))
		(void) XNextEvent(XtDisplay(w), event);
}

static void
processEvent(XEvent *event)
{
	switch(event->type) {
	case KeyPress:
	case ButtonPress:
		processButton(/*&event->xbutton*/);
		break;
	case VisibilityNotify:
		processVisibility(&event->xvisibility);
		break;
	default:
		break;
	}
}

static void
processEvents(RubikWidget w)
{
	XEvent event;

	while (XPending(XtDisplay(w))) {
		getNextEvent(w, &event);
		processEvent(&event);
	}
}
#endif
#endif

static void
movePuzzlePiece(RubikWidget w,
	const int face, const int position, const int direction,
	const int control)
{
#ifdef JMP
#ifdef WINVER
	MSG msg;

	if (PeekMessage(&msg, NULL, 0, 0, 0)) {
		if (!processMessage(msg.message)) {
			if (GetMessage(&msg, NULL, 0, 0))
				DispatchMessage(&msg);
		}
	}
#else
	processEvents(w);
#endif
	if (solvingFlag && abortSolvingFlag)
		longjmp(solve_env, 1);
#endif
	movePuzzleDelay(w, face, position, direction, control);
}

static int cornerFace(RubikWidget w, int quad) {
	/* top left to bottom right */
	switch (quad) {
	case 0:
		return 0;
	case 1:
		return LAST;
	case 2:
		return w->rubik.sizeX * LAST;
	case 3:
		return w->rubik.sizeX * w->rubik.sizeX - 1;
	default:
		return w->rubik.sizeX * w->rubik.sizeX;
	}
}

/* For size 3
 *  0        1
 * 1 2  =>  3 5
 *  3        7
 */
static int sideFace(RubikWidget w, int quad) {
	/* top left to bottom right */
	/* if even return lowest */
	switch (quad) {
	case 0:
		return (LAST >> 1);
	case 1:
		return (LAST >> 1) * w->rubik.sizeX;
	case 2:
		return (LAST >> 1) * w->rubik.sizeX + LAST;
	case 3:
		return w->rubik.sizeX * w->rubik.sizeX
			- (w->rubik.sizeX >> 1) - 1;
	default:
		return w->rubik.sizeX * w->rubik.sizeX;
	}
}

static int centerFace(RubikWidget w) {
	/* if even return lowest */
	return (LAST >> 1) * w->rubik.sizeX + (LAST >> 1);
}

static int
colorSide(RubikWidget w, int m, int c)
{
	int d, i, j;

	d = mapIdealToGeneral[m - 1];
	i = c % w->rubik.sizeX;
	j = c / w->rubik.sizeX;
	return w->rubik.cubeLoc[d][j * w->rubik.sizeX + i].face;
}

static int
mapFaceOrient(RubikWidget w, int m, int pos)
{
	int d;

	d = mapIdealToGeneral[m - 1];
	return ((w->rubik.cubeLoc[d][pos].rotation -
		w->rubik.cubeLoc[d][0].rotation + MAX_ORIENT) % MAX_ORIENT);
}

static int
mapFaceOrientRev(RubikWidget w, int m, int pos)
{
	return ((w->rubik.cubeLoc[m][pos].rotation -
		w->rubik.cubeLoc[m][0].rotation + MAX_ORIENT) % MAX_ORIENT);
}

static int
mapFace(RubikWidget w, int m)
{
	return mapFaceOrient(w, m, centerFace(w));
}

/* This procedure finds the location of a specified corner.  An */
/* improperly-specified color combination will result in a return */
/* value of 9. */
static int
findCorner(RubikWidget w, int color1, int color2, int color3)
{
	int corner = 0, temp = 1;

	do {
		if (colorSide(w, 5, cornerFace(w, 2)) == color1) {
			if (colorSide(w, 1, cornerFace(w, 0)) == color2 &&
					colorSide(w, 4, cornerFace(w, 1)) == color3)
				corner = temp;
		} else if (colorSide(w, 5, cornerFace(w, 2)) == color2) {
			if (colorSide(w, 1, cornerFace(w, 0)) == color3 &&
					colorSide(w, 4, cornerFace(w, 1)) == color1)
				corner = temp;
		} else if (colorSide(w, 5, cornerFace(w, 2)) == color3 &&
				colorSide(w, 1, cornerFace(w, 0)) == color1 &&
				colorSide(w, 4, cornerFace(w, 1)) == color2) {
			corner = temp;
		}
		if (corner == 0) {
			if (temp < 4)
				rotateLeft(w);
			else if (temp == 4) {
				rotateCw(w);
				rotateCw(w);
			} else if (temp < 8)
				rotateRight(w);
			else if (temp == 8)
				corner = 9;
			temp++;
		}
	} while (corner == 0);

	/* put the cube back to the way it was */
	if (corner == 2)
		rotateRight(w);
	else if (corner == 3) {
		rotateRight(w);
		rotateRight(w);
	} else if (corner == 4)
		rotateLeft(w);
	else if (corner == 5) {
		rotateCw(w);
		rotateCw(w);
		rotateLeft(w);
	} else if (corner == 6) {
		rotateCw(w);
		rotateCw(w);
	} else if (corner == 7) {
		rotateCw(w);
		rotateCw(w);
		rotateRight(w);
	} else if (corner == 8) {
		rotateCw(w);
		rotateCw(w);
		rotateRight(w);
		rotateRight(w);
	}
	return (corner);
}

static int offsetRevenge(RubikWidget w, int position)
{
	int x = position % 4;
	int y = position / 4;

	if (x > 1)
		x += w->rubik.sizeX - 4;
	if (y > 1)
		y += w->rubik.sizeX - 4;
	return x + y * w->rubik.sizeX;
}

static int revengeOffset(RubikWidget w, int position)
{
	int x = position % w->rubik.sizeX;
	int y = position / w->rubik.sizeX;

	if (x > (w->rubik.sizeX >> 1))
		x += 4 - w->rubik.sizeX;
	if (y > 1)
		y += 4 - w->rubik.sizeX;
	return x + y * 4;
}

static RubikFE
findClockEdge(RubikWidget w, int matchFace, int matchEdge)
{
	int face, edge;
	RubikFE rubikFE;

	for (face = 0; face < MAX_FACES; face++) {
		for (edge = 0; edge < 2 * MAX_ORIENT; edge++) {
			int position, otherPosition, otherFace, otherEdge;

			position = clockEdgeToPosition[edge];
			position = offsetRevenge(w, position); /* non revenge */
			otherFace = otherFaceFromEdge[face][edge >> 1];
			otherEdge = otherEdgeFromEdge[face][edge];
			otherPosition = clockEdgeToPosition[otherEdge];
			otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
			if (matchFace == w->rubik.cubeLoc[face][position].face &&
					(matchEdge & 1) == (edge & 1) &&
					otherFaceFromEdge[matchFace][matchEdge >> 1] ==
					w->rubik.cubeLoc[otherFace][otherPosition].face) {
				position = revengeOffset(w, position); /* back to revenge */
				rubikFE.face = face;
				rubikFE.edge = positionToClockEdge[position];
				/*rubikFP.x = position % w->rubik.sizeX;
				rubikFP.y = position / w->rubik.sizeX;*/
				return rubikFE;
			}
		}
	}
	rubikFE.face = -1;
	rubikFE.edge = -1;
	/*rubikFP.x = -1;
	rubikFP.y = -1;*/
	return rubikFE;
}

static Boolean
matchEdges(RubikWidget w, int matchFace0, int matchFace1, int matchEdge)
{
	int clockEdge0 = matchEdge << 1;
	int clockEdge1 = clockEdge0 + 1;
	int position0, position1, otherPosition0, otherPosition1;
	int otherFace0, otherFace1, otherClockEdge0, otherClockEdge1;

	position0 = clockEdgeToPosition[clockEdge0];
	position0 = offsetRevenge(w, position0); /* non revenge */
	position1 = clockEdgeToPosition[clockEdge1];
	position1 = offsetRevenge(w, position1); /* non revenge */
	otherFace0 = otherFaceFromEdge[matchFace0][matchEdge];
	otherFace1 = otherFaceFromEdge[matchFace1][matchEdge];
	otherClockEdge0 = otherEdgeFromEdge[matchFace0][clockEdge0];
	otherClockEdge1 = otherEdgeFromEdge[matchFace1][clockEdge1];
	otherPosition0 = clockEdgeToPosition[otherClockEdge0];
	otherPosition0 = offsetRevenge(w, otherPosition0); /* non revenge */
	otherPosition1 = clockEdgeToPosition[otherClockEdge1];
	otherPosition1 = offsetRevenge(w, otherPosition1); /* non revenge */
	return (w->rubik.cubeLoc[matchFace0][position0].face ==
		w->rubik.cubeLoc[matchFace1][position1].face &&
		w->rubik.cubeLoc[otherFace0][otherPosition0].face ==
		w->rubik.cubeLoc[otherFace1][otherPosition1].face);
}

/* This procedure finds the location of a specified edge.  An */
/* improperly-specified color combination will result in a return */
/* value of 13. */
static int
findEdge(RubikWidget w, int color1, int color2)
{
	int edge = 0, temp = 1;

	do {
		/* Top (5) down & Front (1) top edge */
		if (colorSide(w, 5, sideFace(w, 3)) == color1) {
			if (colorSide(w, 1, sideFace(w, 0)) == color2)
				edge = temp;
		} else if (colorSide(w, 5, sideFace(w, 3)) == color2 &&
				colorSide(w, 1, sideFace(w, 0)) == color1) {
			edge = temp;
		}
		if (edge == 0) {
			if (temp < 4)
				rotateLeft(w);
			else if (temp == 4) {
				rotateLeft(w);
				rotateCw(w);
			} else if (temp < 8)
				rotateUp(w);
			else if (temp == 8) {
				rotateUp(w);
				rotateCw(w);
			} else if (temp < 12)
				rotateRight(w);
			else if (temp == 12)
				edge = 13;	/* end condition, just in case */
			temp++;
		}
	} while (edge == 0);

	/* put the cube back to the way it was */
	switch (edge) {
	case 2:
		rotateRight(w);
		break;
	case 3:
		rotateRight(w);
		rotateRight(w);
		break;
	case 4:
		rotateLeft(w);
		break;
	case 5:
		rotateCcw(w);
		break;
	case 6:
		rotateCcw(w);
		rotateRight(w);
		break;
	case 7:
		rotateCcw(w);
		rotateRight(w);
		rotateRight(w);
		break;
	case 8:
		rotateCcw(w);
		rotateLeft(w);
		break;
	case 9:
		rotateCcw(w);
		rotateCcw(w);
		break;
	case 10:
		rotateCcw(w);
		rotateCcw(w);
		rotateRight(w);
		break;
	case 11:
		rotateUp(w);
		rotateUp(w);
		break;
	case 12:
		rotateUp(w);
		rotateUp(w);
		rotateRight(w);
		break;
	default:
		break;
	}
	return (edge);
}

/* This procedure places the specified edge piece in edge position */
/* #1 (front top). */
static void
placeEdge(RubikWidget w, int edge, int topColor)
{
	/* first put the edge piece in position */
	/* #8 (center row, left rear) */
	if (edge == 1) {
		if (colorSide(w, 5, sideFace(w, 3)) == topColor)
			return;	/* already in place */
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCw(w);
	} else if (edge == 2) {
		rotateTopLeft(w);
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCw(w);
		rotateTopRight(w);
	} else if (edge == 3) {
		rotateTopLeft(w);
		rotateTopLeft(w);
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCw(w);
		rotateTopRight(w);
		rotateTopRight(w);
	} else if (edge == 4) {
		rotateTopRight(w);
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCw(w);
		rotateTopLeft(w);
	} else if (edge == 5) {
		rotateCenterLeft(w);
	} else if (edge == 6) {
		rotateCenterLeft(w);
		rotateCenterLeft(w);
	} else if (edge == 7) {
		rotateCenterRight(w);
	} else if (edge == 9) {
		rotateFrontCw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
	} else if (edge == 10) {
		rotateBottomLeft(w);
		rotateFrontCw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
		rotateBottomRight(w);
	} else if (edge == 11) {
		rotateBottomLeft(w);
		rotateBottomLeft(w);
		rotateFrontCw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
		rotateBottomRight(w);
		rotateBottomRight(w);
	} else if (edge == 12) {
		rotateBottomRight(w);
		rotateFrontCw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
		rotateBottomLeft(w);
	}
	/* put the piece in place */
	if (colorSide(w, 4, sideFace(w, 1)) == topColor) {
		rotateFrontCw(w);
		rotateCenterRight(w);
		rotateCenterRight(w);
		rotateFrontCcw(w);
	} else {
		rotateFrontCcw(w);
		rotateCenterRight(w);
		rotateFrontCw(w);
	}
}

/* This procedure solves the top (TOP_FACE) side, except for one edge. */
static void
solveTop(RubikWidget w)
{
	int corner, edge;

	/* put the blue face on the top */
	if ((w->rubik.sizeX & 1) == 1) {
		if (colorSide(w, 1, centerFace(w)) == TOP_FACE)
			rotateUp(w);
		else if (colorSide(w, 2, centerFace(w)) == TOP_FACE)
			rotateCcw(w);
		else if (colorSide(w, 3, centerFace(w)) == TOP_FACE)
			rotateDown(w);
		else if (colorSide(w, 4, centerFace(w)) == TOP_FACE)
			rotateCw(w);
		else if (colorSide(w, 6, centerFace(w)) == TOP_FACE) {
			rotateUp(w);
			rotateUp(w);
		}
	}
	/* first find the blue-red-white corner and place it */
	corner = findCorner(w, TOP_FACE, FRONT_FACE, LEFT_FACE);
	if (corner == 1) {
		if (colorSide(w, 5, cornerFace(w, 2)) == FRONT_FACE) {
			rotateFrontCw(w);
			rotateTopLeft(w);
		} else if (colorSide(w, 5, cornerFace(w, 2)) == LEFT_FACE) {
			rotateLeftUp(w);
			rotateTopRight(w);
		}
	} else if (corner == 2) {
		if (colorSide(w, 5, cornerFace(w, 3)) == TOP_FACE)
			rotateTopLeft(w);
		else if (colorSide(w, 5, cornerFace(w, 3)) == FRONT_FACE) {
			rotateRightUp(w);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (colorSide(w, 5, cornerFace(w, 3)) == LEFT_FACE)
			rotateFrontCcw(w);
	} else if (corner == 3) {
		if (colorSide(w, 5, cornerFace(w, 1)) == TOP_FACE) {
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (colorSide(w, 5, cornerFace(w, 1)) == FRONT_FACE) {
			rotateTopRight(w);
			rotateLeftDown(w);
		} else if (colorSide(w, 5, cornerFace(w, 1)) == LEFT_FACE) {
			rotateRightDown(w);
			rotateTopLeft(w);
		}
	} else if (corner == 4) {
		if (colorSide(w, 5, cornerFace(w, 0)) == TOP_FACE)
			rotateTopRight(w);
		else if (colorSide(w, 5, cornerFace(w, 0)) == FRONT_FACE)
			rotateLeftDown(w);
		else if (colorSide(w, 5, cornerFace(w, 0)) == LEFT_FACE) {
			rotateTopRight(w);
			rotateLeftUp(w);
			rotateTopRight(w);
		}
	} else if (corner == 5) {
		if (colorSide(w, 6, cornerFace(w, 0)) == TOP_FACE) {
			rotateBottomLeft(w);
			rotateLeftUp(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == FRONT_FACE)
			rotateLeftUp(w);
		else if (colorSide(w, 6, cornerFace(w, 0)) == LEFT_FACE)
			rotateFrontCw(w);
	} else if (corner == 6) {
		if (colorSide(w, 6, cornerFace(w, 1)) == TOP_FACE) {
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == FRONT_FACE) {
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == LEFT_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 7) {
		if (colorSide(w, 6, cornerFace(w, 3)) == TOP_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == FRONT_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == LEFT_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 8) {
		if (colorSide(w, 6, cornerFace(w, 2)) == TOP_FACE) {
			rotateLeftUp(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == FRONT_FACE) {
			rotateBottomRight(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == LEFT_FACE) {
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	}
	/* now find the blue-yellow-red corner and place it */
	rotateLeft(w);
	corner = findCorner(w, TOP_FACE, RIGHT_FACE, FRONT_FACE);
	if (corner == 1) {
		if (colorSide(w, 5, cornerFace(w, 2)) == RIGHT_FACE) {
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCcw(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 5, cornerFace(w, 2)) == FRONT_FACE) {
			rotateFrontCw(w);
			rotateFrontCw(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 2) {
		if (colorSide(w, 5, cornerFace(w, 3)) == TOP_FACE) {
			rotateRightDown(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 3)) == RIGHT_FACE) {
			rotateRightDown(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 3)) == FRONT_FACE)
			rotateFrontCcw(w);
	} else if (corner == 3) {
		if (colorSide(w, 5, cornerFace(w, 1)) == TOP_FACE) {
			rotateRightDown(w);
			rotateRightDown(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 1)) == RIGHT_FACE) {
			rotateRightDown(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 5, cornerFace(w, 1)) == FRONT_FACE) {
			rotateRightDown(w);
			rotateRightDown(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 5) {
		if (colorSide(w, 6, cornerFace(w, 0)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == RIGHT_FACE) {
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == FRONT_FACE)
			rotateFrontCw(w);
	} else if (corner == 6) {
		if (colorSide(w, 6, cornerFace(w, 1)) == TOP_FACE) {
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == RIGHT_FACE) {
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == FRONT_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 7) {
		if (colorSide(w, 6, cornerFace(w, 3)) == TOP_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == RIGHT_FACE) {
			rotateBottomLeft(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == FRONT_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 8) {
		if (colorSide(w, 6, cornerFace(w, 2)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == RIGHT_FACE) {
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == FRONT_FACE) {
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	}
	/* now find the blue-orange-yellow corner and place it */
	rotateLeft(w);
	corner = findCorner(w, TOP_FACE, BACK_FACE, RIGHT_FACE);
	if (corner == 1) {
		if (colorSide(w, 5, cornerFace(w, 2)) == BACK_FACE) {
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCcw(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 5, cornerFace(w, 2)) == RIGHT_FACE) {
			rotateFrontCw(w);
			rotateFrontCw(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 2) {
		if (colorSide(w, 5, cornerFace(w, 3)) == TOP_FACE) {
			rotateRightDown(w);
			rotateBottomLeft(w);
			rotateRightUp(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 3)) == BACK_FACE) {
			rotateFrontCw(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 3)) == RIGHT_FACE)
			rotateFrontCcw(w);
	} else if (corner == 5) {
		if (colorSide(w, 6, cornerFace(w, 0)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == BACK_FACE) {
			rotateRightDown(w);
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == RIGHT_FACE)
			rotateFrontCw(w);
	} else if (corner == 6) {
		if (colorSide(w, 6, cornerFace(w, 1)) == TOP_FACE) {
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == BACK_FACE) {
			rotateBottomLeft(w);
			rotateRightDown(w);
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == RIGHT_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 7) {
		if (colorSide(w, 6, cornerFace(w, 3)) == TOP_FACE) {
			rotateBottomLeft(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == BACK_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateRightDown(w);
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == RIGHT_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 8) {
		if (colorSide(w, 6, cornerFace(w, 2)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == BACK_FACE) {
			rotateRightDown(w);
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateRightUp(w);
			rotateFrontCcw(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == RIGHT_FACE) {
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	}
	/* and now find the blue-white-orange corner and place it */
	rotateLeft(w);
	corner = findCorner(w, TOP_FACE, LEFT_FACE, BACK_FACE);
	if (corner == 1) {
		if (colorSide(w, 5, cornerFace(w, 2)) == LEFT_FACE) {
			rotateLeftDown(w);
			rotateBottomRight(w);
			rotateLeftUp(w);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 5, cornerFace(w, 2)) == BACK_FACE) {
			rotateFrontCcw(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		}
	} else if (corner == 5) {
		if (colorSide(w, 6, cornerFace(w, 0)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == LEFT_FACE) {
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 0)) == BACK_FACE) {
			rotateBottomLeft(w);
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	} else if (corner == 6) {
		if (colorSide(w, 6, cornerFace(w, 1)) == TOP_FACE) {
			rotateBottomRight(w);
			rotateFrontCcw(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
			rotateBottomLeft(w);
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == LEFT_FACE) {
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 1)) == BACK_FACE) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	} else if (corner == 7) {
		if (colorSide(w, 6, cornerFace(w, 3)) == TOP_FACE) {
			rotateLeftDown(w);
			rotateBottomRight(w);
			rotateLeftUp(w);
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == LEFT_FACE) {
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 3)) == BACK_FACE) {
			rotateFrontCcw(w);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			rotateFrontCw(w);
		}
	} else if (corner == 8) {
		if (colorSide(w, 6, cornerFace(w, 2)) == TOP_FACE) {
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
			rotateBottomLeft(w);
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == LEFT_FACE) {
			rotateBottomRight(w);
			rotateBottomRight(w);
			rotateLeftDown(w);
			rotateBottomLeft(w);
			rotateLeftUp(w);
		} else if (colorSide(w, 6, cornerFace(w, 2)) == BACK_FACE) {
			rotateFrontCcw(w);
			rotateBottomRight(w);
			rotateFrontCw(w);
		}
	}
	rotateLeft(w);

	if ((w->rubik.sizeX & 1) == 1) {
		/* find the blue-red edge and place it */
		edge = findEdge(w, TOP_FACE, FRONT_FACE);
		placeEdge(w, edge, TOP_FACE);
		rotateLeft(w);

		/* find the blue-yellow edge and place it */
		edge = findEdge(w, TOP_FACE, RIGHT_FACE);
		placeEdge(w, edge, TOP_FACE);
		rotateLeft(w);

		/* find the blue-orange edge and place it */
		edge = findEdge(w, TOP_FACE, BACK_FACE);
		placeEdge(w, edge, TOP_FACE);
		rotateLeft(w);
	}
	rotateUp(w);		/* put the blue side to the back */
}

/* This procedure swaps the bottom front corners. */
static void
swapBottomCorners(RubikWidget w)
{
	rotateTopRight(w);
	rotateFrontCw(w);
	rotateTopLeft(w);
	rotateLeftUp(w);
	rotateTopLeft(w);
	rotateLeftDown(w);
	rotateTopRight(w);
	rotateFrontCw(w);
	rotateFrontCw(w);
}

/* This procedure places the front (BOTTOM_FACE) four corners into */
/* their correct positions. */
static void
alignCorners(RubikWidget w)
{
	int corner;

	/* find and place the green-orange-white corner (upper left) */
	corner = findCorner(w, BOTTOM_FACE, BACK_FACE, LEFT_FACE);
	if (corner == 2)
		rotateFrontCcw(w);
	else if (corner == 5)
		rotateFrontCw(w);
	else if (corner == 6) {
		rotateFrontCw(w);
		rotateFrontCw(w);
	}
	/* find and place the green-yellow-orange corner */
	/* (lower left) */
	corner = findCorner(w, BOTTOM_FACE, RIGHT_FACE, BACK_FACE);
	if (corner == 2) {
		rotateCw(w);
		swapBottomCorners(w);
		rotateCcw(w);
		swapBottomCorners(w);
	} else if (corner == 6)
		swapBottomCorners(w);

	/* find and place the green-red-yellow corner (lower right) */
	corner = findCorner(w, BOTTOM_FACE, FRONT_FACE, RIGHT_FACE);
	if (corner == 2) {
		rotateCw(w);
		swapBottomCorners(w);
		rotateCcw(w);
	}
}

/* This procedure aligns the bottom corner colors (may need to be */
/* repeated). */
static void
colorAlignFront(RubikWidget w)
{
	rotateTopRight(w);
	rotateFrontCw(w);
	rotateFrontCw(w);
	rotateTopLeft(w);
	rotateFrontCw(w);
	rotateTopRight(w);
	rotateFrontCw(w);
	rotateTopLeft(w);
	rotateFrontCw(w);
	rotateFrontCw(w);
}

/* This procedure moves the remaining TOP_FACE edge piece into position */
/* from edge position #6. */
static void
alignLastEdge(RubikWidget w)
{
	/* edge piece should be in edge position #6 */
	/* check its orientation and decide which sequence to */
	/* perform */
	if (colorSide(w, 1, sideFace(w, 2)) == TOP_FACE) {
		rotateCenterLeft(w);
		rotateFrontCw(w);
		rotateCenterRight(w);
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
		rotateCenterRight(w);
		rotateFrontCw(w);
	} else {
		rotateFrontCcw(w);
		rotateCenterLeft(w);
		rotateFrontCw(w);
		rotateCenterRight(w);
		rotateFrontCw(w);
		rotateCenterLeft(w);
		rotateFrontCcw(w);
	}
}

/* This procedure "shuffles" the three center edges on the front and */
/* top faces. */
static void
shuffleEdges(RubikWidget w)
{
	rotateCenterUp(w);
	rotateTopRight(w);
	rotateTopRight(w);
	rotateCenterDown(w);
	rotateTopRight(w);
	rotateTopRight(w);
}

/* This procedure should be used when the two opposing top front and */
/* back edge pieces are in position but not color aligned (this */
/* sequence is known as Rubik's Maneuver). */
static void
recolorTopEdges(RubikWidget w)
{
	rotateCenterUp(w);
	rotateTopRight(w);
	rotateCenterUp(w);
	rotateTopRight(w);
	rotateCenterUp(w);
	rotateTopRight(w);
	rotateTopRight(w);
	rotateCenterDown(w);
	rotateTopRight(w);
	rotateCenterDown(w);
	rotateTopRight(w);
	rotateCenterDown(w);
	rotateTopRight(w);
	rotateTopRight(w);
}

/* This procedure completes the BOTTOM_FACE side by color-aligning the */
/* BOTTOM_FACE corners and putting the BOTTOM_FACE edges in place. */
static void
solveBottom(RubikWidget w)
{
	int aligned;

	/* the BOTTOM_FACE corners (currently in the front) should now */
	/* be in their proper locations; next, we color align them, and */
	/* then move the bottom edges into place */
	do {
		aligned = 0;
		if (colorSide(w, 1, cornerFace(w, 0)) == BOTTOM_FACE)
			aligned++;
		if (colorSide(w, 1, cornerFace(w, 1)) == BOTTOM_FACE)
			aligned++;
		if (colorSide(w, 1, cornerFace(w, 2)) == BOTTOM_FACE)
			aligned++;
		if (colorSide(w, 1, cornerFace(w, 3)) == BOTTOM_FACE)
			aligned++;

		if (aligned == 0) {
			colorAlignFront(w);
		} else if (aligned == 1) {
			/* place aligned corner in upper right */
			if (colorSide(w, 1, cornerFace(w, 0)) == BOTTOM_FACE)
				rotateFrontCw(w);
			if (colorSide(w, 1, cornerFace(w, 2)) == BOTTOM_FACE) {
				rotateFrontCw(w);
				rotateFrontCw(w);
			}
			if (colorSide(w, 1, cornerFace(w, 3)) == BOTTOM_FACE)
				rotateFrontCcw(w);
			colorAlignFront(w);
		} else if (aligned == 2) {
			if (colorSide(w, 1, cornerFace(w, 0)) != BOTTOM_FACE
					|| colorSide(w, 1, cornerFace(w, 1)) == BOTTOM_FACE)
				rotateFrontCw(w);
			if (colorSide(w, 1, cornerFace(w, 0)) != BOTTOM_FACE)
				rotateFrontCw(w);
			colorAlignFront(w);
		} else if (aligned == 3)	/* not sure if this is possible */
			colorAlignFront(w);
	} while (aligned < 4);
}

static void
solve_2_2(RubikWidget w)
{
	int i;

#if 0
	for (i = 0; i < 3; i++)	/* This is not always efficient */
		if (!checkSolved(w, False))
			rotateFrontCw(w);
#else
	if (colorSide(w, 5, cornerFace(w, 0)) == colorSide(w, 4, cornerFace(w, 1))) {
		rotateFrontCw(w);
		return;
	}
	for (i = 0; i < 2; i++) {
		if (colorSide(w, 5, cornerFace(w, 0)) == colorSide(w, 5, cornerFace(w, 2)))
			return;
		rotateFrontCcw(w);
	}
#endif
}

static void
solveBottomEdges(RubikWidget w)
{
	int edge, color;

	/* next we move the bottom edges into place */
	rotateDown(w);		/* put the green face on top */
	rotateCw(w);
	rotateCw(w);

	color = colorSide(w, 1, cornerFace(w, 0));	/* get upper front corner color */
	edge = findEdge(w, BOTTOM_FACE, color);
	placeEdge(w, edge, BOTTOM_FACE);
	rotateTopRight(w);

	color = colorSide(w, 1, cornerFace(w, 0));	/* get upper front corner color */
	edge = findEdge(w, BOTTOM_FACE, color);
	placeEdge(w, edge, BOTTOM_FACE);
	rotateTopRight(w);

	color = colorSide(w, 1, cornerFace(w, 0));	/* get upper front corner color */
	edge = findEdge(w, BOTTOM_FACE, color);
	placeEdge(w, edge, BOTTOM_FACE);
	rotateTopRight(w);

	color = colorSide(w, 1, cornerFace(w, 0));	/* get upper front corner color */
	edge = findEdge(w, BOTTOM_FACE, color);
	placeEdge(w, edge, BOTTOM_FACE);
	rotateTopRight(w);

	/* now, align the fourth blue edge piece (if necessary) */
	rotateCw(w);
	rotateCw(w);
	edge = findEdge(w, TOP_FACE, LEFT_FACE);
	if (edge == 1) {
		if (colorSide(w, 1, sideFace(w, 0)) == TOP_FACE) {
			rotateFrontCcw(w);
			rotateCenterLeft(w);
			rotateFrontCw(w);
			rotateCenterLeft(w);
			rotateFrontCw(w);
			rotateCenterLeft(w);
			rotateFrontCcw(w);
		}
	} else {
		if (edge == 5)
			rotateCenterRight(w);
		else if (edge == 7)
			rotateCenterLeft(w);
		else if (edge == 8) {
			rotateCenterRight(w);
			rotateCenterRight(w);
		}
		alignLastEdge(w);
	}
}

/* This procedure completes the solution process by placing the */
/* remaining edges in place and alignment. */
static void
alignEdges(RubikWidget w)
{
	int aligned, color, edge;

	/* move the red side to the front */
	if (colorSide(w, 1, centerFace(w)) == RIGHT_FACE)
		rotateRight(w);
	else if (colorSide(w, 1, centerFace(w)) == BACK_FACE) {
		rotateRight(w);
		rotateRight(w);
	} else if (colorSide(w, 1, centerFace(w)) == LEFT_FACE)
		rotateLeft(w);

	/* rotate the top until its aligned with the center colors */
	edge = findEdge(w, TOP_FACE, FRONT_FACE);
	if (edge == 2)
		rotateTopLeft(w);
	else if (edge == 3) {
		rotateTopLeft(w);
		rotateTopLeft(w);
	} else if (edge == 4)
		rotateTopRight(w);

	/* rotate the bottom until its aligned with the center */
	/* colors */
	edge = findEdge(w, BOTTOM_FACE, FRONT_FACE);
	if (edge == 10)
		rotateBottomLeft(w);
	else if (edge == 11) {
		rotateBottomLeft(w);
		rotateBottomLeft(w);
	} else if (edge == 12)
		rotateBottomRight(w);

	if (checkSolved(w, False))
		return;

	rotateCcw(w);		/* place unaligned edges vertically */

	/* see if any edges are in correct position */
	aligned = 0;
	edge = findEdge(w, FRONT_FACE, RIGHT_FACE);
	if (edge == 1)
		aligned++;
	edge = findEdge(w, RIGHT_FACE, BACK_FACE);
	if (edge == 3)
		aligned++;
	edge = findEdge(w, BACK_FACE, LEFT_FACE);
	if (edge == 11)
		aligned++;
	edge = findEdge(w, LEFT_FACE, FRONT_FACE);
	if (edge == 9)
		aligned++;

	if (aligned == 0) {
		shuffleEdges(w);	/* put one edge into position */
		aligned++;
	}
	if (aligned == 1) {
		/* find the correct piece and move it to the back */
		/* bottom edge */
		edge = findEdge(w, FRONT_FACE, RIGHT_FACE);
		if (edge == 1) {
			rotateDown(w);
			rotateDown(w);
		} else {
			edge = findEdge(w, RIGHT_FACE, BACK_FACE);
			if (edge == 3)
				rotateUp(w);
			else {
				edge = findEdge(w, LEFT_FACE, FRONT_FACE);
				if (edge == 9)
					rotateDown(w);
			}
		}

		/* shuffle */
		color = colorSide(w, 1, centerFace(w));
		if (colorSide(w, 1, sideFace(w, 3)) == color) {
			rotateRight(w);
			rotateRight(w);
			rotateDown(w);
			shuffleEdges(w);
		} else if (colorSide(w, 6, sideFace(w, 0)) == color) {
			rotateRight(w);
			rotateRight(w);
			rotateDown(w);
			shuffleEdges(w);
		} else
			shuffleEdges(w);
	}
	/* pieces should be in place; complete color alignment */
	/* find number of unaligned edge pieces and fix them */
	aligned = 0;
	if (colorSide(w, 1, sideFace(w, 0)) == colorSide(w, 1, centerFace(w)))
		aligned++;
	if (colorSide(w, 1, sideFace(w, 3)) == colorSide(w, 1, centerFace(w)))
		aligned++;
	if (colorSide(w, 3, sideFace(w, 0)) == colorSide(w, 3, centerFace(w)))
		aligned++;
	if (colorSide(w, 3, sideFace(w, 3)) == colorSide(w, 3, centerFace(w)))
		aligned++;
	if (aligned == 0) {
		recolorTopEdges(w);
		rotateDown(w);
		rotateDown(w);
		recolorTopEdges(w);
	} else if (aligned == 2) {
		if (colorSide(w, 1, sideFace(w, 0)) == colorSide(w, 1, centerFace(w)))
			do {
				rotateDown(w);
			} while (colorSide(w, 1, sideFace(w, 0)) == colorSide(w, 1, centerFace(w)));
		if (colorSide(w, 1, sideFace(w, 3)) != colorSide(w, 1, centerFace(w)))
			rotateUp(w);
		recolorTopEdges(w);
		if (!checkSolved(w, False)) {
			rotateDown(w);
			recolorTopEdges(w);
		}
	}
}

/* Wrong center rotation on top and front */
static void
shuffle2_180(RubikWidget w)
{
	int i;

	for (i = 0; i < 3; i++)
		shuffleEdges(w);
}

/* Wrong center rotation on top */
static void
shuffle1_180(RubikWidget w, Boolean top)
{
	int i;

	if (top) {
		for (i = 0; i < 30; i++) {
			rotateTopRight(w);
			rotateFrontCw(w);
			rotateFrontCw(w);
		}
	} else {
		for (i = 0; i < 30; i++) {
			rotateTopRight(w);
			rotateTopRight(w);
			rotateFrontCw(w);
		}
	}
}

/* Pairs off by 90 degrees */
static void
shuffle2_90(RubikWidget w, Boolean counterTop, Boolean counterFront)
{
	int i, num;

	num = ((counterTop && !counterFront) ||
		(!counterTop && counterFront)) ?  63 : 105;
	for (i = 0; i < num; i++) {
		if (counterFront)
			rotateFrontCcw(w);
		else
			rotateFrontCw(w);
		if (counterTop)
			rotateTopRight(w);
		else
			rotateTopLeft(w);
	}
}

static void
solveAdjCenters(RubikWidget w)
{
	int face0 = mapFace(w, 5), face1 = mapFace(w, 1);

	if ((face0 == 2 && face1 != 0) ||
			(face1 == 2 && face0 != 0)) {
		shuffle2_180(w);
	} else if (face0 == 2 && face1 == 0) {
		shuffle1_180(w, True);
	} else if (face0 == 0 && face1 == 2) {
		shuffle1_180(w, False);
	} else if (face0 == 1 && face1 == 1) {
		shuffle2_90(w, True, True);
	} else if (face0 == 3 && face1 == 1) {
		shuffle2_90(w, True, False);
	} else if (face0 == 1 && face1 == 3) {
		shuffle2_90(w, False, True);
	} else if (face0 == 3 && face1 == 3) {
		shuffle2_90(w, False, False);
	}
}

static void
solveOppCenters(RubikWidget w)
{
	int face0, face1, face2;
	Boolean fixOp = False;

	face0 = mapFace(w, 5);
	face1 = mapFace(w, 1);
	face2 = mapFace(w, 4);
	if ((face1 == 1) || (face1 == 3)) {
		rotateUp(w);
		fixOp = True;
	} else if ((face2 == 1) || (face2 == 3)) {
		rotateCw(w);
		fixOp = True;
	} else if ((face0 == 1) || (face0 == 3))
		fixOp = True;
	if (!fixOp)
		return;
	face0 = mapFace(w, 5);
	face1 = mapFace(w, 1);
	if (face0 == 1)
		shuffle2_90(w, False, True);
	else /* face0 == 3 */
		shuffle2_90(w, True, False);
	rotateUp(w);
	face0 = mapFace(w, 5);
	face1 = mapFace(w, 1);
	if (face0 == 1 && face1 == 1) {
		shuffle2_90(w, True, True);
	} else if (face0 == 3 && face1 == 1) {
		shuffle2_90(w, True, False);
	} else if (face0 == 1 && face1 == 3) {
		shuffle2_90(w, False, True);
	} else if (face0 == 3 && face1 == 3) {
		shuffle2_90(w, False, False);
	}
}

static void
solveCenters(RubikWidget w)
{
	solveAdjCenters(w); /* 5-1 */
	rotateUp(w);
	rotateUp(w);
	solveAdjCenters(w); /* 6-3 */
	rotateCw(w);
	solveAdjCenters(w); /* 4-3 */
	rotateUp(w);
	solveAdjCenters(w); /* 3-2 */
	/* only 90 degree matches left */
	rotateUp(w);
	solveAdjCenters(w); /* 2-1 */
	rotateUp(w);
	solveAdjCenters(w); /* 1-4 */
	rotateCcw(w);
	solveAdjCenters(w); /* 5-4 */
	rotateUp(w);
	solveAdjCenters(w); /* 4-6 */
	rotateUp(w);
	solveAdjCenters(w); /* 6-2 */
	rotateUp(w);
	solveAdjCenters(w); /* 2-5 */
	rotateCcw(w);
	rotateUp(w);
	solveAdjCenters(w); /* 1-6 */
	rotateUp(w);
	rotateUp(w);
	solveAdjCenters(w); /* 3-5 */
	/* only 1 90 degree matches left possible on opposite sides */
	solveOppCenters(w);
}

/* Revenge */

#if 0
static void
solveFrontCorners4(RubikWidget w)
{
	/* put the orange corner on the top right */
	if (colorSide(w, 0, cornerFace(w, 2)) == BACK_FACE)
		rotateDown(w);
	else if (colorSide(w, 1, cornerFace(w, 2)) == BACK_FACE)
		rotateRight(w);
	else if (colorSide(w, 3, cornerFace(w, 2)) == BACK_FACE)
		rotateLeft(w);
	else if (colorSide(w, 4, cornerFace(w, 2)) == BACK_FACE)
		rotateUp(w);
	else if (colorSide(w, 6, cornerFace(w, 2)) == BACK_FACE) {
		rotateUp(w);
		rotateUp(w);
	}
}
#endif

static void
solve4USide(RubikWidget w, int side, int clockEdge)
{
	int parity = clockEdge & 1;
	int edge = clockEdge >> 1;
	RubikFE rubikFE = findClockEdge(w, side, clockEdge);
	int foundEdge = rubikFE.edge >> 1;

	if (parity == 1)
		parity += w->rubik.sizeX - 4;
	if (!(side == LEFT_FACE || side == RIGHT_FACE)) {
		(void) printf("choose a side\n");
		return;
	}

	if (edge == 0) {
		return; /* solve this later, working on U */
	}
	if (rubikFE.face == TOP_FACE) {
		if (foundEdge == 0) {
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		} else if (foundEdge == 1) {
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopLeft(w);
		} else if (foundEdge == 2) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (foundEdge == 3) {
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopRight(w);
		}
	} else if (rubikFE.face == LEFT_FACE) {
		if (rubikFE.edge == clockEdge && side == LEFT_FACE) {
			return; /* left do nothing */
		}
		/* if (foundEdge == 0) do nothing */
		if (foundEdge == 1)
			rotateLeftUp(w);
		else if (foundEdge == 2) {
			rotateLeftUp(w);
			rotateLeftUp(w);
		} else if (foundEdge == 3)
			rotateLeftDown(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		rotateTopLeft(w);
		/* if (foundEdge == 0) do nothing */
		if (foundEdge == 1)
			rotateLeftDown(w);
		else if (foundEdge == 2) {
			rotateLeftUp(w);
			rotateLeftUp(w);
		} else if (foundEdge == 3)
			rotateLeftUp(w);
	} else if (rubikFE.face == FRONT_FACE) {
		if (foundEdge == 0)
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		else if (foundEdge == 1) {
			rotateRightUp(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateRightDown(w);
		} else if (foundEdge == 2) {
			rotateFrontCw(w);
			rotateFrontCw(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateFrontCw(w);
			rotateFrontCw(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		} else if (foundEdge == 3) {
			rotateFrontCw(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateFrontCcw(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		}
	} else if (rubikFE.face == RIGHT_FACE) {
		if (rubikFE.edge == clockEdge && side == RIGHT_FACE) {
			return; /* right do nothing */
		}
		/* if (foundEdge == 0) do nothing */
		if (foundEdge == 1)
			rotateRightDown(w);
		else if (foundEdge == 2) {
			rotateRightUp(w);
			rotateRightUp(w);
		} else if (foundEdge == 3)
			rotateRightUp(w);
		rotateTopLeft(w);
		movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		rotateTopRight(w);
		/* if (foundEdge == 0) do nothing */
		if (foundEdge == 1)
			rotateRightUp(w);
		else if (foundEdge == 2) {
			rotateRightUp(w);
			rotateRightUp(w);
		} else if (foundEdge == 3)
			rotateRightDown(w);
	} else if (rubikFE.face == BOTTOM_FACE) {
		/* if (foundEdge == 0) do nothing */
		if (foundEdge == 1) {
			rotateRightUp(w);
			rotateRightUp(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateRightDown(w);
			rotateRightDown(w);
		} else if (foundEdge == 2) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
		} else if (foundEdge == 3) {
			rotateLeftUp(w);
			rotateLeftUp(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopRight(w);
			rotateLeftDown(w);
			rotateLeftDown(w);
		}
	} else if (rubikFE.face == BACK_FACE) {
		if (foundEdge == 0)
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
		else if (foundEdge == 1) {
			rotateRightDown(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateRightUp(w);
		} else if (foundEdge == 2) {
			rotateTopRight(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopRight(w);
			rotateTopRight(w);
		} else if (foundEdge == 3) {
			rotateLeftDown(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, BOTTOM, FALSE);
			rotateTopRight(w);
			rotateLeftUp(w);
		}
	}
	if (side == LEFT_FACE) {
		if (edge == 1) {
			rotateLeftUp(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopLeft(w);
			rotateLeftDown(w);
		} else if (edge == 2) {
			rotateLeftUp(w);
			rotateLeftUp(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopLeft(w);
			rotateLeftDown(w);
			rotateLeftDown(w);
		} else if (edge == 3) {
			rotateLeftDown(w);
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopLeft(w);
			rotateLeftUp(w);
		}
	} else if (side == RIGHT_FACE) {
		if (edge == 1) {
			rotateRightDown(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopRight(w);
			rotateRightUp(w);
		} else if (edge == 2) {
			rotateRightUp(w);
			rotateRightUp(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopRight(w);
			rotateRightDown(w);
			rotateRightDown(w);
		} else if (edge == 3) {
			rotateRightUp(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, 1 + parity, TOP, FALSE);
			rotateTopRight(w);
			rotateRightDown(w);
		}
	}
}

static void
solve1stTopPairEdge4(RubikWidget w, int edge)
{
	int clockEdge0 = (edge << 1);
	int clockEdge1 = clockEdge0 + 1;
	RubikFE rubikFE0 = findClockEdge(w, TOP_FACE, clockEdge0);
	RubikFE rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	int foundEdge0 = rubikFE0.edge >> 1;
	int foundEdge1 = rubikFE1.edge >> 1;
	Boolean onSide0 = False;
	Boolean onSide1 = False;
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	if (rubikFE0.face == TOP_FACE && rubikFE1.face == TOP_FACE &&
			foundEdge0 == edge && foundEdge1 == edge) {
		return; /* done already */
	}
	/* 3 possibilities 0 on sides, 1 on side or 2 on sides */
	if (rubikFE0.face == LEFT_FACE || rubikFE0.face == RIGHT_FACE ||
			(rubikFE0.face == TOP_FACE && ((rubikFE0.edge >> 1) & 1) == 1))
		onSide0 = True;
	if (rubikFE1.face == LEFT_FACE || rubikFE1.face == RIGHT_FACE ||
			(rubikFE1.face == TOP_FACE && ((rubikFE1.edge >> 1) & 1) == 1))
		onSide1 = True;
	/* preallignment */
	if (onSide0 && onSide1) { /* moving 2 */
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopLeft(w);
	} else if (onSide0 || onSide1) {
		if (!(onSide0 && onSide1) &&
				(rubikFE0.face == BOTTOM_FACE || rubikFE1.face == BOTTOM_FACE ||
				(rubikFE0.face == FRONT_FACE && (rubikFE0.edge >> 1) == 2) ||
				(rubikFE1.face == FRONT_FACE && (rubikFE1.edge >> 1) == 2) ||
				(rubikFE0.face == BACK_FACE && (rubikFE0.edge >> 1) == 0) ||
				(rubikFE1.face == BACK_FACE && (rubikFE1.edge >> 1) == 0))) {
			/* moving out of way */
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		}
		if (rubikFE0.face == RIGHT_FACE || rubikFE1.face == RIGHT_FACE ||
				(rubikFE0.face == TOP && (rubikFE0.edge >> 1) == 1) ||
				(rubikFE1.face == TOP && (rubikFE1.edge >> 1) == 1)) {
			/* moving 1 front TR */
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
			rotateFrontCw(w);
			rotateFrontCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateFrontCw(w);
			rotateFrontCw(w);
			rotateTopRight(w);
		} else {
			/* moving 1 front TL */
			rotateTopRight(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
			rotateFrontCw(w);
			rotateFrontCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateFrontCw(w);
			rotateFrontCw(w);
			rotateTopLeft(w);
		}
	}
	if (onSide0 || onSide1) {
		rubikFE0 = findClockEdge(w, TOP_FACE, clockEdge0);
		rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	}
	if (!(rubikFE0.face == BOTTOM_FACE && rubikFE0.edge == 0)) {
		if (rubikFE0.face == TOP_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == FRONT_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == BACK_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		} else if (rubikFE0.face == FRONT_FACE && rubikFE0.edge == 4) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == TOP_FACE && rubikFE0.edge == 4) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (rubikFE0.face == BOTTOM_FACE && rubikFE0.edge == 4) {
			rotateBackCw(w);
			rotateBackCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateBackCw(w);
			rotateBackCw(w);
		} else if (rubikFE0.face == BACK_FACE && rubikFE0.edge == 4) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		}
		rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	}
	if (!(rubikFE1.face == BOTTOM_FACE && rubikFE1.edge == 1)) {
		if (rubikFE1.face == TOP_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		} else if (rubikFE1.face == FRONT_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		} else if (rubikFE1.face == BACK_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		} else if (rubikFE1.face == FRONT_FACE && rubikFE1.edge == 5) {
			(void) printf("impossible 2 5 %d %d\n", rubikFE1.face, rubikFE1.edge);
		} else if (rubikFE1.face == TOP_FACE && rubikFE1.edge == 5) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (rubikFE1.face == BOTTOM_FACE && rubikFE1.edge == 5) {
			rotateBackCw(w);
			rotateBackCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateBackCw(w);
			rotateBackCw(w);
		} else if (rubikFE1.face == BACK_FACE && rubikFE1.edge == 5) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		}
	}
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
}

static void
solve2ndTopPairEdge4(RubikWidget w, int edge)
{
	int clockEdge0 = (edge << 1);
	int clockEdge1 = clockEdge0 + 1;
	RubikFE rubikFE0 = findClockEdge(w, TOP_FACE, clockEdge0);
	RubikFE rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	int foundEdge0 = rubikFE0.edge >> 1;
	int foundEdge1 = rubikFE1.edge >> 1;
	Boolean onSide0 = False;
	Boolean onSide1 = False;
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	if (rubikFE0.face == TOP_FACE && rubikFE1.face == TOP_FACE &&
			foundEdge0 == edge && foundEdge1 == edge) {
		/* done already */
		return;
	}
	/* 3 possibilities 0 on sides, 1 on side or 2 on sides */
	if (rubikFE0.face == LEFT_FACE || /* rubikFE0.face == RIGHT_FACE || */
			(rubikFE0.face == TOP_FACE && ((rubikFE0.edge >> 1) & 1) == 1))
		onSide0 = True;
	if (rubikFE1.face == LEFT_FACE || /* rubikFE1.face == RIGHT_FACE || */
			(rubikFE1.face == TOP_FACE && ((rubikFE1.edge >> 1) & 1) == 1))
		onSide1 = True;
	/* preallignment */
	if (onSide0 || onSide1) {
		if (!(onSide0 && onSide1) &&
				(rubikFE0.face == BOTTOM_FACE || rubikFE1.face == BOTTOM_FACE ||
				(rubikFE0.face == FRONT_FACE && (rubikFE0.edge >> 1) == 2) ||
				(rubikFE1.face == FRONT_FACE && (rubikFE1.edge >> 1) == 2) ||
				(rubikFE0.face == BACK_FACE && (rubikFE0.edge >> 1) == 0) ||
				(rubikFE1.face == BACK_FACE && (rubikFE1.edge >> 1) == 0))) {
			/* moving out of way */
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		}
		/* moving 1 front TR */
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateFrontCw(w);
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateFrontCw(w);
		rotateFrontCw(w);
		rotateTopLeft(w);
	}
	if (onSide0 || onSide1) {
		rubikFE0 = findClockEdge(w, TOP_FACE, clockEdge0);
		rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	}
	if (!(rubikFE0.face == BOTTOM_FACE && rubikFE0.edge == 0)) {
		if (rubikFE0.face == TOP_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == FRONT_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == BACK_FACE && rubikFE0.edge == 0) {
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		} else if (rubikFE0.face == FRONT_FACE && rubikFE0.edge == 4) {
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			rotateBottomLeft(w);
			rotateBottomLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		} else if (rubikFE0.face == TOP_FACE && rubikFE0.edge == 4) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (rubikFE0.face == BOTTOM_FACE && rubikFE0.edge == 4) {
			rotateBackCw(w);
			rotateBackCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateBackCw(w);
			rotateBackCw(w);
		} else if (rubikFE0.face == BACK_FACE && rubikFE0.edge == 4) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		}
		rubikFE1 = findClockEdge(w, TOP_FACE, clockEdge1);
	}
	if (!(rubikFE1.face == BOTTOM_FACE && rubikFE1.edge == 1)) {
		if (rubikFE1.face == TOP_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		} else if (rubikFE1.face == FRONT_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		} else if (rubikFE1.face == BACK_FACE && rubikFE1.edge == 1) {
			movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		} else if (rubikFE1.face == FRONT_FACE && rubikFE1.edge == 5) {
			(void) printf("impossible 2 5 %d %d\n", rubikFE1.face, rubikFE1.edge);
		} else if (rubikFE1.face == TOP_FACE && rubikFE1.edge == 5) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (rubikFE1.face == BOTTOM_FACE && rubikFE1.edge == 5) {
			rotateBackCw(w);
			rotateBackCw(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateBackCw(w);
			rotateBackCw(w);
		} else if (rubikFE1.face == BACK_FACE && rubikFE1.edge == 5) {
			rotateTopLeft(w);
			rotateTopLeft(w);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
			rotateTopLeft(w);
			rotateTopLeft(w);
		}
	}
	rotateTopRight(w);
	rotateFrontCw(w);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopLeft(w);
}

static void
mix3Edges(RubikWidget w)
{
	int pair1 = w->rubik.sizeX - 2;
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
}

static void
flipDiagonal2Pairs(RubikWidget w)
{
	int pair1 = w->rubik.sizeX - 2;

	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
}

static void
rotate1stPair(RubikWidget w, int pair, int face)
{
	switch (face) {
	case 1:
		movePuzzlePiece(w, FRONT_FACE, pair, BOTTOM, FALSE);
		return;
	case 2:
		movePuzzlePiece(w, FRONT_FACE, pair, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair, BOTTOM, FALSE);
		return;
	case 3:
		movePuzzlePiece(w, FRONT_FACE, pair, TOP, FALSE);
		return;
	default:
		return;
	}
}

static void
rotate2ndPair(RubikWidget w, int face)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;
	switch (face) {
	case 2:
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		return;
	case 1:
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		return;
	default:
		return;
	}
}

static void topPairFlip(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
}

static void frontPairFlip(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateFrontCw(w);
	rotateFrontCw(w);
}

static Boolean
pair1stEdges(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;
	int face0, face1;

	for (face0 = 0; face0 < 4; face0++) {
		for (face1 = 0; face1 < 4; face1++) {
			if (matchEdges(w, lastColumn[face0], lastColumn[face1], 0)) {
				rotate1stPair(w, pair0, face0);
				rotate1stPair(w, pair1, face1);
				return True;
			}
		}
	}
	return False;
}

static Boolean
pair2ndEdges(RubikWidget w)
{
	int face;

	for (face = 1; face < 4; face++) {
		int last = lastColumn[face];
		if (matchEdges(w, last, last, 0)) {
			rotate2ndPair(w, face);
			return True;
		}
	}
	return False;
}

static void
pair1stPairOf8Edges(RubikWidget w)
{
	int i;
	for (i = 0; i < 2; i++) {
		if (pair1stEdges(w)) {
			return;
		} else {
			mix3Edges(w);
		}
	}
	/* 1st not working */
}

static void
pair2ndPairOf8Edges(RubikWidget w)
{
	int i;
	for (i = 0; i < 5; i++) {
		if (pair2ndEdges(w)) {
			return;
		} else {
			mix3Edges(w);
		}
	}
	/* 2nd not working */
}

static void
pairLastEdges(RubikWidget w)
{
	int quad0 = 1;
	int quad1 = w->rubik.sizeX - 2;
	int quad2 = w->rubik.sizeX * w->rubik.sizeX - w->rubik.sizeX + 1;
	int quad3 = w->rubik.sizeX * w->rubik.sizeX - 2;

	if (w->rubik.cubeLoc[FRONT_FACE][quad0].face ==
			w->rubik.cubeLoc[FRONT_FACE][quad2].face &&
			w->rubik.cubeLoc[FRONT_FACE][quad1].face ==
			w->rubik.cubeLoc[FRONT_FACE][quad3].face) {
		topPairFlip(w);
	}
	if (!(w->rubik.cubeLoc[FRONT_FACE][quad0].face ==
			w->rubik.cubeLoc[FRONT_FACE][quad1].face &&
			w->rubik.cubeLoc[FRONT_FACE][quad2].face ==
			w->rubik.cubeLoc[FRONT_FACE][quad3].face)) {
		flipDiagonal2Pairs(w);
	}
}

static void
solve42U(RubikWidget w)
{
	solve4USide(w, LEFT_FACE, 2);
	solve4USide(w, LEFT_FACE, 3);
	solve4USide(w, LEFT_FACE, 4);
	solve4USide(w, LEFT_FACE, 5);
	solve4USide(w, LEFT_FACE, 6);
	solve4USide(w, LEFT_FACE, 7);
	solve4USide(w, RIGHT_FACE, 2);
	solve4USide(w, RIGHT_FACE, 3);
	solve4USide(w, RIGHT_FACE, 4);
	solve4USide(w, RIGHT_FACE, 5);
	solve4USide(w, RIGHT_FACE, 6);
	solve4USide(w, RIGHT_FACE, 7);
}

static void
solve2PairEdge4(RubikWidget w)
{
	solve1stTopPairEdge4(w, 1);
	solve2ndTopPairEdge4(w, 3);
}

static void
pairLast8Edges(RubikWidget w)
{
	pair1stPairOf8Edges(w);
	pair2ndPairOf8Edges(w);
	pairLastEdges(w);
}


static void place4thPair(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;
	RubikFE rubikFE = findClockEdge(w, BACK_FACE, 0);
	/* if (rubikFE.face == BACK_FACE && rubikFE.edge == 0)
		return; *//* 4th pair already set */
	if (rubikFE.face == TOP_FACE && rubikFE.edge == 0) {
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	} else if (rubikFE.face == FRONT_FACE && rubikFE.edge == 0) {
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	} else if (rubikFE.face == BOTTOM_FACE && rubikFE.edge == 0) {
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	} else if (rubikFE.face == BACK_FACE && rubikFE.edge == 4) {
		rotateBackCw(w);
		rotateBackCw(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateBackCw(w);
		rotateBackCw(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	} else if (rubikFE.face == TOP_FACE && rubikFE.edge == 4) {
		rotateBackCw(w);
		rotateBackCw(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateBackCw(w);
		rotateBackCw(w);
	} else if (rubikFE.face == FRONT_FACE && rubikFE.edge == 4) {
		rotateFrontCw(w);
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateFrontCw(w);
		rotateFrontCw(w);
	} else if (rubikFE.face == BOTTOM_FACE && rubikFE.edge == 4) {
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateBottomRight(w);
		rotateBottomRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateBottomRight(w);
		rotateBottomRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	}
}

#if 0
static void topPairExchange(RubikWidget w)
{
	int pair1 = w->rubik.sizeX - 2;

	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
}
#endif

static void frontPairExchange(RubikWidget w)
{
	int pair1 = w->rubik.sizeX - 2;

	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateFrontCw(w);
	rotateFrontCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
}

static void place3rdPair(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;
	RubikFE rubikFE = findClockEdge(w, TOP_FACE, 0);
	/*if (rubikFE.face == TOP_FACE && rubikFE.edge == 0)
		return; *//* 3rd pair already set */
	if (rubikFE.face == FRONT_FACE && rubikFE.edge == 0) {
		topPairFlip(w);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	} else if (rubikFE.face == BOTTOM_FACE && rubikFE.edge == 0) {
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
	} else if (rubikFE.face == BACK_FACE && rubikFE.edge == 4) {
		topPairFlip(w);
	} else if (rubikFE.face == TOP_FACE && rubikFE.edge == 4) {
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	} else if (rubikFE.face == FRONT_FACE && rubikFE.edge == 4) {
		frontPairFlip(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
	}
}

static void upsetSingleTopFrontPair(RubikWidget w)
{
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	/* upset */
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	rotateBottomLeft(w);
	rotateBottomLeft(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	rotateBottomLeft(w);
	rotateBottomLeft(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateTopRight(w);
	rotateTopRight(w);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);

	/* vb */
	movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
	rotateBackCw(w);
	rotateBackCw(w);
	movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
	movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
	rotateBackCw(w);
	rotateBackCw(w);

	/* flip */
	rotateFrontCw(w);
	rotateFrontCw(w);
	topPairFlip(w);
	rotateFrontCw(w);
	rotateFrontCw(w);
}

static void place2ndPair(RubikWidget w)
{
	RubikFE rubikFE0 = findClockEdge(w, FRONT_FACE, 0);
	RubikFE rubikFE1 = findClockEdge(w, FRONT_FACE, 4);
	int pair0 = 1;
	int pair1 = w->rubik.sizeX - 2;

	if ((rubikFE0.face == FRONT_FACE && rubikFE0.edge == 4) ||
			(rubikFE1.face == FRONT_FACE && rubikFE1.edge == 0)) {
		frontPairExchange(w);
	} else if (rubikFE0.face != FRONT_FACE && rubikFE1.face != FRONT_FACE &&
			(rubikFE0.edge == 4 || rubikFE1.edge == 0)) {
		frontPairFlip(w);
	} else if ((rubikFE0.face == BOTTOM_FACE && rubikFE0.edge == 0) ||
			(rubikFE1.face == TOP_FACE && rubikFE1.edge == 4)) {
		frontPairExchange(w);
		frontPairFlip(w);
	}
	rubikFE0 = findClockEdge(w, FRONT_FACE, 0);
	rubikFE1 = findClockEdge(w, FRONT_FACE, 4);
	if (rubikFE0.face == TOP_FACE && rubikFE0.edge == 4) {
		if (rubikFE1.face == FRONT_FACE) {
			upsetSingleTopFrontPair(w);
		}
	} else if (rubikFE1.face == BOTTOM_FACE && rubikFE1.edge == 0) {
		if (rubikFE0.face == FRONT_FACE) {
			/* prepare upset */
			movePuzzlePiece(w, FRONT_FACE, pair0, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
			upsetSingleTopFrontPair(w);
			movePuzzlePiece(w, FRONT_FACE, pair0, BOTTOM, FALSE);
			movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		}
	}
}

static void place4EdgePairs(RubikWidget w)
{
	place4thPair(w);
	place3rdPair(w);
	place2ndPair(w);
}

static void
solveLast8Edges(RubikWidget w)
{
	pairLast8Edges(w);
	place4EdgePairs(w);
}

#if 0
static void adj2PairFrontTop(RubikWidget w)
{
	int pair1 = w->rubik.sizeX - 2;
	int i;

	for (i = 0; i < 5; i++) {
		movePuzzlePiece(w, FRONT_FACE, pair1, TOP, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, pair1, BOTTOM, FALSE);
		rotateTopRight(w);
		rotateTopRight(w);
	}
}
#endif

static void
rotateFace(RubikWidget w, int face, int rotation)
{
	int rot = (rotation + MAX_ORIENT * MAX_ORIENT) % MAX_ORIENT;
	if (rot == 0)
		return;
	if (face == TOP_FACE) {
		if (rot == 1) {
			rotateTopLeft(w);
		} else if (rot == 2) {
			rotateTopLeft(w);
			rotateTopLeft(w);
		} else if (rot == 3) {
			rotateTopRight(w);
		}
	} else if (face == LEFT_FACE) {
		if (rot == 1) {
			rotateLeftDown(w);
		} else if (rot == 2) {
			rotateLeftDown(w);
			rotateLeftDown(w);
		} else if (rot == 3) {
			rotateLeftUp(w);
		}
	} else if (face == FRONT_FACE) {
		if (rot == 1) {
			rotateFrontCw(w);
		} else if (rot == 2) {
			rotateFrontCw(w);
			rotateFrontCw(w);
		} else if (rot == 3) {
			rotateFrontCcw(w);
		}
	} else if (face == RIGHT_FACE) {
		if (rot == 1) {
			rotateRightUp(w);
		} else if (rot == 2) {
			rotateRightUp(w);
			rotateRightUp(w);
		} else if (rot == 3) {
			rotateRightDown(w);
		}
	} else if (face == BOTTOM_FACE) {
		if (rot == 1) {
			rotateBottomRight(w);
		} else if (rot == 2) {
			rotateBottomRight(w);
			rotateBottomRight(w);
		} else if (rot == 3) {
			rotateBottomLeft(w);
		}
	} else if (face == BACK_FACE) {
		if (rot == 1) {
			rotateBackCw(w);
		} else if (rot == 2) {
			rotateBackCw(w);
			rotateBackCw(w);
		} else if (rot == 3) {
			rotateBackCcw(w);
		}
	}
}

static void
opp2PairMove(RubikWidget w, int fromFace, int toFace)
{
	int quad = w->rubik.sizeX + 1;
	int i;

	if (fromFace == TOP_FACE && toFace == BOTTOM_FACE) {
		for (i = 0; i < 2; i++) {
			movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
			movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
			movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
			movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		}
	} else if (fromFace == LEFT_FACE && toFace == RIGHT_FACE) {
		for (i = 0; i < 2; i++) {
			movePuzzlePiece(w, LEFT_FACE, quad, RIGHT, FALSE);
			movePuzzlePiece(w, LEFT_FACE, quad, RIGHT, FALSE);
			movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
			movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		}
	} else if (fromFace == FRONT_FACE && toFace == BACK_FACE) {
		for (i = 0; i < 2; i++) {
			movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
			movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
			movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		}
	} else {
		(void) printf("op2PairMove unknown %d %d\n", fromFace, toFace);
	}
}

static int
bestEmptySpot(RubikWidget w, int face, int fromFace, int offset)
{
	int i, center, position;

	for (i = 0; i < 4; i++) {
		center = (bestOrder[i] + offset) % MAX_ORIENT;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face == fromFace ||
			w->rubik.cubeLoc[face][position].face == oppositeFace[face]) {
			return (center + MAX_ORIENT - offset) % MAX_ORIENT;
		}
	}
	for (i = 0; i < 4; i++) {
		center = (bestOrder[i] + offset) % MAX_ORIENT;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face != face &&
				w->rubik.cubeLoc[face][position].face != oppositeFace[fromFace]) {
			return (center + MAX_ORIENT - offset) % MAX_ORIENT;
		}
	}
	for (i = 0; i < 4; i++) {
		center = (bestOrder[i] + offset) % MAX_ORIENT;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face != face) {
			return (center + MAX_ORIENT - offset) % MAX_ORIENT;
		}
	}
	return -1;
}


static int
bestEmptySpotRev(RubikWidget w, int face, int fromFace, int offset)
{
	int i, center, position;

	for (i = 0; i < 4; i++) {
		center = (bestOrder[i] + offset) % MAX_ORIENT;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face == fromFace ||
				w->rubik.cubeLoc[face][position].face == oppositeFace[face]) {
			return (center + MAX_ORIENT - offset) % MAX_ORIENT;
		}
	}
	return -1;
}

static int
findMoveSpot(RubikWidget w, int currentFace, int spotFace)
{
	int i, center, position;

	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[currentFace][position].face == spotFace) {
			return center;
		}
	}
	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[currentFace][position].face == oppositeFace[currentFace]) {
			return center;
		}
	}
	return -1;
}

static int
findMoveSpotRev(RubikWidget w, int currentFace, int spotFace)
{
	int i, center, position;

	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[currentFace][position].face != currentFace &&
				w->rubik.cubeLoc[currentFace][position].face != oppositeFace[spotFace]) {
			return center;
		}
	}
	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[currentFace][position].face != currentFace) {
			return center;
		}
	}
	return -1;
}


static int
bestEmptySpotsForOpp(RubikWidget w, int face, int offset)
{
	int i, center, position;
	int otherCenter, otherPosition;

	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		otherCenter = (bestOrder[i] + 3 + offset) % 4;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		otherPosition = clockCenterToPosition[otherCenter];
		otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face == oppositeFace[face] &&
				w->rubik.cubeLoc[face][otherPosition].face == oppositeFace[face]) {
			return center;
		}
	}
	for (i = 0; i < 4; i++) {
		center = (bestOrder[i]) % 4;
		otherCenter = (bestOrder[i] + 3 + offset) % 4;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		otherPosition = clockCenterToPosition[otherCenter];
		otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
		if ((w->rubik.cubeLoc[face][position].face == oppositeFace[face] ||
				w->rubik.cubeLoc[face][otherPosition].face == oppositeFace[face]) &&
				w->rubik.cubeLoc[face][position].face != face &&
				w->rubik.cubeLoc[face][otherPosition].face != face) {
			return center;
		}
	}
	for (i = 0; i < 4; i++) {
		center = (bestOrder[i]) % 4;
		otherCenter = (bestOrder[i] + 3) % 4;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		otherPosition = clockCenterToPosition[otherCenter];
		otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face != face &&
				w->rubik.cubeLoc[face][otherPosition].face != face) {
			return center;
		}
	}
	return -1;
}

static int
findMoveSpotsForOpp(RubikWidget w, int currentFace, int offset)
{
	int i, center, position;
	int otherCenter, otherPosition;

	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		otherCenter = (bestOrder[i] + 3 + offset) % 4;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		otherPosition = clockCenterToPosition[otherCenter];
		otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
		if (w->rubik.cubeLoc[currentFace][position].face == oppositeFace[currentFace] &&
				w->rubik.cubeLoc[currentFace][otherPosition].face == oppositeFace[currentFace]) {
			return center;
		}
	}
	for (i = 0; i < 4; i++) {
		center = bestOrder[i];
		otherCenter = (bestOrder[i] + 3 + offset) % 4;
		position = clockCenterToPosition[center];
		position = offsetRevenge(w, position); /* non revenge */
		otherPosition = clockCenterToPosition[otherCenter];
		otherPosition = offsetRevenge(w, otherPosition); /* non revenge */
		if ((w->rubik.cubeLoc[currentFace][position].face == oppositeFace[currentFace] ||
				w->rubik.cubeLoc[currentFace][otherPosition].face == oppositeFace[currentFace]) &&
				w->rubik.cubeLoc[currentFace][position].face != currentFace &&
				w->rubik.cubeLoc[currentFace][otherPosition].face != currentFace) {
			return center;
		}
	}
	return -1;
}

static void
adj3Move(RubikWidget w, int fromFace, int toFace)
{
	int quad = w->rubik.sizeX + 1;

	if (fromFace == FRONT_FACE && toFace == LEFT_FACE) {
		rotateLeftDown(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
	} else if (fromFace == LEFT_FACE && toFace == TOP_FACE) {
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		rotateTopLeft(w);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
	} else if (fromFace == TOP_FACE && toFace == RIGHT_FACE) {
		rotateRightDown(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		rotateRightUp(w);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
	} else if (fromFace == RIGHT_FACE && toFace == BACK_FACE) {
		rotateBackCcw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		rotateBackCw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
	} else if (fromFace == BACK_FACE && toFace == BOTTOM_FACE) {
		rotateBottomLeft(w);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateBottomRight(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
	} else if (fromFace == BOTTOM_FACE && toFace == FRONT_FACE) {
		rotateFrontCcw(w);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
	} else if (fromFace == FRONT_FACE && toFace == TOP_FACE) {
		rotateTopRight(w);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
		rotateTopLeft(w);
		movePuzzlePiece(w, TOP_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
	} else if (fromFace == TOP_FACE && toFace == BACK_FACE) {
		rotateBackCcw(w);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		rotateBackCw(w);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
	} else if (fromFace == BACK_FACE && toFace == LEFT_FACE) {
		rotateLeftDown(w);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		rotateLeftUp(w);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
	} else if (fromFace == LEFT_FACE && toFace == BOTTOM_FACE) {
		rotateBottomRight(w);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		rotateBottomLeft(w);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
	} else if (fromFace == BOTTOM_FACE && toFace == RIGHT_FACE) {
		rotateRightDown(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		rotateRightUp(w);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
	} else if (fromFace == RIGHT_FACE && toFace == FRONT_FACE) {
		rotateFrontCw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		rotateFrontCcw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
	} else {
		(void) printf("adj3Move Unknown %d %d\n", fromFace, toFace);
	}
}

static void
adj3MoveRev(RubikWidget w, int fromFace, int toFace)
{
	int quad = w->rubik.sizeX + 1;

	if (fromFace == FRONT_FACE && toFace == LEFT_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		rotateLeftDown(w);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateLeftUp(w);
	} else if (fromFace == LEFT_FACE && toFace == TOP_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		rotateTopRight(w);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		rotateTopLeft(w);
	} else if (fromFace == TOP_FACE && toFace == RIGHT_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		rotateRightDown(w);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateRightUp(w);
	} else if (fromFace == RIGHT_FACE && toFace == BACK_FACE) {
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		rotateBackCcw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		rotateBackCw(w);
	} else if (fromFace == BACK_FACE && toFace == BOTTOM_FACE) {
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateBottomLeft(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		rotateBottomRight(w);
	} else if (fromFace == BOTTOM_FACE && toFace == FRONT_FACE) {
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateFrontCcw(w);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		rotateFrontCw(w);
	} else if (fromFace == FRONT_FACE && toFace == TOP_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
		rotateTopRight(w);
		movePuzzlePiece(w, TOP_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		rotateTopLeft(w);
	} else if (fromFace == TOP_FACE && toFace == BACK_FACE) {
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		rotateBackCcw(w);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, TOP_FACE, quad, RIGHT, FALSE);
		rotateBackCw(w);
	} else if (fromFace == BACK_FACE && toFace == LEFT_FACE) {
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		rotateLeftDown(w);
		movePuzzlePiece(w, BACK_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, BACK_FACE, quad, TOP, FALSE);
		rotateLeftUp(w);
	} else if (fromFace == LEFT_FACE && toFace == BOTTOM_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		rotateBottomRight(w);
		movePuzzlePiece(w, LEFT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, LEFT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, LEFT, FALSE);
		rotateBottomLeft(w);
	} else if (fromFace == BOTTOM_FACE && toFace == RIGHT_FACE) {
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		rotateRightDown(w);
		movePuzzlePiece(w, BOTTOM_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, BOTTOM_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, FRONT_FACE, quad, TOP, FALSE);
		rotateRightUp(w);
	} else if (fromFace == RIGHT_FACE && toFace == FRONT_FACE) {
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		rotateFrontCw(w);
		movePuzzlePiece(w, RIGHT_FACE, quad, RIGHT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, BOTTOM, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, LEFT, FALSE);
		movePuzzlePiece(w, RIGHT_FACE, quad, TOP, FALSE);
		rotateFrontCcw(w);
	} else {
		(void) printf("adj3MoveRev Unknown %d %d\n", fromFace, toFace);
	}
}

static void
checkForOppMove(RubikWidget w, int fromFace, int toFace, int offset)
{
	int i;
	for (i = 0; i < 2; i++) {
		int foundMoveSpot = findMoveSpotsForOpp(w, fromFace, offset);
		int foundEmptySpot = bestEmptySpotsForOpp(w, toFace, offset);
		if (foundMoveSpot == -1) {
			break;
		}
		if (foundEmptySpot == -1) {
			return;
		}
		rotateFace(w, toFace, -foundEmptySpot);
		rotateFace(w, fromFace, -foundMoveSpot);
		opp2PairMove(w, fromFace, toFace);
		rotateFace(w, fromFace, foundMoveSpot);
		rotateFace(w, toFace, foundEmptySpot);
	}
	for (i = 0; i < 2; i++) {
		int foundMoveSpot = findMoveSpotsForOpp(w, toFace, offset);
		int foundEmptySpot = bestEmptySpotsForOpp(w, fromFace, offset);
		if (foundMoveSpot == -1 || foundEmptySpot == -1) {
			return;
		}
		rotateFace(w, fromFace, -foundEmptySpot);
		rotateFace(w, toFace, -foundMoveSpot);
		opp2PairMove(w, fromFace, toFace);
		rotateFace(w, toFace, foundMoveSpot);
		rotateFace(w, fromFace, foundEmptySpot);
	}
}

static void
checkForAdjMove(RubikWidget w, int fromFace, int toFace, int offset)
{
	int i;
	for (i = 0; i < 4; i++) {
		int foundMoveSpot = findMoveSpot(w, fromFace, toFace);
		int foundEmptySpot = bestEmptySpot(w, toFace, fromFace, offset);
		int movePosition, emptyPosition;
		if (foundMoveSpot == -1) {
			break;
		}
		if (foundEmptySpot == -1) {
			return;
		}
		movePosition = clockCenterToPosition[foundMoveSpot];
		emptyPosition = clockCenterToPosition[(foundEmptySpot + offset) % MAX_ORIENT];
		movePosition = offsetRevenge(w, movePosition); /* non revenge */
		emptyPosition = offsetRevenge(w, emptyPosition); /* non revenge */
		if (w->rubik.cubeLoc[fromFace][movePosition].face ==
				w->rubik.cubeLoc[toFace][emptyPosition].face) {
			/* do not make move of same color */
			break;
		}
		rotateFace(w, toFace, -foundEmptySpot);
		rotateFace(w, fromFace, -foundMoveSpot);
		adj3Move(w, fromFace, toFace);
		rotateFace(w, fromFace, foundMoveSpot);
		rotateFace(w, toFace, foundEmptySpot);
	}
	for (i = 0; i < 4; i++) {
		int foundMoveSpot = findMoveSpotRev(w, fromFace, toFace);
		int foundEmptySpot = bestEmptySpotRev(w, toFace, fromFace, offset);
		int movePosition, emptyPosition;
		if (foundMoveSpot == -1 || foundEmptySpot == -1) {
			return;
		}
		movePosition = clockCenterToPosition[foundMoveSpot];
		emptyPosition = clockCenterToPosition[(foundEmptySpot + offset) % MAX_ORIENT];
		movePosition = offsetRevenge(w, movePosition); /* non revenge */
		emptyPosition = offsetRevenge(w, emptyPosition); /* non revenge */
		if (w->rubik.cubeLoc[fromFace][movePosition].face ==
				w->rubik.cubeLoc[toFace][emptyPosition].face) {
			/* do not make move of same color */
			return;
		}
		rotateFace(w, toFace, -foundEmptySpot);
		rotateFace(w, fromFace, -foundMoveSpot);
		adj3Move(w, fromFace, toFace);
		rotateFace(w, fromFace, foundMoveSpot);
		rotateFace(w, toFace, foundEmptySpot);
	}
}

static void
mixUp(RubikWidget w, int face, int clockEdge)
{
	int position = clockCenterToPosition[clockEdge];
	position = offsetRevenge(w, position); /* non revenge */

	movePuzzlePiece(w, face, position, LEFT, FALSE);
	movePuzzlePiece(w, face, position, BOTTOM, FALSE);
	movePuzzlePiece(w, face, position, RIGHT, FALSE);
	movePuzzlePiece(w, face, position, TOP, FALSE);
}

static RubikFE
centersSolvedOnFace(RubikWidget w, int face)
{
	RubikFE rubikFE;
	int i;

	int color = w->rubik.cubeLoc[face][0].face;
	for (i = 0; i < MAX_ORIENT; i++) {
		int position = clockCenterToPosition[i];
		position = offsetRevenge(w, position); /* non revenge */
		if (w->rubik.cubeLoc[face][position].face != color) {
			rubikFE.face = face;
			rubikFE.edge = i;
			return rubikFE;
		}
	}
	rubikFE.face = -1;
	rubikFE.edge = -1;
	return rubikFE;
}


static RubikFE
centersSolved(RubikWidget w)
{
	int face;
	RubikFE rubikFE;
	rubikFE.face = -1;
	rubikFE.edge = -1;

	for (face = 0; face < MAX_FACES; face++) {
		rubikFE = centersSolvedOnFace(w, face);
		if (rubikFE.face != -1) {
			return rubikFE;
		}
	}
	return rubikFE;
}


static void
solveAdj(RubikWidget w)
{
	int i;
	for (i = 0; i < 2; i++) {
		checkForAdjMove(w, FRONT_FACE, LEFT_FACE, 0);
		checkForAdjMove(w, LEFT_FACE, TOP_FACE, 1);
		checkForAdjMove(w, TOP_FACE, RIGHT_FACE, 1);
		checkForAdjMove(w, RIGHT_FACE, BACK_FACE, 2);
		checkForAdjMove(w, BACK_FACE, BOTTOM_FACE, 0);
		checkForAdjMove(w, BOTTOM_FACE, FRONT_FACE, 0);

		checkForAdjMove(w, FRONT_FACE, TOP_FACE, 0);
		checkForAdjMove(w, TOP_FACE, BACK_FACE, 0);
		checkForAdjMove(w, BACK_FACE, LEFT_FACE, 2);
		checkForAdjMove(w, LEFT_FACE, BOTTOM_FACE, 3);
		checkForAdjMove(w, BOTTOM_FACE, RIGHT_FACE, 3);
		checkForAdjMove(w, RIGHT_FACE, FRONT_FACE, 0);
	}
}

static void
solveCenter4(RubikWidget w)
{
	RubikFE rubikFE;

	checkForOppMove(w, TOP_FACE, BOTTOM_FACE, 0);
	checkForOppMove(w, FRONT_FACE, BACK_FACE, 0);
	checkForOppMove(w, LEFT_FACE, RIGHT_FACE, 2);
	solveAdj(w);
	/* rare case of opposites unsolved */
	rubikFE = centersSolved(w);
	if (rubikFE.face >= 0) {
		/* trying again */
		mixUp(w, rubikFE.face, rubikFE.edge);
		rubikFE.face = oppositeFace[rubikFE.face];
		rubikFE = centersSolvedOnFace(w, rubikFE.face);
		mixUp(w, rubikFE.face, rubikFE.edge);
		solveAdj(w);
	}
}

static void
orientCentersFace(RubikWidget w, int face, int adjFace, int faceRotation)
{
	int i, clockCenter, orient;
	int faceColor = w->rubik.cubeLoc[face][0].face;
	/*int faceRotation = w->rubik.cubeLoc[face][0].rotation;
	faceRotation = 0;*/
	/* for each piece out of place */
	for (i = 0; i < 4; i++) {
		clockCenter = clockCenterToPosition[i];
		orient = mapFaceOrientRev(w, face, clockCenter);
		if (orient != 0) {
			if (i != 0) {
				rotateFace(w, face, MAX_ORIENT - i);
			}
			/* rotate, take out using adj3MoveRev */
			adj3MoveRev(w, face, adjFace);
			/* then rotate put back and undo with adj3Move */
			rotateFace(w, face, orient);
			adj3Move(w, face, adjFace);
			rotateFace(w, face, MAX_ORIENT - orient);
			clockCenter = clockCenterToPosition[3];
			orient = mapFaceOrientRev(w, adjFace, clockCenter);
			rotateFace(w, adjFace, 1);
			rotateFace(w, face, orient + 1 + i + faceRotation);
			adj3Move(w, face, adjFace);
			rotateFace(w, face, MAX_ORIENT - orient - 1 - i - faceRotation);
			rotateFace(w, adjFace, 3);

			clockCenter = clockCenterToPosition[2];
			if (faceColor != w->rubik.cubeLoc[adjFace][clockCenter].face) {
				if (i != 0) {
					rotateFace(w, face, i);
				}
				continue;
			}
			orient = mapFaceOrientRev(w, adjFace, clockCenter);
			rotateFace(w, adjFace, 2);
			rotateFace(w, face, orient + 2 + i + faceRotation);
			adj3Move(w, face, adjFace);
			rotateFace(w, face, MAX_ORIENT - orient - 2 - i - faceRotation);
			rotateFace(w, adjFace, 2);
			clockCenter = clockCenterToPosition[1];
			if (faceColor != w->rubik.cubeLoc[adjFace][clockCenter].face) {
				if (i != 0) {
					rotateFace(w, face, i);
				}
				continue;
			}
			orient = mapFaceOrientRev(w, adjFace, clockCenter);
			rotateFace(w, adjFace, 3);
			rotateFace(w, face, orient + 3 + i + faceRotation);
			adj3Move(w, face, adjFace);
			rotateFace(w, face, MAX_ORIENT - orient - 3 - i - faceRotation);
			rotateFace(w, adjFace, 1);
			if (i != 0) {
				rotateFace(w, face, i);
			}
		}
	}
}

static void
orientLastCenterFace(RubikWidget w) {
	/* 12 possibilities
	  1 solved, 8 possibilities with 3 cubies are wrong,
	  3 possibilities with all 4 cubies are wrong
	  last 3 are solved by moving 3 cubies twice. */
	int clockCenter0 = clockCenterToPosition[0];
	int clockCenter1 = clockCenterToPosition[1];
	int rotation0 = w->rubik.cubeLoc[2][clockCenter0].rotation;
	int rotation1 = w->rubik.cubeLoc[2][clockCenter1].rotation;
	int quad2 = w->rubik.sizeX * w->rubik.sizeX - w->rubik.sizeX - 1;

	if (rotation0 == 0 && rotation1 == 2) { /* 3 off 0211 */
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3MoveRev(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCcw(w);
	} else if (rotation0 == 0 && rotation1 == 3) { /* 3 off 0332 */
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3Move(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCcw(w);
	} else if (rotation0 == 1 && rotation1 == 0) { /* 3 off 1021 */
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3MoveRev(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
	} else if (rotation0 == 1 && rotation1 == 1) { /* 3 off 1102 */
		rotateFrontCcw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3MoveRev(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCw(w);
	} else if (rotation0 == 2 && rotation1 == 0) { /* 3 off 2033 */
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3Move(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
	} else if (rotation0 == 2 && rotation1 == 1) { /* 3 off 2110 */
		rotateFrontCw(w);
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3MoveRev(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCcw(w);
		rotateFrontCcw(w);
	} else if (rotation0 == 3 && rotation1 == 2) { /* 3 off 3203 */
		rotateFrontCcw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3Move(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCw(w);
	} else if (rotation0 == 3 && rotation1 == 3) { /* 3 off 3320 */
		rotateFrontCw(w);
		rotateFrontCw(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3Move(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		rotateFrontCcw(w);
		rotateFrontCcw(w);
	} else if ((rotation0 == 1 && rotation1 == 3) /* 4 off 1331 */
			|| (rotation0 == 2 && rotation1 == 2) /* 4 off 2222 */
			|| (rotation0 == 3 && rotation1 == 1)) { /* 4 off 3113 */
		movePuzzlePiece(w, FRONT_FACE, quad2, LEFT, FALSE);
		rotateLeftDown(w);
		adj3Move(w, FRONT_FACE, LEFT_FACE);
		rotateLeftUp(w);
		movePuzzlePiece(w, FRONT_FACE, quad2, RIGHT, FALSE);
		orientLastCenterFace(w);
	} else if ((rotation0 == 0 && rotation1 == 1)
			|| (rotation0 == 1 && rotation1 == 2)
			|| (rotation0 == 2 && rotation1 == 3)
			|| (rotation0 == 3 && rotation1 == 0)) {
		(void) printf("unknown case %d %d\n", rotation0, rotation1);
	}
}

static void
orientCenters(RubikWidget w)
{
	int face;
	for (face = 0; face < MAX_FACES; face++) {
		rotateRight(w);
		if (face % 2 == 1)
			rotateCw(w);
		else
			rotateCcw(w);
		if (face < 5)
			orientCentersFace(w, FRONT_FACE, LEFT_FACE, offsetCenters[face]);
	}
	orientLastCenterFace(w);
}

void
printSomePieces(RubikWidget w)
{
}

/* This procedure coordinates the solution process. */
void
solveSomePieces(RubikWidget w)
{
	setPuzzle(w, ACTION_RESET);
	if (solvingFlag)
		return;
#ifdef JMP
	if (!setjmp(solve_env))
#endif
	{
		solvingFlag = True;
		if (!checkSolved(w, w->rubik.orient)) {
			solveTop(w);
			alignCorners(w);
			solveBottom(w);
			if ((w->rubik.sizeX & 1) == 1) {
				solveBottomEdges(w);
				alignEdges(w);
				if (w->rubik.orient)
					solveCenters(w);
			} else {
				solve_2_2(w);
				if (w->rubik.sizeX > 2) {
					/* 4x4x4 */
					rotateDown(w);
					solve42U(w);
					solve2PairEdge4(w);
					solveLast8Edges(w);
					solveCenter4(w);
					if (w->rubik.orient)
						orientCenters(w);
				}
			}
		}
	}
#ifdef JMP
	else {
		drawAllPieces(w);
	}
	abortSolvingFlag = False;
#endif
	solvingFlag = False;
	w->rubik.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_CHEAT);
	setPuzzle(w, ACTION_COMPUTED);
}
