#pragma once
#include<unordered_set>
#include<unordered_map>
#include<string>

namespace std{
    template<>
    struct hash<std::pair<int,int>>{
        size_t operator()(const std::pair<int,int>& p) const{
            return (std::hash<int>()(p.first)<<1) ^ std::hash<int>()(p.second);
        }
    };
}

class DirectedGraph{
    private:
        std::unordered_set<int> _vertices;
        std::unordered_map<int,std::unordered_set<int>> _inbounds;
        std::unordered_map<int,std::unordered_set<int>> _outbounds;
        std::unordered_map<std::pair<int,int>,int> _weights;
    public:
        DirectedGraph();
        ~DirectedGraph();
        DirectedGraph(const DirectedGraph& g);
        DirectedGraph operator=(const DirectedGraph& g);
        DirectedGraph reindex() const;

        int numVertices() const;
        int numEdges() const;
        std::unordered_set<int> vertices() const;
        std::unordered_map<std::pair<int,int>,int> edges() const;
        bool isEdge(int v1, int v2) const;
        int inDegree(int v) const;
        int outDegree(int v) const;
        std::unordered_set<int> inbounds(int v) const;
        std::unordered_set<int> outbounds(int v) const;
        int weight(int v1, int v2) const;
        void weight(int v1, int v2, int w);
        void addVertex(int v);
        void removeVertex(int v);
        void addEdge(int v1, int v2, int w);
        void removeEdge(int v1, int v2);
};

DirectedGraph read_from_file(std::string filename);
void write_to_file(const DirectedGraph& g, std::string filename);
DirectedGraph random_graph(int num_vertices, int num_edges);