#include<stdio.h>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<set>
#define lowbit(o) o&(-o)
#define P 1000000007
#define N 200005
using namespace std;
int i,j,k,l,s,r,n,m,jc[N],ny[N],fa[N],A[2][N],B[2][N],tot,T,vis[2][N],id[N];
int ans,Ans,si,G[N];
set<int>GT[2][N],V[2][N];
set<int>::iterator GTW;
struct orz {
	int x,y;
}a[N],q[N],Q[N];
struct node {
	int tot,last[N],to[N],next[N];
	inline void add(int x,int y) {
		next[++tot]=last[x]; last[x]=tot; to[tot]=y;
	}
}A1;
struct Node {
	int tot,chu[N],ru[N],l,r,last[N],next[N],to[N],f[N],si[N];
	orz q[N];
	inline void add(int x,int y) {
		chu[x]++; ru[y]++;
		next[++tot]=last[x]; last[x]=tot; to[tot]=y;
	}
}A2;
inline bool cmp(int x,int y) {
	return G[x]<G[y];
}
inline int findl(int x) {
	x=G[x];
	int l=1,r=2*n,s=0;
	while (l<=r) {
		int mid=(l+r)>>1;
		if (G[id[mid]]<x) s=mid,l=mid+1;
		else r=mid-1;
	}
	return s+1;
}
inline int findr(int x) {
	x=G[x];
	int l=1,r=2*n,s=2*n+1;
	while (l<=r) {
		int mid=(l+r)>>1;
		if (G[id[mid]]>x) s=mid,r=mid-1;
		else l=mid+1;
	}
	return s-1;
}
inline int quick(int x,int y) {
	int s=1;
	while (y) {
		if (y&1) s=1ll*s*x%P;
		x=1ll*x*x%P;
		y>>=1;
	}
	return s;
}
inline int get(int x) {
	if (fa[x]==x) return x;
	return fa[x]=get(fa[x]);
}
inline int C(int x,int y) {
	return 1ll*jc[x]*ny[y]%P*ny[x-y]%P;
}
inline int getans(int r) {
	T=s=0;
	for (int i=1;i<=r;i++) V[q[i].x^1][B[q[i].x][q[i].y]].insert(q[i].y);
//	for (int i=0;i<=1;i++) for (int j=1;j<=n;j++) if (get(i*n+j)==get(q[1].x*n+q[1].y)) {
	int L=findl(get(q[1].x*n+q[1].y)),R=findr(get(q[1].x*n+q[1].y));
	for (int i=L;i<=R;i++) {
		s++;
		Q[++T].x=(id[i]-1)/n,Q[T].y=(id[i]-1)%n+1;
		if (A[Q[T].x][Q[T].y]) {
 			for (GTW=V[Q[T].x][Q[T].y].begin();GTW!=V[Q[T].x][Q[T].y].end();GTW++) {
				if ((*GTW)>A[Q[T].x][Q[T].y]) break;
				A2.add((Q[T].x^1)*n+(*GTW),Q[T].x*n+Q[T].y);
			}
		}
		else {
			for (GTW=V[Q[T].x][Q[T].y].begin();GTW!=V[Q[T].x][Q[T].y].end();GTW++) {
				if ((*GTW)>B[Q[T].x][Q[T].y]) break;
				A2.add((Q[T].x^1)*n+(*GTW),Q[T].x*n+Q[T].y);
			}
		}
	}
	A2.l=A2.r=0;
	A2.f[A2.si[0]=0]=1;
	for (int i=1;i<=T;i++) A2.f[Q[i].x*n+Q[i].y]=1;
	for (int i=1;i<=T;i++) if (!A2.chu[Q[i].x*n+Q[i].y]) A2.add(Q[i].x*n+Q[i].y,0);
	for (int i=1;i<=T;i++) if (!A2.ru[Q[i].x*n+Q[i].y]) A2.q[++A2.r].x=Q[i].x,A2.q[A2.r].y=Q[i].y;
	while (A2.l<A2.r) {
		A2.l++;
		A2.si[A2.q[A2.l].x*n+A2.q[A2.l].y]++;
		for (int i=A2.last[A2.q[A2.l].x*n+A2.q[A2.l].y];i;i=A2.next[i]) {
			A2.ru[A2.to[i]]--;
			A2.si[A2.to[i]]+=A2.si[A2.q[A2.l].x*n+A2.q[A2.l].y];
			(A2.f[A2.to[i]]=1ll*A2.f[A2.to[i]]*A2.f[A2.q[A2.l].x*n+A2.q[A2.l].y]%P*C(A2.si[A2.to[i]],A2.si[A2.q[A2.l].x*n+A2.q[A2.l].y])%P);
			if (!A2.ru[A2.to[i]]) A2.q[++A2.r].x=(A2.to[i]-1)/n,A2.q[A2.r].y=(A2.to[i]-1)%n+1;
		}
	}
	for (int i=1;i<=r;i++) V[q[i].x^1][B[q[i].x][q[i].y]].erase(q[i].y);
	A2.tot=0;
	for (int i=1;i<=A2.r;i++) A2.last[A2.q[i].x*n+A2.q[i].y]=0,A2.chu[A2.q[i].x*n+A2.q[i].y]=A2.ru[A2.q[i].x*n+A2.q[i].y]=0;
	for (int i=1;i<=T;i++) A2.f[A2.q[i].x*n+A2.q[i].y]=A2.si[A2.q[i].x*n+A2.q[i].y]=0;
	return A2.f[0];
}
inline void work(int x,int y) {
	vis[x][y]=1;
	int l=0,r=1; q[1].x=x,q[1].y=y;
	while (l<r) {
		++l;
		for (GTW=GT[q[l].x][q[l].y].begin();GTW!=GT[q[l].x][q[l].y].end();GTW++) if (!vis[q[l].x^1][(*GTW)]) q[++r].x=q[l].x^1,q[r].y=(*GTW),vis[q[r].x][q[r].y]=1;
	}
	for (int i=1;i<=r;i++) for (GTW=GT[q[i].x][q[i].y].begin();GTW!=GT[q[i].x][q[i].y].end();GTW++) A1.add(q[i].x*n+q[i].y,(*GTW));
	int xx=x,yy=y;
	B[xx][yy]=A1.to[A1.last[xx*n+yy]];
	yy=B[xx][yy],xx^=1;
	while (xx!=x||yy!=y) {
		for (int i=A1.last[xx*n+yy];i;i=A1.next[i]) if (B[xx^1][A1.to[i]]!=yy) {
			B[xx][yy]=A1.to[i],yy=B[xx][yy],xx^=1; break;
		}
	}
	Ans=getans(r);
	
	for (int i=1;i<=r;i++) B[q[i].x][q[i].y]=0;
	B[xx][yy]=A1.to[A1.next[A1.last[xx*n+yy]]];
	yy=B[xx][yy],xx^=1;
	while (xx!=x||yy!=y) {
		for (int i=A1.last[xx*n+yy];i;i=A1.next[i]) if (B[xx^1][A1.to[i]]!=yy) {
			B[xx][yy]=A1.to[i],yy=B[xx][yy],xx^=1; break;
		}
	}
	A1.tot=0;
	for (int i=1;i<=r;i++) A1.last[q[i].x*n+q[i].y]=0;
	(Ans+=getans(r))%=P;
	si+=s;
	(ans=1ll*ans*Ans%P*C(si,s)%P);
}
inline void doit() {
	jc[0]=1;
	for (int i=1;i<=200000;i++) jc[i]=1ll*jc[i-1]*i%P;
	ny[200000]=quick(jc[200000],P-2);
	for (int i=199999;i;i--) ny[i]=1ll*ny[i+1]*(i+1)%P;
	ny[0]=1;
}
int main() {
	doit();
	ans=1; si=0;
	scanf("%d",&n);
	for (i=0;i<=1;i++) for (j=1;j<=n;j++) fa[i*n+j]=i*n+j;
	for (i=1;i<=2*n;i++) {
		scanf("%d%d",&a[i].x,&a[i].y);
		if (get(a[i].x)!=get(n+a[i].y)) fa[get(a[i].x)]=get(n+a[i].y);
		GT[0][a[i].x].insert(a[i].y);
		GT[1][a[i].y].insert(a[i].x);
	}
	for (i=1;i<=2*n;i++) if (get(id[i]=i)==i) G[i]=++tot;
	for (i=1;i<=2*n;i++) G[i]=G[get(i)];
	tot=0;
	sort(id+1,id+1+2*n,cmp);
	for (i=0;i<=1;i++) for (j=1;j<=n;j++) if (!GT[i][j].size()) return puts("0"),0;
	else if (GT[i][j].size()==1) q[++r].x=i,q[r].y=j;
	while (l<r) {
		++l;
		if (!GT[q[l].x][q[l].y].size()) return puts("0"),0;
		A[q[l].x][q[l].y]=(*GT[q[l].x][q[l].y].begin());
		GT[q[l].x^1][A[q[l].x][q[l].y]].erase(q[l].y);
		if (GT[q[l].x^1][A[q[l].x][q[l].y]].size()==1) q[++r].x=q[l].x^1,q[r].y=A[q[l].x][q[l].y];
	}
	for (i=0;i<=1;i++) for (j=1;j<=n;j++) if (A[i][j]) V[i^1][A[i][j]].insert(j);
	for (i=0;i<=1;i++) for (j=1;j<=n;j++) if (!vis[i][j]&&GT[i][j].size()==2) 
	work(i,j);
	printf("%d\n",ans);
}
 

Measured memory_bytes: 80128