package cs.min2phase;
class CoordCube {
static final int N_MOVES = 18;
static final int N_MOVES2 = 10;
static final int N_SLICE = 495;
static final int N_TWIST_SYM = 324;
static final int N_FLIP_SYM = 336;
static final int N_PERM_SYM = 2768;
static final int N_MPERM = 24;
//XMove = Move Table
//XPrun = Pruning Table
//XConj = Conjugate Table
//phase1
static char[][] UDSliceMove = new char[N_SLICE][N_MOVES];
static char[][] TwistMove = new char[N_TWIST_SYM][N_MOVES];
static char[][] FlipMove = new char[N_FLIP_SYM][N_MOVES];
static char[][] UDSliceConj = new char[N_SLICE][8];
static int[] UDSliceTwistPrun = new int[N_SLICE * N_TWIST_SYM / 8 + 1];
static int[] UDSliceFlipPrun = new int[N_SLICE * N_FLIP_SYM / 8];
static int[] TwistFlipPrun = Tools.USE_TWIST_FLIP_PRUN ? new int[N_FLIP_SYM * N_TWIST_SYM * 8 / 8] : null;
//phase2
static char[][] CPermMove = new char[N_PERM_SYM][N_MOVES];
static char[][] EPermMove = new char[N_PERM_SYM][N_MOVES2];
static char[][] MPermMove = new char[N_MPERM][N_MOVES2];
static char[][] MPermConj = new char[N_MPERM][16];
static int[] MCPermPrun = new int[N_MPERM * N_PERM_SYM / 8];
static int[] MEPermPrun = new int[N_MPERM * N_PERM_SYM / 8];
static void setPruning(int[] table, int index, int value) {
table[index >> 3] ^= (0x0f ^ value) << ((index & 7) << 2);
}
static int getPruning(int[] table, int index) {
return (table[index >> 3] >> ((index & 7) << 2)) & 0x0f;
}
static void initUDSliceMoveConj() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_SLICE; i++) {
c.setUDSlice(i);
for (int j=0; j<N_MOVES; j+=3) {
CubieCube.EdgeMult(c, CubieCube.moveCube[j], d);
UDSliceMove[i][j] = (char) d.getUDSlice();
}
for (int j=0; j<16; j+=2) {
CubieCube.EdgeConjugate(c, CubieCube.SymInv[j], d);
UDSliceConj[i][j>>>1] = (char) (d.getUDSlice() & 0x1ff);
}
}
for (int i=0; i<N_SLICE; i++) {
for (int j=0; j<N_MOVES; j+=3) {
int udslice = UDSliceMove[i][j];
for (int k=1; k<3; k++) {
int cx = UDSliceMove[udslice & 0x1ff][j];
udslice = Util.permMult[udslice>>>9][cx>>>9]<<9|cx&0x1ff;
UDSliceMove[i][j+k] = (char)(udslice);
}
}
}
}
static void initFlipMove() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_FLIP_SYM; i++) {
c.setFlip(CubieCube.FlipS2R[i]);
for (int j=0; j<N_MOVES; j++) {
CubieCube.EdgeMult(c, CubieCube.moveCube[j], d);
FlipMove[i][j] = (char) d.getFlipSym();
}
}
}
static void initTwistMove() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_TWIST_SYM; i++) {
c.setTwist(CubieCube.TwistS2R[i]);
for (int j=0; j<N_MOVES; j++) {
CubieCube.CornMult(c, CubieCube.moveCube[j], d);
TwistMove[i][j] = (char) d.getTwistSym();
}
}
}
static void initCPermMove() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_PERM_SYM; i++) {
c.setCPerm(CubieCube.EPermS2R[i]);
for (int j=0; j<N_MOVES; j++) {
CubieCube.CornMult(c, CubieCube.moveCube[j], d);
CPermMove[i][j] = (char) d.getCPermSym();
}
}
}
static void initEPermMove() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_PERM_SYM; i++) {
c.setEPerm(CubieCube.EPermS2R[i]);
for (int j=0; j<N_MOVES2; j++) {
CubieCube.EdgeMult(c, CubieCube.moveCube[Util.ud2std[j]], d);
EPermMove[i][j] = (char) d.getEPermSym();
}
}
}
static void initMPermMoveConj() {
CubieCube c = new CubieCube();
CubieCube d = new CubieCube();
for (int i=0; i<N_MPERM; i++) {
c.setMPerm(i);
for (int j=0; j<N_MOVES2; j++) {
CubieCube.EdgeMult(c, CubieCube.moveCube[Util.ud2std[j]], d);
MPermMove[i][j] = (char) d.getMPerm();
}
for (int j=0; j<16; j++) {
CubieCube.EdgeConjugate(c, CubieCube.SymInv[j], d);
MPermConj[i][j] = (char) d.getMPerm();
}
}
}
static void initTwistFlipPrun() {
int depth = 0;
int done = 8;
boolean inv;
int select;
int check;
// TwistFlipPrun = new int[N_FLIP_SYM * N_TWIST_SYM * 8 / 8];
for (int i=0; i<N_FLIP_SYM*N_TWIST_SYM*8/8; i++) {
TwistFlipPrun[i] = -1;
}
for (int i=0; i<8; i++) {
setPruning(TwistFlipPrun, i, 0);
}
while (done < N_FLIP_SYM*N_TWIST_SYM*8) {
inv = depth > 6;
select = inv ? 0x0f : depth;
check = inv ? depth : 0x0f;
depth++;
for (int i=0; i<N_FLIP_SYM*N_TWIST_SYM*8; i++) {
if (getPruning(TwistFlipPrun, i) == select) {
int twist = i / 2688;
int flip = i % 2688;
int fsym = i & 7;
flip >>>= 3;
for (int m=0; m<N_MOVES; m++) {
int twistx = TwistMove[twist][m];
int tsymx = twistx & 7;
twistx >>>= 3;
int flipx = FlipMove[flip][CubieCube.Sym8Move[fsym][m]];
int fsymx = CubieCube.Sym8MultInv[CubieCube.Sym8Mult[flipx & 7][fsym]][tsymx];
flipx >>>= 3;
int idx = ((twistx * 336 + flipx) << 3 | fsymx);
if (getPruning(TwistFlipPrun, idx) == check) {
done++;
if (inv) {
setPruning(TwistFlipPrun, i, depth);
break;
} else {
setPruning(TwistFlipPrun, idx, depth);
char sym = CubieCube.SymStateTwist[twistx];
char symF = CubieCube.SymStateFlip[flipx];
if (sym != 1 || symF != 1) {
for (int j=0; j<8; j++, symF >>= 1) {
if ((symF & 1) == 1) {
int fsymxx = CubieCube.Sym8MultInv[fsymx][j];
for (int k=0; k<8; k++) {
if ((sym & (1 << k)) != 0) {
int idxx = twistx * 2688 + (flipx << 3 | CubieCube.Sym8MultInv[fsymxx][k]);
if (getPruning(TwistFlipPrun, idxx) == 0x0f) {
setPruning(TwistFlipPrun, idxx, depth);
done++;
}
}
}
}
}
}
}
}
}
}
}
// System.out.println(String.format("%2d%10d", depth, done));
}
}
static void initRawSymPrun(int[] PrunTable, final int INV_DEPTH,
final char[][] RawMove, final char[][] RawConj,
final char[][] SymMove, final char[] SymState,
final byte[] SymSwitch, final int[] moveMap, final int SYM_SHIFT) {
final int SYM_MASK = (1 << SYM_SHIFT) - 1;
final int N_RAW = RawMove.length;
final int N_SYM = SymMove.length;
final int N_SIZE = N_RAW * N_SYM;
final int N_MOVES = RawMove[0].length;
for (int i=0; i<(N_RAW*N_SYM+7)/8; i++) {
PrunTable[i] = -1;
}
setPruning(PrunTable, 0, 0);
int depth = 0;
int done = 1;
while (done < N_SIZE) {
boolean inv = depth > INV_DEPTH;
int select = inv ? 0x0f : depth;
int check = inv ? depth : 0x0f;
depth++;
for (int i=0; i<N_SIZE;) {
int val = PrunTable[i>>3];
if (!inv && val == -1) {
i += 8;
continue;
}
for (int end=Math.min(i+8, N_SIZE); i<end; i++, val>>=4) {
if ((val & 0x0f)/*getPruning(PrunTable, i)*/ == select) {
int raw = i % N_RAW;
int sym = i / N_RAW;
for (int m=0; m<N_MOVES; m++) {
int symx = SymMove[sym][moveMap == null ? m : moveMap[m]];
int rawx = RawConj[RawMove[raw][m] & 0x1ff][symx & SYM_MASK];
symx >>>= SYM_SHIFT;
int idx = symx * N_RAW + rawx;
if (getPruning(PrunTable, idx) == check) {
done++;
if (inv) {
setPruning(PrunTable, i, depth);
break;
} else {
setPruning(PrunTable, idx, depth);
for (int j=1, symState = SymState[symx]; (symState >>= 1) != 0; j++) {
if ((symState & 1) == 1) {
int idxx = symx * N_RAW + RawConj[rawx][j ^ (SymSwitch == null ? 0 : SymSwitch[j])];
if (getPruning(PrunTable, idxx) == 0x0f) {
setPruning(PrunTable, idxx, depth);
done++;
}
}
}
}
}
}
}
}
}
// System.out.println(String.format("%2d%10d", depth, done));
}
}
static void initSliceTwistPrun() {
initRawSymPrun(UDSliceTwistPrun, 6,
UDSliceMove, UDSliceConj,
TwistMove, CubieCube.SymStateTwist,
null, null, 3
);
}
static void initSliceFlipPrun() {
initRawSymPrun(UDSliceFlipPrun, 6,
UDSliceMove, UDSliceConj,
FlipMove, CubieCube.SymStateFlip,
null, null, 3
);
}
static void initMEPermPrun() {
initRawSymPrun(MEPermPrun, 7,
MPermMove, MPermConj,
EPermMove, CubieCube.SymStatePerm,
null, null, 4
);
}
static void initMCPermPrun() {
initRawSymPrun(MCPermPrun, 10,
MPermMove, MPermConj,
CPermMove, CubieCube.SymStatePerm,
CubieCube.e2c, Util.ud2std, 4
);
}
}