// Words are flowing out like endless rain into a paper cup
// They slither while they pass they slip away across the universe
// Pools of sorrow, waves of joy are drifting through my open mind
// Possessing and caressing me

#include <bits/stdc++.h>
using namespace std;

using LL = long long;

namespace _buff {

const size_t BUFF = 1 << 19;
char ibuf[BUFF], *ib = ibuf, *ie = ibuf;
char getc() {
    if (ib == ie) {
        ib = ibuf;
        ie = ibuf + fread(ibuf, 1, BUFF, stdin);
    }
    return ib == ie ? -1 : *ib++;
}

}

LL read() {
    using namespace _buff;
    LL ret = 0;
    bool pos = true;
    char c = getc();
    for (; (c < '0' || c > '9') && c != '-'; c = getc()) {
        assert(~c);
    }
    if (c == '-') {
        pos = false;
        c = getc();
    }
    for (; c >= '0' && c <= '9'; c = getc()) {
        ret = (ret << 3) + (ret << 1) + (c ^ 48);
    }
    return pos ? ret : -ret;
}

const size_t N = 2e5 + 5;
const int MOD = 1e9 + 7;

int qpow(int base, int e) {
    int ret = 1;
    for (; e; e >>= 1) {
        if (e & 1) {
            ret = (LL) ret * base % MOD;
        }
        base = (LL) base * base % MOD;
    }
    return ret;
}

int n, fac[N], inv[N], finv[N];

void prep() {
    fac[0] = 1;
    for (int i = 1; i <= n; ++i) {
        fac[i] = (LL) fac[i - 1] * i % MOD;
    }
    inv[n] = qpow(fac[n], MOD - 2);
    for (int i = n; i >= 1; --i) {
        inv[i - 1] = (LL) inv[i] * i % MOD;
    }
    for (int i = 1; i <= n; ++i) {
        finv[i] = (LL) inv[i] * fac[i - 1] % MOD;
    }
}

namespace CalcTop {

bool vis[N];
int sz[N];
vector<int> vc, g[N];

void add_edge(int x, int y) {
    vc.emplace_back(x);
    vc.emplace_back(y);
    g[x].emplace_back(y);
}

int dfs(int x) {
    if (vis[x]) return 1;
    vis[x] = true;
    int ret = sz[x] = 1;
    for (int y : g[x]) {
        ret = (LL) ret * dfs(y) % MOD;
        sz[x] += sz[y];
    }
    ret = (LL) ret * finv[sz[x]] % MOD;
    return ret;
}

int calc() {
    int ret = 1;
    for (int x : vc) {
        ret = (LL) ret * dfs(x) % MOD;
    }
    for (int x : vc) {
        vis[x] = false;
        g[x].clear();
    }
    vc.clear();
    return ret;
}

}

int dom[N];
vector<int> vc, g[N];
bool vis[N];
int v1, v2, v3;

void get_tp(int x, int p = 0) {
    if (vis[x]) {
        if (v1) {
            puts("0");
            exit(0);
        }
        v1 = x; v2 = p;
        return;
    }
    vc.emplace_back(x);
    vis[x] = true;
    bool flag = true;
    for (int y : g[x]) {
        if (y == p) continue;
        if (x == v1 && y == v2) continue;
        get_tp(y, x);
        if (flag && v1 == x) {
            v3 = y;
            flag = false;
        }
    }
}

void dfs(int rt, int x, int p = 0) {
    dom[x] = p;
    if (rt == x && p) return;
    for (int y : g[x]) {
        if (y != p) {
            dfs(rt, y, x);
        }
    }
}

int calc(int rt) {
    dfs(rt, rt);
    for (int x : vc) {
        if (x < dom[dom[x]]) {
            CalcTop::add_edge(dom[x], x);
        }
    }
    return CalcTop::calc();
}

int main() {
    n = read();
    for (int i = 0, _ = n << 1; i < _; ++i) {
        int x = read(), y = read() + n;
        g[x].emplace_back(y);
        g[y].emplace_back(x);
    }

    n <<= 1;
    prep();
    int ans = fac[n];
    for (int i = 1; i <= n; ++i) {
        if (vis[i]) continue;
        v1 = 0;
        get_tp(i);
        int x = v1;
        if (!x) return puts("0"), 0;
        assert(v2 != v3);
        iter_swap(find(g[x].begin(), g[x].end(), v2), g[x].begin());
        iter_swap(find(g[x].begin(), g[x].end(), v3), g[x].rbegin());

        int val1 = calc(x);
        swap(g[x].front(), g[x].back());
        int val2 = calc(x);
        ans = (LL) ans * (val1 + val2) % MOD;

        vc.clear();
    }

    cout << ans;
    return 0;
}
