// Verificador de "Arbitraje entre monedas": verificador ENTRADA ESPERADA SALIDA.
// En ESPERADA importa solo la primera palabra, SI o NO. Si hay arbitraje, se comprueba la cadena y se
// recalcula su producto con las tasas de la entrada. 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::vector<std::vector<long double>> rate(n, std::vector<long double>(n, 0));
    for (std::size_t e = 0; e < m; e++) {
        std::size_t u = 0;
        std::size_t v = 0;
        long double r = 0;
        input >> u >> v >> r;
        rate[u][v] = r;
    }

    std::string want;
    expected >> want;
    std::string got;
    if (!(output >> got)) reject("la salida está vacía");
    if (got != "SI" && got != "NO") reject("se esperaba SI o NO, salió " + got);
    if (got != want) reject("se esperaba " + want + ", salió " + got);

    std::string word;
    if (got == "NO") {
        if (output >> word) reject("sobra texto después de NO: " + word);
        return 0;
    }

    std::size_t k = 0;
    if (!(output >> word) || !parse_number(word, k)) reject("k no es un número válido");
    if (k < 2 || k > n) reject("k fuera de rango: " + word);
    std::vector<std::size_t> chain(k + 1);
    for (std::size_t& c : chain) {
        if (!(output >> word) || !parse_number(word, c)) reject("falta una moneda, o no es un número");
        if (c >= n) reject("moneda fuera de rango: " + word);
    }
    if (output >> word) reject("sobra texto después de la cadena: " + word);
    if (chain[0] != chain[k]) reject("la cadena no vuelve a la moneda inicial");

    std::vector<bool> seen(n, false);
    long double product = 1;
    for (std::size_t i = 0; i < k; i++) {
        if (seen[chain[i]]) reject("la moneda " + std::to_string(chain[i]) + " se repite");
        seen[chain[i]] = true;
        const long double r = rate[chain[i]][chain[i + 1]];
        if (r == 0) reject("no hay tasa de " + std::to_string(chain[i]) + " a " + std::to_string(chain[i + 1]));
        product *= r;
    }
    if (product <= 1.0000005L) reject("el producto de la cadena no supera 1,0000005");
}
