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

public class Tests {
	private static File dir = new File("tests");
	private int curTest = 1;
	private static boolean verbose = true;

	private String testName;
	private PrintWriter out;
	private Random r = new Random(566843513814L);
	
	final static int MAX = 50000;
	final static int MAX_LEN = 139;
	final static int MAX_HANDLE = 20;

	public void run() {
		t("@Petr: Leaving for #NEERC tomorrow!",
				"@Roman: This #NEERC is going to be awesome!",
				"@Stone_in_forest: Nothing happened today.",
				"@NEERCNews: @Petr Don't forget an umbrella :)",
				"@Lydia: @NEERCNews cares about @Petr - so cute ^_^",
				"@Lydia: @Lydia @NEERCNews @Petr it won't be raining though!");
		t("@Jury: Let's check minimal test");
		t("@Jury: The author of this problem is...", "@mikhail_dvorkin: @Jury ...me! #neerc2013");
		t("@Jury: Prepare for the tWint of Evil!", "@Jury:   #cornercase @ @Jury @@  Jury@  ");
		t("@jury: Prepare for the case-dependent tWint of Evil!", "@Jury:   #cornercase @ @jury @Jury @@  Jury@  ");
		for (int p = 0; p <= 1; p++) {
			String[] s = new String[MAX];
			for (int i = 0; i < s.length; i++) {
				int j = (p == 1 && i % 97 == 0 ? i - 2 : i - 1);
				s[i] = "@u" + i + ": mentioning @u" + j + " here";
			}
			t(s);
		}
		for (int t = 0; t <= 1; t++) {
			int n = (int) Math.sqrt(MAX);
			String[] s = new String[n * n];
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < n; j++) {
					s[n * i + j] = "@" + i + "_" + j + ": " +
							"@" + (i - 1) + "_" + j + " <- #up, " +
							"@" + i + "_" + (j - 1) + " <- #left, " +
							"@" + (i - 1) + "_" + (j - 1) + " <- #diag" +
							(t > 0 ? " @" + (i - 1) + "_" + (j + 1) : "");
				}
			}
			t(s);
		}
		for (int m = 1; m <= 1000; m *= 9) {
			int n = MAX;
			String[] handles = new String[m];
			int handleLen = Math.min(Math.max(MAX_LEN / m - 1, 2), MAX_HANDLE);
			for (int i = 0; i < m; i++) {
				handles[i] = randomHandle(handleLen);
			}
			ArrayList<String> hs = new ArrayList<String>();
			String[] s = new String[n];
			for (int i = 0; i < n; i++) {
				hs.clear();
                int len = 0;
				for (int j = r.nextInt(m); j > 0; j--) {
                    if (len + 1 + handles[r.nextInt(m)].length() <= MAX_LEN) {
                        hs.add(handles[r.nextInt(m)]);
                        if (len > 0) {
                            len++;
                        }
                        len += handles[r.nextInt(m)].length();
                    }
					if (hs.size() > MAX_LEN / 2 - 1) {
						break;
					}
				}
                assert len <= MAX_LEN;
				s[i] = handles[r.nextInt(m)] + ": " + randomMessage(hs, 0.3, 0.3);
			}
			t(s);
		}
		t(mentionPrev(MAX, 1, 1));
		t(mentionPrev(MAX, 5, 0.1));
		t(mentionPrev(MAX, 5, 0.2));
		t(mentionPrev(MAX, 5, 0.3));
		t(mentionPrev(MAX, 5, 0.5));
		t(mentionPrev(MAX, 5, 0.75));
		t(mentionPrev(MAX, 5, 1));
	}
	
	String[] mentionPrev(int n, int prev, double prob) {
		String[] handle = new String[n];
		String[] s = new String[n];
		for (int i = 0; i < n; i++) {
			handle[i] = randomHandle();
			ArrayList<String> handles = new ArrayList<String>();
            int len = 0;
			for (int j = i - prev; j < i; j++) {
				if (j < 0) {
					continue;
				}
				if (r.nextDouble() < prob) {
                    if (len > 0) {
                        len++;
                    }
                    len += handle[j].length();
					handles.add(handle[j]);
				}
			}
            assert len <= MAX_LEN;
			s[i] = handle[i] + ": " + randomMessage(handles, 0, 0);
		}
		return s;
	}
	
	private String randomMessage(ArrayList<String> tokens, double pNone, double pSingle) {
		int len = 0;
		for (String s : tokens) {
			if (len > 0) {
				len++;
			}
			len += s.length();
		}
        assert len <= MAX_LEN;
		double v = r.nextDouble();
		if (v < pNone) {
			if (len == 0) {
				tokens.add(randomText(1));
			}
		} else if (v < pNone + pSingle) {
			while (len + 2 <= MAX_LEN) {
				tokens.add(randomText(1));
				len += 2;
			}
		} else {
			if (len + 2 <= MAX_LEN) {
				tokens.add(randomText(MAX_LEN - len - 1));
			}
		}
		Collections.shuffle(tokens, r);
		StringBuilder sb = new StringBuilder();
		for (String s : tokens) {
			if (sb.length() > 0) {
				sb.append(" ");
			}
			sb.append(s);
		}
        assert sb.length() <= MAX_LEN;
		return sb.toString();
	}

	String randomHandle() {
		return randomHandle(MAX_HANDLE);
	}

	String randomHandle(int len) {
		StringBuilder sb = new StringBuilder("@");
		while (sb.length() < len) {
			char c = (char) (r.nextInt(127 - 32) + 32);
			if (c == ':' || c == ' ') {
				continue;
			}
			sb.append(c);
		}
		return sb.toString();
	}

	String randomText(int len) {
		StringBuilder sb = new StringBuilder();
		while (sb.length() < len) {
			char c = (char) (r.nextInt(127 - 32) + 32);
			sb.append(c);
		}
		return sb.toString();
	}

	private void t(String... messages) {
        for (String message : messages) {
            String[] parts = message.split(": ", 2);
            assert parts[1].length() <= MAX_LEN;
        }
        open();
		out.println(messages.length);
		for (Object line : messages) {
			out.println(line);
		}
		close();
	}

	private void open() {
		try {
			testName = "" + curTest / 10 + curTest % 10;
			out = new PrintWriter(new File(dir, testName));
			if (verbose) { 
				System.out.print("Generating " + testName + "...");
			}
		} catch (IOException e) {
			throw new RuntimeException("Unable to open " + testName + " for writing", e);
		}
	}

	private void close() {
		out.close();
		if (verbose) {
			System.out.println(" done.");
		}
		curTest++;
	}

	public static void main(String[] args) {
		Locale.setDefault(Locale.US);
		dir.mkdirs();
		new Tests().run();
	}
}
