package cs.threephase;
import java.util.Arrays;
import static cs.threephase.Util.*;
/*
13 1
4 17
16 5
0 12
4 16 0 12 5 17 1 13
9 20 20 11 11 22 22 9
21 8 8 23 23 10 10 21
19 7 15 3 18 6 14 2
15 3
7 18
19 6
2 14
*/
public class Edge3 {
static final boolean IS_64BIT_PLATFORM = false;
static final int N_SYM = 1538;
static final int N_RAW = 20160;
static final int N_EPRUN = N_SYM * N_RAW;
static final int MAX_DEPTH = 10;
static final int[] prunValues = {1, 4, 16, 55, 324, 1922, 12275, 77640, 485359, 2778197, 11742425, 27492416, 31002941, 31006080};
static int[] eprun = new int[N_EPRUN / 16];
static int[] sym2raw = new int[N_SYM];
static char[] symstate = new char[N_SYM];
static int[] raw2sym = new int[11880];
static int[] syminv = {0, 1, 6, 3, 4, 5, 2, 7};
int[] edge = new int[12];
int[] edgeo = new int[12];
int[] temp;
boolean isStd = true;
static int[][] mvrot = new int[20*8][12];
static int[][] mvroto = new int[20*8][12];
static int[] factX = {1, 1, 2/2, 6/2, 24/2, 120/2, 720/2, 5040/2, 40320/2, 362880/2, 3628800/2, 39916800/2, 479001600/2};
static int done = 0;
public static double initStatus() {
return done * 1.0 / prunValues[MAX_DEPTH - 1];
}
static void initMvrot() {
Edge3 e = new Edge3();
for (int m=0; m<20; m++) {
for (int r=0; r<8; r++) {
e.set(0);
e.move(m);
e.rotate(r);
for (int i=0; i<12; i++) {
mvrot[m<<3|r][i] = e.edge[i];
}
e.std();
for (int i=0; i<12; i++) {
mvroto[m<<3|r][i] = e.temp[i];
}
}
}
}
static void initRaw2Sym() {
Edge3 e = new Edge3();
byte[] occ = new byte[11880/8];
int count = 0;
for (int i=0; i<11880; i++) {
if ((occ[i>>>3]&(1<<(i&7))) == 0) {
e.set(i * factX[8]);
for (int j=0; j<8; j++) {
int idx = e.get(4);
if (idx == i) {
symstate[count] |= 1 << j;
}
occ[idx>>3] |= (1<<(idx&7));
raw2sym[idx] = count << 3 | syminv[j];
e.rot(0);
if (j%2==1) {
e.rot(1);
e.rot(2);
}
}
sym2raw[count++] = i;
}
}
assert count == 1538;
}
static void setPruning(int[] table, int index, int value) {
table[index >> 4] ^= (0x3 ^ value) << ((index & 0xf) << 1);
}
static int getPruning(int[] table, int index) {
return (table[index >> 4] >> ((index & 0xf) << 1)) & 0x3;
}
static int getprun(int edge, int prun) {
int depm3 = getPruning(eprun, edge);
if (depm3 == 0x3) {
return MAX_DEPTH;
}
return (depm3 - prun + 16) % 3 + prun - 1;
}
static int getprun(int edge) {
Edge3 e = new Edge3();
int depth = 0;
int depm3 = getPruning(eprun, edge);
if (depm3 == 0x3) {
return MAX_DEPTH;
}
while (edge!=0) {
if (depm3 == 0) {
depm3 = 2;
} else {
depm3--;
}
int symcord1 = edge / N_RAW;
int cord1 = sym2raw[symcord1];
int cord2 = edge % N_RAW;
e.set(cord1 * N_RAW + cord2);
for (int m=0; m<17; m++) {
int cord1x = getmvrot(e.edge, m<<3, 4);
int symcord1x = raw2sym[cord1x];
int symx = symcord1x & 0x7;
symcord1x >>= 3;
int cord2x = getmvrot(e.edge, m<<3|symx, 10) % N_RAW;
int idx = symcord1x * N_RAW + cord2x;
if (getPruning(eprun, idx) == depm3) {
depth++;
edge = idx;
break;
}
}
}
return depth;
}
static void createPrun() {
Edge3 e = new Edge3();
Edge3 f = new Edge3();
Edge3 g = new Edge3();
Arrays.fill(eprun, -1);
int depth = 0;
done = 1;
setPruning(eprun, 0, 0);
while (done != N_EPRUN) {
boolean inv = depth > 9;
int depm3 = depth % 3;
int dep1m3 = (depth + 1) % 3;
int find = inv ? 0x3 : depm3;
int chk = inv ? depm3 : 0x3;
if (depth >= MAX_DEPTH - 1) {
break;
}
for (int i_=0; i_<N_EPRUN; i_+=16) {
int val = eprun[i_ >> 4];
if (!inv && val == -1) {
continue;
}
for (int i=i_, end=i_+16; i<end; i++, val>>=2) {
if ((val & 0x3) != find) {
continue;
}
int symcord1 = i / N_RAW;
int cord1 = sym2raw[symcord1];
int cord2 = i % N_RAW;
e.set(cord1 * N_RAW + cord2);
for (int m=0; m<17; m++) {
int cord1x = getmvrot(e.edge, m<<3, 4);
int symcord1x = raw2sym[cord1x];
int symx = symcord1x & 0x7;
symcord1x >>= 3;
int cord2x = getmvrot(e.edge, m<<3|symx, 10) % N_RAW;
int idx = symcord1x * N_RAW + cord2x;
if (getPruning(eprun, idx) != chk) {
continue;
}
setPruning(eprun, inv ? i : idx, dep1m3);
done++;
// if ((done & 0x3ffff) == 0) {
// System.out.print(String.format("%d\r", done));
// }
if (inv) {
break;
}
char symState = symstate[symcord1x];
if (symState == 1){
continue;
}
f.set(e);
f.move(m);
f.rotate(symx);
for (int j=1; (symState >>= 1) != 0; j++) {
if ((symState & 1) != 1) {
continue;
}
g.set(f);
g.rotate(j);
int idxx = symcord1x * N_RAW + g.get(10) % N_RAW;
if (getPruning(eprun, idxx) == chk) {
setPruning(eprun, idxx, dep1m3);
done++;
// if ((done & 0x3ffff) == 0) {
// System.out.print(String.format("%d\r", done));
// }
}
}
}
}
}
depth++;
System.out.println(depth + "\t" + done);
}
}
static int[] FullEdgeMap = {0, 2, 4, 6, 1, 3, 7, 5, 8, 9, 10, 11};
int getsym() {
int cord1x = get(4);
int symcord1x = raw2sym[cord1x];
int symx = symcord1x & 0x7;
symcord1x >>= 3;
rotate(symx);
int cord2x = get(10) % N_RAW;
return symcord1x * N_RAW + cord2x;
}
int set(EdgeCube c) {
if (temp == null) {
temp = new int[12];
}
for (int i=0; i<12; i++) {
temp[i] = i;
edge[i] = c.ep[FullEdgeMap[i]+12]%12;
}
int parity = 1; //because of FullEdgeMap
for (int i=0; i<12; i++) {
while (edge[i] != i) {
int t = edge[i];
edge[i] = edge[t];
edge[t] = t;
int s = temp[i];
temp[i] = temp[t];
temp[t] = s;
parity ^= 1;
}
}
for (int i=0; i<12; i++) {
edge[i] = temp[c.ep[FullEdgeMap[i]]%12];
}
return parity;
}
void set(Edge3 e) {
for (int i=0; i<12; i++) {
edge[i] = e.edge[i];
edgeo[i] = e.edgeo[i];
}
isStd = e.isStd;
}
static int getmvrot(int[] ep, int mrIdx, int end) {
int[] movo = mvroto[mrIdx];
int[] mov = mvrot[mrIdx];
int idx = 0;
if (IS_64BIT_PLATFORM) {
long val = 0xba9876543210L;
for (int i=0; i<end; i++) {
int v = movo[ep[mov[i]]] << 2;
idx *= 12 - i;
idx += (val >> v) & 0xf;
val -= 0x111111111110L << v;
}
} else { //long is not as fast as expected
int vall = 0x76543210;
int valh = 0xba98;
for (int i=0; i<end; i++) {
int v = movo[ep[mov[i]]] << 2;
idx *= 12 - i;
if (v >= 32) {
idx += (valh >> (v - 32)) & 0xf;
valh -= 0x1110 << (v - 32);
} else {
idx += (vall >> v) & 0xf;
valh -= 0x1111;
vall -= 0x11111110 << v;
}
}
}
return idx;
}
void std() {
if (temp == null) {
temp = new int[12];
}
for (int i=0; i<12; i++) {
temp[edgeo[i]] = i;
}
for (int i=0; i<12; i++) {
edge[i] = temp[edge[i]];
edgeo[i] = i;
}
isStd = true;
}
int get(int end) {
if (!isStd) {
std();
}
int idx = 0;
long val = 0xba9876543210L;
for (int i=0; i<end; i++) {
int v = edge[i] << 2;
idx *= 12 - i;
idx += (val >> v) & 0xf;
val -= 0x111111111110L << v;
}
return idx;
}
void set(int idx) {
long val = 0xba9876543210L;
int parity = 0;
for (int i=0; i<11; i++) {
int p = factX[11-i];
int v = idx / p;
idx = idx % p;
parity ^= v;
v <<= 2;
edge[i] = (int) ((val >> v) & 0xf);
long m = (1L << v) - 1;
val = (val & m) + ((val >> 4) & ~m);
}
if ((parity & 1) == 0) {
edge[11] = (int)val;
} else {
edge[11] = edge[10];
edge[10] = (int)val;
}
for (int i=0; i<12; i++) {
edgeo[i] = i;
}
isStd = true;
}
void move(int i) {
isStd = false;
switch (i) {
case 0: //U
circle(edge, 0, 4, 1, 5);
circle(edgeo, 0, 4, 1, 5);
break;
case 1: //U2
swap(edge, 0, 4, 1, 5);
swap(edgeo, 0, 4, 1, 5);
break;
case 2: //U'
circle(edge, 0, 5, 1, 4);
circle(edgeo, 0, 5, 1, 4);
break;
case 3: //R2
swap(edge, 5, 10, 6, 11);
swap(edgeo, 5, 10, 6, 11);
break;
case 4: //F
circle(edge, 0, 11, 3, 8);
circle(edgeo, 0, 11, 3, 8);
break;
case 5: //F2
swap(edge, 0, 11, 3, 8);
swap(edgeo, 0, 11, 3, 8);
break;
case 6: //F'
circle(edge, 0, 8, 3, 11);
circle(edgeo, 0, 8, 3, 11);
break;
case 7: //D
circle(edge, 2, 7, 3, 6);
circle(edgeo, 2, 7, 3, 6);
break;
case 8: //D2
swap(edge, 2, 7, 3, 6);
swap(edgeo, 2, 7, 3, 6);
break;
case 9: //D'
circle(edge, 2, 6, 3, 7);
circle(edgeo, 2, 6, 3, 7);
break;
case 10: //L2
swap(edge, 4, 8, 7, 9);
swap(edgeo, 4, 8, 7, 9);
break;
case 11: //B
circle(edge, 1, 9, 2, 10);
circle(edgeo, 1, 9, 2, 10);
break;
case 12: //B2
swap(edge, 1, 9, 2, 10);
swap(edgeo, 1, 9, 2, 10);
break;
case 13: //B'
circle(edge, 1, 10, 2, 9);
circle(edgeo, 1, 10, 2, 9);
break;
case 14: //u2
swap(edge, 0, 4, 1, 5);
swap(edgeo, 0, 4, 1, 5);
swap(edge, 9, 11);
swap(edgeo, 8, 10);
break;
case 15: //r2
swap(edge, 5, 10, 6, 11);
swap(edgeo, 5, 10, 6, 11);
swap(edge, 1, 3);
swap(edgeo, 0, 2);
break;
case 16: //f2
swap(edge, 0, 11, 3, 8);
swap(edgeo, 0, 11, 3, 8);
swap(edge, 5, 7);
swap(edgeo, 4, 6);
break;
case 17: //d2
swap(edge, 2, 7, 3, 6);
swap(edgeo, 2, 7, 3, 6);
swap(edge, 8, 10);
swap(edgeo, 9, 11);
break;
case 18: //l2
swap(edge, 4, 8, 7, 9);
swap(edgeo, 4, 8, 7, 9);
swap(edge, 0, 2);
swap(edgeo, 1, 3);
break;
case 19: //b2
swap(edge, 1, 9, 2, 10);
swap(edgeo, 1, 9, 2, 10);
swap(edge, 4, 6);
swap(edgeo, 5, 7);
break;
}
}
void rot(int r) {
isStd = false;
switch (r) {
case 0:
move(14);
move(17);
break;
case 1:
circlex(11, 5, 10, 6);//r
circlex(5, 10, 6, 11);
circlex(1, 2, 3, 0);
circlex(4, 9, 7, 8);//l'
circlex(8, 4, 9, 7);
circlex(0, 1, 2, 3);
break;
case 2:
swapx(4, 5); swapx(5, 4);
swapx(11, 8); swapx(8, 11);
swapx(7, 6); swapx(6, 7);
swapx(9, 10); swapx(10, 9);
swapx(1, 1); swapx(0, 0);
swapx(3, 3); swapx(2, 2);
break;
}
}
void rotate(int r) {
while (r >= 2) {
r -= 2;
rot(1);
rot(2);
}
if (r != 0) {
rot(0);
}
}
void circle(int[] arr, int a, int b, int c, int d) {
int temp = arr[d];
arr[d] = arr[c];
arr[c] = arr[b];
arr[b] = arr[a];
arr[a] = temp;
}
void swap(int[] arr, int a, int b, int c, int d) {
int temp = arr[a];
arr[a] = arr[c];
arr[c] = temp;
temp = arr[b];
arr[b] = arr[d];
arr[d] = temp;
}
void swap(int[] arr, int x, int y) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
void swapx(int x, int y) {
int temp = edge[x];
edge[x] = edgeo[y];
edgeo[y] = temp;
}
void circlex(int a, int b, int c, int d) {
int temp = edgeo[d];
edgeo[d] = edge[c];
edge[c] = edgeo[b];
edgeo[b] = edge[a];
edge[a] = temp;
}
}