import java.io.*;
import java.util.*;

public class JavaTestGen {

	final static Random rng = new Random("java".hashCode());

	final int MAX_N = 100_000;
	final int MAX_LEN = 100_000;
	final int MAX_NAME = 10;
	final int MAX_A = 1_000_000_000;

	final int ALPH = 26;

	public static void main(String[] args) throws Exception {
		new JavaTestGen().generate();
	}

	PrintWriter out;
	int countTests = 0;

	void generate() {
		generateSamples();

		generateSmall();
		generateMedium();
		generateLarge();
	}

	private void generateSmall() {
		int minLen = 1, maxLen = 5;
		for (int n = 2; n <= 20; n += 4) {
			TestCase tc = new TestCase();
			HashSet<String> names = new HashSet<>();
			List<Array> arrays = new ArrayList<>();

			for (int k = 0; k < n / 2; k++) {
				String name = newName(names, 1);
				int len = randRange(minLen, maxLen);
				Array arr = new Array(name, getNIntegers(len, 1, 20));
				names.add(name);
				arrays.add(arr);
				tc.addStatement(new InitStatement(arr.name, arr.list));
			}

			for (int k = 0; k < n / 2; k++) {
				Array arr = arrays.get(rng.nextInt(n / 2));
				int pos = randRange(arr.from, arr.to);
				tc.addStatement(new GetStatement(arr.name, pos));
			}

			printTest(tc);
		}

		printBranch(10, 20);
		printBranchDown(10, 30);
		printSegmentTree(16, 100);
	}

	private void generateMedium() {
		int minLen = 10, maxLen = 50;

		for (int n = 100; n <= 1000; n += 100) {
			TestCase tc = new TestCase();
			HashSet<String> names = new HashSet<>();
			List<Array> arrays = new ArrayList<>();

			for (int k = 0; k < n / 2; k++) {
				String name = newName(names, 4);
				int len = randRange(minLen, maxLen);
				Array arr = new Array(name, getNIntegers(len, 1, 20));
				names.add(name);
				arrays.add(arr);
				tc.addStatement(new InitStatement(arr.name, arr.list));
			}

			for (int k = 0; k < n / 2; k++) {
				Array arr = arrays.get(rng.nextInt(n / 2));
				int pos = randRange(arr.from, arr.to);
				tc.addStatement(new GetStatement(arr.name, pos));
			}

			printTest(tc);
		}

		printBranch(200, 500);
		printBranchDown(200, 1000);
		printSegmentTree(256, 1000);
	}

	private void generateLarge() {
		int minLen = 50, maxLen = 140;

		for (int n = 10000; n <= MAX_N; n += 10000) {
			TestCase tc = new TestCase();
			HashSet<String> names = new HashSet<>();
			List<Array> arrays = new ArrayList<>();

			for (int k = 0; k < n / 1000; k++) {
				String name = newName(names, MAX_NAME);
				int len = randRange(minLen, maxLen);
				Array arr = new Array(name, getNIntegers(len, 1, 20));
				names.add(name);
				arrays.add(arr);
				tc.addStatement(new InitStatement(arr.name, arr.list));
			}

			for (int k = n / 1000; k < n; k++) {
				Array arr = arrays.get(rng.nextInt(n / 1000));
				int pos = randRange(arr.from, arr.to);
				tc.addStatement(new GetStatement(arr.name, pos));
			}

			printTest(tc);
		}

		printBranch(MAX_N / 2, MAX_N / 2);
		printBranchDown(MAX_N / 2, MAX_N / 2);
		printSegmentTree(1 << 15, (MAX_N - (1 << 15) * 2 + 1));

		printAdds(MAX_LEN, MAX_N / 2, MAX_N / 2);
		printAdds(MAX_LEN, MAX_N - 10, 10);

		printSublistsAndAdds(MAX_LEN, MAX_N / 4, MAX_N / 4, MAX_N / 2);
		printSublistsAndAdds(MAX_LEN, MAX_N / 4, 3 * MAX_N / 4 - 10, 10);

		printAddsAndSublists(MAX_LEN, MAX_N / 4, MAX_N / 4, MAX_N / 2);
		printAddsAndSublists(MAX_LEN, MAX_N / 4, 3 * MAX_N / 4 - 10, 10);

		printRandom(10, 5000, 100, MAX_N - 100);
		printRandom(10, 500, 100, MAX_N - 100);

		printRandom(100, 500, 1000, MAX_N - 1000);
		printRandom(5000, 5, 20000, MAX_N - 20000);
	}

