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

public class SemipalTestGen {

	final static Random rng = new Random("semipal".hashCode());
	final int MAX_ALPH = 26;
	final int MAX_N = 3000;

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

	PrintWriter out;
	int countTests = 0;

	void generate() {
		generateSamples();

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

	private void generateSmall() {
		printTest("a");
		printTest("z");
		printTest("bc");
		printTest("dd");

		for (int len = 5; len <= 9; len++) {
			printTest(randomString(len, 2));
		}

		printTest(repeat("a", 10));
		printTest(repeat("ab", 5));
		printTest(repeat("abc", 3));
		printTest(repeat("aba", 3));

		printTest(thueMorseString(10));
		printTest(fibonacciString(10));
		printTest(abacabaString(10));
	}

	private void generateMedium() {

		for (int len = 20; len <= 100; len += 20) {
			printTest(randomString(len, rng.nextInt(3) + 2));
		}

		printTest(repeat("a", 100));
		printTest(repeat("cd", 50));

		printTest(thueMorseString(100));
		printTest(fibonacciString(100));
		printTest(abacabaString(100));
		printTest(strangeAbacaba(100));

		printRandomReplaces(abacabaString(100), 1, 3);
		printRandomReplaces(abacabaString(100), 1, 10);

		printRandomReplaces(thueMorseString(100), 1, 3);
		printRandomReplaces(thueMorseString(100), 1, 10);
		
		printRandomReplaces(strangeAbacaba(100), 1, 3);
		printRandomReplaces(strangeAbacaba(100), 1, 10);
	}

	private void generateLarge() {

		for (int len = 500; len <= MAX_N; len += 500) {
			printTest(randomString(len, rng.nextInt(6) + 2));
		}

		printTest(fibonacciString(MAX_N));

		printRandomReplaces(abacabaString(MAX_N), 1, 0);
		printRandomReplaces(abacabaString(MAX_N), 1, 5);
		printRandomReplaces(abacabaString(MAX_N), 1, 20);
		printRandomReplaces(abacabaString(MAX_N), 1, 100);

		printRandomReplaces(thueMorseString(MAX_N), 1, 0);
		printRandomReplaces(thueMorseString(MAX_N), 1, 5);
		printRandomReplaces(thueMorseString(MAX_N), 1, 20);
		printRandomReplaces(thueMorseString(MAX_N), 1, 100);

		printTest(randomString(MAX_N, 3));
		printTest(randomString(MAX_N, 8));
		printTest(randomString(MAX_N, 15));
		printTest(randomString(MAX_N, MAX_ALPH));
		
		printRandomReplaces(repeat("z", MAX_N), 1, 0);
		printRandomReplaces(repeat("z", MAX_N), 1, 10);
		printRandomReplaces(repeat("z", MAX_N), 1, 100);

		printRandomReplaces(repeat("pq", MAX_N / 2), 1, 0);
		printRandomReplaces(repeat("pq", MAX_N / 2), 1, 20);
		printRandomReplaces(repeat("pq", MAX_N / 2), 1, 200);
		
		printRandomReplaces(strangeAbacaba(MAX_N), 1, 0);
		printRandomReplaces(strangeAbacaba(MAX_N), 1, 5);
		printRandomReplaces(strangeAbacaba(MAX_N), 1, 20);
		printRandomReplaces(strangeAbacaba(MAX_N), 1, 100);
	}
	
	private String strangeAbacaba(int length) {
		int order = 1;
		while ((1 << (order + 1)) - 1 <= length) {
			order++;
		}
		
		char[] alph1 = randomAlphabet(order), alph2 = randomAlphabet(order);
		String s1 = specialAbacaba(alph1, (1 << (order - 1)) - 1);
		String s2 = specialAbacaba(alph2, (1 << (order - 1)) - 1);
		
		String result = s1 + 'a' + s2;
		for (char c = 'a'; c <= 'z'; c++) {
			if (new String(alph1).indexOf(c) == -1 && new String(alph2).indexOf(c) == -1) {
				result = s1 + c + s2;
				break;
			}
		}
		StringBuilder sb = new StringBuilder(result);
		while (sb.length() < length) {
			sb.append((char) ('a' + rng.nextInt(MAX_ALPH)));
		}
		return sb.toString();
	}
	
	private String specialAbacaba(char[] alph1, int len) {
		char[] c = new char[len];
		for (int i = 0; i < len; i++) {
			c[i] = alph1[Integer.numberOfTrailingZeros(i + 1)];
		}
		return new String(c);
	}

	private void printRandomReplaces(String s, int countPrints, int replaces) {
		for (int i = 0; i < countPrints; i++) {
			char[] c = s.toCharArray();
			for (int j = 0; j < replaces; j++) {
				int pos = rng.nextInt(s.length());
				c[pos] = (char) ('a' + rng.nextInt(26));
			}
			printTest(new String(c));
		}
	}

	private String thueMorseString(int length) {
		char[] c = new char[length];
		for (int i = 0; i < length; i++) {
			c[i] = (char) ('a' + Integer.bitCount(i));
		}
		return new String(c);
	}

	private String fibonacciString(int length) {
		String cur = "a";
		while (cur.length() < length) {
			StringBuilder sb = new StringBuilder();
			for (char c : cur.toCharArray()) {
				if (c == 'a') {
					sb.append('b');
				} else {
					sb.append("ba");
				}
			}
			cur = sb.toString();
		}

		return cur.substring(0, length);
	}

	private String abacabaString(int length) {
		char[] c = new char[length];
		for (int i = 0; i < length; i++) {
			c[i] = (char) ('a' + Integer.numberOfTrailingZeros(i + 1));
		}
		return new String(c);
	}

	private String repeat(String s, int times) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < times; i++) {
			sb.append(s);
		}
		return sb.toString();
	}

	private String randomString(int length, int alph) {
		char[] c = new char[length];
		char[] symbols = randomAlphabet(alph);
		for (int i = 0; i < length; i++) {
			c[i] = symbols[rng.nextInt(alph)];
		}
		return new String(c);
	}

	private char[] randomAlphabet(int alph) {
		char[] result = new char[26];
		for (int i = 0; i < 26; i++) {
			int pos = rng.nextInt(i + 1);
			result[i] = result[pos];
			result[pos] = (char) ('a' + i);
		}
		return Arrays.copyOf(result, alph);
	}

	private void generateSamples() {
		printTest("aaacaba");
		printTest("aabbaccaa");
	}

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

			out.println(s.length());
			out.println(s);

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