/*
 * cu2.cpp v 20051216 - Fri 2005 Dec 16
 * 2x2x2 Cube Solver class (c) 2005 by Eric Dietz
 * notes: readme.txt  email: root@wrongway.org
 */

// includes
#include <stdlib.h>
#include <stdio.h>
#include <string>
// namespace
using namespace std;

// local includes
#include "cu2.h"

// defines

// cu2 class definition

// constructor
cu2::cu2()
{
  // values for shorten:
  // SHORTEN_NONE, SHORTEN_STRIP_SOME, SHORTEN_STRIP_ALL,
  // SHORTEN_STRIP_ROTATE_SOME, SHORTEN_STRIP_ROTATE_ALL
  // cube rotations means trying to solve the cube with every possible different
  // color on top and on front.  This will greatly affect the speed of solvecube().
  shorten = SHORTEN_STRIP_ROTATE_ALL;
  erval = 0;
  solution = "";
  resetcube();
  numcubes++;
}
// destructor
cu2::~cu2()
{
  numcubes--;
}
// version string
const char* cu2::version = "20051216";
// number of instantiated cubes
int cu2::numcubes = 0;
// overloaded == comparison
const bool cu2::operator==(const cu2 &q)
{
  for (int i = 1; i <= N; ++i)
    for (int j = 1; j <= N; ++j)
      if
      (q.cube[i][N+1][j] != cube[i][N+1][j] ||
       q.cube[0][i][j]   != cube[0][i][j]   ||
       q.cube[i][j][0]   != cube[i][j][0]   ||
       q.cube[N+1][i][j] != cube[N+1][i][j] ||
       q.cube[i][j][N+1] != cube[i][j][N+1] ||
       q.cube[i][0][j]   != cube[i][0][j]  )
        return false;
  return true;
}
// overloaded != comparison
const bool cu2::operator!=(const cu2 &q)
{
  return !operator==(q);
}
// pointer to cube array
int *cu2::face(int x, int y, int z)
{
  if (x < 0 || x > N+1 || y < 0 || y > N+1 || z < 0 || z > N+1)
    return NULL;
  return &cube[x][y][z];
}
// display cube diagram
const void cu2::renderscreen()
{
  /*
  for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= N; j++) printf("  ");
    printf(" ");
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[j][N+1][N+1-i]);
    printf("\n");
  }
  for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[0][N+1-i][N+1-j]);
    printf(" ");
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[j][N+1-i][0]);
    printf(" ");
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[N+1][N+1-i][j]);
    printf(" ");
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[N+1-j][N+1-i][N+1]);
    printf("\n");
  }
  for (int i = 1; i <= N; i++) {
    for (int j = 1; j <= N; j++) printf("  ");
    printf(" ");
    for (int j = 1; j <= N; j++)
      printf(" %i", cube[j][0][i]);
    printf("\n");
  }
  */
  printf(
"\
      %i %i\n\
      %i %i\n\
 %i %i  %i %i  %i %i  %i %i\n\
 %i %i  %i %i  %i %i  %i %i\n\
      %i %i\n\
      %i %i\n\
",
cube[1][3][2], cube[2][3][2],
cube[1][3][1], cube[2][3][1],
cube[0][2][2], cube[0][2][1],
cube[1][2][0], cube[2][2][0],
cube[3][2][1], cube[3][2][2],
cube[2][2][3], cube[1][2][3],
cube[0][1][2], cube[0][1][1],
cube[1][1][0], cube[2][1][0],
cube[3][1][1], cube[3][1][2],
cube[2][1][3], cube[1][1][3],
cube[1][0][1], cube[2][0][1],
cube[1][0][2], cube[2][0][2]
  );
}
// return solvedness of cube
const bool cu2::issolved()
{
  int c[7], d;
  c[1] = cube[1][N+1][1]; c[2] = cube[0][1][1];
  c[3] = cube[1][1][0];   c[4] = cube[N+1][1][1];
  c[5] = cube[1][1][N+1]; c[6] = cube[1][0][1];
  for (int i = 1; i <= 6; i++) {
    d = 0;
    for (int j = 1; j <= 6; j++)
      if (c[i] == j)
        d++;
    if (d != 1)
      return false;
  }
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= N; j++)
      if
      (cube[i][N+1][j] != c[1] ||
       cube[0][i][j]   != c[2] ||
       cube[i][j][0]   != c[3] ||
       cube[N+1][i][j] != c[4] ||
       cube[i][j][N+1] != c[5] ||
       cube[i][0][j]   != c[6])
        return false;
  return true;
}
// reset the cube
const void cu2::resetcube()
{
  solution = "";
  for (int i = 0; i <= MOV; i++)
    mov[i] = 0;
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        cube[i][j][k] = 0;
  for (int i = 1; i <= N; i++)
    for (int j = 1; j <= N; j++)
    {
      cube[i][N+1][j] = 1;
      cube[0][i][j]   = 2;
      cube[i][j][0]   = 3;
      cube[N+1][i][j] = 4;
      cube[i][j][N+1] = 5;
      cube[i][0][j]   = 6;
    }
  erval = 0;
  fx = 0; fy = 0; fz = 0;
  inited = true;
}
// temporary copier function
void cu2::copytemp(int c[N+2][N+2][N+2], int t[N+2][N+2][N+2])
{
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        t[i][j][k] = c[i][j][k];
}
// rotate given slice left
const void cu2::slice_l(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[i][0][j] = temp[N+1-j][0][i];
    else if (s == N)
      for (int j = 1; j <= N; j++)
        cube[i][N+1][j] = temp[N+1-j][N+1][i];
    cube[i][s][0]   = temp[N+1][s][i];
    cube[i][s][N+1] = temp[0][s][i];
    cube[0][s][i]   = temp[N+1-i][s][0];
    cube[N+1][s][i] = temp[N+1-i][s][N+1];
  }
}
// rotate given slice right
const void cu2::slice_r(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[i][0][j] = temp[j][0][N+1-i];
    else if (s == N)
      for (int j = 1; j <= N; j++)
        cube[i][N+1][j] = temp[j][N+1][N+1-i];
    cube[i][s][0]   = temp[0][s][N+1-i];
    cube[i][s][N+1] = temp[N+1][s][N+1-i];
    cube[0][s][i]   = temp[i][s][N+1];
    cube[N+1][s][i] = temp[i][s][0];
  }
}
// rotate given slice up
const void cu2::slice_u(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[0][i][j] = temp[0][j][N+1-i];
    if (s == N)
      for (int j = 1; j <= N; j++)
        cube[N+1][i][j] = temp[N+1][j][N+1-i];
    cube[s][i][0]   = temp[s][0][N+1-i];
    cube[s][i][N+1] = temp[s][N+1][N+1-i];
    cube[s][0][i]   = temp[s][i][N+1];
    cube[s][N+1][i] = temp[s][i][0];
  }
}
// rotate given slice down
const void cu2::slice_d(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[0][i][j] = temp[0][N+1-j][i];
    if (s == N)
      for (int j = 1; j <= N; j++)
        cube[N+1][i][j] = temp[N+1][N+1-j][i];
    cube[s][i][0]   = temp[s][N+1][i];
    cube[s][i][N+1] = temp[s][0][i];
    cube[s][0][i]   = temp[s][N+1-i][0];
    cube[s][N+1][i] = temp[s][N+1-i][N+1];
  }
}
// rotate given slice clockwise
const void cu2::slice_c(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[i][j][0] = temp[N+1-j][i][0];
    if (s == N)
      for (int j = 1; j <= N; j++)
        cube[i][j][N+1] = temp[N+1-j][i][N+1];
    cube[i][0][s]   = temp[N+1][i][s];
    cube[i][N+1][s] = temp[0][i][s];
    cube[0][i][s]   = temp[N+1-i][0][s];
    cube[N+1][i][s] = temp[N+1-i][N+1][s];
  }
}
// rotate given slice counterclockwise
const void cu2::slice_a(int s)
{
  if (s < 1 || s > N) return;
  int temp[N+2][N+2][N+2];
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        temp[i][j][k] = cube[i][j][k];
  //copytemp(cube, temp);
  for (int i = 1; i <= N; i++)
  {
    if (s == 1)
      for (int j = 1; j <= N; j++)
        cube[i][j][0] = temp[j][N+1-i][0];
    if (s == N)
      for (int j = 1; j <= N; j++)
        cube[i][j][N+1] = temp[j][N+1-i][N+1];
    cube[i][0][s]   = temp[0][N+1-i][s];
    cube[i][N+1][s] = temp[N+1][N+1-i][s];
    cube[0][i][s]   = temp[i][N+1][s];
    cube[N+1][i][s] = temp[i][0][s];
  }
}
// cube rotator aliases
const void cu2::UL() { slice_l(N  ); }  // rotate top slice left
const void cu2::UR() { slice_r(N  ); }  // rotate top slice right
const void cu2::DL() { slice_l(1  ); }  // rotate bottom slice left
const void cu2::DR() { slice_r(1  ); }  // rotate bottom slice right
const void cu2::LU() { slice_u(1  ); }  // rotate left slice up
const void cu2::LD() { slice_d(1  ); }  // rotate left slice down
const void cu2::RU() { slice_u(N  ); }  // rotate right slice up
const void cu2::RD() { slice_d(N  ); }  // rotate right slice down
const void cu2::FC() { slice_c(1  ); }  // rotate front slice clockwise
const void cu2::FA() { slice_a(1  ); }  // rotate front slice counterclockwise
const void cu2::BC() { slice_c(N  ); }  // rotate rear slice clockwise (from front view)
const void cu2::BA() { slice_a(N  ); }  // rotate rear slice counterclockwise (from front view)
const void cu2::CL() { for (int i = 1; i <= N; i++) slice_l(i); } // rotate whole cube left
const void cu2::CR() { for (int i = 1; i <= N; i++) slice_r(i); } // rotate whole cube right
const void cu2::CU() { for (int i = 1; i <= N; i++) slice_u(i); } // rotate whole cube up
const void cu2::CD() { for (int i = 1; i <= N; i++) slice_d(i); } // rotate whole cube down
const void cu2::CC() { for (int i = 1; i <= N; i++) slice_c(i); } // rotate whole cube clockwise
const void cu2::CA() { for (int i = 1; i <= N; i++) slice_a(i); } // rotate whole cube counterclockwise
const void cu2::U2() { UL(); UL(); }    // rotate top slice twice
const void cu2::D2() { DR(); DR(); }    // rotate bottom slice twice
const void cu2::L2() { LD(); LD(); }    // rotate left slice twice
const void cu2::R2() { RU(); RU(); }    // rotate right slice twice
const void cu2::F2() { FC(); FC(); }    // rotate front slice twice
const void cu2::B2() { BA(); BA(); }    // rotate rear slice twice
// end of cube rotator aliases
// scramble the cube
const void cu2::scramblecube()
{
  int axis_direction, slice;
  // come up with a better calculation for a good number of random moves based on cube size later
  int nummoves = (N-2)*25+10;
  nummoves += rand() % nummoves;
  resetcube();
  for (int i = 1; i <= nummoves; i++)
  {
    axis_direction = rand() % 6 + 1;  // which axis and direction
    slice = rand() % N + 1;           // which slice
    switch (axis_direction)
    {
      case 1: slice_l(slice); break;
      case 2: slice_r(slice); break;
      case 3: slice_u(slice); break;
      case 4: slice_d(slice); break;
      case 5: slice_c(slice); break;
      case 6: slice_a(slice); break;
    }
  }
}
// do a series of moves
const void cu2::domoves(string s)
{
  string a = "";
  for (int i = 1; i <= (int)(s.length() / 3); i++) {
    a = s.substr(i * 3 - 3, 2);
    if      (a == "UL") UL();
    else if (a == "UR") UR();
    else if (a == "DL") DL();
    else if (a == "DR") DR();
    else if (a == "LU") LU();
    else if (a == "LD") LD();
    else if (a == "RU") RU();
    else if (a == "RD") RD();
    else if (a == "FC") FC();
    else if (a == "FA") FA();
    else if (a == "BC") BC();
    else if (a == "BA") BA();
    else if (a == "CL") CL();
    else if (a == "CR") CR();
    else if (a == "CU") CU();
    else if (a == "CD") CD();
    else if (a == "CC") CC();
    else if (a == "CA") CA();
    else if (a == "U2") U2();
    else if (a == "D2") D2();
    else if (a == "L2") L2();
    else if (a == "R2") R2();
    else if (a == "F2") F2();
    else if (a == "B2") B2();
  }
}
// execute solution
const void cu2::dosolution()
{
  domoves(solution);
}
// find given corner in clockwise order
const int cu2::findcorner_c(int a, int b, int c)
{
  // for speed I just broke this into a series of if's instead of for's
  if      (cube[1][3][1] == a && cube[1][2][0] == b && cube[0][2][1] == c) // top left front
  { fx = 1; fy = 3; fz = 1; return POSY; }
  else if (cube[1][2][0] == a && cube[0][2][1] == b && cube[1][3][1] == c)
  { fx = 1; fy = 2; fz = 0; return NEGZ; }
  else if (cube[0][2][1] == a && cube[1][3][1] == b && cube[1][2][0] == c)
  { fx = 0; fy = 2; fz = 1; return NEGX; }
  else if (cube[2][3][1] == a && cube[3][2][1] == b && cube[2][2][0] == c) // top right front
  { fx = 2; fy = 3; fz = 1; return POSY; }
  else if (cube[3][2][1] == a && cube[2][2][0] == b && cube[2][3][1] == c)
  { fx = 3; fy = 2; fz = 1; return POSX; }
  else if (cube[2][2][0] == a && cube[2][3][1] == b && cube[3][2][1] == c)
  { fx = 2; fy = 2; fz = 0; return NEGZ; }
  else if (cube[1][3][2] == a && cube[0][2][2] == b && cube[1][2][3] == c) // top left back
  { fx = 1; fy = 3; fz = 2; return POSY; }
  else if (cube[0][2][2] == a && cube[1][2][3] == b && cube[1][3][2] == c)
  { fx = 0; fy = 2; fz = 2; return NEGX; }
  else if (cube[1][2][3] == a && cube[1][3][2] == b && cube[0][2][2] == c)
  { fx = 1; fy = 2; fz = 3; return POSZ; }
  else if (cube[2][3][2] == a && cube[2][2][3] == b && cube[3][2][2] == c) // top right back
  { fx = 2; fy = 3; fz = 2; return POSY; }
  else if (cube[2][2][3] == a && cube[3][2][2] == b && cube[2][3][2] == c)
  { fx = 2; fy = 2; fz = 3; return POSZ; }
  else if (cube[3][2][2] == a && cube[2][3][2] == b && cube[2][2][3] == c)
  { fx = 3; fy = 2; fz = 2; return POSX; }
  else if (cube[1][0][1] == a && cube[0][1][1] == b && cube[1][1][0] == c) // bottom left front
  { fx = 1; fy = 0; fz = 1; return NEGY; }
  else if (cube[0][1][1] == a && cube[1][1][0] == b && cube[1][0][1] == c)
  { fx = 0; fy = 1; fz = 1; return NEGX; }
  else if (cube[1][1][0] == a && cube[1][0][1] == b && cube[0][1][1] == c)
  { fx = 1; fy = 1; fz = 0; return NEGZ; }
  else if (cube[2][0][1] == a && cube[2][1][0] == b && cube[3][1][1] == c) // bottom right front
  { fx = 2; fy = 0; fz = 1; return NEGY; }
  else if (cube[2][1][0] == a && cube[3][1][1] == b && cube[2][0][1] == c)
  { fx = 2; fy = 1; fz = 0; return NEGZ; }
  else if (cube[3][1][1] == a && cube[2][0][1] == b && cube[2][1][0] == c)
  { fx = 3; fy = 1; fz = 1; return POSX; }
  else if (cube[1][0][2] == a && cube[1][1][3] == b && cube[0][1][2] == c) // bottom left back
  { fx = 1; fy = 0; fz = 2; return NEGY; }
  else if (cube[1][1][3] == a && cube[0][1][2] == b && cube[1][0][2] == c)
  { fx = 1; fy = 1; fz = 3; return POSZ; }
  else if (cube[0][1][2] == a && cube[1][0][2] == b && cube[1][1][3] == c)
  { fx = 0; fy = 1; fz = 2; return NEGX; }
  else if (cube[2][0][2] == a && cube[3][1][2] == b && cube[2][1][3] == c) // bottom right back
  { fx = 2; fy = 0; fz = 2; return NEGY; }
  else if (cube[3][1][2] == a && cube[2][1][3] == b && cube[2][0][2] == c)
  { fx = 3; fy = 1; fz = 2; return POSX; }
  else if (cube[2][1][3] == a && cube[2][0][2] == b && cube[3][1][2] == c)
  { fx = 2; fy = 1; fz = 3; return POSZ; }
  return 0;
}
// find given corner in counterclockwise order
const int cu2::findcorner_a(int a, int b, int c)
{
  // for speed I just broke this into a series of if's instead of for's
  if      (cube[1][3][1] == a && cube[0][2][1] == b && cube[1][2][0] == c) // top left front
  { fx = 1; fy = 3; fz = 1; return POSY; }
  else if (cube[1][2][0] == a && cube[1][3][1] == b && cube[0][2][1] == c)
  { fx = 1; fy = 2; fz = 0; return NEGZ; }
  else if (cube[0][2][1] == a && cube[1][2][0] == b && cube[1][3][1] == c)
  { fx = 0; fy = 2; fz = 1; return NEGX; }
  else if (cube[2][3][1] == a && cube[2][2][0] == b && cube[3][2][1] == c) // top right front
  { fx = 2; fy = 3; fz = 1; return POSY; }
  else if (cube[3][2][1] == a && cube[2][3][1] == b && cube[2][2][0] == c)
  { fx = 3; fy = 2; fz = 1; return POSX; }
  else if (cube[2][2][0] == a && cube[3][2][1] == b && cube[2][3][1] == c)
  { fx = 2; fy = 2; fz = 0; return NEGZ; }
  else if (cube[1][3][2] == a && cube[1][2][3] == b && cube[0][2][2] == c) // top left back
  { fx = 1; fy = 3; fz = 2; return POSY; }
  else if (cube[0][2][2] == a && cube[1][3][2] == b && cube[1][2][3] == c)
  { fx = 0; fy = 2; fz = 2; return NEGX; }
  else if (cube[1][2][3] == a && cube[0][2][2] == b && cube[1][3][2] == c)
  { fx = 1; fy = 2; fz = 3; return POSZ; }
  else if (cube[2][3][2] == a && cube[3][2][2] == b && cube[2][2][3] == c) // top right back
  { fx = 2; fy = 3; fz = 2; return POSY; }
  else if (cube[2][2][3] == a && cube[2][3][2] == b && cube[3][2][2] == c)
  { fx = 2; fy = 2; fz = 3; return POSZ; }
  else if (cube[3][2][2] == a && cube[2][2][3] == b && cube[2][3][2] == c)
  { fx = 3; fy = 2; fz = 2; return POSX; }
  else if (cube[1][0][1] == a && cube[1][1][0] == b && cube[0][1][1] == c) // bottom left front
  { fx = 1; fy = 0; fz = 1; return NEGY; }
  else if (cube[0][1][1] == a && cube[1][0][1] == b && cube[1][1][0] == c)
  { fx = 0; fy = 1; fz = 1; return NEGX; }
  else if (cube[1][1][0] == a && cube[0][1][1] == b && cube[1][0][1] == c)
  { fx = 1; fy = 1; fz = 0; return NEGZ; }
  else if (cube[2][0][1] == a && cube[3][1][1] == b && cube[2][1][0] == c) // bottom right front
  { fx = 2; fy = 0; fz = 1; return NEGY; }
  else if (cube[2][1][0] == a && cube[2][0][1] == b && cube[3][1][1] == c)
  { fx = 2; fy = 1; fz = 0; return NEGZ; }
  else if (cube[3][1][1] == a && cube[2][1][0] == b && cube[2][0][1] == c)
  { fx = 3; fy = 1; fz = 1; return POSX; }
  else if (cube[1][0][2] == a && cube[0][1][2] == b && cube[1][1][3] == c) // bottom left back
  { fx = 1; fy = 0; fz = 2; return NEGY; }
  else if (cube[1][1][3] == a && cube[1][0][2] == b && cube[0][1][2] == c)
  { fx = 1; fy = 1; fz = 3; return POSZ; }
  else if (cube[0][1][2] == a && cube[1][1][3] == b && cube[1][0][2] == c)
  { fx = 0; fy = 1; fz = 2; return NEGX; }
  else if (cube[2][0][2] == a && cube[2][1][3] == b && cube[3][1][2] == c) // bottom right back
  { fx = 2; fy = 0; fz = 2; return NEGY; }
  else if (cube[3][1][2] == a && cube[2][0][2] == b && cube[2][1][3] == c)
  { fx = 3; fy = 1; fz = 2; return POSX; }
  else if (cube[2][1][3] == a && cube[3][1][2] == b && cube[2][0][2] == c)
  { fx = 2; fy = 1; fz = 3; return POSZ; }
  return 0;
}
// find given corner
const int cu2::findcorner(int a, int b, int c)
{
  int x;
  if ((x = findcorner_c(a, b, c))) return x;
  if ((x = findcorner_a(a, b, c))) return x;
  return 0;
}
// convert standard string to metric
const string cu2::std_to_metr(string s)
{
  string a = s;
  char slice_std, slice_metr='\0', dir, dir_prime='\0';
  for (int i = 1; i <= (int)(s.length() / 3); i++)
  {
    slice_std = s.at(i * 3 - 3);
    dir = s.at(i * 3 - 2);
    if (slice_std == 'C')
      continue;
    if (dir == '2')
    {
      if
      (slice_std == 'U' || slice_std == 'D')
        dir_prime = 'L';
      else if
      (slice_std == 'L' || slice_std == 'R')
        dir_prime = 'U';
      else if
      (slice_std == 'F' || slice_std == 'B')
        dir_prime = 'C';
    }
    if
    (slice_std == 'D' || slice_std == 'L' || slice_std == 'F')
      slice_metr = '1';
    else if
    (slice_std == 'U' || slice_std == 'R' || slice_std == 'B')
      slice_metr = '2';
    a.at(i * 3 - 3) = slice_metr;
    if (dir == '2')
    {
      a.at(i * 3 - 2) = dir_prime;
      a.insert(i * 3, "  .");
      a.at(i * 3) = slice_metr;
      a.at(i * 3 + 1) = dir_prime;
      i++;
    }
  }
  return a;
}
// convert metric string to standard
const string cu2::metr_to_std(string s)
{
  string a = s;
  char slice_std='\0', slice_metr, dir;
  for (int i = 1; i <= (int)(s.length() / 3); i++)
  {
    slice_metr = s.at(i * 3 - 3);
    dir = s.at(i * 3 - 2);
    if (slice_metr == 'C')
      continue;
    switch (dir)
    {
    case 'L':
    case 'R':
      switch (slice_metr)
      {
        case '1': slice_std = 'D'; break;
        case '2': slice_std = 'U'; break;
      }
      break;
    case 'U':
    case 'D':
      switch (slice_metr)
      {
        case '1': slice_std = 'L'; break;
        case '2': slice_std = 'R'; break;
      }
      break;
    case 'C':
    case 'A':
      switch (slice_metr)
      {
        case '1': slice_std = 'F'; break;
        case '2': slice_std = 'B'; break;
      }
      break;
    } 
    a.at(i * 3 - 3) = slice_std;
  }
  return a;
}
// convert standard string to relative
const string cu2::std_to_rel(string s)
{
  string a = s;
  char slice, dir_std, dir_rel='\0';
  for (int i = 1; i <= (int)(s.length() / 3); i++)
  {
    slice = s.at(i * 3 - 3);
    dir_std = s.at(i * 3 - 2);
    if (slice == 'C')
      continue;
    switch (dir_std)
    {
      case 'L': dir_rel = '+'; break;
      case 'R': dir_rel = '-'; break;
      case 'U': dir_rel = '-'; break;
      case 'D': dir_rel = '+'; break;
      case 'C': dir_rel = '+'; break;
      case 'A': dir_rel = '-'; break;
      case '2': dir_rel = '2'; break;
    }
    a.at(i * 3 - 2) = dir_rel;
  }
  return a;
}
// convert relative string to standard
const string cu2::rel_to_std(string s)
{
  string a = s;
  char slice, dir_std='\0', dir_rel;
  for (int i = 1; i <= (int)(s.length() / 3); i++)
  {
    slice = s.at(i * 3 - 3);
    dir_rel = s.at(i * 3 - 2);
    if (slice == 'C')
      continue;
    switch (slice)
    {
    case 'U':
    case 'D':
      switch (dir_rel)
      {
        case '+': dir_std = 'L'; break;
        case '-': dir_std = 'R'; break;
      }
      break;
    case 'L':
    case 'R':
      switch (dir_rel)
      {
        case '+': dir_std = 'D'; break;
        case '-': dir_std = 'U'; break;
      }
      break;
    case 'F':
    case 'B':
      switch (dir_rel)
      {
        case '+': dir_std = 'C'; break;
        case '-': dir_std = 'A'; break;
      }
      break;
    }
    if (dir_rel == '2')
      dir_std = '2';
    a.at(i * 3 - 2) = dir_std;
  }
  return a;
}
// convert metric string to relative
const string cu2::metr_to_rel(string s)
{
  return std_to_rel(metr_to_std(s)); // the lazy way
}
// convert relative string to metric
const string cu2::rel_to_metr(string s)
{
  return std_to_metr(rel_to_std(s)); // the lazy way
}
// allow the use of the half turn
const string cu2::usehalfturns(string s, int b=0)
{
  // note: ALL this routine does is change things like LU.LU. to L2.
  // it does NOT change LU.LU.LU. to LD. nor does it strip out things
  // like L2.L2.  Use concise() for that.
  string a = s;
  char slice, dir, slice_next, dir_next;
  for (int i = 1; i <= (int)(a.length() / 3) - 1; i++)
  {
    slice = a.at(i * 3 - 3);
    dir = a.at(i * 3 - 2);
    slice_next = a.at(i * 3);
    dir_next = a.at(i * 3 + 1);
    if (slice == 'C' || dir == '2') continue;
    if (slice == slice_next && dir == dir_next)
    {
      a.at(i * 3 - 2) = '2';
      a.erase(i * 3, 3);
      // change mov[] if necessary
      if (b) {
        shortenmov(i);
      }
      //
      i--;
    }
  }
  return a;
}
// strip out redundant moves from a solution string
const string cu2::concise(string s, int b=0)
{
  int strips = 0;
  // total revamp, Nov/04
  string a = std_to_metr(s);
  int redundancies;
  // step 1: interpolate whole cube moves, e.g., CL,LU,FC, to FA,RU,
  {
    char slice, dir;
    int sUD, sLR, sFB;
    char dL, dR, dU, dD, dC, dA;
    for (int i = 1; i <= (int)(a.length() / 3); i++)
    {
      slice = a.at(i * 3 - 3);
      if (slice == 'C')
      {
        dir = a.at(i * 3 - 2);
        sUD = 1; sLR = 1; sFB = 1;
        dL = 'L'; dR = 'R'; dU = 'U'; dD = 'D'; dC = 'C'; dA = 'A';
        switch (dir)
        {
        case 'L':
          sFB = -1; dU = 'A'; dD = 'C'; dC = 'U'; dA = 'D'; break;
        case 'R':
          sLR = -1; dU = 'C'; dD = 'A'; dC = 'D'; dA = 'U'; break;
        case 'U':
          sUD = -1; dL = 'C'; dR = 'A'; dC = 'R'; dA = 'L'; break;
        case 'D':
          sFB = -1; dL = 'A'; dR = 'C'; dC = 'L'; dA = 'R'; break;
        case 'C':
          sUD = -1; dL = 'D'; dR = 'U'; dU = 'L'; dD = 'R'; break;
        case 'A':
          sLR = -1; dL = 'U'; dR = 'D'; dU = 'R'; dD = 'L'; break;
        }
        a.erase(i * 3 - 3, 3);
        // change mov[] if necessary
        if (b) {
          strips++; if (strips > b) shortenmov(i);
        }
        //
        for (int j = i; j <= (int)(a.length() / 3); j++)
        {
          slice = a.at(j * 3 - 3);
          dir = a.at(j * 3 - 2);
          switch (dir)
          {
          case 'L':
            if (sUD == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dL; break;
          case 'R':
            if (sUD == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dR; break;
          case 'U':
            if (sLR == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dU; break;
          case 'D':
            if (sLR == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dD; break;
          case 'C':
            if (sFB == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dC; break;
          case 'A':
            if (sFB == -1 && slice != 'C') slice = ((N+1-(slice-'0'))+'0');
            dir = dA; break;
          }
          a.at(j * 3 - 3) = slice;
          a.at(j * 3 - 2) = dir;
        }
        i--;
      }
    }
  }
  // the next steps are nested to make this condensation iterative
  redundancies = 1;
  while (redundancies)
  {
    redundancies = 0;
    // step 2: re-order parallel slice groups, e.g., LU,RU,LD,RU, to LU,LD,RU,RU,
    {
      char slice, dir, slice_next, dir_next, dir_reverse='\0';
      for (int i = 1; i <= (int)(a.length() / 3) - 2; i++)
      {
        slice = a.at(i * 3 - 3);
        dir = a.at(i * 3 - 2);
        slice_next = a.at(i * 3);
        dir_next = a.at(i * 3 + 1);
        if (slice_next == slice) continue;
        switch (dir)
        {
          case 'L': dir_reverse = 'R'; break;
          case 'R': dir_reverse = 'L'; break;
          case 'U': dir_reverse = 'D'; break;
          case 'D': dir_reverse = 'U'; break;
          case 'C': dir_reverse = 'A'; break;
          case 'A': dir_reverse = 'C'; break;
        }
        for
        (int j = i + 2;
         j <= (int)(a.length() / 3) && (dir_next == dir || dir_next == dir_reverse);
         j++ )
        {
          slice_next = a.at(j * 3 - 3);
          dir_next = a.at(j * 3 - 2);
          if
          (slice_next == slice && (dir_next == dir || dir_next == dir_reverse))
          {
            for (int k = j; k >= i + 2; k--)
            {
              a.at(k * 3 - 3) = a.at(k * 3 - 6);
              a.at(k * 3 - 2) = a.at(k * 3 - 5);
            }
            a.at(i * 3) = slice_next;
            a.at(i * 3 + 1) = dir_next;
            i++;
          }
        }
      }
    }
    // step 3: change 3/4 turns to 1/4 turn, e.g., LU,LU,LU, to LD,
    {
      char slice, dir, slice_next, dir_next, slice_after, dir_after, dir_reverse='\0';
      for (int i = 1; i <= (int)(a.length() / 3) - 2; i++)
      {
        slice = a.at(i * 3 - 3);
        dir = a.at(i * 3 - 2);
        slice_next = a.at(i * 3);
        dir_next = a.at(i * 3 + 1);
        slice_after = a.at(i * 3 + 3);
        dir_after = a.at(i * 3 + 4);
        if
        ((slice == slice_next) && (slice == slice_after) &&
         (dir == dir_next) && (dir == dir_after) )
        {
          switch (dir)
          {
            case 'L': dir_reverse = 'R'; break;
            case 'R': dir_reverse = 'L'; break;
            case 'U': dir_reverse = 'D'; break;
            case 'D': dir_reverse = 'U'; break;
            case 'C': dir_reverse = 'A'; break;
            case 'A': dir_reverse = 'C'; break;
          }
          a.at(i * 3 - 2) = dir_reverse;
          a.erase(i * 3, 6);
          // change mov[] if necessary
          if (b) {
            strips++; if (strips > b) shortenmov(i);
            strips++; if (strips > b) shortenmov(i);
          }
          //
          i -= 2; if (i < 0) i = 0;
          redundancies++;
        }
      }
    }
    // step 4: remove contradicting moves, e.g., LU,LD,
    {
      char slice, dir, slice_next, dir_next, dir_reverse='\0';
      for (int i = 1; i <= (int)(a.length() / 3) - 1; i++)
      {
        slice = a.at(i * 3 - 3);
        dir = a.at(i * 3 - 2);
        slice_next = a.at(i * 3);
        dir_next = a.at(i * 3 + 1);
        switch (dir)
        {
          case 'L': dir_reverse = 'R'; break;
          case 'R': dir_reverse = 'L'; break;
          case 'U': dir_reverse = 'D'; break;
          case 'D': dir_reverse = 'U'; break;
          case 'C': dir_reverse = 'A'; break;
          case 'A': dir_reverse = 'C'; break;
        }
        if (slice == slice_next && dir_next == dir_reverse)
        {
          a.erase(i * 3 - 3, 6);
          // change mov[] if necessary
          if (b) {
            strips++; if (strips > b) shortenmov(i);
            strips++; if (strips > b) shortenmov(i);
          }
          //
          i -= 2; if (i < 0) i = 0;
          redundancies++;
        }
      }
    }
    // now try again until no redundancies are found.
  }
  // something to consider adding to this algorithm:
  // seek out patterns like "UL.uL.dL.DL." and change them to "CL." and start over, or
  // changing "UL.uL.dL." to "CL.DR." and starting over...
  return metr_to_std(a);
}
// shorten mov[]
const void cu2::shortenmov(int m)
{
  int tot = 0, cur = 0;
  for (int i = 1; i <= MOV; i++) {
    tot += mov[i];
    while (tot >= m && cur < 1) {
      mov[i]--;
      tot--;
      cur++;
    }
  }
}
// solve the cube
const int cu2::solvecube()
{
  // possible return codes:
  // 0, ERR_MISPAINTED, ERR_NONDESCRIPT,
  // ERR_PARITY_CORNER_ROTATION, ERR_PARITY_EDGE_SWAP, ERR_PARITY_EDGE_FLIP
  if (!inited) // make sure cube is initialized
    return (erval = ERR_NOTINITED);
  erval = 0;
  int tcube[N+2][N+2][N+2];
  int tmov[MOV+1], curmoves;
  int U=0, D=0, L=0, R=0, F=0, B=0;
  int cen[7];
  string tsolution = "";
  string prefix = "";
  string cursolution;
  // clear mov[] and solution
  solution = "";
  for (int i = 0; i <= MOV; i++)
    mov[i] = 0;
  for (int i = 1; i <= MOV; i++)
    tmov[i] = 0;
  tmov[0] = -1;
  // step 1(ish) back up original cube
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        tcube[i][j][k] = cube[i][j][k];
  // make sure there is the right number of each color
  for (int i = 0; i <= 6; i++)
    cen[i] = 0;
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        if (cube[i][j][k] >= 1 && cube[i][j][k] <= 6)
          cen[cube[i][j][k]]++;
  for (int i = 1; i <= 6; i++)
    if (cen[i] != N * N)
      return (erval = ERR_MISPAINTED);
  // set up interpolation table so U,D,L,R,F,B=1,2,3,4,5,6
  for (int i = 1; i <= 6; i++)
    cen[i] = 0;
  U = cube[1][N+1][1];
  L = cube[0][N][1];
  F = cube[1][N][0];
  for (int i = 1; i <= 6; i++)
  {
    if (i != U && i != L && i != F)
    {
      if      (findcorner_c(F,U,i)) R = i;
      else if (findcorner_c(U,L,i)) B = i;
      else if (findcorner_c(L,F,i)) D = i;
      else if (findcorner_a(F,U,i)) R = i; // normally there'd just be findcorner_c,
      else if (findcorner_a(U,L,i)) B = i; // but I added _a to find backward corner
      else if (findcorner_a(L,F,i)) D = i; // parity errors...
    }
  }
  // make sure we have different colors for every face
  if (U == L || L == F || F == U || R == 0 || D == 0 || B == 0)
    return (erval = ERR_MISPAINTED);
  cen[U] = 1; cen[L] = 2; cen[F] = 3;
  cen[R] = 4; cen[B] = 5; cen[D] = 6;
  // apply interpolation
  for (int i = 1; i <= N; i++)
  {
    for (int j = 1; j <= N; j++)
    {
      cube[i][N+1][j] = cen[cube[i][N+1][j]];
      cube[0][i][j]   = cen[cube[0][i][j]];
      cube[i][j][0]   = cen[cube[i][j][0]];
      cube[N+1][i][j] = cen[cube[N+1][i][j]];
      cube[i][j][N+1] = cen[cube[i][j][N+1]];
      cube[i][0][j]   = cen[cube[i][0][j]];
    }
  }
  // step 2(ish) make sure all cubelets are present (e.g., cube all there)
  cen[0] = 0;
  for (int i = 2; i <= 5 && !erval; i++)
  {
    if (!findcorner_c(1, (i<5?i+1:i-3), i)) {
      if (findcorner_a(1, (i<5?i+1:i-3), i)) cen[0]++;
      else erval = ERR_MISPAINTED;
    }
    if (!findcorner_c(6, i, (i<5?i+1:i-3))) {
      if (findcorner_a(6, i, (i<5?i+1:i-3))) cen[0]++;
      else erval = ERR_MISPAINTED;
    }
  }
  if (cen[0] > 0 && !erval)
    erval = ERR_PARITY_CORNER_BACKWARD;
  for (int i = 0; i <= N+1; i++)
    for (int j = 0; j <= N+1; j++)
      for (int k = 0; k <= N+1; k++)
        cube[i][j][k] = tcube[i][j][k];
  // the cube appears solvable (unless there's a parity error); try solution
  if (erval) return erval;
  // step 3(ish) start from each possible orientation to find shortest solution
  for (int h = 1; h <= 6; h++)
  {
    for (int g = 1; g <= 4; g++)
    {
      for (int i = 0; i <= N+1; i++)
        for (int j = 0; j <= N+1; j++)
          for (int k = 0; k <= N+1; k++)
            tcube[i][j][k] = cube[i][j][k];
      // step 4(ish) set up interpolation table so U,D,L,R,F,B=1,2,3,4,5,6
      for (int i = 1; i <= 6; i++)
        cen[i] = 0;
      U = cube[1][N+1][1];
      L = cube[0][N][1];
      F = cube[1][N][0];
      for (int i = 1; i <= 6; i++)
      {
        if (i != U && i != L && i != F)
        {
          if      (findcorner_c(F,U,i)) R = i;
          else if (findcorner_c(L,F,i)) D = i;
          else if (findcorner_c(U,L,i)) B = i;
        }
      }
      // now set the matrix
      cen[U] = 1; cen[L] = 2; cen[F] = 3;
      cen[R] = 4; cen[B] = 5; cen[D] = 6;
      // apply interpolation
      for (int i = 1; i <= N; i++)
      {
        for (int j = 1; j <= N; j++)
        {
          cube[i][N+1][j] = cen[cube[i][N+1][j]];
          cube[0][i][j]   = cen[cube[0][i][j]];
          cube[i][j][0]   = cen[cube[i][j][0]];
          cube[N+1][i][j] = cen[cube[N+1][i][j]];
          cube[i][j][N+1] = cen[cube[i][j][N+1]];
          cube[i][0][j]   = cen[cube[i][0][j]];
        }
      }
      if (!erval)
      {
        // step 5(ish) (FINALLLLLY): find a solution
        cursolution = findsolution();
        // strip redundancies from solution...
        if (shorten >= SHORTEN_STRIP_ALL)
        {
          string allmov = prefix + cursolution;
          int prelen = (int)prefix.length() / 3;
          if (prelen == 0) prelen = -1;
          cursolution = concise(allmov, prelen);
        }
        // see if the current solution is shorter than best solution
        curmoves = (int)cursolution.length() / 3;
        if (curmoves < tmov[0] || tmov[0] < 0)
        {
          tsolution = cursolution;
          for (int i = 1; i <= MOV; i++)
            tmov[i] = mov[i];
          tmov[0] = curmoves;
        }
      }
      for (int i = 0; i <= N+1; i++)
        for (int j = 0; j <= N+1; j++)
          for (int k = 0; k <= N+1; k++)
            cube[i][j][k] = tcube[i][j][k];
      // now try the next orientation
      if (shorten < SHORTEN_STRIP_ROTATE_ALL) break;
      prefix += "CL."; CL();
    }
    if (shorten < SHORTEN_STRIP_ROTATE_SOME) break;
    if (h % 2) {
      prefix += "CU."; CU();
    }
    else {
      prefix += "CA."; CA();
    }
  }
  // step 6(ish) set the members appropriately and return
  if (erval) return erval;
  for (int i = 0; i <= MOV; i++)
    mov[i] = tmov[i];
  solution = tsolution;
  return 0;
}
// find a solution for a prepared cube
const string cu2::findsolution()
{
  string s = "";
  int step = 0;
  // step 1: top corners
  {
    step++;
    string a = "";
    int iter = 0;
    // fix
    while
    (!erval &&
     !((findcorner_c(1,3,2) == POSY && fx == 1 && fz == 1) &&
       (findcorner_c(1,4,3) == POSY && fx == 2 && fz == 1) &&
       (findcorner_c(1,5,4) == POSY && fx == 2 && fz == 2) &&
       (findcorner_c(1,2,5) == POSY && fx == 1 && fz == 2) ) )
    {
      for (int i = 1; i <= 4; i++)
      {
        switch (findcorner_c(1, (i<4?i+2:i-2), (i+1)))
        {
        case POSY:
          if      (fx == 1 && fz == 1) ;
          else if (fx == 1 && fz == 2) {
            a += "BA.DR.BC.FA.DL.FC.";
            BA(); DR(); BC(); FA(); DL(); FC();
          }
          else if (fx == 2 && fz == 1) {
            a += "RD.DL.RU.LD.DR.LU.";
            RD(); DL(); RU(); LD(); DR(); LU();
          }
          else if (fx == 2 && fz == 2) {
            a += "BC.DL.BA.DL.LD.DR.LU.";
            BC(); DL(); BA(); DL(); LD(); DR(); LU();
          }
          break;
        case NEGY:
          if      (fx == 1 && fz == 1) ;
          else if (fx == 1 && fz == 2) {
            a += "DR.";
            DR();
          }
          else if (fx == 2 && fz == 1) {
            a += "DL.";
            DL();
          }
          else if (fx == 2 && fz == 2) {
            a += "DL.DL.";
            DL(); DL();
          }
          a += "FA.DR.FC.DL.LD.DL.LU.";
          FA(); DR(); FC(); DL(); LD(); DL(); LU();
          break;
        case NEGX:
          if      (fy == 1 && fz == 1) {
            a += "LD.DR.LU.";
            LD(); DR(); LU();
          }
          else if (fy == 1 && fz == 2) {
            a += "DR.FA.DL.FC.";
            DR(); FA(); DL(); FC();
          }
          else if (fy == 2 && fz == 1) {
            a += "LD.DR.LU.DL.LD.DR.LU.";
            LD(); DR(); LU(); DL(); LD(); DR(); LU();
          }
          else if (fy == 2 && fz == 2) {
            a += "LU.DL.LD.DL.LD.DL.LU.";
            LU(); DL(); LD(); DL(); LD(); DL(); LU();
          }
          break;
        case NEGZ:
          if      (fx == 1 && fy == 1) {
            a += "FA.DL.FC.";
            FA(); DL(); FC();
          }
          else if (fx == 1 && fy == 2) {
            a += "FA.DL.FC.DR.FA.DL.FC.";
            FA(); DL(); FC(); DR(); FA(); DL(); FC();
          }
          else if (fx == 2 && fy == 1) {
            a += "DL.LD.DR.LU.";
            DL(); LD(); DR(); LU();
          }
          else if (fx == 2 && fy == 2) {
            a += "FC.DR.FA.DR.FA.DR.FC.";
            FC(); DR(); FA(); DR(); FA(); DR(); FC();
          }
          break;
        case POSX:
          if      (fy == 1 && fz == 1) {
            a += "LD.DL.LU.";
            LD(); DL(); LU();
          }
          else if (fy == 1 && fz == 2) {
            a += "DR.FA.DR.FC.";
            DR(); FA(); DR(); FC();
          }
          else if (fy == 2 && fz == 1) {
            a += "RD.LD.DL.RU.LU.";
            RD(); LD(); DL(); RU(); LU();
          }
          else if (fy == 2 && fz == 2) {
            a += "RU.DR.RD.FA.DR.FC.";
            RU(); DR(); RD(); FA(); DR(); FC();
          }
          break;
        case POSZ:
          if      (fx == 1 && fy == 1) {
            a += "FA.DR.FC.";
            FA(); DR(); FC();
          }
          else if (fx == 1 && fy == 2) {
            a += "BA.FA.DR.BC.FC.";
            BA(); FA(); DR(); BC(); FC();
          }
          else if (fx == 2 && fy == 1) {
            a += "DL.LD.DL.LU.";
            DL(); LD(); DL(); LU();
          }
          else if (fx == 2 && fy == 2) {
            a += "BC.DL.BA.LD.DL.LU.";
            BC(); DL(); BA(); LD(); DL(); LU();
          }
          break;
        }
        a += "CL."; CL();
      }
      iter++;
      if (iter >= ITER_THRESHOLD) erval = ERR_NONDESCRIPT;
    }
    // check for trouble
    if (erval)
      return "";
    if (shorten >= SHORTEN_STRIP_SOME)
      a = concise(a);
    mov[step] = a.length() / 3;
    s += a;
  }
  // step 2: bottom corners positioning
  {
    step++;
    string a = "";
    int iter = 0;
    // fix
    int good, corn[4];
    good = 0; for (int i = 0; i < 4; i++) corn[i] = 0;
    findcorner_c(6,2,3); if (fx <= 1 && fz <= 1) { corn[0]=1; good++; }
    findcorner_c(6,3,4); if (fx >= 2 && fz <= 1) { corn[1]=1; good++; }
    findcorner_c(6,4,5); if (fx >= 2 && fz >= 2) { corn[2]=1; good++; }
    findcorner_c(6,5,2); if (fx <= 1 && fz >= 2) { corn[3]=1; good++; }
    for (int i = 1; i <= 4 && good < 2; i++)
    {
      a += "DR."; DR();
      good = 0; for (int j = 0; j < 4; j++) corn[j] = 0;
      findcorner_c(6,2,3); if (fx <= 1 && fz <= 1) { corn[0]=1; good++; }
      findcorner_c(6,3,4); if (fx >= 2 && fz <= 1) { corn[1]=1; good++; }
      findcorner_c(6,4,5); if (fx >= 2 && fz >= 2) { corn[2]=1; good++; }
      findcorner_c(6,5,2); if (fx <= 1 && fz >= 2) { corn[3]=1; good++; }
    }
    //if (good < 2) erval = ERR_NONDESCRIPT;
    while (!erval && good < 4)
    {
      for (int i = 0; i < 4; i++)
      {
        if (corn[i] && corn[i<3?i+1:i-3])
        {
          switch (i)
          {
          case 0: a += "CR.CR."; CR(); CR(); break;
          case 1: a += "CR."; CR(); break;
          case 2: break;
          case 3: a += "CL."; CL(); break;
          }
          a += "RD.DL.RU.FC.DR.FA.RD.DR.RU.DR.DR.";
          RD(); DL(); RU(); FC(); DR(); FA(); RD(); DR(); RU(); DR(); DR();
          switch (i)
          {
          case 0: a += "CL.CL."; CL(); CL(); break;
          case 1: a += "CL."; CL(); break;
          case 2: break;
          case 3: a += "CR."; CR(); break;
          }
          break;
        }
        else if (corn[i] && corn[i<2?i+2:i-2])
        {
          switch (i)
          {
          case 0: break;
          case 1: a += "CL."; CL(); break;
          case 2: break;
          case 3: a += "CR."; CR(); break;
          }
          a += "RD.DL.RU.FC.DR.FA.RD.DR.RU.DR.CL."
               "RD.DL.RU.FC.DR.FA.RD.DR.RU.DR.DR.CR.";
          RD(); DL(); RU(); FC(); DR(); FA(); RD(); DR(); RU(); DR(); CL();
          RD(); DL(); RU(); FC(); DR(); FA(); RD(); DR(); RU(); DR(); DR(); CR();
          switch (i)
          {
          case 0: break;
          case 1: a += "CR."; CR(); break;
          case 2: break;
          case 3: a += "CL."; CL(); break;
          }
          break;
        }
      }
      good = 0; for (int i = 0; i < 4; i++) corn[i] = 0;
      findcorner_c(6,2,3); if (fx <= 1 && fz <= 1) { corn[0]=1; good++; }
      findcorner_c(6,3,4); if (fx >= 2 && fz <= 1) { corn[1]=1; good++; }
      findcorner_c(6,4,5); if (fx >= 2 && fz >= 2) { corn[2]=1; good++; }
      findcorner_c(6,5,2); if (fx <= 1 && fz >= 2) { corn[3]=1; good++; }
      for (int i = 1; i <= 4 && good < 2; i++)
      {
        a += "DR."; DR();
        good = 0; for (int j = 0; j < 4; j++) corn[j] = 0;
        findcorner_c(6,2,3); if (fx <= 1 && fz <= 1) { corn[0]=1; good++; }
        findcorner_c(6,3,4); if (fx >= 2 && fz <= 1) { corn[1]=1; good++; }
        findcorner_c(6,4,5); if (fx >= 2 && fz >= 2) { corn[2]=1; good++; }
        findcorner_c(6,5,2); if (fx <= 1 && fz >= 2) { corn[3]=1; good++; }
      }
      iter++;
      if (iter >= ITER_THRESHOLD) erval = ERR_NONDESCRIPT;
    }
    // check for trouble
    if (erval)
      return "";
    if (shorten >= SHORTEN_STRIP_SOME)
      a = concise(a);
    mov[step] = a.length() / 3;
    s += a;
  }
  // step 3: bottom corners orienting
  {
    step++;
    string a = "";
    int iter = 0;
    // fix
    int good, corn[4];
    good = 0;
    if ((corn[0] = findcorner_c(6,2,3)) == NEGY) good++;
    if ((corn[1] = findcorner_c(6,3,4)) == NEGY) good++;
    if ((corn[2] = findcorner_c(6,4,5)) == NEGY) good++;
    if ((corn[3] = findcorner_c(6,5,2)) == NEGY) good++;
    while (!erval && good < 4)
    {
      if      (good == 3)
      {
        erval = ERR_PARITY_CORNER_ROTATION;
        break;
      }
      switch (good)
      {
      case 0:
        {
        int f = 0;
        for (int i = 0; i < 4; i++)
        {
          if (corn[i] == corn[i>0?i-1:i+3] || corn[i] == -(corn[i>0?i-1:i+3]))
          {
            f = 1;
            switch (i)
            {
            case 0: a += "CR.CR."; CR(); CR(); break;
            case 1: a += "CR."; CR(); break;
            case 2: break;
            case 3: a += "CL."; CL(); break;
            }
            if (cube[3][1][1] == 6)
            {
              a += "RD.DL.DL.RU.DR.RD.DR.RU."
                   "LD.DR.DR.LU.DL.LD.DL.LU.";
              RD(); DL(); DL(); RU(); DR(); RD(); DR(); RU();
              LD(); DR(); DR(); LU(); DL(); LD(); DL(); LU();
           }
            else // if (cube[2][1][0] == 6)
            {
              a += "LD.DR.LU.DR.LD.DL.DL.LU."
                   "RD.DL.RU.DL.RD.DR.DR.RU.";
              LD(); DR(); LU(); DR(); LD(); DL(); DL(); LU();
              RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU();
            }
            switch (i)
            {
            case 0: a += "CL.CL."; CL(); CL(); break;
            case 1: a += "CL."; CL(); break;
            case 2: break;
            case 3: a += "CR."; CR(); break;
            }
            break;
          }
        }
        if (!f)
        {
          erval = ERR_PARITY_CORNER_ROTATION;
          /*
          // the slightly lazy way.....
          a += "RD.DL.RU.DL.RD.DR.DR.RU.DR.DR.";
          RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU(); DR(); DR();
          */
        }
        } // ya i know... just so we could squeeze int f inside a case.
        break;
      case 1:
        for (int i = 0; i < 4; i++)
        {
          if (corn[i] == NEGY)
          {
            switch (i)
            {
            case 0: break;
            case 1: a += "CL."; CL(); break;
            case 2: a += "CR.CR."; CR(); CR(); break;
            case 3: a += "CR."; CR(); break;
            }
            if (cube[2][1][0] == 6)
            {
              a += "RD.DL.RU.DL.RD.DR.DR.RU.DR.DR.";
              RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU(); DR(); DR();
            }
            else // if (cube[3][1][1] == 6)
            {
              a += "DL.DL.RD.DL.DL.RU.DR.RD.DR.RU.";
              DL(); DL(); RD(); DL(); DL(); RU(); DR(); RD(); DR(); RU();
            }
            switch (i)
            {
            case 0: break;
            case 1: a += "CR."; CR(); break;
            case 2: a += "CL.CL."; CL(); CL(); break;
            case 3: a += "CL."; CL(); break;
            }
            break;
          }
        }
        break;
      case 2:
        for (int i = 0; i < 4; i++)
        {
          if (corn[i] == NEGY && corn[i>0?i-1:i+3] == NEGY)
          {
            switch (i)
            {
            case 0: break;
            case 1: a += "CL."; CL(); break;
            case 2: a += "CR.CR."; CR(); CR(); break;
            case 3: a += "CR."; CR(); break;
            }
            if (cube[3][1][1] == 6)
            {
              a += "RD.DL.DL.RU.DR.RD.DR.RU."
                   "LD.DR.DR.LU.DL.LD.DL.LU.";
              RD(); DL(); DL(); RU(); DR(); RD(); DR(); RU();
              LD(); DR(); DR(); LU(); DL(); LD(); DL(); LU();
            }
            else // if (cube[2][1][0] == 6)
            {
              a += "LD.DR.LU.DR.LD.DL.DL.LU."
                   "RD.DL.RU.DL.RD.DR.DR.RU.";
              LD(); DR(); LU(); DR(); LD(); DL(); DL(); LU();
              RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU();
            }
            switch (i)
            {
            case 0: break;
            case 1: a += "CR."; CR(); break;
            case 2: a += "CL.CL."; CL(); CL(); break;
            case 3: a += "CL."; CL(); break;
            }
            break;
          }
          else if (corn[i] == NEGY && corn[i<2?i+2:i-2] == NEGY)
          {
            switch (i)
            {
            case 0: break;
            case 1: a += "CL."; CL(); break;
            case 2: break;
            case 3: a += "CR."; CR(); break;
            }
            if (cube[2][1][0] == 6)
            {
              a += "CL.LD.DR.LU.DR.LD.DL.DL.LU."
                   "RD.DL.RU.DL.RD.DR.DR.RU."
                   "CR.LD.DR.LU.DR.LD.DL.DL.LU."
                   "RD.DL.RU.DL.RD.DR.DR.RU.";
              CL(); LD(); DR(); LU(); DR(); LD(); DL(); DL(); LU();
              RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU();
              CR(); LD(); DR(); LU(); DR(); LD(); DL(); DL(); LU();
              RD(); DL(); RU(); DL(); RD(); DR(); DR(); RU();
            }
            else // if (cube[3][1][1] == 6)
            {
              a += "RD.DL.DL.RU.DR.RD.DR.RU."
                   "LD.DR.DR.LU.DL.LD.DL.LU.CL."
                   "RD.DL.DL.RU.DR.RD.DR.RU."
                   "LD.DR.DR.LU.DL.LD.DL.LU.CR.";
              RD(); DL(); DL(); RU(); DR(); RD(); DR(); RU();
              LD(); DR(); DR(); LU(); DL(); LD(); DL(); LU(); CL();
              RD(); DL(); DL(); RU(); DR(); RD(); DR(); RU();
              LD(); DR(); DR(); LU(); DL(); LD(); DL(); LU(); CR();
            }
            switch (i)
            {
            case 0: break;
            case 1: a += "CR."; CR(); break;
            case 2: break;
            case 3: a += "CL."; CL(); break;
            }
            break;
          }
        }
        break;
      }
      good = 0;
      if ((corn[0] = findcorner_c(6,2,3)) == NEGY) good++;
      if ((corn[1] = findcorner_c(6,3,4)) == NEGY) good++;
      if ((corn[2] = findcorner_c(6,4,5)) == NEGY) good++;
      if ((corn[3] = findcorner_c(6,5,2)) == NEGY) good++;
      iter++;
      if (iter >= ITER_THRESHOLD) erval = ERR_NONDESCRIPT;
    }
    // check for trouble
    if (erval)
      return "";
    if (shorten >= SHORTEN_STRIP_SOME)
      a = concise(a);
    mov[step] = a.length() / 3;
    s += a;
  }
  /*
  // step X: center rotation
  {
    step++;
    string a = "";
    int iter = 0;
    // fix
    //
    // check for trouble
    if (erval)
      return "";
    if (shorten >= SHORTEN_STRIP_SOME)
      a = concise(a);
    mov[step] = a.length() / 3;
    s += a;
  }
  */
  /*
  if (!issolved())
  {
    erval = ERR_NONDESCRIPT;
    return "";
  }
  */
  return s;
}

// end of cu2 class definition

//
