#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <functional>
#include <utility>

bool operator>(const GameObject& a, const GameObject& b) {
    return a.id > b.id;
}

template<class Tp, template<class> class Compare>
class DereferenceCompare {
    Compare<Tp> comp;

 public:
    bool operator()(const Tp* const a, const Tp* const b) const {
        return comp(*a, *b);
    }
};

class GameDatabase {
    std::map<ObjectId, GameObject, std::greater<ObjectId>> fastId;
    std::map<std::pair<size_t, size_t>,
    std::set<GameObject*, DereferenceCompare<GameObject,
    std::greater>>> fastPos;
    std::unordered_map<string, std::set<GameObject*,
    DereferenceCompare<GameObject, std::greater>>> fastName;

 public:
    GameDatabase() = default;
    void Insert(ObjectId id, string name, size_t x, size_t y) {
        GameObject* obj = new GameObject;
        obj->id = id;
        obj->name = name;
        obj->x = x;
        obj->y = y;
        fastId[id] = *obj;
        fastPos[{x, y}].insert(obj);
        fastName[name].insert(obj);
    }
    void Remove(ObjectId id) {
        auto idIt = fastId.find(id);
        if (idIt != fastId.end()) {
            auto posIt = fastPos[{idIt->second.x, idIt->second.y}];
            for (auto tempIt = posIt.begin(); tempIt != posIt.end(); ++tempIt) {
                if ((*tempIt)->id == id) {
                    posIt.erase(tempIt);
                    break;
                }
            }
            auto nameIt = fastName[idIt->second.name];
            for (auto tempIt = nameIt.begin();
            tempIt != nameIt.end(); ++tempIt) {
                if ((*tempIt)->id == id) {
                    nameIt.erase(tempIt);
                    break;
                }
            }
            fastId.erase(idIt);
        }
    }
    vector<GameObject> DataByName(string name) const {
        auto s = fastName.at(name);
        return vector<GameObject> {s.begin(), s.end()};
    }
    vector<GameObject> DataByPosition(size_t x, size_t y) const {
        auto s = fastPos.at({x, y});
        return vector<GameObject> {s.begin(), s.end()};
    }
    vector<GameObject> Data() const {
        return vector<GameObject>{fastId.begin(), fastId.end()};
    }
};