import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Random;

/**
 * Created by ioffeg on 21.11.14.
 */
public class TestGen {

    /*
        1--2--3--4
        |  |  |  |
        5--6--7--8
        |  |  |  |
        9-10-11-12
     */

    // TODO: good tests

    /*
        -1 -- any number
        0 -- no way
        positive number -- random number with such degree
     */

    final int MAXC = (int)1e9;
    final Random random = new Random(42424242L);

    final int[][] PATTERN_SPECIAL_1 = new int[][]{{0, 12, 0},
            {6, 4, 10, 6}, {0, 0, 0},
            {4, 10, 2, 4}, {12, 0, 8}};
    

    final int[][] PATTERN_ONLY_HOR = new int[][]{{-1, -1, -1},
            {0, 0, 0, 0}, {-1, -1, -1},
            {0, 0, 0, 0}, {-1, -1, -1}};

    final int[][] PATTERN_ONLY_VER = new int[][]{{0, 0, 0},
            {-1, -1, -1, -1}, {0, 0, 0},
            {-1, -1, -1, -1}, {0, 0, 0}};

    final int[][] PATTERN_SNAKE_HOR = new int[][]{{-1, -1, -1},
            {0, 0, 0, -1}, {-1, -1, -1},
            {-1, 0, 0, 0}, {-1, -1, -1}};

    final int[][] PATTERN_SNAKE_VER = new int[][]{{0, -1, 0},
            {-1, -1, -1, -1}, {0, 0, 0},
            {-1, -1, -1, -1}, {-1, 0, -1}};

    final int[][] PATTERN_SPECIAL_H = new int[][]{{24, 26, 0},
            {0, 3, 0, 4}, {0, 0, 10},
            {0, 3, 0, 4}, {20, 25, 0}};

    final int[][] PATTERN_SPECIAL_BRIDGES = new int[][]{{3, 1, 26},
            {0, 5, 24, 22}, {0, 0, 12},
            {3, 0, 18, 28}, {11, 1, 28}};

    final int[][] PATTERN_SPECIAL_5 = new int[][]{{1, 3, 5},
            {5, 13, 0, 0}, {0, 23, 25},
            {7, 0, 0, 21}, {0, 11, 21}};

    final int[][] PATTERN_ANY = new int[][]{{-1, -1, -1},
            {-1, -1, -1, -1}, {-1, -1, -1},
            {-1, -1, -1, -1}, {-1, -1, -1}};

    final int[][] PATTERN_LOTS_OF_BRIDGES = new int[][]{{3, 0, 3},
            {0, 1, 0, 1}, {0, 27, 27},
            {3, 27, 0, 27}, {1, 27, 0}};

    final int[][] PATTERN_LOTS_OF_BRIDGES_2 = new int[][]{{3, 0, 3},
            {0, 1, 0, 1}, {0, 3, 3},
            {3, 3, 0, 3}, {1, 3, 0}};

    final int[][] PATTERN_LOTS_OF_BRIDGES_3 = new int[][]{{3, 0, 3},
            {0, 1, 0, 1}, {0, 4, 4},
            {3, 4, 0, 4}, {1, 4, 0}};

    final int[][] PATTERN_LOTS_OF_BRIDGES_4 = new int[][]{{3, 0, 3},
            {0, 1, 0, 1}, {0, 26, 26},
            {3, 26, 0, 26}, {1, 26, 0}};
    /*
        1--2--3--4
        |  |  |  |
        5--6--7--8
        |  |  |  |
        9-10-11-12
     */

    int tests;

    PrintWriter descrWritter;

