package march;

public class Cases{

    public static final boolean T = true;
    public static final boolean F = false;

    public static BaseCase[] base_cases;

    public static int[][] facet_data;
    public static int[][] edge_data;
    
    static{
        base_cases = new BaseCase[16];
        base_cases[0] 
          = new BaseCase(new boolean[]{T,T,T,T,T,T,T,T},
                         new int[]{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1});
        base_cases[1] 
          = new BaseCase(new boolean[]{T,T,T,F,F,T,T,T},
                         new int[]{2,11,3,4,7,8,-1,-1,-1,-1,-1,-1});
        base_cases[2]
          = new BaseCase(new boolean[]{F,T,F,T,F,T,T,T},
                         new int[]{1,10,2,7,4,3,4,0,3,-1,-1,-1});
        base_cases[3] 
          = new BaseCase(new boolean[]{F,T,T,T,F,F,T,F},
                         new int[]{11,6,3,6,5,3,5,0,3,5,9,0});
        base_cases[4]
          = new BaseCase(new boolean[]{T,T,F,F,F,T,F,T},
                         new int[]{1,5,3,11,3,6,3,5,6,4,7,8});
        base_cases[5]
          = new BaseCase(new boolean[]{T,T,T,T,F,T,T,T},
                         new int[]{4,7,8,-1,-1,-1,-1,-1,-1,-1,-1,-1});
        base_cases[6]
          = new BaseCase(new boolean[]{T,T,F,T,F,T,T,T},
                         new int[]{4,7,8,1,10,2,-1,-1,-1,-1,-1,-1});
        base_cases[7]
          = new BaseCase(new boolean[]{T,F,T,F,F,T,T,T},
                         new int[]{4,7,8,9,1,0,2,11,10,-1,-1,-1});
        base_cases[8]
          = new BaseCase(new boolean[]{T,F,F,T,F,T,T,F},
                         new int[]{9,2,0,9,10,2,4,8,11,4,11,6});
        base_cases[9]
          = new BaseCase(new boolean[]{T,F,T,F,F,T,F,T},
                         new int[]{9,1,0,2,11,10,5,6,10,4,7,8});
        base_cases[10]
          = new BaseCase(new boolean[]{F,T,T,T,F,T,T,T},
                         new int[]{4,0,3,4,3,7,-1,-1,-1,-1,-1,-1});
        base_cases[11]
          = new BaseCase(new boolean[]{F,T,T,T,F,T,T,F},
                         new int[]{4,0,6,6,0,3,6,3,1,-1,-1,-1});
        base_cases[12]
          = new BaseCase(new boolean[]{F,F,T,T,F,F,T,T},
                         new int[]{5,1,3,7,5,3,-1,-1,-1,-1,-1,-1});
        base_cases[13]
          = new BaseCase(new boolean[]{F,T,F,F,F,T,T,T},
                         new int[]{4,0,7,7,0,1,7,1,11,10,11,1});
        base_cases[14]
          = new BaseCase(new boolean[]{T,T,F,F,F,T,T,F},
                         new int[]{4,8,6,6,8,3,6,3,10,10,3,1});
        base_cases[15]
          = new BaseCase(new boolean[]{F,F,F,F,F,F,F,F},
                         new int[]{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1});

        initializeData();     
    }
    
    /**
     * 立方体を正面から見て(yz平面を+側から眺める)
     * 時計回りに回転したときの
     * (それぞれの位置に対応する)頂点の移り方を計算
     */
    public static int[] rotateVertexC(int[] vert){
        int[] ret = new int[8];
        ret[0] = vert[3];
        ret[1] = vert[2];
        ret[2] = vert[6];
        ret[3] = vert[7];
        ret[4] = vert[0];
        ret[5] = vert[1];
        ret[6] = vert[5];
        ret[7] = vert[4];
        return ret;
    }

    public static boolean[] rotateInoutC(boolean[] inout){
        boolean[] ret = new boolean[8];
        ret[0] = inout[3];
        ret[1] = inout[2];
        ret[2] = inout[6];
        ret[3] = inout[7];
        ret[4] = inout[0];
        ret[5] = inout[1];
        ret[6] = inout[5];
        ret[7] = inout[4];
        return ret;
    }


    public static int[] rotateEdgeC(int[] edge){
        int[] ret = new int[12];
        ret[0] = edge[2];
        ret[1] = edge[10];
        ret[2] = edge[6];
        ret[3] = edge[11];
        ret[4] = edge[0];
        ret[5] = edge[9];
        ret[6] = edge[4];
        ret[7] = edge[8];
        ret[8] = edge[3];
        ret[9] = edge[1];
        ret[10] = edge[5];
        ret[11] = edge[7];
        return ret;
    }

    /**
     * 立方体を水平に右回りに回転したときの頂点の移り方を計算
     */
    public static int[] rotateVertexHR(int[] vert){
        int[] ret = new int[8];
        ret[0] = vert[3];
        ret[1] = vert[0];
        ret[2] = vert[1];
        ret[3] = vert[2];
        ret[4] = vert[7];
        ret[5] = vert[4];
        ret[6] = vert[5];
        ret[7] = vert[6];
        return ret;
    }

