#include  <iostream>
#include  <stdio.h>
#include <algorithm>
using namespace std;
#include <vector>
#define rep(i,n) for (int i = 0; i < (n) ; i++)
#define INF 1e9
#define llINF 1e18
#define MOD 1000000007
#define pb push_back
#define ll long long
#define ull unsigned long long
#define vint vector<int>
#define vll vector<ll>

//#include <stack>
//#include <queue>

/*
#include <math.h>
int standerd = int(pow(10.0,9.0)) + 7;
*/
string ans_Yes = "Yes"; 
string ans_No = "No"; 
string ans_yes = "yes"; 
string ans_no = "no"; 
ll A[1000000];
ll B[1000000];
ll C;
ll N;
ll M;
ll K;

vector<int> root_size1; // parent[i]:iの親の番号　(例) parent[3] = 2 : 3の親が2
vector<int> root_size2; // parent[i]:iの親の番号　(例) parent[3] = 2 : 3の親が2
class UnionFind {
public:
    vector<int> parent; // parent[i]:iの親の番号　(例) parent[3] = 2 : 3の親が2
    UnionFind(int N){ //最初は全てが根であるとして初期化
        for(int node_i = 0; node_i < N; node_i++){
            int parent_i = node_i;
            parent.push_back(parent_i);
            root_size1.push_back(1);
            root_size2.push_back(0);
        }
    }
    int getRoot(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
        if (parent[x] == x) return x;
        return parent[x] = getRoot(parent[x]);
    }
    void unite(int x, int y) { // xとyの木を併合
        int rx = getRoot(x); //xの根をrx
        int ry = getRoot(y); //yの根をry
        if (rx == ry) return; //xとyの根が同じ(=同じ木にある)時はそのまま
        parent[rx] = ry; //xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
        root_size1[ry]=root_size2[rx]+root_size1[ry];
        root_size2[ry]=root_size1[rx]+root_size2[ry];
        root_size1[rx]=root_size2[rx]+root_size1[ry];
        root_size2[rx]=root_size1[rx]+root_size2[ry];
    }
    bool isSameRoot(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
        int rx = getRoot(x);
        int ry = getRoot(y);
        return rx == ry;
    }
};



ll getPower(ll value,ll shisu,ll mod){
    ll ans = 1;
    ll cnt = shisu;
    while(cnt>0){
        if(cnt%2==1){
            ans = ans * value % mod;
        }
        value = value * value % mod;
        cnt = cnt/2;
    }
    return ans;
}

ll ans = 1;
int main(){

    
    cin >> N;
    rep(ni,N){
        cin >> A[ni];
        cin >> B[ni];
    }
    UnionFind uf(N);
    setupCombination(N,MOD);

    for( int ni = 0 ; ni <=N-1-1 ; ni++ ){
        for( int mi = ni+1 ; mi <=N-1 ; mi++ ){
            //cout << ni << ":" << mi << endl;
            //cout << A[ni] << "*" << A[mi] << endl;
            //cout << B[ni] << "*" << B[mi] << endl;
            if(A[ni]*A[mi] + B[ni]*B[mi] == 0){
                uf.unite(ni,mi);
            }
        }
    }

    rep(ni,N){

        if(uf.getRoot(ni)==ni){
            //cout << "size " << ni << endl;
            ll base = ans;
            ans = ans + base *( getPower(2,root_size1[ni],MOD)-1) % MOD;            
            if(root_size2[ni]>0) ans = ans + base * ( getPower(2,root_size2[ni],MOD)-1)% MOD;            
            ans = ans % MOD;
        }
        //cout << ans << endl;
    }
    cout << ans-1 << endl;

    

}