// Verificador de "Un diff pequeño": verificador ENTRADA ESPERADA SALIDA.
// En ESPERADA está solo el mínimo de cambios. Aquí se comprueba que la lista tenga ese largo, que
// esté bien escrita y que, al aplicarla, las dos versiones queden idénticas. Termina con 0 si todo
// está bien, y con 1 y un mensaje si no.
#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::string line;
    std::getline(input, line);
    std::vector<std::string> old_lines(n);
    std::vector<std::string> new_lines(m);
    for (std::string& text : old_lines) std::getline(input, text);
    for (std::string& text : new_lines) std::getline(input, text);

    std::size_t minimum = 0;
    expected >> minimum;

    std::string word;
    std::size_t count = 0;
    if (!(output >> word) || !parse_number(word, count)) reject("la primera palabra no es un número");
    if (count != minimum) reject("mínimo de cambios esperado: " + std::to_string(minimum) + ", salieron " + word);

    std::vector<bool> erased(n, false);
    std::vector<bool> added(m, false);
    std::size_t last_erased = 0;
    std::size_t last_added = 0;
    for (std::size_t k = 0; k < count; k++) {
        std::string sign;
        std::string number;
        std::size_t index = 0;
        if (!(output >> sign >> number)) reject("faltan cambios en la lista");
        if (!parse_number(number, index)) reject("número inválido: " + number);
        if (sign == "-") {
            if (index < 1 || index > n) reject("línea vieja fuera de rango: " + number);
            if (index <= last_erased) reject("los números de los - deben ser crecientes");
            last_erased = index;
            erased[index - 1] = true;
        } else if (sign == "+") {
            if (index < 1 || index > m) reject("línea nueva fuera de rango: " + number);
            if (index <= last_added) reject("los números de los + deben ser crecientes");
            last_added = index;
            added[index - 1] = true;
        } else {
            reject("se esperaba - o +, salió " + sign);
        }
    }
    if (output >> word) reject("sobra texto después de la lista: " + word);

    // Lo que no está marcado tiene que coincidir, línea por línea y en orden.
    std::size_t i = 0;
    std::size_t j = 0;
    while (true) {
        while (i < n && erased[i]) i++;
        while (j < m && added[j]) j++;
        if (i == n || j == m) break;
        if (old_lines[i] != new_lines[j]) {
            reject("la línea vieja " + std::to_string(i + 1) + " se conserva, pero no es igual a la nueva " +
                   std::to_string(j + 1));
        }
        i++;
        j++;
    }
    if (i != n || j != m) reject("después de aplicar la lista, una versión tiene más líneas que la otra");
    return 0;
}
