#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <map>

// Helper types and aliases
using size_type = std::string::size_type;
using letters_and_indices = std::map<size_type, std::string>;

// Function to read candidates from a file
std::vector<std::string> read_candidates(std::istream &input) {
    std::vector<std::string> candidates;
    std::string word;

    while (input >> word) {
        if (word.size() == 5) {
            std::transform(word.begin(), word.end(), word.begin(), ::tolower);
            candidates.push_back(word);
        }
    }

    // Remove duplicates
    std::sort(candidates.begin(), candidates.end());
    candidates.erase(std::unique(candidates.begin(), candidates.end()), candidates.end());

    return candidates;
}

// Helper functions
bool contains_any_of(const std::string &s, const std::string &cs) {
    return std::any_of(cs.begin(), cs.end(), [&s](char c) { return s.find(c) != std::string::npos; });
}

bool contains_at(const std::string &s, char c, size_type pos) {
    return pos < s.size() && s[pos] == c;
}

bool contains_but_not_at(const std::string &s, char c, size_type pos) {
    return s.find(c) != std::string::npos && !contains_at(s, c, pos);
}

// Functor for wrong letters
struct wrong_fn {
    explicit wrong_fn(const std::string &letters) : l{letters} {}

    bool operator()(const std::string &word) const {
        return contains_any_of(word, l);
    }

private:
    std::string l;
};

// Functor for correct letters
struct correct_fn {
    explicit correct_fn(const letters_and_indices &idxs) : m{idxs} {}

    bool operator()(const std::string &word) const {
        return std::all_of(m.begin(), m.end(), [&word](const auto &pair) {
            return contains_at(word, pair.second[0], pair.first);
        });
    }

private:
    letters_and_indices m;
};

// Functor for misplaced letters
struct misplaced_fn {
    explicit misplaced_fn(const letters_and_indices &idxs) : m{idxs} {}

    bool operator()(const std::string &word) const {
        return std::all_of(m.begin(), m.end(), [&word](const auto &pair) {
            return contains_but_not_at(word, pair.second[0], pair.first);
        });
    }

private:
    letters_and_indices m;
};

// Function to filter candidates
void do_filter(std::vector<std::string> &candidates, const std::string &wrong,
               const letters_and_indices &green, const letters_and_indices &yellow) {
    auto predicate = [&wrong, &green, &yellow](const std::string &word) {
        return wrong_fn(wrong)(word) || !correct_fn(green)(word) || !misplaced_fn(yellow)(word);
    };

    candidates.erase(std::remove_if(candidates.begin(), candidates.end(), predicate), candidates.end());
}

// User interaction function
letters_and_indices build_list(const std::string &line) {
    std::istringstream iss(line);
    letters_and_indices result;
    char letter;
    size_type index;

    while (iss >> letter >> index) {
        result[index] = std::string(1, letter);
    }

    return result;
}

std::tuple<std::string, letters_and_indices, letters_and_indices> prompt() {
    std::string wrong;
    std::cout << "Enter wrong letters:\n";
    std::getline(std::cin, wrong);

    std::string correct;
    std::cout << "Enter correct letters (letter index)*:\n";
    std::getline(std::cin, correct);
    auto green = build_list(correct);

    std::string misplaced;
    std::cout << "Enter misplaced letters (letter index)*:\n";
    std::getline(std::cin, misplaced);
    auto yellow = build_list(misplaced);

    return {wrong, green, yellow};
}