#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
 
using namespace std;
using namespace __gnu_pbds;
 
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define fbo find_by_order
#define ook order_of_key
 
typedef long long ll;
typedef long double ld; 
typedef pair<ll,ll> ii;
typedef vector<int> vi;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> pbds;
typedef set<ll>::iterator sit;
typedef map<ll,ll>::iterator mit;

struct Combi
{
	vector<ll> fact;
	vector<ll> ifact;
	vector<ll> inv;
	vector<ll> pow2;
	const int MOD = 1e9 + 7;
	ll add(ll a, ll b)
	{
		a+=b;
		while(a>=MOD) a-=MOD;
		return a;
	}
	ll mult(ll a, ll b)
	{
		return (a*b)%MOD;
	}
	ll modpow(ll a, ll b)
	{
		ll r=1;
		while(b)
		{
			if(b&1) r=mult(r,a);
			a=mult(a,a);
			b>>=1;
		}
		return r;
	}
	ll choose(ll a, ll b)
	{
		if(a<b) return 0;
		if(b==0) return 1;
		if(a==b) return 1;
		return mult(fact[a],mult(ifact[b],ifact[a-b]));
	}
	ll inverse(ll a)
	{
		return modpow(a,MOD-2);
	}
	void init(int _n)
	{
		fact.clear(); ifact.clear(); inv.clear(); pow2.clear();
		fact.resize(_n+1);
		ifact.resize(_n+1);
		inv.resize(_n+1);
		pow2.resize(_n+1);
		pow2[0]=1;
		ifact[0]=1;
		fact[0]=1;
		for(int i=1;i<=_n;i++)
		{
			pow2[i]=add(pow2[i-1],pow2[i-1]);
			inv[i] = inverse(i);
			fact[i]=mult(fact[i-1],i);
			ifact[i]=mult(ifact[i-1],inv[i]);
		}
	}
};


Combi combi;
vi adj[211111];
set<ii> S;
set<ii> ad[211111];
vector<vi> cyc;
bool vis[211111];

void dfscyc(int u)
{
	vis[u]=1;
	cyc.back().pb(u);
	for(auto it=ad[u].begin();it!=ad[u].end();it++)
	{
		int v=(*it).fi;
		if(!vis[v])
		{
			dfscyc(v);
		}
	}
}

map<ii,int> ma;
int n; 
int match[200011];
int imatch[200011];
vi comp[200011];

void dfs(int u, int idx)
{
	vis[u]=1;
	comp[idx].pb(u);
	//////////cerr<<u<<'\n';
	for(int i=0;i<adj[u].size();i++)
	{
		int v=adj[u][i];
		if(vis[v]) continue;
		ii cur = mp(u,v%n);
		if(u>=n) cur = mp(v,u%n);
		match[ma[cur]] = v;
		imatch[v] = ma[cur];
		dfs(v,idx);
	}
}
const int MOD=1e9+7;

ll add(ll &a, ll b)
{
	a+=b;
	while(a>=MOD) a-=MOD;
	return a;
}

vector<ii> pt;
vi T[200001];
int indeg[200001];

ll mult(ll a, ll b)
{
	return (a*b)%MOD;
}

ii calctree(int u ,int p = -1)
{
	ll ans = 1;
	ll siz=1;
	for(int i=0;i<T[u].size();i++)
	{
		int v=T[u][i];
		ii res = calctree(v,u);
		ans = mult(ans,res.fi);
		ans = mult(ans,combi.ifact[res.se]);
		siz+=res.se;
	}
	ans = mult(ans,combi.fact[siz-1]);
	//////cerr<<u<<' '<<p<<' '<<ans<<' '<<siz<<'\n';
	return mp(ans,siz);
}

int timer;

