#include <bits/stdc++.h>
using namespace std;
using P_rep= pair<int64_t, int64_t>;

constexpr int64_t MOD= 1000000007;
constexpr bool is_FACTORIAL_STORED= false; // 階乗を配列に格納をする前処理をする/しない
constexpr int Nfac= is_FACTORIAL_STORED ? 2000007
                                        : 0;
/* 剰余環 Z/nZ */
struct mint{ // Z/nZ に関する演算(n:素数の場合は除算も)をサポートする構造体
  int64_t rep; // 出力すべき代表元 rep \in {0,1,..,MOD- 1}
  mint(int64_t x=0): rep(((x%MOD)+ MOD)% MOD){}
  mint operator-() const { return mint(-rep);}
  mint& operator+=(const mint& x){
    rep+= x.rep; 
    if(rep>= MOD) rep-= MOD;
    return *this;
  }
  mint& operator-=(const mint& x){
    rep-= x.rep;
    if(rep< 0) rep+= MOD;
    return *this;
  }
  mint& operator*=(const mint& x){
    rep*= x.rep; rep%= MOD;
    return *this;
  }
  mint operator+(const mint x) const {
    mint ans(*this);
    ans+= x;
    return ans;
  }
  mint operator-(const mint x) const {
    mint ans(*this);
    ans-= x;
    return ans;
  }
  mint operator*(const mint x) const {
    mint ans(*this);
    ans*= x;
    return ans;
  }
  bool operator==(const mint x) const {
    return rep==x.rep;
  }
  bool operator!=(const mint x) const {
    return rep!=x.rep;
  }

  mint pow(int64_t d) const{ // d \geq 0
    mint ans= 1;
    mint x= *this;
    while(d > 0){
      if(d&1){ ans*= x; }
      d >>= 1;
      x*= x;
    }

    return ans;
  }

  friend istream& operator >> (istream &is, mint& x) noexcept{
    int64_t a;
    is >> a;
    x= a;
    return is;
  }
  friend ostream& operator << (ostream &os, const mint& x) noexcept{
      return os << x.rep;
  }
  // MOD が素数(<=> Z/nZ が体)のとき
  mint inv() const{
    return pow(MOD-2);
  }
  mint& operator/=(const mint& x){
    *this *= x.inv();
    return *this;
  }
  mint operator/(const mint x) const {
    mint ans(*this);
    ans/= x;
    return ans;
  }
};

/* 階乗,二項係数 */
vector<mint> g_mfac(Nfac+1), g_ifac(Nfac+1);
void fact_modp_init(int N= Nfac){
  if constexpr(is_FACTORIAL_STORED){
    static bool initialized = false;
    if(!initialized){
      g_mfac.at(0)= 1;
      for(int i=1; i<=N; i++){
        g_mfac.at(i)= g_mfac.at(i-1)* (mint)i;
      }
  
      g_ifac.at(N)= g_mfac.at(N).inv();
      for(int i=N; i>0; i--){
        g_ifac.at(i-1)= g_ifac.at(i)* (mint)i;
      }

      initialized= true;
    }
    else{ return; }
  }
  else{
    return;
  }
}

inline mint mfac(int N){
  if constexpr(is_FACTORIAL_STORED){
    fact_modp_init();
    return g_mfac.at(N);
  }
  else{
    mint ans= 1;
    for(int i=1; i<=N; i++){
      ans*= (mint)i;
    }
    return ans;
  }
}

inline mint ifac(int N){
  if constexpr(is_FACTORIAL_STORED){
    fact_modp_init();
    return g_ifac.at(N);
  }
  else{
    return mfac(N).inv();
  }
}

mint binom_modp(int n, int k){
  if constexpr(is_FACTORIAL_STORED){
    if(n < 0 || min(k,n-k) < 0) return 0;
    else{
      fact_modp_init();
      return g_mfac.at(n)* g_ifac.at(k)* g_ifac.at(n-k);
    }
  }
  else{
    if(n < 0 || min(k,n-k) < 0) return 0;
    else{
      k= min(k, n-k);
      mint ans= 1;
      for(int i=1; i<=k; i++){
        ans*= (mint)(n-i+1);
      }
      ans/= mfac(k);

      return ans;
    }
  }
}

mint multiset_modp(int n, int r){
  if(n==0 && r==0) return 1;
  else{
    return binom_modp(n+r-1, r);
  }
}
// ^^^ テンプレここまで ^^^

inline P_rep canonical(int64_t x, int64_t y){
  if(x < 0 || (x==0 && y < 0)){
    x*= -1;
    y*= -1;
  }
  int64_t g= gcd(abs(x), abs(y));

  return make_pair(x/g, y/g);
}

int main(){
  int N;
  cin >> N;

  map<P_rep, int64_t> data;
  int64_t x,y;
  mint Norigin= 0;
  for(int i=0; i<N; i++){
    cin >> x >> y;
    if(!(x==0LL && y==0LL)){
      P_rep p_this= canonical(x, y);
      
      if(data.count(p_this)){ data.at(p_this)+= 1; }
      else                  { data[p_this]= 1; }
    }
    else{
      Norigin+= 1;
    }
  }

  set<P_rep> checked;
  mint ans= 1;
  for(auto elem:data){
    P_rep p_this;
    int64_t cnt;
    tie(p_this, cnt)= elem;

    int64_t x_rep,y_rep;
    tie(x_rep, y_rep)= p_this;
    
    P_rep p_vert= canonical(-y_rep,  x_rep);

    if(!checked.count(p_this)){
      if(data.count(p_vert)){
        ans*= ((mint)2).pow(cnt)+ ((mint)2).pow(data.at(p_vert))- (mint)1;
        checked.insert(p_vert);
      }
      else{
        ans*= ((mint)2).pow(cnt);
      }
    }
  }
  ans-= 1; // 0匹選ぶパターンは除外
  ans+= Norigin; // 原点にあるイワシの内の1匹を選ぶパターンもありえる
  
  cout << ans << endl;
}

Measured memory_bytes: 16004