	private void printRandom(int roots, int expLen, int lists, int qs) {
		List<String> names = getLexminNames(lists, MAX_NAME);
		List<Array> arrays = new ArrayList<>();
		TestCase tc = new TestCase();

		for (int i = 0; i < roots; i++) {
			int len = expLen + (randRange(-50, 50));
			while (len <= 0) {
				len = expLen + (randRange(-5, 5));
			}
			List<Integer> numbers = getNIntegers(len, 1, MAX_A);
			Array arr = new Array(names.get(i), numbers);
			arrays.add(arr);
			tc.addStatement(new InitStatement(arr.name, arr.list));
		}

		for (int i = roots; i < lists; i++) {
			int r = rng.nextInt(i);
			Array parent = arrays.get(r);
			int from, to;

			if (parent.size() > 4) {
				from = rng.nextInt(parent.size() / 4);
				to = parent.size() - 1 - rng.nextInt(parent.size() / 4);
			} else {
				from = 0;
				to = parent.size() - 1;
			}

			Array cur = new Array(names.get(i), parent, from, to);
			arrays.add(cur);
			tc.addStatement(new SubListStatement(cur.name, parent.name, from,
					to));
		}

		for (int i = 0; i < qs; i++) {
			int type = rng.nextInt(3);
			if (type == 0) {
				int pos = rng.nextInt(roots);
				Array ar = arrays.get(pos);

				int newVal = randRange(1, MAX_A);

				ar.list.add(newVal);
				ar.to++;

				tc.addStatement(new AddStatement(ar.name, newVal));
			} else if (type == 1) {

				int pos = rng.nextInt(lists);
				Array ar = arrays.get(pos);

				int ind = rng.nextInt(ar.size());
				tc.addStatement(new GetStatement(ar.name, ind));
			} else {
				int pos = rng.nextInt(lists);
				Array ar = arrays.get(pos);

				int ind = rng.nextInt(ar.size());
				int newVal = randRange(1, MAX_A);

				tc.addStatement(new SetStatement(ar.name, ind, newVal));
			}
		}

		printTest(tc);
	}

	private void printSublistsAndAdds(int n, int lists, int adds, int qs) {
		
		List<String> names = getLexminNames(lists, MAX_NAME);
		List<Integer> numbers = getNIntegers(n, 1, MAX_A);
		List<Array> arrays = new ArrayList<>();

		Array root = new Array(names.get(0), numbers);
		arrays.add(root);

		TestCase tc = new TestCase();
		tc.addStatement(new InitStatement(root.name, root.list));

		for (int i = 1; i < lists; i++) {
			int from = rng.nextInt(n / 4), to = n / 2 + rng.nextInt(n / 4);
			Array array = new Array(names.get(i), root, from, to);
			tc.addStatement(new SubListStatement(array.name, root.name, from,
					to));
			arrays.add(array);
		}

		for (int i = 1; i < adds; i++) {
			int newVal = randRange(1, MAX_A);
			root.list.add(newVal);
			root.to++;

			tc.addStatement(new AddStatement(root.name, newVal));
		}

		for (int i = 0; i < qs; i++) {
			int arr = rng.nextInt(arrays.size());
			Array a = arrays.get(arr);
			int pos = rng.nextInt(a.size());

			if (rng.nextBoolean()) {
				int newVal = randRange(1, MAX_A);
				tc.addStatement(new SetStatement(a.name, pos, newVal));
			} else {
				tc.addStatement(new GetStatement(a.name, pos));
			}
		}

		printTest(tc);
	}

