#include <bits/stdc++.h>

using namespace std;
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n - 1); }
template <class T> using V = vector<T>;
template <class T> using VV = V<V<T>>;

template <uint MD> struct ModInt {
    using M = ModInt;
    const static M G;
    uint v;
    ModInt(ll _v = 0) { set_v(_v % MD + MD); }
    M& set_v(uint _v) {
        v = (_v < MD) ? _v : _v - MD;
        return *this;
    }
    explicit operator bool() const { return v != 0; }
    M operator-() const { return M() - *this; }
    M operator+(const M& r) const { return M().set_v(v + r.v); }
    M operator-(const M& r) const { return M().set_v(v + MD - r.v); }
    M operator*(const M& r) const { return M().set_v(ull(v) * r.v % MD); }
    M operator/(const M& r) const { return *this * r.inv(); }
    M& operator+=(const M& r) { return *this = *this + r; }
    M& operator-=(const M& r) { return *this = *this - r; }
    M& operator*=(const M& r) { return *this = *this * r; }
    M& operator/=(const M& r) { return *this = *this / r; }
    M pow(ll n) const {
        M x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    M inv() const { return pow(MD - 2); }
    friend ostream& operator<<(ostream& os, const M& r) { return os << r.v; }
};
using Mint = ModInt<TEN(9) + 7>;

struct UnionFind {
    V<int> p, r;
    int gn;
    UnionFind(int n = 0) : p(n, -1), r(n, 1), gn(n) {}
    void merge(int a, int b) {
        int x = group(a), y = group(b);
        if (x == y) return;  // same
        gn--;
        if (r[x] < r[y]) {
            p[x] = y;
        } else {
            p[y] = x;
            if (r[x] == r[y]) r[x]++;
        }
    }
    int group(int a) {
        if (p[a] == -1) return a;
        return p[a] = group(p[a]);
    }
    bool same(int a, int b) { return group(a) == group(b); }
};

const int N = 233333;
int n;
Mint fac[N], ifac[N];
int a[N], b[N];
V<int> cc[N];

void first() {
    fac[0] = 1;
    for (int i = 1; i < N; i++) fac[i] = Mint(i) * fac[i - 1];
    for (int i = 0; i < N; i++) ifac[i] = fac[i].inv();
}

Mint C(int n, int k) {
    return fac[n] * ifac[k] * ifac[n - k];
}

struct E {
    int to, id;
};

using P = pair<int, int>;
Mint gao(int n, int m, V<int> a, V<int> b, V<int> dir) {
    V<V<P>> xs(n), ys(m);
    for (int i = 0; i < n + m; i++) {
        xs[a[i]].push_back(P(b[i], i));
        ys[b[i]].push_back(P(a[i], i));
    }
    
    V<V<int>> g(n + m);
    
    for (int d = 0; d < n; d++) {
        sort(xs[d].begin(), xs[d].end());
        for (int k = 0; k < (int)xs[d].size(); k++) {
            int i = xs[d][k].second;
            if (dir[i] == 0) {
                for (int j = 0; j < k; j++) {
                    g[i].push_back(xs[d][j].second);
                }
            }
        }
    }
    
    for (int d = 0; d < m; d++) {
        sort(ys[d].begin(), ys[d].end());
        for (int k = 0; k < (int)ys[d].size(); k++) {
            int i = ys[d][k].second;
            if (dir[i] == 1) {
                for (int j = 0; j < k; j++) {
                    g[i].push_back(ys[d][j].second);
                }
            }
        }
    }
    
    V<bool> rt(n + m, true);
    for (int i = 0; i < n + m; i++) {
        for (int v: g[i]) rt[v] = false;
    }
    V<int> to;
    for (int i = 0; i < n + m; i++) {
        if (rt[i]) to.push_back(i);
    }
    g.push_back(to);
    
    Mint pr = 1;
    function<int(int)> dfs = [&](int v) {
        int sz = 1;
        for (int u: g[v]) {
            int csz = dfs(u);
            sz += csz;
            pr *= C(sz - 1, csz);
        }
        return sz;
    };
    dfs(n + m);
    
    return pr;
}

Mint calc(int n, int m, V<int> a, V<int> b) {
    if (n + m != (int)a.size()) return Mint(0);
    
    V<V<E>> g(n + m);
    for (int i = 0; i < n + m; i++) {
        g[a[i]].push_back(E{n + b[i], i});
        g[n + b[i]].push_back(E{a[i], i});
    }
    
    V<int> deg(n + m);
    for (int i = 0; i < n + m; i++) {
        deg[i] = g[i].size();
    }
    
    queue<int> que;
    for (int i = 0; i < n + m; i++) {
        if (deg[i] == 1) {
            que.push(i);
        }
    }
    
    V<bool> used(n + m);
    V<int> dir(n + m, -1);
    while (que.size()) {
        int v = que.front(); que.pop();
        used[v] = true;
        int i = -1;
        for (E e: g[v]) {
            if (used[e.to]) continue;
            i = e.id;
            deg[e.to]--;
            if (deg[e.to] == 1) que.push(e.to);
        }
        assert(i != -1);
        dir[i] = (v >= n);
    }
    
    int i2 = -1;
    for (int i = 0; i < n + m; i++) {
        if (dir[i] == -1) i2 = i;
    }
    if (i2 == -1) return gao(n, m, a, b, dir);
    
    V<int> memo = dir;
    Mint ans = 0;
    for (int c = 0; c < 2; c++) {
        dir = memo;
        int st = -1;
        if (c == 0) {
            st = a[i2];
            dir[i2] = 0;
        } else {
            st = n + b[i2];
            dir[i2] = 1;
        }
        
        V<bool> used(n + m);
        used[st] = true;
        queue<int> que;
        que.push(st);
        while (que.size()) {
            int v = que.front(); que.pop();
            for (E e: g[v]) {
                if (dir[e.id] >= 0) continue;
                dir[e.id] = (e.to >= n);
                if (!used[e.to]) {
                    used[e.to] = true;
                    que.push(e.to);
                }
            }
        }
        ans += gao(n, m, a, b, dir);
    }
    return ans;
}

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << setprecision(20) << fixed;
    first();
    
    cin >> n;
    UnionFind uf(2 * n);
    for (int i = 0; i < 2 * n; i++) {
        cin >> a[i] >> b[i]; a[i]--; b[i]--;
        uf.merge(a[i], b[i] + n);
    }
    
    for (int i = 0; i < 2 * n; i++) {
        cc[uf.group(a[i])].push_back(i);
    }
    
    Mint ans = 1;
    int r = 2 * n;
    for (int i = 0; i < 2 * n; i++) {
        auto v = cc[i];
        if (v.empty()) continue;
        V<int> c, d;
        for (int i: v) {
            c.push_back(a[i]);
            d.push_back(b[i]);
        }
        V<int> c2 = c, d2 = d;
        sort(c2.begin(), c2.end());
        sort(d2.begin(), d2.end());
        c2.erase(unique(c2.begin(), c2.end()), c2.end());
        d2.erase(unique(d2.begin(), d2.end()), d2.end());
        for (int& u: c) {
            u = lower_bound(c2.begin(), c2.end(), u) - c2.begin();
        }
        for (int& u: d) {
            u = lower_bound(d2.begin(), d2.end(), u) - d2.begin();
        }
        ans *= calc(c2.size(), d2.size(), c, d) * C(r, int(v.size()));
        r -= int(v.size());
    }
    
    cout << ans.v << endl;
    return 0;
}

Measured memory_bytes: 60960