#include <bits/stdc++.h>
#define gc getchar()
#define ll long long
#define N 100009
#define mod 1000000007
using namespace std;
int n,a[N<<1],b[N<<1],first[N<<1],number=1,vis[N<<1],size[N<<1];
int jc[N<<1],inv[N<<1],Ans[N<<1],cnt,len[N<<1],fa[N<<1];
int sta[N<<1],top,First[N<<1],Number,to[N<<1],Fa[N<<1];
int Sta[N<<1],num,Size[N<<1],dp[N<<1],flag,pd[N<<1];
map<pair<int,int>,int> mp;
struct edge
{
	int to,next;
	void add(int x,int y)
	{
		to=y,next=first[x],first[x]=number;
	}
}e[N<<2];
struct Edge
{
	int to,next;
	void add(int x,int y)
	{
		to=y,next=First[x],First[x]=Number;
	}
}E[N<<2];
int read()
{
	int x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	int s=ch-'0';
	while (ch=gc,ch>='0'&&ch<='9') s=s*10+ch-'0';
	return x*s;
}
int c(int n,int m)
{
	return (ll)jc[n]*inv[m]%mod*inv[n-m]%mod;
}
void dfs(int x)
{
	vis[x]=size[x]=1;
	Sta[++num]=x;
	for (int i=first[x];i;i=e[i].next)
		if (e[i].to!=fa[x])
		{
			if (!vis[e[i].to])
			{
				fa[e[i].to]=x;
				dfs(e[i].to);
				size[x]+=size[e[i].to];
			}
			else
				if (flag)
				{
					int now=x;
					while (now!=e[i].to)
					{
						sta[++top]=now;
						now=fa[now];
					}
					sta[++top]=e[i].to;
					flag=0;
				}
		}
}
void tree_dp(int x)
{
	Size[x]=0;
	dp[x]=1;
	for (int i=First[x];i;i=E[i].next)
	{
		tree_dp(E[i].to);
		Size[x]+=Size[E[i].to];
		dp[x]=(ll)dp[x]*dp[E[i].to]%mod*c(Size[x],Size[E[i].to])%mod;
	}
	Size[x]++;
}
void Dfs(int x)
{
	for (int i=first[x];i;i=e[i].next)
		if (!to[e[i].to])
		{
			to[e[i].to]=i,Dfs(e[i].to);
			if (e[i].to<e[to[x]^1].to)
			{
				E[++Number].add(x,e[i].to);
				Fa[e[i].to]=x;
			}
		}
}
int solve(int sz)
{
	for (int i=1;i<=num;i++)
		Fa[Sta[i]]=to[Sta[i]]=Size[Sta[i]]=dp[Sta[i]]=First[Sta[i]]=0;
	Number=0;
	for (int i=1;i<=top;i++)
	{
		to[sta[i]]=mp[make_pair(sta[i==top?1:(i+1)],sta[i])];
		if (sta[i==1?top:(i-1)]<sta[i==top?1:(i+1)])
		{
			E[++Number].add(sta[i],sta[i==1?top:(i-1)]);
			Fa[sta[i==1?top:(i-1)]]=sta[i];
		}
	}
	for (int i=1;i<=top;i++) Dfs(sta[i]);
	int ret=1,l=0;
	for (int i=1;i<=num;i++)
		if (!Fa[Sta[i]]&&First[Sta[i]])
		{
			tree_dp(Sta[i]);
			int ans=dp[Sta[i]],L=Size[Sta[i]];
			ret=(ll)ret*ans%mod*c(L+l,l)%mod,l+=L;
		}
	ret=(ll)ret*jc[sz-l]%mod*c(sz,l)%mod;
	for (int i=1;i*2<=top;i++) swap(sta[i],sta[top-i+1]);
	for (int i=1;i<=num;i++)
		Fa[Sta[i]]=to[Sta[i]]=Size[Sta[i]]=dp[Sta[i]]=First[Sta[i]]=0;
	Number=0;
	for (int i=1;i<=top;i++)
	{
		to[sta[i]]=mp[make_pair(sta[i==top?1:(i+1)],sta[i])];
		if (sta[i==1?top:(i-1)]<sta[i==top?1:(i+1)])
		{
			E[++Number].add(sta[i],sta[i==1?top:(i-1)]);
			Fa[sta[i==1?top:(i-1)]]=sta[i];
		}
	}
	for (int i=1;i<=top;i++) Dfs(sta[i]);
	int Ret=1;
	l=0;
	for (int i=1;i<=num;i++)
		if (!Fa[Sta[i]]&&First[Sta[i]])
		{
			tree_dp(Sta[i]);
			int ans=dp[Sta[i]],L=Size[Sta[i]];
			Ret=(ll)Ret*ans%mod*c(L+l,l)%mod,l+=L;
		}
	Ret=(ll)Ret*jc[sz-l]%mod*c(sz,l)%mod;
	return ret+Ret;
}
int main()
{
	jc[0]=1;
	inv[0]=inv[1]=1;
	for (int i=1;i<(N<<1);i++)
		jc[i]=(ll)jc[i-1]*i%mod;
	for (int i=2;i<(N<<1);i++)
		inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
	for (int i=2;i<(N<<1);i++)
		inv[i]=(ll)inv[i]*inv[i-1]%mod;
	n=read();
	for (int i=1;i<=n*2;i++)
	{
		a[i]=read(),b[i]=read();
		e[++number].add(a[i],b[i]+n);
		mp[make_pair(a[i],b[i]+n)]=number;
		e[++number].add(b[i]+n,a[i]);
		mp[make_pair(b[i]+n,a[i])]=number;
		pd[a[i]]=1,pd[b[i]+n]=1;
	}
	for (int i=1;i<=n*2;i++)
		if (!pd[i])
		{
			puts("0");
			return 0;
		}
	for (int i=1;i<=n*2;i++)
		if (!vis[i])
		{
			flag=1;
			num=top=0,dfs(i);
			Ans[++cnt]=solve(len[cnt]=size[i]);
		}
	int ret=1,l=0;
	for (int i=1;i<=cnt;i++)
	{
		ret=(ll)ret*Ans[i]%mod*c(len[i]+l,l)%mod;
		l+=len[i];
	}
	printf("%d\n",ret);
	return 0;
}


Measured memory_bytes: 46080