int main()
{
	ios_base::sync_with_stdio(0); cin.tie(0);
	cin>>n;
	for(int i=0;i<2*n;i++)
	{
		int x, y; cin>>x>>y;
		x--; y--;
		adj[x].pb(y+n);
		adj[y+n].pb(x);
		ad[x].insert(mp(y+n,++timer));
		ad[y+n].insert(mp(x,timer));
		ma[mp(x,y)] = i;
		pt.pb(mp(x,y));
	}
	for(int i=0;i<2*n;i++)
	{
		S.insert(mp(int(ad[i].size()),i));
	}
	while(!S.empty())
	{
		ii tmp = (*S.begin());
		//////cerr<<tmp.fi<<' '<<tmp.se<<'\n';
		if(tmp.fi==0)
		{
			cout<<0<<'\n';
			return 0;
		}
		else if(tmp.fi==1)
		{
			int u = tmp.se;
			int v = (*ad[u].begin()).fi;
			S.erase(mp(ad[u].size(),u));
			S.erase(mp(ad[v].size(),v));
			//////cerr<<"ERASE : "<<v<<' '<<ad[v].size()<<'\n';
			ad[u].erase(ad[u].lower_bound(mp(v,-1)));
			ad[v].erase(ad[v].lower_bound(mp(u,-1)));
			//////cerr<<"ERASE : "<<v<<' '<<ad[v].size()<<'\n';
			S.insert(mp(ad[v].size(),v));
		}
		else
		{
			break;
		}
	}
	vi vertices;
	for(auto it = S.begin(); it != S.end(); it++)
	{
		if(!vis[it->se]) 
		{
			cyc.pb(vi());
			dfscyc(it->se);
		}
	}
	memset(vis,0,sizeof(vis));
	for(int i=0;i<cyc.size();i++) 
	{
		for(int j=0;j<cyc[i].size();j++)
		{
			////cerr<<cyc[i][j]<<' ';
			vis[cyc[i][j]]=1;
		}
		////cerr<<'\n';
	}
	for(int i=0;i<cyc.size();i++)
	{
		for(int j=0;j<cyc[i].size();j++)
		{
			dfs(cyc[i][j],i);
		}
	}
	
	for(int i=0;i<2*n;i++)
	{
		sort(adj[i].begin(),adj[i].end());
	}
	combi.init(212121);
	vector<ll> results;
	ll res = combi.fact[2*n];
	for(int z=0;z<cyc.size();z++)
	{
		sort(comp[z].begin(),comp[z].end());
		res=mult(res,combi.ifact[comp[z].size()]);
		ll ans=1;
		for(int i=0;i<cyc[z].size();i++)
		{
			int u=cyc[z][i];
			int v=cyc[z][(i+1)%int(cyc[z].size())];
			//////////cerr<<u<<' '<<v<<'\n';
			ii cur = mp(u,v%n);
			if(u>=n) cur = mp(v,u%n);
			match[ma[cur]] = u;
			imatch[u] = ma[cur];
			//////////cerr<<u<<' '<<ma[cur]<<'\n';
		}
		vi FF;
		for(int f=0;f<comp[z].size();f++)
		{
			int i=comp[z][f];
			int x = imatch[i];
			ii V = pt[x];
			int cr = V.se + n;
			if(i>=n)
			{
				cr = V.fi;
			}
			if(i<n) FF.pb(ma[mp(i,cr-n)]);
			else FF.pb(ma[mp(cr,i-n)]);
			for(int j=0;j<adj[i].size();j++)
			{
				int v=adj[i][j];
				if(v<cr)
				{
					if(i<n)
					{
						//////////cerr<<i<<' '<<v-n<<' '<<i<<' '<<cr-n<<'\n';
						T[ma[mp(i,cr-n)]].pb(ma[mp(i,v-n)]);
					}
					else
					{
						//////////cerr<<v<<' '<<i-n<<' '<<cr<<' '<<i-n<<'\n';
						T[ma[mp(cr,i-n)]].pb(ma[mp(v,i-n)]);
					}
				}
			}
		}
		sort(FF.begin(),FF.end());
		FF.erase(unique(FF.begin(),FF.end()),FF.end());
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			for(int j=0;j<T[i].size();j++)
			{
				//cerr<<"T1 : "<<i<<' '<<T[i][j]<<'\n';
				indeg[T[i][j]]++;
			}
		}
		
		vi vec;
		
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			if(indeg[i]==0)
			{
				ii tmp = calctree(i);
				////////cerr<<"DEG : "<<i<<' '<<tmp.fi<<' '<<tmp.se<<'\n';
				ans = mult(ans,tmp.fi);
				//////cerr<<i<<' '<<tmp.fi<<' '<<tmp.se<<'\n';
				vec.pb(tmp.se);
			}
		}
		
		ll r=0;
		for(int i=0;i<vec.size();i++)
		{
			r+=vec[i];
			ans=mult(ans,combi.ifact[vec[i]]);
		}
		ans=mult(ans,combi.fact[r]);
		//////////cerr<<ans<<'\n';
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			T[i].clear();
			indeg[i]=0;
		}
		
		for(int i=0;i<cyc[z].size();i++)
		{
			int u=cyc[z][i];
			int v=cyc[z][(i+1)%int(cyc[z].size())];
			ii cur = mp(u,v%n);
			if(u>=n) cur = mp(v,u%n);
			match[ma[cur]] = v;
			imatch[v] = ma[cur];
		}
		FF.clear();
		for(int f=0;f<comp[z].size();f++)
		{
			int i=comp[z][f];
			int x = imatch[i];
			ii V = pt[x];
			int cr = V.se + n;
			if(i>=n)
			{
				cr = V.fi;
			}
			if(i<n) FF.pb(ma[mp(i,cr-n)]);
			else FF.pb(ma[mp(cr,i-n)]);
			for(int j=0;j<adj[i].size();j++)
			{
				int v=adj[i][j];
				if(v<cr)
				{
					if(i<n)
					{
						//////////cerr<<i<<' '<<v-n<<' '<<i<<' '<<cr-n<<'\n';
						T[ma[mp(i,cr-n)]].pb(ma[mp(i,v-n)]);
					}
					else
					{
						//////////cerr<<v<<' '<<i-n<<' '<<cr<<' '<<i-n<<'\n';
						T[ma[mp(cr,i-n)]].pb(ma[mp(v,i-n)]);
					}
				}
			}
		}
		sort(FF.begin(),FF.end());
		FF.erase(unique(FF.begin(),FF.end()),FF.end());
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			for(int j=0;j<T[i].size();j++)
			{
				//cerr<<"T2 : "<<i<<' '<<T[i][j]<<'\n';
				indeg[T[i][j]]++;
			}
		}
		
		ll ans2 = 1;
		vec.clear();
		
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			if(indeg[i]==0)
			{
				ii tmp = calctree(i);
				ans2 = mult(ans2,tmp.fi);
				//////cerr<<i<<' '<<tmp.fi<<' '<<tmp.se<<'\n';
				vec.pb(tmp.se);
			}
		}
		
		r=0;
		for(int i=0;i<vec.size();i++)
		{
			r+=vec[i];
			ans2=mult(ans2,combi.ifact[vec[i]]);
		}
		
		ans2=mult(ans2,combi.fact[r]);
	
		add(ans,ans2);
		res = mult(res,ans);
		for(int f=0;f<FF.size();f++)
		{
			int i=FF[f];
			T[i].clear();
			indeg[i]=0;
		}
		//cerr<<comp[z].size()<<' '<<ans<<'\n';
	}
	cout<<res<<'\n';
}


Measured memory_bytes: 101100