    void printTest(int[][] a, String desc) throws FileNotFoundException {
        tests++;
        String file = String.format("%03d", tests);
        descrWritter.println(file + " : " + desc);
        System.out.println("[" + file + "]");
        PrintWriter writer = new PrintWriter("../tests/" + file);
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[i].length; j++) {
                if (j > 0) {
                    writer.print(" ");
                }
                writer.print(a[i][j]);
            }
            writer.println();
        }
        writer.close();
    }

    public void run() throws FileNotFoundException {
        new File("../tests/").mkdirs();
        descrWritter = new PrintWriter("../tests/tests.description");
        tests = 0;
        printTest(new int[][]{{1, 2, 3}, {4, 5, 6, 7},
                {8, 9, 10}, {11, 12, 13, 14},
                {15, 16, 17}}, "Sample test");
        printTest(new int[][]{{1, 1, 1}, {0, 0, 0, 0},
                {1, 1, 1}, {0, 0, 0, 1}, {1, 1, 1}}, "Sample test");
        printTest(new int[][]{{1, 1, 1}, {0, 0, 0, 0},
                {1, 1, 1}, {0, 0, 1, 0}, {1, 1, 1}}, "Hand test test");
        printTest(new int[][]{{1, 1, 1}, {0, 0, 0, 0},
                {1, 1, 1}, {0, 1, 0, 0}, {1, 1, 1}}, "Hand test test");
        printTest(genEqual(MAXC), "Max test");
        printTest(genEqual(1 << 29), "Max test");
        printTest(genEqual(1), "Min test");
        printTest(genEqual(0), "Min test");
        for (int i = 0; i < 30; i++) {
            printTest(genRandomWithPattern(1, 42, genRandomPattern2(0, 2)), "Small random test. Small degrees");
        }
        for (int i = 0; i < 60; i++) {
            printTest(genRandomWithPattern(1, 42, genRandomPattern2(0, 4)), "Small random test. Small degrees");
        }
        printTest(genRandomWithPattern(1, 2, genRandomPattern(50)), "Small random test. from = 1, to = 2, density = 50");
        printTest(genRandomWithPattern(1, 3, genRandomPattern(35)), "Small random test. from = 1, to = 3, density = 35");
        printTest(genRandomWithPattern(1, 4, genRandomPattern(75)), "Small random test. from = 1, to = 4, density = 75");
        printTest(genRandomWithPattern(1, 5, genRandomPattern(42)), "Small random test. from = 1, to = 5, density = 42");
        printTest(genRandomWithPattern(1, 6, genRandomPattern(90)), "Small random test. from = 1, to = 6, density = 90");
        printTest(genRandomWithPattern(23, 325, genRandomPattern(23)), "Random test. from = 23, to = 325, density = 23");
        printTest(genRandomWithPattern(1, MAXC, genRandomPattern(50)), "Random test. from = 1, to = 10^9, density = 50");
        printTest(genRandomWithPattern(2347, 322385, genRandomPattern(12)), "Random test. density = 12");
        printTest(genRandomWithPattern(1, 1, PATTERN_SPECIAL_1), "Special test. Euler path");
        printTest(genRandomWithPattern(1, 2, PATTERN_ONLY_HOR), "Only horizontal lines, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_ONLY_HOR), "Only horizontal lines, small values");
        printTest(genRandomWithPattern(1, 4, PATTERN_ONLY_HOR), "Only horizontal lines, small values");
        printTest(genRandomWithPattern(1, 5, PATTERN_ONLY_HOR), "Only horizontal lines, small values");
        printTest(genRandomWithPattern(1, 15, PATTERN_ONLY_HOR), "Only horizontal lines, small values");
        printTest(genRandomWithPattern(1, 2, PATTERN_ONLY_VER), "Only vertical lines, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_ONLY_VER), "Only vertical lines, small values");
        printTest(genRandomWithPattern(1, 4, PATTERN_ONLY_VER), "Only vertical lines, small values");
        printTest(genRandomWithPattern(1, 5, PATTERN_ONLY_VER), "Only vertical lines, small values");
        printTest(genRandomWithPattern(1, 13, PATTERN_ONLY_VER), "Only vertical lines, small values");
        printTest(genRandomWithPattern(1, 11, PATTERN_SNAKE_VER), "Vertical snake, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_SNAKE_VER), "Vertical snake, small values");
        printTest(genRandomWithPattern(1, 2, PATTERN_SNAKE_VER), "Vertical snake, small values");
        printTest(genRandomWithPattern(1, MAXC, PATTERN_SNAKE_VER), "Vertical snake, large values");
        printTest(genRandomWithPattern(1, 4, PATTERN_SNAKE_HOR), "Horizontal snake, small values");
        printTest(genRandomWithPattern(1, 5, PATTERN_SNAKE_HOR), "Horizontal snake, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_SNAKE_HOR), "Horizontal snake, small values");
        printTest(genRandomWithPattern(1, 2, PATTERN_SNAKE_HOR), "Horizontal snake, small values");
        printTest(genRandomWithPattern(1, 6, PATTERN_SNAKE_HOR), "Horizontal snake, small values");
        printTest(genRandomWithPattern(1, MAXC, PATTERN_SPECIAL_H), "Special test, H");
        printTest(genRandomWithPattern(1, MAXC, PATTERN_SPECIAL_5), "Special test, 5");
        printTest(genRandomWithPattern(1, MAXC, PATTERN_SPECIAL_BRIDGES), "Special test, bridges");
        printTest(genRandomWithPattern(1, 2, PATTERN_ANY), "Random full, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_ANY), "Random full, small values");
        printTest(genRandomWithPattern(1, 5, PATTERN_ANY), "Random full, small values");
        printTest(genRandomWithPattern(1, 10, PATTERN_ANY), "Random full, small values");
        printTest(genRandomWithPattern(32823, 2348234, PATTERN_ANY), "Random full, large values");
        printTest(genRandomWithPattern(MAXC - 1, MAXC, PATTERN_ANY), "Random full, small values");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES), "Lots of bridges, one big component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES), "Lots of bridges, one big odd component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_2), "Lots of bridges, one small odd component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_2), "Lots of bridges, one small odd component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_3), "Lots of bridges, one small even component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_3), "Lots of bridges, one small even component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_4), "Lots of bridges, one big even component");
        printTest(genRandomWithPattern(1, 3, PATTERN_LOTS_OF_BRIDGES_4), "Lots of bridges, one big even component");
        descrWritter.close();
    }

    private int[][] genRandomWithPattern(int from, int to, int[][] pattern) {
        int[][] ret = new int[pattern.length][];
        for (int i = 0; i < pattern.length; i++) {
            ret[i] = new int[pattern[i].length];
            for (int j = 0; j < ret[i].length; j++) {
                if (pattern[i][j] == -1) {
                    ret[i][j] = random.nextInt(to - from + 1) + from;
                } else {
                    if (pattern[i][j] == 0) {
                        ret[i][j] = 0;
                    } else {
                        ret[i][j] = randIntWithDegree(pattern[i][j]);
                    }
                }
            }
        }
        return ret;
    }

    private int randIntWithDegree(int degree) {
        if (degree <= 0 || degree >= 30) {
            throw new AssertionError();
        }
        int from = 1 << (degree - 1);
        int to = Math.min(MAXC, (1 << degree) - 1);
        return random.nextInt(to - from + 1) + from;
    }

    private int[][] genRandomPattern2(int from, int to) {
        int [][]ret = new int[5][];
        for (int i = 0; i < 5; i++) {
            int size = i % 2 == 0 ? 3 : 4;
            ret[i] = new int[size];
            for (int j = 0; j < size; j++) {
                ret[i][j] = random.nextInt(to - from + 1) + from;
            }
        }
        return ret;
    }

    private int[][] genRandomPattern(int density) {
        int[][] ret = new int[5][];
        for (int i = 0; i < 5; i++) {
            int size = i % 2 == 0 ? 3 : 4;
            ret[i] = new int[size];
            for (int j = 0; j < size; j++) {
                int p = random.nextInt(100);
                ret[i][j] = p < density ? 0 : -1;
            }
        }
        return ret;
    }


    private int[][] genEqual(int c) {
        return new int[][]{{c, c, c}, {c, c, c, c},
                {c, c, c}, {c, c, c, c}, {c, c, c}};
    }

    public static void main(String[] args) {
        try {
            new TestGen().run();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(42);
        }
    }
}