	private void printAddsAndSublists(int n, int lists, int adds, int qs) {
		List<String> names = getLexminNames(lists, MAX_NAME);
		List<Integer> numbers = getNIntegers(n, 1, MAX_A);
		List<Array> arrays = new ArrayList<>();

		Array root = new Array(names.get(0), numbers);
		arrays.add(root);

		TestCase tc = new TestCase();
		tc.addStatement(new InitStatement(root.name, root.list));

		for (int i = 1; i < adds; i++) {
			int newVal = randRange(1, MAX_A);
			root.list.add(newVal);
			root.to++;

			tc.addStatement(new AddStatement(root.name, newVal));
		}

		for (int i = 1; i < lists; i++) {
			int curLen = root.size();
			int from = rng.nextInt(curLen / 4), to = curLen / 2
					+ rng.nextInt(curLen / 4);
			Array array = new Array(names.get(i), root, from, to);
			tc.addStatement(new SubListStatement(array.name, root.name, from,
					to));
			arrays.add(array);
		}

		for (int i = 0; i < qs; i++) {
			int arr = rng.nextInt(arrays.size());
			Array a = arrays.get(arr);
			int pos = rng.nextInt(a.size());

			if (rng.nextBoolean()) {
				int newVal = randRange(1, MAX_A);
				tc.addStatement(new SetStatement(a.name, pos, newVal));
			} else {
				tc.addStatement(new GetStatement(a.name, pos));
			}
		}

		printTest(tc);
	}

	private void printAdds(int n, int adds, int qs) {
		List<Integer> numbers = getNIntegers(n, 1, MAX_A);
		Array root = new Array(randomString(MAX_NAME), numbers);
		TestCase tc = new TestCase();

		tc.addStatement(new InitStatement(root.name, root.list));
		for (int i = 1; i < adds; i++) {
			int newVal = randRange(1, MAX_A);
			root.list.add(newVal);
			root.to++;

			tc.addStatement(new AddStatement(root.name, newVal));
		}

		for (int i = 0; i < qs; i++) {
			int pos = rng.nextInt(root.size());
			if (rng.nextBoolean()) {
				int newVal = randRange(1, MAX_A);
				tc.addStatement(new SetStatement(root.name, pos, newVal));
			} else {
				tc.addStatement(new GetStatement(root.name, pos));
			}
		}

		printTest(tc);
	}

	private void printSegmentTree(int n, int q) {
		TestCase tc = new TestCase();

		List<String> names = getLexminNames(2 * n - 1, 4);
		List<Array> arrays = new ArrayList<>();
		List<Integer> numbers = range(1, n);

		Array root = new Array(names.get(0), numbers);
		tc.addStatement(new InitStatement(root.name, root.list));

		int id = 1;
		Queue<Array> queue = new ArrayDeque<>();
		queue.add(root);

		while (!queue.isEmpty()) {
			Array cur = queue.poll();
			arrays.add(cur);
			if (cur.size() > 1) {
				int mid = cur.size() / 2;

				Array left = new Array(names.get(id), cur, 0, mid - 1);
				Array right = new Array(names.get(id + 1), cur, mid,
						cur.size() - 1);
				id += 2;

				queue.add(left);
				queue.add(right);

				tc.addStatement(new SubListStatement(left.name, cur.name, 0,
						mid - 1));
				tc.addStatement(new SubListStatement(right.name, cur.name, mid,
						cur.size() - 1));
			}
		}

		for (int i = 0; i + (root.list.size() - n) + 1< q; i++) {
			int type = rng.nextInt(5) / 2;
			if (type == 0) {
				int ar = rng.nextInt(arrays.size());
				Array array = arrays.get(ar);
				int pos = rng.nextInt(array.size());

				tc.addStatement(new GetStatement(array.name, pos));
			} else if (type == 1) {
				int ar = rng.nextInt(arrays.size());
				Array array = arrays.get(ar);
				int pos = rng.nextInt(array.size());
				int newVal = randRange(1, MAX_A);

				tc.addStatement(new SetStatement(array.name, pos, newVal));
			} else {
				int newVal = randRange(1, MAX_A);

				tc.addStatement(new AddStatement(root.name, newVal));
				root.list.add(newVal);
				root.to++;
			}
		}

		for (int i = n; i < root.list.size(); i++) {
			tc.addStatement(new GetStatement(root.name, i));
		}

		printTest(tc);
	}

