/*
 * Copyright (C) 2014 Indeed Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import com.google.common.collect.Lists;
import org.apache.log4j.Logger;

import java.util.Arrays;
import java.util.List;

/**
 * @author jplaisance
 */
public final class GenerateTables {

    private static final Logger log = Logger.getLogger(GenerateTables.class);

    public static void main(String[] args) {
        int[] vec_lookup = new int[4096];
        List<int[]> vectors = Lists.newArrayList();
        int[] bytesConsumed = new int[180];
        make6table(vec_lookup, vectors, bytesConsumed);
        make4table(vec_lookup, vectors, bytesConsumed);
        make2table(vec_lookup, vectors, bytesConsumed);
        for (int i = 0; i < 4096/32; i++) {
            final StringBuilder stb = new StringBuilder();
            for (int j = 0; j < 32; j++) {
                stb.append(vec_lookup[i*32+j]).append(", ");
            }
            System.out.println(stb);
        }
        final StringBuilder vectorsString = new StringBuilder();
        for (int[] vector : vectors) {
            final StringBuilder vectorString = new StringBuilder("vectors["+vector[16]+"] = _mm_setr_epi8(");
            final String str = Arrays.toString(Arrays.copyOfRange(vector, 0, 16));
            vectorString.append(str.substring(1, str.length()-1));
            vectorString.append(");");
            vectorsString.append(vectorString).append("\n");
        }
        System.out.println(vectorsString);
        for (int i = 0; i < bytesConsumed.length; i+=16) {
            final StringBuilder stb = new StringBuilder();
            for (int j = 0; j < Math.min(16, bytesConsumed.length-i); j++) {
                stb.append(bytesConsumed[i+j]).append(", ");
            }
            System.out.println(stb);
        }
    }

    public static void make6table(int[] vec_lookup, List<int[]> vectors, int[] bytesConsumed) {
        int count = 109;
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    for (int l = 0; l < 2; l++) {
                        for (int m = 0; m < 2; m++) {
                            for (int n = 0; n < 2; n++) {
                                int bits = 0;
                                final int[] vector = new int[17];
                                Arrays.fill(vector, -1);
                                vector[16] = count;
                                int current = 0;
                                vector[0] = current++;
                                if (i == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[1] = current++;
                                }
                                bits = bits << 1;
                                vector[2] = current++;
                                if (j == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[3] = current++;
                                }
                                bits = bits << 1;
                                vector[4] = current++;
                                if (k == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[5] = current++;
                                }
                                bits = bits << 1;
                                vector[6] = current++;
                                if (l == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[7] = current++;
                                }
                                bits = bits << 1;
                                vector[8] = current++;
                                if (m == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[9] = current++;
                                }
                                bits = bits << 1;
                                vector[10] = current++;
                                if (n == 1) {
                                    bits = (bits << 1) | 1;
                                    vector[11] = current++;
                                }
                                bits = bits << 1;
                                vectors.add(vector);
                                final int replace = 6 - i - j - k - l - m - n;
                                for (int r = 0; r < (1<<replace); r++) {
                                    if (i == 0 && j == 0 && k == 0 && l == 0 && m == 0 && n == 0) {
                                        final int index = (bits << replace) + r;
                                        System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                                        final int reverse = Integer.reverse(index) >>> 20;
                                        final int nBytes = Integer.numberOfTrailingZeros(reverse | 0x1000);
                                        vec_lookup[reverse] = nBytes+167;
                                        bytesConsumed[nBytes+167] = nBytes;
                                    } else {
                                        final int index = (bits << replace) + r;
                                        System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                                        vec_lookup[Integer.reverse(index)>>>20] = count;
                                    }
                                }
                                bytesConsumed[count] = i+j+k+l+m+n+6;
                                count++;
                            }
                        }
                    }
                }
            }
        }
    }

    public static void make4table(int[] vec_lookup, List<int[]> vectors, int[] bytesConsumed) {
        int count = 27;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 3; k++) {
                    for (int l = 0; l < 3; l++) {
                        int bits = 0;
                        final int[] vector = new int[17];
                        Arrays.fill(vector, -1);
                        vector[16] = count;
                        int current = 0;
                        vector[0] = current++;
                        for (int ii = 0; ii < i; ii++) {
                            bits = (bits << 1) | 1;
                            vector[ii+1] = current++;
                        }
                        bits = bits << 1;
                        vector[4] = current++;
                        for (int jj = 0; jj < j; jj++) {
                            bits = (bits << 1) | 1;
                            vector[jj+5] = current++;
                        }
                        bits = bits << 1;
                        vector[8] = current++;
                        for (int kk = 0; kk < k; kk++) {
                            bits = (bits << 1) | 1;
                            vector[kk+9] = current++;
                        }
                        bits = bits << 1;
                        vector[12] = current++;
                        for (int ll = 0; ll < l; ll++) {
                            bits = (bits << 1) | 1;
                            vector[ll+13] = current++;
                        }
                        bits = bits << 1;
                        vectors.add(vector);
                        final int replace = 8 - i - j - k - l;
                        for (int r = 0; r < (1<<replace); r++) {
                            if (i == 0 && j == 0 && k == 0 && l == 0) {
                                final int index = (bits << replace) + r;
                                System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                                final int reverse = Integer.reverse(index) >>> 20;
                                final int nBytes = 4;
                                if (vec_lookup[reverse] == 0) {
                                    vec_lookup[reverse] = 108;
                                }
                                bytesConsumed[108] = nBytes;
                            } else {
                                final int index = (bits << replace) + r;
                                System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                                final int reverse = Integer.reverse(index) >>> 20;
                                if (vec_lookup[reverse] == 0) {
                                    vec_lookup[reverse] = count;
                                }
                            }
                        }
                        bytesConsumed[count] = i+j+k+l+4;
                        count++;
                    }
                }
            }
        }
    }

    public static void make2table(int[] vec_lookup, List<int[]> vectors, int[] bytesConsumed) {
        int count = 0;

        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                int bits = 0;
                final int[] vector = new int[17];
                Arrays.fill(vector, -1);
                vector[16] = count;
                int current = 0;
                vector[7] = current++;
                for (int ii = 0; ii < i; ii++) {
                    bits = (bits << 1) | 1;
                    vector[ii*2] = current++;
                }
                bits = bits << 1;
                vector[15] = current++;
                for (int jj = 0; jj < j; jj++) {
                    bits = (bits << 1) | 1;
                    vector[8+jj*2] = current++;
                }
                bits = bits << 1;
                vectors.add(vector);
                final int replace = 10 - i - j;
                for (int r = 0; r < (1<<replace); r++) {
                    if (i == 0 && j == 0) {
                        final int index = (bits << replace) + r;
                        System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                        final int reverse = Integer.reverse(index) >>> 20;
                        final int nBytes = (reverse & 4) > 0 ? 2 : 3;
                        if (vec_lookup[reverse] == 0) {
                            vec_lookup[reverse] = 23+nBytes;
                        }
                        bytesConsumed[23+nBytes] = nBytes;
                    } else {
                        final int index = (bits << replace) + r;
                        System.out.println(String.format("%12s", Integer.toBinaryString(index)).replaceAll(" ", "0"));
                        final int reverse = Integer.reverse(index) >>> 20;
                        if (vec_lookup[reverse] == 0) {
                            vec_lookup[reverse] = count;
                        }
                    }
                }
                bytesConsumed[count] = i+j+2;
                count++;
            }
        }
    }
}