    public static boolean[] rotateInoutHR(boolean[] inout){
        boolean[] ret = new boolean[8];
        ret[0] = inout[3];
        ret[1] = inout[0];
        ret[2] = inout[1];
        ret[3] = inout[2];
        ret[4] = inout[7];
        ret[5] = inout[4];
        ret[6] = inout[5];
        ret[7] = inout[6];
        return ret;
    }

    public static int[] rotateEdgeHR(int[] edge){
        int[] ret = new int[12];
        ret[0] = edge[3];
        ret[1] = edge[0];
        ret[2] = edge[1];
        ret[3] = edge[2];
        ret[4] = edge[7];
        ret[5] = edge[4];
        ret[6] = edge[5];
        ret[7] = edge[6];
        ret[8] = edge[11];
        ret[9] = edge[8];
        ret[10] = edge[9];
        ret[11] = edge[10];
        return ret;
    }

    /**
     * 立方体を垂直に向こう側へ回転したときの頂点の移り変わりを計算
     */
    public static int[] rotateVertexVO(int[] vert){
        int[] ret = new int[8];
        ret[0] = vert[4];
        ret[1] = vert[0];
        ret[2] = vert[3];
        ret[3] = vert[7];
        ret[4] = vert[5];
        ret[5] = vert[1];
        ret[6] = vert[2];
        ret[7] = vert[6];
        return ret;
    }

    public static boolean[] rotateInoutVO(boolean[] inout){
        boolean[] ret = new boolean[8];
        ret[0] = inout[4];
        ret[1] = inout[0];
        ret[2] = inout[3];
        ret[3] = inout[7];
        ret[4] = inout[5];
        ret[5] = inout[1];
        ret[6] = inout[2];
        ret[7] = inout[6];
        return ret;
    }

    public static int[] rotateEdgeVO(int[] edge){
        int[] ret = new int[12];
        ret[0] = edge[8];
        ret[1] = edge[3];
        ret[2] = edge[11];
        ret[3] = edge[7];
        ret[4] = edge[9];
        ret[5] = edge[1];
        ret[6] = edge[10];
        ret[7] = edge[5];
        ret[8] = edge[4];
        ret[9] = edge[0];
        ret[10] = edge[2];
        ret[11] = edge[6];
        return ret;
    }
            
    public static int[][] makeAllVertexPermutation(){
        int[][] ret = new int[24][8];
        int   pi = 0;
        int[] initv = new int[]{0,1,2,3,4,5,6,7};
        int[] v;

        // s0
        v = initv;
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateVertexC(v);
        }
        
        // s1, s2, s3
        int j = 0;
        v = initv;
        while(j < 3){
            v = rotateVertexHR(v);
            for(int i = 0; i < 4; i++){
                ret[pi] = v;
                pi++;
                v = rotateVertexC(v);
            }
            j++;
        }
        