	private void printBranch(int n, int q) {
		TestCase tc = new TestCase();

		List<Array> arrays = new ArrayList<>();
		List<String> names = getNNames(n, 10);
		List<Integer> numbers = getNIntegers(n, 1, MAX_A);

		arrays.add(new Array(names.get(0), numbers));
		tc.addStatement(new InitStatement(names.get(0), numbers));

		for (int i = 1; i < n; i++) {
			Array prev = arrays.get(i - 1);
			int from = 0, to = prev.size() - 1;
			if (rng.nextBoolean()) {
				from++;
			} else {
				to--;
			}

			arrays.add(new Array(names.get(i), prev, from, to));
			tc.addStatement(new SubListStatement(names.get(i),
					names.get(i - 1), from, to));
		}

		for (int i = 0; i < q; i++) {
			int a = rng.nextInt(n);
			int pos = rng.nextInt(arrays.get(a).size());
			if (rng.nextBoolean()) {
				int newVal = randRange(1, MAX_A);
				tc.addStatement(new SetStatement(arrays.get(a).name, pos,
						newVal));
			} else {
				tc.addStatement(new GetStatement(arrays.get(a).name, pos));
			}
		}

		printTest(tc);
	}

	private void printBranchDown(int n, int q) {
		TestCase tc = new TestCase();

		List<Array> arrays = new ArrayList<>();
		List<String> names = getNNames(n, 10);
		List<Integer> numbers = getNIntegers(n, 1, MAX_A);

		arrays.add(new Array(names.get(0), numbers));
		tc.addStatement(new InitStatement(names.get(0), numbers));

		for (int i = 1; i < n; i++) {
			Array prev = arrays.get(i - 1);
			int from = 0, to = prev.size() - 1;
			if (rng.nextBoolean()) {
				from++;
			} else {
				to--;
			}

			arrays.add(new Array(names.get(i), prev, from, to));
			tc.addStatement(new SubListStatement(names.get(i),
					names.get(i - 1), from, to));
		}

		int queries = 0;
		for (int i = n - 1; i >= 0; i--) {
			Array arr = arrays.get(i);
			for (int j = 0; j < arr.size(); j++) {
				if (rng.nextBoolean()) {
					int newVal = randRange(1, MAX_A);
					tc.addStatement(new SetStatement(arr.name, j, newVal));
				} else {
					tc.addStatement(new GetStatement(arr.name, j));
				}
				queries++;
				if (queries == q) {
					printTest(tc);
					return;
				}
			}
		}

		printTest(tc);
		return;
	}

	List<String> getLexminNames(int n, int len) {
		char[] c = new char[len];
		Arrays.fill(c, 'a');
		List<String> list = new ArrayList<>();
		for (int i = 0; i < n; i++) {
			list.add(new String(c));

			int j = len - 1;
			while (c[j] == 'z') {
				c[j] = 'a';
				j--;
			}
			c[j]++;
		}
		return list;
	}

	List<String> getNNames(int n, int len) {
		HashSet<String> names = new HashSet<>();
		while (names.size() < n) {
			names.add(newName(names, len));
		}
		return new ArrayList<>(names);
	}

	List<Integer> range(int from, int to) {
		List<Integer> list = new ArrayList<>();
		for (int i = from; i <= to; i++) {
			list.add(i);
		}
		return list;
	}

	List<Integer> getNIntegers(int howMuch, int min, int max) {
		List<Integer> ans = new ArrayList<>();
		for (int i = 0; i < howMuch; i++) {
			ans.add(randRange(min, max));
		}
		return ans;
	}

	private String newName(HashSet<String> names, int length) {
		char[] c = new char[length];
		while (true) {
			for (int i = 0; i < length; i++) {
				c[i] = (char) ('a' + rng.nextInt(ALPH));
			}
			String result = new String(c);
			if (!names.contains(result)) {
				return result;
			}
		}
	}

	private String randomString(int len) {
		char[] c = new char[len];
		for (int i = 0; i < len; i++) {
			c[i] = (char) ('a' + rng.nextInt(ALPH));
		}
		return new String(c);
	}

	private int randRange(int from, int to) {
		return from + rng.nextInt(to - from + 1);
	}

	private void generateSamples() {
		printFirst();
		printSecond();
		printThird();
	}

	private void printFirst() {
		TestCase tc = new TestCase();
		tc.addStatement(new InitStatement("a", Arrays.asList(2, 3, 5)));
		tc.addStatement(new SubListStatement("b", "a", 1, 2));
		tc.addStatement(new GetStatement("b", 0));
		printTest(tc);
	}

