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

#define rep(i, N) for (int i = 0; i < N; i++)
#define pb push_back
typedef long long ll;
typedef pair<int, int> i_i;
typedef pair<ll, int> ll_i;
const int MOD = 1e9 + 7;
const int INF = INT_MAX / 2;
struct edge { int i, v; };

int f[300000], fi[300000];

int pow_mod(ll x, ll n, int M) {
	ll ans = 1;
	for (; n; n>>=1) {
		if (n & 1) ans = ans * x % M;
		x = x * x % M;
	}
	return ans;
}

int inv_mod(int x, int p) {
	return pow_mod(x, p - 2, p);
}

int C(int x, int y) {
	return (ll)f[x] * fi[y] % MOD * fi[x - y] % MOD;
}

struct union_find {
	vector<int> v;
	union_find(int n) : v(n, -1) {}
	int find(int x) { return v[x] < 0 ? x : v[x] = find(v[x]); }
	void unite(int x, int y) {
		x = find(x); y = find(y);
		if (x == y) return;
		if (-v[x] < -v[y]) swap(x, y);
		v[x] += v[y]; v[y] = x;
	}
	bool root(int x) { return v[x] < 0; }
	bool same(int x, int y) { return find(x) == find(y); }
	int size(int x) { return -v[find(x)]; }
};

int prod;

int dfs(int u, vector<vector<int> >& G) {
	int tot = 0;
	for (int v: G[u]) {
		int sz = dfs(v, G);
		tot += sz;
		prod = (ll)prod * C(tot, sz) % MOD;
	}
	return tot + 1;
}

int calc(int N, int M, vector<int> a, vector<int> b, vector<int> dir) {
	rep(i, N + M) if (dir[i] == -1) return 0;
	vector<int> unko(N + M);
	rep(i, N + M) {
		if (dir[i] == 0) unko[a[i]]++;
		if (dir[i] == 1) unko[N + b[i]]++;
	}
	rep(u, N + M) if (unko[u] != 1) return 0;
	vector<vector<i_i> > xs(N), ys(M);
	rep(i, N + M) xs[a[i]].pb(i_i(b[i], i));
	rep(i, N + M) ys[b[i]].pb(i_i(a[i], i));
	vector<vector<int> > G(N + M);
	vector<bool> root(N + M, true);
	rep(k, N) {
		sort(xs[k].begin(), xs[k].end());
		rep(l, xs[k].size()) {
			int i = xs[k][l].second;
			if (dir[i] == 0)
				rep(_l, l)
					G[i].pb(xs[k][_l].second);
		}
	}
	rep(k, M) {
		sort(ys[k].begin(), ys[k].end());
		rep(l, ys[k].size()) {
			int i = ys[k][l].second;
			if (dir[i] == 1)
				rep(_l, l)
					G[i].pb(ys[k][_l].second);
		}
	}
	rep(u, N + M) for (int v: G[u]) root[v] = false;
	vector<int> to;
	rep(u, N + M) if (root[u]) to.pb(u);
	G.pb(to);
	prod = 1;
	dfs(N + M, G);
	return prod;
}

int solve(int N, int M, vector<int> a, vector<int> b) {
	if (N + M != a.size()) return 0;
	vector<vector<edge> > G(N + M);
	rep(i, N + M) G[a[i]].pb(edge{i, N + b[i]}), G[N + b[i]].pb(edge{i, a[i]});
	vector<int> d(N + M);
	rep(u, N + M) d[u] = G[u].size();
	queue<int> q;
	rep(u, N + M) if (d[u] == 1) q.push(u);
	vector<bool> exist(N + M, true);
	vector<int> dir(N + M, -1);
	while (!q.empty()) {
		int u = q.front(); q.pop();
		exist[u] = false;
		int i;
		for (edge e: G[u]) if (exist[e.v]) {
			i = e.i;
			d[e.v]--;
			if (d[e.v] == 1) q.push(e.v);
		}
		dir[i] = (u >= N);
	}
	int i0 = -1;
	rep(i, N + M) if (dir[i] == -1) i0 = i;
	if (i0 == -1) return calc(N, M, a, b, dir);
	vector<int> dir0 = dir;
	int ans = 0;
	rep(t, 2) {
		dir = dir0;
		int s;
		if (t == 0) s = a[i0], dir[i0] = 0;
		if (t == 1) s = N + b[i0], dir[i0] = 1;
		vector<bool> vis(N + M); vis[s] = true;
		queue<int> q; q.push(s);
		while (!q.empty()) {
			int u = q.front(); q.pop();
			for (edge e: G[u]) if (dir[e.i] == -1) {
				dir[e.i] = (e.v >= N);
				if (!vis[e.v]) {
					vis[e.v] = true;
					q.push(e.v);
				}
			}
		}
		ans = (ans + calc(N, M, a, b, dir)) % MOD;
	}
	return ans;
}

int main() {
	f[0] = fi[0] = 1;
	for (int x = 1; x < 300000; x++) {
		f[x] = (ll)f[x - 1] * x % MOD;
		fi[x] = (ll)fi[x - 1] * inv_mod(x, MOD) % MOD;
	}
	int N; cin >> N;
	vector<int> a(N * 2), b(N * 2);
	rep(i, N * 2) scanf("%d%d", &a[i], &b[i]), a[i]--, b[i]--;
	union_find uf(N * 2);
	rep(i, N * 2) uf.unite(a[i], b[i] + N);
	vector<vector<int> > vv(N * 2);
	rep(i, N * 2) vv[uf.find(a[i])].pb(i);
	int ans = 1;
	for (vector<int> v: vv) if (!v.empty()) {
		vector<int> _a, _b;
		for (int i: v) _a.pb(a[i]), _b.pb(b[i]);
		vector<int> A = _a, B = _b;
		sort(A.begin(), A.end());
		sort(B.begin(), B.end());
		A.erase(unique(A.begin(), A.end()), A.end());
		B.erase(unique(B.begin(), B.end()), B.end());
		for (int& u: _a) u = lower_bound(A.begin(), A.end(), u) - A.begin();
		for (int& u: _b) u = lower_bound(B.begin(), B.end(), u) - B.begin();
		ans = (ll)ans * solve(A.size(), B.size(), _a, _b) % MOD;
	}
	int tot = 0;
	rep(u, N * 2) if (uf.root(u)) {
		tot += uf.size(u);
		ans = (ll)ans * C(tot, uf.size(u)) % MOD;
	}
	cout << ans << endl;
}


Measured memory_bytes: 59164