#include <iostream>
#include <sstream>
#include <set>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <iomanip>
#include <vector>
#include <map>
#include <queue>
#include <numeric>
#include <string>
#include <cmath>
#include <climits>
#include <stack>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <array>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <bitset>
#include <cassert>
#include <tuple>

#include <iostream>
#include <sstream>
#include <set>
#include <fstream>
#include <algorithm>
#include <string>
#include <cmath>
#include <unordered_set>

using namespace std;
typedef long long ll;

class StringSet {
private:
    set <string> wrds;
    multiset <string> wrdss;
    void procText(const string &txt) {
        stringstream ss(txt);
        string w;
        while (ss >> w) {
            // Remove punctuation and convert to lowercase
            w.erase(remove_if(w.begin(), w.end(), ::ispunct), w.end());
            transform(w.begin(), w.end(), w.begin(), ::tolower);
            if (!w.empty()) {
                wrds.insert(w);
                wrdss.insert(w);
            }
        }
    }

public:
    // Constructor to load words from a file or text
    StringSet(const string &filename, bool txt = false) {
        if (txt) {
            procText(filename);
            return;
        }
        ifstream file(filename);
        string line;
        while (getline(file, line)) {
            procText(line);
        }
        file.close();
    }

    void add(const string &w) {
        wrds.insert(w);
        wrdss.insert(w);
    }

    void remove(const string &w) {
        wrdss.erase(wrdss.find(w));
        if (!wrdss.count(w)) wrds.erase(w);
    }

    void clear() {
        wrds.clear();
    }

    int size() const {
        return wrds.size();
    }

    void display() const {
        for (const auto &w : wrds) {
            cout << w << " ";
        }
        cout << endl;
    }

    // Return union of two sets
    StringSet operator+(const StringSet &other) const {
        StringSet res("",true);
        res.wrds = wrds;
        res.wrdss=wrdss;
        res.wrds.insert(other.wrds.begin(), other.wrds.end());
        res.wrdss.insert(other.wrdss.begin(), other.wrdss.end());
        return res;
    }

    // Return intersection of two sets
    StringSet operator*(const StringSet &other) const {
        StringSet res("",true);
        for (const auto &w : wrds) {
            if (other.wrds.count(w)) {
                res.wrds.insert(w);
            }
        }
        return res;
    }

    // Compute similarity using binary cosine coefficient
    double similarity(const StringSet &other) const {
        int commonWrds = (*this * other).size(); // Intersection size
        return 1.*commonWrds / (sqrt(size()) * sqrt(other.size()));
    }
};

int main() {
    int programChoice;
    cout << "Choose a program:\n1. Document Similarity\n2. Task Manager\n";
    cin >> programChoice;

    if (programChoice == 1) {
        StringSet docSet("",true), querySet("",true);
        string input, word;
        int isFileInput;


        cout << "Enter first document:\n1. From file\n2. Manually enter text\n";
        cin >> isFileInput;
        cin.ignore();

        if (isFileInput == 1) {
            cout << "Enter file name: ";
            getline(cin, input);
            docSet = StringSet(input);
        } else {
            cout << "Enter text: ";
            getline(cin, input);
            docSet = StringSet(input, true);
        }


        cout << "Enter second document/query:\n1. From file\n2. Manually enter text\n";
        cin >> isFileInput;
        cin.ignore();

        if (isFileInput == 1) {
            cout << "Enter file name: ";
            getline(cin, input);
            querySet = StringSet(input);
        } else {
            cout << "Enter text: ";
            getline(cin, input);
            querySet = StringSet(input, true);
        }

        int option;
        do {
            cout << "\nChoose an operation:\n";
            cout << "1. Add word\n2. Remove word\n3. Display set\n";
            cout << "4. Calculate similarity\n5. Clear set\n6. Quit\n";
            cin >> option;
            cin.ignore();

            if (option == 1) {
                cout << "Enter word to add: ";
                getline(cin, word);
                docSet.add(word);
            } else if (option == 2) {
                cout << "Enter word to remove: ";
                getline(cin, word);
                docSet.remove(word);
            } else if (option == 3) {
                cout << "Document Set Words: ";
                docSet.display();
                cout << "Query Set Words: ";
                querySet.display();
            } else if (option == 4) {
                cout << "Similarity: " << docSet.similarity(querySet) << endl;
            } else if (option == 5) {
                docSet.clear();
                cout << "Document set cleared.\n";
            } else if (option == 6) {
                cout << "Exiting Document Similarity program.\n";
            } else {
                cout << "Invalid option, try again.\n";
            }
        } while (option != 6);

    } else if (programChoice == 2) {

        cout << "Task Manager program not implemented yet.\n";
    } else {
        cout << "Invalid program choice.\n";
    }

    return 0;
}
