package cs.sq12phase;
import java.util.Arrays;
class Shape {
//1 = corner, 0 = edge.
static int[] halflayer = {0x00, 0x03, 0x06, 0x0c, 0x0f, 0x18, 0x1b, 0x1e,
0x30, 0x33, 0x36, 0x3c, 0x3f};
static int[] ShapeIdx = new int[3678];
static int[] ShapePrun = new int[3768 * 2];
static int[] ShapePrunOpt = new int[3768 * 2];
static int[] TopMove = new int[3678 * 2];
static int[] BottomMove = new int[3678 * 2];
static int[] TwistMove = new int[3678 * 2];
private Shape(){}
int top;
int bottom;
int parity;
static int getShape2Idx(int shp) {
int ret = Arrays.binarySearch(ShapeIdx, shp & 0xffffff)<<1 | shp>>24;
return ret;
}
int getIdx() {
int ret = Arrays.binarySearch(ShapeIdx, top<<12|bottom)<<1|parity;
return ret;
}
void setIdx(int idx) {
parity = idx & 1;
top = ShapeIdx[idx >> 1];
bottom = top & 0xfff;
top >>= 12;
}
int topMove() {
int move = 0;
int moveParity = 0;
do {
if ((top & 0x800) == 0) {
move += 1;
top = top << 1;
} else {
move += 2;
top = (top << 2) ^ 0x3003;
}
moveParity = 1 - moveParity;
} while ((Integer.bitCount(top & 0x3f) & 1) != 0);
if ((Integer.bitCount(top)&2)==0) {
parity ^= moveParity;
}
return move;
}
int bottomMove() {
int move = 0;
int moveParity = 0;
do {
if ((bottom & 0x800) == 0) {
move +=1;
bottom = bottom << 1;
} else {
move +=2;
bottom = (bottom << 2) ^ 0x3003;
}
moveParity = 1 - moveParity;
} while ((Integer.bitCount(bottom & 0x3f) & 1) != 0);
if ((Integer.bitCount(bottom)&2)==0) {
parity ^= moveParity;
}
return move;
}
void twistMove() {
int temp = top & 0x3f;
int p1 = Integer.bitCount(temp);
int p3 = Integer.bitCount(bottom&0xfc0);
parity ^= 1 & ((p1&p3)>>1);
top = (top & 0xfc0) | ((bottom >> 6) & 0x3f);
bottom = (bottom & 0x3f) | temp << 6;
}
static boolean inited = false;
static void init() {
if (inited) {
return;
}
int count = 0;
for (int i=0; i<13*13*13*13; i++) {
int dr = halflayer[i % 13];
int dl = halflayer[i / 13 % 13];
int ur = halflayer[i / 13 / 13 % 13];
int ul = halflayer[i / 13 / 13 / 13];
int value = ul<<18|ur<<12|dl<<6|dr;
if (Integer.bitCount(value) == 16) {
ShapeIdx[count++] = value;
}
}
System.out.println(count);
Shape s = new Shape();
for (int i=0; i<3678*2; i++) {
s.setIdx(i);
TopMove[i] = s.topMove();
TopMove[i] |= s.getIdx() << 4;
s.setIdx(i);
BottomMove[i] = s.bottomMove();
BottomMove[i] |= s.getIdx() << 4;
s.setIdx(i);
s.twistMove();
TwistMove[i] = s.getIdx();
}
for (int i=0; i<3768*2; i++) {
ShapePrun[i] = -1;
ShapePrunOpt[i] = -1;
}
//0 110110110110 011011011011
//1 110110110110 110110110110
//1 011011011011 011011011011
//0 011011011011 110110110110
ShapePrun[getShape2Idx(0x0db66db)] = 0;
ShapePrun[getShape2Idx(0x1db6db6)] = 0;
ShapePrun[getShape2Idx(0x16db6db)] = 0;
ShapePrun[getShape2Idx(0x06dbdb6)] = 0;
ShapePrunOpt[new FullCube().getShapeIdx()] = 0;
int done = 4;
int done0 = 0;
int depth = -1;
while (done != done0) {
done0 = done;
++depth;
System.out.println(done);
for (int i=0; i<3768*2; i++) {
if (ShapePrun[i] == depth) {
// try top
int m = 0;
int idx = i;
do {
idx = TopMove[idx];
m += idx & 0xf;
idx >>= 4;
if (ShapePrun[idx] == -1) {
++done;
ShapePrun[idx] = depth + 1;
}
} while (m != 12);
// try bottom
m = 0;
idx = i;
do {
idx = BottomMove[idx];
m += idx & 0xf;
idx >>= 4;
if (ShapePrun[idx] == -1) {
++done;
ShapePrun[idx] = depth + 1;
}
} while (m != 12);
// try twist
idx = TwistMove[i];
if (ShapePrun[idx] == -1) {
++done;
ShapePrun[idx] = depth + 1;
}
}
}
}
done = 1;
done0 = 0;
depth = -1;
while (done != done0) {
done0 = done;
++depth;
System.out.println(done);
for (int i=0; i<3768*2; i++) {
if (ShapePrunOpt[i] == depth) {
// try top
int m = 0;
int idx = i;
do {
idx = TopMove[idx];
m += idx & 0xf;
idx >>= 4;
if (ShapePrunOpt[idx] == -1) {
++done;
ShapePrunOpt[idx] = depth + 1;
}
} while (m != 12);
// try bottom
m = 0;
idx = i;
do {
idx = BottomMove[idx];
m += idx & 0xf;
idx >>= 4;
if (ShapePrunOpt[idx] == -1) {
++done;
ShapePrunOpt[idx] = depth + 1;
}
} while (m != 12);
// try twist
idx = TwistMove[i];
if (ShapePrunOpt[idx] == -1) {
++done;
ShapePrunOpt[idx] = depth + 1;
}
}
}
}
inited = true;
}
}