#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

typedef long long LL;
typedef unsigned long long uLL;

#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
#define rep(i, a, b) for (register int (i) = (a); (i) <= (b); ++(i))

using namespace std;

inline void proc_status()
{
	ifstream t("/proc/self/status");
	cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read() 
{
	register int x = 0; register int f = 1; register char c;
	while (!isdigit(c = getchar())) if (c == '-') f = -1;
	while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
	return x * f;
}
template<class T> inline void write(T x) 
{
	static char stk[30]; static int top = 0;
	if (x < 0) { x = -x, putchar('-'); }
	while (stk[++top] = x % 10 xor 48, x /= 10, x);
	while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }

const int maxN = (int) 4e5;
const int mod = (int) 1e9 + 7;

struct Edge
{
	int v, w;
	Edge() { }
	Edge(int v, int w) : v(v), w(w) { }
} ;

int n;
bool vis[maxN + 2], instk[maxN + 2], found, in_cir[maxN + 2], choose[maxN + 2];
int top, val[maxN + 2], par[maxN + 2], size[maxN + 2];
PII stk[maxN + 2];
vector<pair<int, int> > circle;
vector<int> node;
vector<Edge> g[maxN + 2];

namespace math
{
	int fac[2 * maxN + 2], ifac[2 * maxN + 2];
	LL qpow(LL a, LL b)
	{
		LL ans = 1;
		while (b)
		{
			if (b & 1) 
				ans = ans * a % mod;
			a = a * a % mod;
			b >>= 1;
		}
		return ans;
	}
	void init()
	{
		fac[0] = 1;
		for (int i = 1; i <= 2 * n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
		ifac[2 * n] = qpow(fac[2 * n], mod - 2);
		for (int i = 2 * n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
	}
	int C(int n, int m) { return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod; }
	int merge(int a, int b) { return C(a + b, a); }
}
using namespace math;

void input()
{
	n = read() << 1;
	for (int i = 1; i <= n; ++i) 
	{
		int x = read(), y = read();
		g[x].emplace_back(y + n / 2, x + y);
		g[y + n / 2].emplace_back(x, x + y);
		choose[x] = choose[y + n / 2] = 1;
	}
}

void dfs_circle(int x, int fa, int edge)
{
	if (found) return;
	stk[++top] = MP(x, edge);
	instk[x] = 1;
	for (Edge E : g[x])
	{
		int v = E.v, w = E.w;
		if (v == fa) continue;
		if (!instk[v])
		{
			dfs_circle(v, x, w);
		}
	   	else
		{
			int To = v;
			do
			{
				//cerr << v << endl;
				v = stk[top].first;
				in_cir[v] = 1;
				circle.push_back(stk[top--]);
			} while (v != To);
			circle.back().second = w;
			found = 1;
			return;
		}
		if (found) return;
	}
	instk[x] = 0;
	top--;
}

void dfs1(int u, int fa)
{
	vis[u] = 1;
	node.push_back(u);
	for (Edge E : g[u])
	{
		int v = E.v, w = E.w;
		if (v != fa and !in_cir[v])
		{
			val[v] = w;
			dfs1(v, u);
		}
	}
}

int dfs2(int u)
{
	int ans = 1;
	size[u] = 0;
	for (Edge E : g[u])
	{
		int v = E.v;
		if (par[v] != u) continue;
		int cur = dfs2(v);
		ans = 1ll * ans * cur % mod * merge(size[u], size[v]) % mod;
		size[u] += size[v];
	}
	size[u]++;
	return ans;
}

int calc()
{
	for (int u : node)
	{
		size[u] = 0;
		par[u] = 0;
	}
	for (int u : node)
	{
		for (Edge E : g[u])
		{
			int v = E.v, w = E.w;
			if (w < val[u]) par[v] = u;
		}
	}
	int SIZE = 0, ans = 1;
	for (int u : node)
	{
		if (!par[u])
		{
			int cur = dfs2(u);
			ans = 1ll * ans * cur % mod * merge(SIZE, size[u]) % mod;
			SIZE += size[u];
		}
	}
	return ans;
}

void solve()
{
	int cnt = 0;
	for (int i = 1; i <= n; ++i)
		cnt += choose[i];
	if (cnt != n)
	{
		cout << 0 << endl;
		return;
	}
	int SIZE = 0, ans = 1;
	for (int i = 1; i <= n && ans; ++i)
	{
		if (!vis[i])
		{
			int cur = 0;
			top = 0;
			found = 0;
			circle.clear();
			dfs_circle(i, 0, 0);

			node.clear();
			for (int j = 0; j < SZ(circle); ++j) dfs1(circle[j].first, 0);

			for (int j = 0; j < SZ(circle); ++j) 
				val[circle[j].first] = circle[j].second;
			(cur += calc()) %= mod;
			//DE(cur);

			circle.push_back(circle[0]);
			for (int j = 1; j < SZ(circle); ++j)
				val[circle[j].first] = circle[j - 1].second;
			(cur += calc()) %= mod;

			ans = 1ll * ans * cur % mod * merge(SIZE, node.size()) % mod;
			SIZE += node.size();
		}
	}
	printf("%d\n", ans);
}

int main() 
{ 
	input();
	math::init();
	solve();
	return 0;
}


Measured memory_bytes: 35060