/*--------------------------------------------------------------*/
/* ACM ICPC 2011-2012, NEERC                                    */
/* Northern Subregional Contest                                 */
/* St Petersburg, October 29, 2011                              */
/*--------------------------------------------------------------*/
/* Problem C. Cube Coloring                                     */
/*                                                              */
/* Original idea         Georgiy Korneev                        */
/* Problem statement     Maxim Buzdalov                         */
/* Testset               Maxim Buzdalov                         */
/*--------------------------------------------------------------*/
/* Solution                                                     */
/*                                                              */
/* Author                Maxim Buzdalov                         */
/*--------------------------------------------------------------*/
 
import ru.ifmo.testlib.Checker;
import ru.ifmo.testlib.InStream;
import ru.ifmo.testlib.Outcome;

import java.util.*;

/**
 * Checker for Northern QF'2011 Problem C: Cube Coloring.
 * @author Maxim Buzdalov
 */
public class Check implements Checker {
    private static final int[][] neighbours = {
        {0, 1, 2, 3, 4, 5},
        {1, 0, 4, 5, 2, 3},
        {2, 0, 1, 5, 3, 4},
        {3, 0, 2, 5, 4, 1},
        {4, 0, 3, 5, 1, 2},
        {5, 4, 3, 2, 1, 0}
    };

    private static String sort(String v) {
        char[] chars = v.toCharArray();
        Arrays.sort(chars, 1, chars.length);
        return String.valueOf(chars);
    }

    private static void checkString(String s, String role, int expectedLength, Outcome.Type onFail) {
        if (s.length() != expectedLength) {
            throw new Outcome(onFail, role + " '" + s + "' should have had a length of " + expectedLength);
        }
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) < 'A' || 'Z' < s.charAt(i)) {
                throw new Outcome(onFail, role + " '" + s + "' should not contain a character '" + s.charAt(i) + "'");
            }
        }
    }

    private static String smallest(String source) {
        char[] optimumChars = source.toCharArray();
        char[] currentChars = new char[6];
        for (int[] n : neighbours) {
            for (int a = 0; a < 4; ++a) {
                currentChars[0] = source.charAt(n[0]);
                currentChars[5] = source.charAt(n[5]);
                for (int i = 0; i < 4; ++i) {
                    currentChars[1 + i] = source.charAt(n[1 + (i + a) % 4]);
                }
                for (int i = 0; i < 6; ++i) {
                    if (currentChars[i] != optimumChars[i]) {
                        if (currentChars[i] < optimumChars[i]) {
                            System.arraycopy(currentChars, 0, optimumChars, 0, 6);
                        }
                        break;
                    }
                }
            }
        }
        return String.valueOf(optimumChars);
    }

    private static String[] vision(String cube) {
        String[] rv = new String[6];
        for (int i = 0; i < 6; ++i) {
            StringBuilder v = new StringBuilder();
            for (int j = 0; j < 5; ++j) {
                v.append(cube.charAt(neighbours[i][j]));
            }
            rv[i] = sort(v.toString());
        }
        Arrays.sort(rv);
        return rv;
    }

    private List<String> readAnswers(InStream stream, String role, String[] input, Outcome.Type onFail) {
	    List<String> answers = new ArrayList<String>();
	    Map<String, String> smallestMap = new HashMap<String, String>();
	    boolean isImpossible = false;
	    while (!stream.isEoF()) {
	        String line = stream.nextLine();
	        if (line.equals("Impossible")) {
	            isImpossible = true;
	        } else {
	            checkString(line, role + " answer line", 6, onFail);
	            answers.add(line);
	            String small = smallest(line);
	            if (smallestMap.containsKey(small)) {
	                throw new Outcome(onFail, role + " answer contains equal cubes: '" +
	                    smallestMap.get(small) + "' and '" + line + "'");
	            }
	            smallestMap.put(small, line);
	        }
	    }
	    if (isImpossible != answers.isEmpty()) {
	        throw new Outcome(onFail, isImpossible
	                ? role + " answer contains 'Impossible' and something else"
	                : role + " answer is empty");
	    }
	    if (answers.size() > 2) {
	        throw new Outcome(onFail, role + " answer has more than 2 lines");
	    }
	    for (String s : answers) {
	        if (!Arrays.equals(input, vision(s))) {
	            throw new Outcome(onFail, role + " answer line '" + s + "' contradicts with vision");
	        }
	    }
	    return answers;
    }

	public Outcome test(InStream inf, InStream ouf, InStream ans) {
	    String[] input = new String[6];
        String inl = inf.nextLine();
        if (!inf.isEoF()) {
	        throw new Outcome(Outcome.Type.FAIL, "Input has more than 1 line");
	    }
        if (inl.isEmpty()) {
            throw new Outcome(Outcome.Type.FAIL, "The input line is empty");
        } else if (!inl.equals(inl.trim())) {
            throw new Outcome(Outcome.Type.FAIL, "The input line starts or ends with spaces");
        } else if (inl.indexOf("  ") != -1) {
            throw new Outcome(Outcome.Type.FAIL, "The input line contains two or more consecutive spaces");
        } else {
            for (int i = 0; i < inl.length(); ++i) {
                if (inl.charAt(i) < ' ') {
                    throw new Outcome(Outcome.Type.FAIL, "The input line contains a character with code " + (int) (inl.charAt(i)));
                }
            }
        }
        StringTokenizer st = new StringTokenizer(inl);
        int stct = st.countTokens();
        if (stct != 6) {
            throw new Outcome(Outcome.Type.FAIL, "The input line contains " + stct + " tokens, but should contain 6");
        }
	    for (int i = 0; i < 6; ++i) {
	        String v = st.nextToken();
	        checkString(v, "Input word", 5, Outcome.Type.FAIL);
	        input[i] = sort(v);
	    }
	    Arrays.sort(input);

        List<String> jury = readAnswers(ans, "Jury", input.clone(), Outcome.Type.FAIL);
        List<String> cont = readAnswers(ouf, "Contestant's", input.clone(), Outcome.Type.WA);
        if (jury.size() < cont.size()) {
            throw new Outcome(Outcome.Type.FAIL, "Contestant found more answers than jury");
        }
        if (jury.size() > cont.size()) {
            throw new Outcome(Outcome.Type.WA, "Contestant found less answers than jury");
        }
        String[] okStrings = {"Impossible", "One", "Many"};
        return new Outcome(Outcome.Type.OK, okStrings[jury.size()]);
	}
}