        // s5
        v = initv;
        v = rotateVertexVO(v);
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateVertexC(v);
        }

        // s4
        v = initv;
        v = rotateVertexVO(v);
        v = rotateVertexVO(v);
        v = rotateVertexVO(v);
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateVertexC(v);
        }

        return ret;
    }

    public static int[][] makeAllEdgePermutation(){
        int[][] ret = new int[24][12];
        int   pi = 0;
        int[] initv = new int[]{0,1,2,3,4,5,6,7,8,9,10,11};
        int[] v;

        // s0
        v = initv;
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateEdgeC(v);
        }
        
        // s1, s2, s3
        int j = 0;
        while(j < 3){
            v = rotateEdgeHR(initv);
            for(int i = 0; i < 4; i++){
                ret[pi] = v;
                pi++;
                v = rotateEdgeC(v);
            }
            j++;
        }
        
        // s5
        v = rotateEdgeVO(v);
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateEdgeC(v);
        }

        // s4
        v = rotateEdgeVO(initv);
        v = rotateEdgeVO(v);
        for(int i = 0; i < 4; i++){
            ret[pi] = v;
            pi++;
            v = rotateEdgeC(v);
        }
        return ret;
    }


    /**
     * 外部の時true
     */
    public static int cubeIndex(boolean[] v){
        int index = 0x00000000;
        
        if(v[0]){ index |= 1; }
        if(v[1]){ index |= 2; }
        if(v[2]){ index |= 4; }
        if(v[3]){ index |= 8; }
        if(v[4]){ index |= 16; }
        if(v[5]){ index |= 32; }
        if(v[6]){ index |= 64; }
        if(v[7]){ index |= 128; }
        
        return index;        
    }

    public static void initializeData(){
        facet_data = new int[256][];
        edge_data  = new int[256][];

        int[][] vertex_perm = makeAllVertexPermutation();
        int[][] edge_perm  = makeAllEdgePermutation();

        print2DArray(vertex_perm);
        print2DArray(edge_perm);

        // make facet_data
        for(int i = 0; i < 16; i++){
            BaseCase bc = base_cases[i];
            for(int j = 0; j < 24; j++){
                int[] vp = vertex_perm[j];
                int[] ep = edge_perm[j];

                facet_data[cubeIndex(permutateInout(bc.inout, vp))]
                  = permutateFacet(bc.facet, ep);

                facet_data[cubeIndex(permutateInout(inverse(bc.inout), vp))]
                  = inverse_facet(permutateFacet(bc.facet, ep));
            }
        }   

        // make edge_data
        for(int i = 0; i < 256; i++){
            if(facet_data[i] == null){
                edge_data[i] = null;
            }
            else{
                edge_data[i] = calcEdgeData(facet_data[i]);
            }
        }
    }

    /**
     * Inoutを反転
     */
    public static boolean[] inverse(boolean[] inout){
        boolean[] ret = new boolean[8];
        for(int i = 0; i < 8; i++){
            ret[i] = !inout[i];
        }
        return ret;
    }

    /**
     * ファセットの反転(面の方向を逆にする)
     */
    public static int[] inverse_facet(int[] facet){
        int[] ret = new int[12];
        int i = 0;
        while(i < 12){
            if(facet[i] == -1){
                ret[i] = -1;
            }
            int j = i;
            int v1 = facet[i++];
            int v2 = facet[i++];
            int v3 = facet[i++];
            ret[j]   = v3;
            ret[j+1] = v2;
            ret[j+2] = v1;
        }
        return ret;
    }

    public static boolean[] permutateInout(boolean[] inout, int[] perm){
        boolean[] ret = new boolean[8];
        for(int i = 0; i < 8; i++){
            ret[i] = inout[perm[i]];
        }
        return ret;
    }

    public static int[] permutateFacet(int[] facet, int[] perm){
        int[] ret = new int[12];
        for(int i = 0; i < 12; i++){
            if(facet[i] == -1){
                ret[i] = -1;
            }
            else{
                ret[i] = perm[facet[i]];
            }
        }
        return ret;
    }

    public static void print2DArray(int[][] perm){
        String s;
        for(int i = 0; i < perm.length; i++){
            s = "perm["+i+"]={";
            for(int j = 0; j < perm[i].length; j++){
                s += perm[i][j];
                if(j != perm[i].length-1){
                    s += ", ";
                }
            }
            s += "}";
            System.err.println(s);
        }
    }

    /**
     * ファセットデータの１エントリからエッジデータを計算
     */
    public static int[] calcEdgeData(int[] fd){
        /*
           重複するものを削除し、かつ、
           1 < 2 < 11 < 0 < 4 < 6 < 3 < 7 < 5 < 8 < 9 < 10
           の順に並べる。
           */
        int[] e = delRed(fd);
        return order(e);
    }
    
    public static int[] delRed(int[] fd){
        int[] aa = new int[12];
        // copy
        for(int i = 0; i < 12; i++){
            aa[i] = fd[i];
        }

        for(int i = 0; i < 12; i++){
            int v = aa[i];
            for(int j = i+1; j < 12; j++){
                if(aa[j] == v){
                    aa[j] = -1;
                }
            }
        }

        int[] aaa = new int[12];
        for(int i = 0; i < 12; i++){
            aaa[i] = -1;
        }
        int j = 0;
        for(int i = 0; i < 12; i++){
           if(aa[i] != -1){
               aaa[j] = aa[i];
               j++;
           }
        }
        return aaa;
    }

    public static int[] order(int[] e){
        /*
         * 0   1   2    3   4   5   6   7   8   9   10  11
         * 1 < 2 < 11 < 0 < 4 < 6 < 3 < 7 < 5 < 8 < 9 < 10
         * の順に並べる。
         */
        int[] aa = new int[12];

        for(int i = 0; i < 12; i++){
            aa[i] = -1;
        }

        int[] rank = new int[]{3,0,1,6,4,8,5,7,9,10,11,2};

        for(int i = 0; i < 12; i++){
            if(e[i] != -1){
                aa[rank[e[i]]] = e[i];
            }
        }

        int[] aaa = new int[12];
        int j = 0;

        for(int i = 0; i < 12; i++){
            aaa[i] = -1;
        }

        for(int i = 0; i < 12; i++){
            if(aa[i] != -1){
                aaa[j] = aa[i];
                j++;
            }
        }
        return aaa;
    } 

    public static void main(String[] argv){
        String s;
        for(int i = 0; i < 256; i++){
            if(facet_data[i] == null){
                s = "facet_data[" + i + "] = null";
            }
            else{
                s = "facet_data[" + i + "] = new int[]{";
                for(int j = 0; j < 12; j++){
                    s += (facet_data[i][j]);
                    if(j != 11){
                        s += ", ";
                    }
                }
                s += "}";
            }
            System.out.println(s);
        }

        System.err.println("");

        for(int i = 0; i < 256; i++){
            if(edge_data[i] == null){
                s = "edge_data[" + i + "] = null";
            }
            else{
                s = "edge_data[" + i + "] = new int[]{";
                for(int j = 0; j < 12; j++){
                    s += (edge_data[i][j]);
                    if(j != 11){
                        s += ", ";
                    }
                }
                s += "}";
            }
            System.out.println(s);
        }   
    }

}








