#include<cstdio>
#include<vector>
#include<cstdlib>

using namespace std;
const int maxn=200100;
const long long md=1000000007;
void broke(){
	printf("0\n");
	exit(0);
}
long long fac[maxn+10],inv[maxn+10];
long long powd(long long x,long long y){
	long long res=1;
	while(y){
		if(y&1) res=res*x%md;
		x=x*x%md;
		y>>=1;
	}
	return res;
}
struct point{
	int x,y;
}a[maxn];
int dr[maxn],d[maxn];//1->x,2->y
vector<int> vx[maxn],vy[maxn];
vector<int> vec[maxn];
bool us[maxn];
int cntx[maxn],cnty[maxn];
int q[maxn];
int f[maxn];
int fa(int x){
	return f[x]==x?x:f[x]=fa(f[x]);
}
struct node{
	long long len,ans;
};
long long C(int x,int y){
	return fac[x]*inv[y]%md*inv[x-y]%md;
}
void merge(node &A,node B){
	A.len=A.len+B.len;
	A.ans=A.ans*C(A.len,B.len)%md;
	A.ans=A.ans*B.ans%md;
}
bool vis[maxn],havef[maxn];
node dp[maxn];
void dfs(int u){
	if(vis[u]) return;
	vis[u]=1;
	dp[u]=(node){0,1};
	int v;
	if(d[u]==1){
		for(int i=0;i<(int)vx[a[u].x].size();i++){
			v=vx[a[u].x][i];
			if(a[v].y<a[u].y){
				havef[v]=1;
				dfs(v);
				merge(dp[u],dp[v]);
			}
		}
		dp[u].len++;
	}
	else{
		for(int i=0;i<(int)vy[a[u].y].size();i++){
			v=vy[a[u].y][i];
			if(a[v].x<a[u].x){
				havef[v]=1;
				dfs(v);
				merge(dp[u],dp[v]);
			}
		}
		dp[u].len++;
	}
}
bool getall(int u){
	int v;
	if(d[u]==1){
		for(int i=0;i<(int)vx[a[u].x].size();i++){
			v=vx[a[u].x][i];
			if(u==v) continue;
			if(!d[v]){
				d[v]=3-d[u];
				if(!getall(v)) return 0;
			}
			else if(d[v]==d[u]) return 0;
		}
	}
	if(d[u]==2){
		for(int i=0;i<(int)vy[a[u].y].size();i++){
			v=vy[a[u].y][i];
			if(u==v) continue;
			if(!d[v]){
				d[v]=3-d[u];
				if(!getall(v)) return 0;
			}
			else if(d[v]==d[u]) return 0;
		}
	}
	return 1;
}
long long Solve(int x,int y){
	int v;
	for(int i=0;i<(int)vec[x][i];i++){
		v=vec[x][i];
		d[v]=dr[v];
		vis[v]=havef[v]=0;
	}
	bool bo=1;
	for(int i=0;i<(int)vec[x].size();i++)
		if(!d[vec[x][i]]){
			d[vec[x][i]]=y;
			bo=getall(vec[x][i]);
			break;
		}
	if(bo){
		node st=(node){0,1};
		for(int i=0;i<(int)vec[x].size();i++)
			if(!vis[vec[x][i]])
				dfs(vec[x][i]);
		for(int i=0;i<(int)vec[x].size();i++)
			if(!havef[vec[x][i]])
				merge(st,dp[vec[x][i]]);
		return st.ans;
	}
	return 0;
}
node solve(int x){
	node ans=(node){(int)vec[x].size(),0};
	ans.ans=ans.ans+Solve(x,1);
	ans.ans=ans.ans+Solve(x,2);
	return ans;
}
int main(){
//	freopen("F.in","r",stdin);
	fac[0]=1;
	for(int i=1;i<=maxn;i++)
		fac[i]=fac[i-1]*i%md;
	inv[maxn]=powd(fac[maxn],md-2);
	for(int i=maxn;i>=1;i--)
		inv[i-1]=inv[i]*i%md;

	int n;
	scanf("%d",&n);
	for(int i=1;i<=n*2;i++)
	{
		scanf("%d%d",&a[i].x,&a[i].y);
		f[i]=i;
		if(!vx[a[i].x].empty()) f[fa(i)]=fa(vx[a[i].x].front());
		vx[a[i].x].push_back(i);
		if(!vy[a[i].y].empty()) f[fa(i)]=fa(vy[a[i].y].front());
		vy[a[i].y].push_back(i);
	}
	int l=1,r=0;
	for(int i=1;i<=n;i++){
		if(vx[i].size()==0)
			broke();
		if(vx[i].size()==1){
			q[++r]=vx[i][0];
			if(dr[vx[i][0]]) broke();
			dr[vx[i][0]]=1;
		}
		cntx[i]=vx[i].size();
	}
	for(int i=1;i<=n;i++){
		if(vy[i].size()==0)
			broke();
		if(vy[i].size()==1)
		{
			q[++r]=vy[i][0];
			if(dr[vy[i][0]]) broke();
			dr[vy[i][0]]=2;
		}
		cnty[i]=vy[i].size();
	}
	int u;
	while(l<=r){
		u=q[l],l++;
		cntx[a[u].x]--;
		if(cntx[a[u].x]==1){
			for(int i=0;i<(int)vx[a[u].x].size();i++){
				if(!dr[vx[a[u].x][i]]){
					dr[vx[a[u].x][i]]=1;
					q[++r]=vx[a[u].x][i];
					break;
				}
				if(i==(int)vx[a[u].x].size()-1) broke();
			}
		}
		cnty[a[u].y]--;
		if(cnty[a[u].y]==1){
			for(int i=0;i<(int)vy[a[u].y].size();i++){
				if(!dr[vy[a[u].y][i]]){
					dr[vy[a[u].y][i]]=2;
					q[++r]=vy[a[u].y][i];
					break;
				}
				if(i==(int)vy[a[u].y].size()-1) broke();
			}
		}
	}
	for(int i=1;i<=n;i++)
		if(cntx[i]>2||cnty[i]>2)
			broke();
	for(int i=1;i<=n*2;i++)
		vec[fa(i)].push_back(i);
	node ans=(node){0,1},st;
	for(int i=1;i<=n*2;i++){
		if(us[fa(i)]) continue;
		us[fa(i)]=1;
		st=solve(fa(i));
		merge(ans,st);
	}
	printf("%lld\n",ans.ans);
	return 0;
}


Measured memory_bytes: 35448