// Verificador: se llama con `verificador NN.in NN.out salida`, y termina con 0 si la salida es correcta.
// Hay sudokus con varias soluciones, así que se revisa la salida, sin compararla con una respuesta guardada.
// NN.out solo dice si existe solución: empieza con "imposible" cuando no existe.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

namespace {

int reject(const std::string& reason) {
    std::cout << reason << "\n";
    return 1;
}

}  // namespace

int main(int argc, char** argv) {
    if (argc != 4) return reject("uso: verificador NN.in NN.out salida");
    std::ifstream input(argv[1]), expected(argv[2]), output(argv[3]);

    int k = 0;
    input >> k;
    const int n = k * k;
    std::vector<std::vector<int>> given(n, std::vector<int>(n));
    for (auto& row : given) {
        for (int& value : row) input >> value;
    }

    std::string first_expected, first_output;
    expected >> first_expected;
    output >> first_output;
    const bool impossible = first_expected == "imposible";
    if (first_output.empty()) return reject("salida vacía");
    if (impossible) {
        if (first_output != "imposible") return reject("este sudoku no tiene solución, y la salida dice otra cosa");
        std::string extra;
        if (output >> extra) return reject("hay texto después de imposible");
        return 0;
    }
    if (first_output == "imposible") return reject("este sudoku sí tiene solución");

    std::vector<std::vector<int>> grid(n, std::vector<int>(n));
    for (int r = 0; r < n; r++) {
        for (int c = 0; c < n; c++) {
            if (r == 0 && c == 0) {
                if (first_output.size() > 3 || first_output.find_first_not_of("0123456789") != std::string::npos) {
                    return reject("se esperaba un número");
                }
                grid[0][0] = std::stoi(first_output);
            } else if (!(output >> grid[r][c])) {
                return reject("faltan valores en la salida");
            }
            if (grid[r][c] < 1 || grid[r][c] > n) return reject("un valor está fuera de rango");
            if (given[r][c] != 0 && given[r][c] != grid[r][c]) {
                return reject("cambió un valor dado en la fila " + std::to_string(r + 1));
            }
        }
    }
    std::string extra;
    if (output >> extra) return reject("sobran valores en la salida");

    for (int a = 0; a < n; a++) {
        std::vector<char> row(n + 1, 0), column(n + 1, 0), box(n + 1, 0);
        for (int b = 0; b < n; b++) {
            int in_row = grid[a][b];
            int in_column = grid[b][a];
            int in_box = grid[(a / k) * k + b / k][(a % k) * k + b % k];
            if (row[in_row]++) return reject("un valor se repite en la fila " + std::to_string(a + 1));
            if (column[in_column]++) return reject("un valor se repite en la columna " + std::to_string(a + 1));
            if (box[in_box]++) return reject("un valor se repite en la caja " + std::to_string(a + 1));
        }
    }
    return 0;
}
