#include<bits/stdc++.h>
using namespace std;

#define int long long

#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
#define all(v) (v).begin(),(v).end()
#define fi first
#define se second
typedef vector<int>vint;
typedef pair<int,int>pint;
typedef vector<pint>vpint;

template<typename A,typename B>inline void chmin(A &a,B b){if(a>b)a=b;}
template<typename A,typename B>inline void chmax(A &a,B b){if(a<b)a=b;}

const int mod=1000000007;
int mpow(int n,int m){
    int ret=1;
    while(m){
        if(m&1)ret=ret*n%mod;
        n=n*n%mod;
        m>>=1;
    }
    return ret;
}
const int FACT_SIZE=1111111;
int fact[FACT_SIZE];
int inv[FACT_SIZE];
struct fact_exec{
    fact_exec(){
        fact[0]=1;
        for(int i=1;i<FACT_SIZE;i++)fact[i]=fact[i-1]*i%mod;
        inv[FACT_SIZE-1]=mpow(fact[FACT_SIZE-1],mod-2);
        for(int i=FACT_SIZE-2;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
    }
}factexec;
int nCk(int n,int k){
    return fact[n]*inv[k]%mod*inv[n-k]%mod;
}
int nPk(int n,int k){
    return fact[n]*inv[n-k]%mod;
}

struct UnionFindTree{
    vector<int>par,sz;
    UnionFindTree(int n){
        par.resize(n);
        sz.resize(n);
        for(int i=0;i<n;i++){
            par[i]=i;
            sz[i]=1;
        }
    }
    int find(int x){
        return x==par[x]?x:par[x]=find(par[x]);
    }
    void unite(int x,int y){
        x=find(x);y=find(y);
        if(x==y)return;
        if(sz[x]<sz[y])swap(x,y);
        sz[x]+=sz[y];
        par[y]=x;
    }
    bool areSame(int x,int y){
        return find(x)==find(y);
    }
    int size(int x){
        return sz[find(x)];
    }
};

int N;
int X[222222],Y[222222];

vint G[222222];
int sz[222222];
int dfs(int v,int p){
    sz[v]=0;
    int ret=1;
    for(auto u:G[v]){
        ret=ret*dfs(u,v)%mod*nCk(sz[v]+sz[u],sz[u])%mod;
        sz[v]+=sz[u];
    }
    sz[v]++;
    return ret;
}
int calc2(vint to,vector<vint>&g){
    int n=to.size();

    vint tto(n,-1);
    rep(i,n){
        int j=to[i];
        int k=to[j];
        if(i<k)tto[i]=j;
    }

    rep(i,n+1)G[i].clear();
    rep(i,n){
        if(tto[i]==-1)G[n].pb(i);
        else G[tto[i]].pb(i);
    }

    return dfs(n,-1);
}


int calc(vpint es){
    int n=es.size();
    vint lis;
    rep(i,es.size())lis.pb(es[i].fi),lis.pb(es[i].se);
    sort(all(lis));lis.erase(unique(all(lis)),lis.end());
    rep(i,es.size()){
        es[i].fi=lower_bound(all(lis),es[i].fi)-lis.begin();
        es[i].se=lower_bound(all(lis),es[i].se)-lis.begin();
    }

    vector<vint>g(n);
    vint deg(n);
    for(auto &e:es){
        g[e.fi].pb(e.se);
        g[e.se].pb(e.fi);
        deg[e.fi]++;deg[e.se]++;
    }

    queue<int>que;
    rep(i,n)if(deg[i]==1)que.push(i);
    vint to(n,-1);
    while(que.size()){
        int v=que.front();
        que.pop();
        for(auto u:g[v]){
            if(to[u]!=-1)continue;
            to[v]=u;
            if(--deg[u]==1){
                que.push(u);
            }
        }
    }

    int s;
    rep(i,n)if(to[i]==-1)s=i;

    int ret=0;
    for(auto u:g[s]){
        if(to[u]!=-1)continue;

        vint tto=to;
        tto[s]=u;
        int v=u;
        while(true){
            int w=-1;
            for(auto x:g[v]){
                if(tto[x]!=-1)continue;
                w=x;
            }
            if(w!=-1){
                tto[v]=w;
                v=w;
            }
            else{
                tto[v]=s;
                break;
            }
        }
        ret=(ret+calc2(tto,g))%mod;
    }
    return ret;
}

signed main(){
    cin>>N;N*=2;
    rep(i,N){
        cin>>X[i]>>Y[i];
        X[i]--;Y[i]--;
        Y[i]+=N/2;
    }

    UnionFindTree uf(N);
    rep(i,N){
        uf.unite(X[i],Y[i]);
    }

    vint cnt(N);
    rep(i,N)cnt[uf.find(X[i])]++;

    rep(i,N)if(uf.find(i)==i&&cnt[i]!=uf.size(i)){
        cout<<0<<endl;
        return 0;
    }

    vector<vpint>lis(N);
    rep(i,N){
        lis[uf.find(X[i])].pb({X[i],Y[i]});
    }

    int ans=1;
    int num=0;
    rep(i,N){
        if(lis[i].size()==0)continue;
        ans=ans*calc(lis[i])%mod*nCk(num+lis[i].size(),num)%mod;
        num+=lis[i].size();
    }

    cout<<ans<<endl;
    return 0;
}


Measured memory_bytes: 70764