// Verificador de "Asignar estudiantes a secciones": verificador ENTRADA ESPERADA SALIDA.
// En ESPERADA está solo la mayor cantidad posible. Primero se comprueba que la lista sea una asignación
// válida y después que tenga esa cantidad. Termina con 0 si todo está bien, y con 1 y un mensaje si no.
#include <algorithm>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

namespace {

[[noreturn]] void reject(const std::string& reason) {
    std::cout << reason << '\n';
    std::exit(1);
}

// Convierte una palabra en número sin riesgo de overflow: solo dígitos, y a lo sumo 9.
bool parse_number(const std::string& word, std::size_t& value) {
    if (word.empty() || word.size() > 9) return false;
    value = 0;
    for (char c : word) {
        if (c < '0' || c > '9') return false;
        value = value * 10 + static_cast<std::size_t>(c - '0');
    }
    return true;
}

}  // namespace

int main(int argc, char* argv[]) {
    if (argc != 4) reject("uso: verificador ENTRADA ESPERADA SALIDA");
    std::ifstream input(argv[1]);
    std::ifstream expected(argv[2]);
    std::ifstream output(argv[3]);

    std::size_t n = 0;
    std::size_t m = 0;
    input >> n >> m;
    std::vector<long long> room(m);
    for (long long& q : room) input >> q;
    std::vector<std::vector<std::size_t>> options(n);
    for (auto& list : options) {
        std::size_t count = 0;
        input >> count;
        list.resize(count);
        for (std::size_t& section : list) input >> section;
    }
    std::size_t best = 0;
    expected >> best;

    std::string word;
    std::size_t count = 0;
    if (!(output >> word) || !parse_number(word, count)) reject("la primera palabra no es un número");

    std::vector<bool> assigned(n, false);
    for (std::size_t k = 0; k < count; k++) {
        std::string student_word;
        std::string section_word;
        std::size_t student = 0;
        std::size_t section = 0;
        if (!(output >> student_word >> section_word)) reject("anunció " + word + " pares y faltan");
        if (!parse_number(student_word, student) || student < 1 || student > n) {
            reject("estudiante inválido: " + student_word);
        }
        if (!parse_number(section_word, section) || section < 1 || section > m) {
            reject("sección inválida: " + section_word);
        }
        if (assigned[student - 1]) reject("el estudiante " + student_word + " aparece dos veces");
        assigned[student - 1] = true;
        const auto& list = options[student - 1];
        if (std::find(list.begin(), list.end(), section) == list.end()) {
            reject("el estudiante " + student_word + " no puede ir a la sección " + section_word);
        }
        if (--room[section - 1] < 0) reject("la sección " + section_word + " supera su cupo");
    }
    if (output >> word) reject("sobra texto después de la lista: " + word);
    if (count != best) {
        reject("la asignación es válida, pero con " + std::to_string(count) + " asignados, y se puede con " +
               std::to_string(best));
    }
    return 0;
}