	private void printSecond() {
		TestCase tc = new TestCase();
		tc.addStatement(new InitStatement("p", Arrays.asList(2, 4, 8, 16)));
		tc.addStatement(new GetStatement("p", 3));
		tc.addStatement(new InitStatement("q", Arrays.asList(3, 9, 27)));
		tc.addStatement(new AddStatement("q", 5));
		tc.addStatement(new GetStatement("q", 3));
		printTest(tc);
	}

	private void printThird() {
		TestCase tc = new TestCase();
		tc.addStatement(new InitStatement("x", 1, 2, 5, 14, 42));
		tc.addStatement(new SubListStatement("y", "x", 0, 3));
		tc.addStatement(new SubListStatement("z", "y", 1, 3));
		tc.addStatement(new SetStatement("y", 0, 7));
		tc.addStatement(new GetStatement("x", 0));
		tc.addStatement(new GetStatement("z", 0));
		tc.addStatement(new SetStatement("z", 1, 100));
		tc.addStatement(new GetStatement("x", 2));
		tc.addStatement(new GetStatement("y", 2));
		tc.addStatement(new AddStatement("x", 132));
		tc.addStatement(new SetStatement("x", 4, 43));
		tc.addStatement(new GetStatement("x", 4));
		tc.addStatement(new GetStatement("y", 3));
		printTest(tc);
	}

	class Array {
		String name;
		List<Integer> list;
		int from, to;

		public Array(String name, List<Integer> list) {
			this.list = list;
			this.name = name;
			this.from = 0;
			this.to = list.size() - 1;
		}

		public Array(String name, Array other, int from, int to) {
			this.name = name;
			this.from = from + other.from;
			this.list = other.list;
			this.to = this.from + (to - from);
			if (size() <= 0) {
				throw new AssertionError();
			}
		}

		public int size() {
			return to - from + 1;
		}
	}

	class TestCase {
		List<Statement> statements;

		public TestCase() {
			this.statements = new ArrayList<>();
		}

		public TestCase(List<Statement> statements) {
			super();
			this.statements = statements;
		}

		public void addStatement(Statement stmt) {
			statements.add(stmt);
		}
	}

	class Statement {
	}

	class InitStatement extends Statement {
		String name;
		List<Integer> ints;

		public InitStatement(String name, List<Integer> ints) {
			this.name = name;
			this.ints = new ArrayList<>(ints);
		}

		public InitStatement(String name, int... list) {
			this.name = name;
			ints = new ArrayList<>();
			for (int item : list) {
				ints.add(item);
			}
		}
		
		public String listRep() {
			StringBuilder sb = new StringBuilder();
			sb.append(ints.get(0));
			for (int i = 1; i < ints.size(); i++) {
				sb.append(",").append(ints.get(i));
			}
			return sb.toString();
		}

		public String toString() {
			return "List " + name + " = new List(" + listRep() + ")";
		}
	}

	class SubListStatement extends Statement {
		String name, source;
		int from, to;

		public SubListStatement(String name, String source, int from, int to) {
			this.name = name;
			this.source = source;
			this.from = from;
			this.to = to;
		}

		public String toString() {
			return "List " + name + " = " + source + ".subList(" + (from + 1)
					+ "," + (to + 1) + ")";
		}
	}

	class SetStatement extends Statement {
		String name;
		int pos, val;

		public SetStatement(String name, int pos, int val) {
			this.name = name;
			this.pos = pos;
			this.val = val;
		}

		public String toString() {
			return name + ".set(" + (pos + 1) + "," + val + ")";
		}
	}

	class AddStatement extends Statement {
		String name;
		int val;

		public AddStatement(String name, int val) {
			super();
			this.name = name;
			this.val = val;
		}

		@Override
		public String toString() {
			return name + ".add(" + val + ")";
		}
	}

	class GetStatement extends Statement {
		String name;
		int pos;

		public GetStatement(String name, int pos) {
			this.name = name;
			this.pos = pos;
		}

		@Override
		public String toString() {
			return name + ".get(" + (pos + 1) + ")";
		}
	}

	void printTest(TestCase tc) {
		try {
			countTests++;
			String fileName = "../tests/";
			fileName += String.format("%02d", countTests);
			out = new PrintWriter(fileName);

			out.println(tc.statements.size());
			for (Statement stmt : tc.statements) {
				out.println(stmt);
			}

			System.out.println("Done printing test #" + countTests);
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}
}
