#include <bits/stdc++.h>
using namespace std;
class ParseError {};
template <typename T>
class Zip {
  vector<T> d;
  bool flag;
  void init() {
    sort(d.begin(), d.end());
    d.erase(unique(d.begin(), d.end()), d.end());
    flag = false;
  }

 public:
  Zip() { flag = false; }
  void add(T x) {
    d.push_back(x);
    flag = true;
  }
  long long getNum(T x) {
    if (flag) init();
    return lower_bound(d.begin(), d.end(), x) - d.begin();
  }
  long long size() {
    if (flag) init();
    return (long long)d.size();
  }
};
long long N, M, K, a, b, c, d, e, H, W, L, T;
long long x, y, z;
long long A[2000004] = {};
long long B[2000004] = {};
long long C[2000004] = {};
long long D[1000006] = {};
long long E[1000006] = {};
bool f;
string S[1000006];
string SS;
set<long long> sll;
pair<long long, long long> bufpl;
vector<long long> vl[200005];
vector<long long> vll;
vector<long long> v;
vector<pair<long long, long long>> vpl;
vector<string> vs;
set<long long> llset;
set<string> Sset;
multiset<long long> llmset;
queue<long long> ql;
multiset<pair<long long, long long>> plmset;
typedef struct ST {
  long long first;
  long long second;
  long long cost;
  bool operator<(const ST& another) const { return cost < another.cost; };
  bool operator>(const ST& another) const { return cost > another.cost; };
} ST;
priority_queue<ST, vector<ST>, greater<ST>> qst;
long long modinv(long long aa, long long mm) {
  long long bb = mm, uu = 1, vv = 0;
  while (bb) {
    long long tt = aa / bb;
    aa -= tt * bb;
    swap(aa, bb);
    uu -= tt * vv;
    swap(uu, vv);
  }
  uu %= mm;
  if (uu < 0) uu += mm;
  return uu;
}
long long zettai(long long aa) {
  if (aa < 0) {
    aa *= -1;
  }
  return aa;
}
float zettai(float aa) {
  if (aa < 0) {
    aa *= -1;
  }
  return aa;
}
class UnionFind {
 public:
  vector<long long> pairent;
  vector<long long> depth;
  vector<long long> size;
  UnionFind(long long Amount)
      : pairent(Amount, 1), depth(Amount, 1), size(Amount, 1) {
    for (long long i = 0; i < Amount; i++) {
      pairent[i] = i;
    }
  }
  long long FindPairent(long long x) {
    if (pairent[x] == x)
      return x;
    else
      return pairent[x] = FindPairent(pairent[x]);
  }
  long long Merge(long long x, long long y) {
    x = FindPairent(x);
    y = FindPairent(y);
    if (x != y) {
      if (depth[x] > depth[y]) {
        pairent[y] = pairent[x];
        return size[x] += size[y];
      } else {
        pairent[x] = pairent[y];
        if (depth[x] == depth[y]) {
          depth[y]++;
        }
        return size[y] += size[x];
      }
    } else {
      return -1;
    }
  }
  bool IsSame(long long x, long long y) {
    if (FindPairent(x) == FindPairent(y))
      return true;
    else
      return false;
  }
  long long GetSize(long long x) {
    x = FindPairent(x);
    return size[x];
  }
};
struct Edge {
  long long a, b, cost;
  bool operator<(const Edge& other) const { return cost < other.cost; }
};
struct Graph {
  long long n;
  vector<Edge> es;
};
class Kruskal {
  Graph origin_G;
  Graph MST;
  long long total_cost = 0;

 public:
  void Solve() {
    UnionFind uf = UnionFind(MST.n);
    for (long long i = 0; i < origin_G.es.size(); i++) {
      long long a = origin_G.es[i].a;
      long long b = origin_G.es[i].b;
      long long cost = origin_G.es[i].cost;
      if (!uf.IsSame(a, b)) {
        uf.Merge(a, b);
        MST.es.push_back(origin_G.es[i]);
        total_cost += cost;
      }
    }
  }
  Kruskal(Graph graph) {
    origin_G = graph;
    MST = graph;
    MST.es.clear();
    sort(origin_G.es.begin(), origin_G.es.end());
  }
  long long GetMinCost() { return total_cost; }
};
long long RepeatSquaring(long long N, long long P, long long M) {
  if (P == 0) return 1;
  if (P % 2 == 0) {
    long long t = RepeatSquaring(N, P / 2, M) % M;
    return t * t % M;
  }
  return N * RepeatSquaring(N, P - 1, M) % M;
}
long long GCD(long long a, long long b) {
  if (a % b == 0)
    return b;
  else
    return GCD(b, a % b);
}
long long Min(long long a, long long b) {
  if (a < b)
    return a;
  else
    return b;
}
long long Max(long long a, long long b) {
  if (a > b)
    return a;
  else
    return b;
}
long long Sum(long long a, long long b) { return a + b; }
template <typename T>
class SegmentTree {
  long long n;
  vector<T> node;
  function<T(T, T)> fun, fun2;
  bool customChange;
  T outValue, initValue;

 public:
  void init(long long num, function<T(T, T)> resultFunction, T init, T out,
            function<T(T, T)> changeFunction = NULL) {
    fun = resultFunction;
    fun2 = changeFunction;
    customChange = changeFunction != NULL;
    n = 1;
    while (n < num) n *= 2;
    node.resize(2 * n - 1);
    fill(node.begin(), node.end(), init);
    outValue = out;
    initValue = init;
  }
  void valueChange(long long num, T value) {
    num += n - 1;
    if (customChange)
      node[num] = fun2(value, node[num]);
    else
      node[num] = value;
    while (num > 0)
      num = (num - 1) / 2,
      node[num] = fun(node[num * 2 + 1], node[num * 2 + 2]);
  }
  T rangeQuery(long long a, long long b, long long l = 0, long long r = -1,
               long long k = 0) {
    if (r == -1) r = n;
    if (a <= l && r <= b) return node[k];
    if (b <= l || r <= a) return outValue;
    long long mid = (l + r) / 2;
    return fun(rangeQuery(a, b, l, mid, 2 * k + 1),
               rangeQuery(a, b, mid, r, 2 * k + 2));
  }
};
class Combination {
  vector<long long> factorial;
  vector<long long> factorial_inv;
  long long mod;
  long long max_n;

 public:
  void Init(long long init_max_n, long long init_mod) {
    max_n = init_max_n;
    mod = init_mod;
    factorial.resize(max_n + 1);
    factorial[0] = 1;
    for (long long i = 1; i < factorial.size(); i++) {
      factorial[i] = factorial[i - 1] * i;
      factorial[i] %= mod;
    }
    factorial_inv.resize(max_n + 1);
    factorial_inv[0] = 1;
    for (long long i = 1; i < factorial_inv.size(); i++) {
      factorial_inv[i] = factorial_inv[i - 1] * modinv(i, mod);
      factorial_inv[i] %= mod;
    }
  }
  long long GetComb(long long n, long long k) {
    long long comb = factorial[n];
    comb *= factorial_inv[k];
    comb %= mod;
    comb *= factorial_inv[n - k];
    comb %= mod;
    return comb;
  }
  long long GetH(long long n, long long k) {
    long long comb = factorial[n + k - 1];
    comb *= factorial_inv[n];
    comb %= mod;
    comb *= factorial_inv[k - 1];
    comb %= mod;
    return comb;
  }
};
class Tree {
  long long node_N;
  vector<long long> node;
  vector<vector<pair<long long, long long>>> pass;
  long long diameter = -1;
  vector<long long> dist_Diamieter[2];
  pair<long long, long long> maxDist_Num;

 public:
  void Init(long long node_Num) {
    node_N = node_Num;
    node.resize(node_N + 1);
    pass.resize(node_N + 1);
    dist_Diamieter[0].resize(node_N + 1);
    for (long long i = 0; i < node_N + 1; i++) dist_Diamieter[0][i] = -1;
    dist_Diamieter[1].resize(node_N + 1);
    for (long long i = 0; i < node_N + 1; i++) dist_Diamieter[1][i] = -1;
  }
  void AddEdge(long long a, long long b, long long dist) {
    bufpl.first = b;
    bufpl.second = dist;
    pass[a].push_back(bufpl);
    bufpl.first = a;
    pass[b].push_back(bufpl);
  }
  void DFS(long long step, long long now, long long dist) {
    dist_Diamieter[step][now] = dist;
    if (dist_Diamieter[step][now] > maxDist_Num.first) {
      maxDist_Num.first = dist_Diamieter[step][now];
      maxDist_Num.second = now;
    }
    for (long long i = 0; i < pass[now].size(); i++) {
      long long next_node = pass[now][i].first;
      if (dist_Diamieter[step][next_node] == -1) {
        DFS(step, next_node, dist + pass[now][i].second);
      }
    }
  }
  long long GetDiameter(long long min_node_num) {
    if (diameter >= 0)
      return diameter;
    else {
      maxDist_Num.first = -1;
      maxDist_Num.second = -1;
      DFS(0, min_node_num, 0ll);
      long long step2_start = maxDist_Num.second;
      maxDist_Num.first = -1;
      maxDist_Num.second = -1;
      DFS(1, step2_start, 0ll);
      diameter = maxDist_Num.first;
      return diameter;
    }
  }
  long long GetDistFromMinNode(long long num) { return dist_Diamieter[0][num]; }
};
void Nibu(long long node, long long co) {
  C[node] = co % 2;
  D[co % 2]++;
  for (long long i = 0; i < vl[node].size(); i++) {
    long long next = vl[node][i];
    if (C[next] == -1) {
      Nibu(next, co + 1);
    }
  }
}
Combination comb;
vector<pair<long long, long long>> vpll;
vector<pair<long long, long long>> zn;
vector<pair<long long, long long>> nz;
map<pair<long long, long long>, long long> oo;
map<pair<long long, long long>, long long> tt;
int main() {
  cin >> N;
  M = N;
  for (long long i = 0; i < M; i++) {
    cin >> A[i] >> B[i];
    bufpl.first = A[i];
    bufpl.second = B[i];
    if (A[i] == 0 && B[i] == 0) {
      N--;
    } else if (A[i] == 0) {
      zn.push_back(bufpl);
    } else if (B[i] == 0) {
      nz.push_back(bufpl);
    } else if (A[i] < 0 && B[i] < 0) {
      if (bufpl.first < 0) bufpl.first *= -1;
      if (bufpl.second < 0) bufpl.second *= -1;
      a = GCD(bufpl.first, bufpl.second);
      bufpl.first /= a;
      bufpl.second /= a;
      oo[bufpl]++;
    } else if (A[i] > 0 && B[i] > 0) {
      if (bufpl.first < 0) bufpl.first *= -1;
      if (bufpl.second < 0) bufpl.second *= -1;
      a = GCD(bufpl.first, bufpl.second);
      bufpl.first /= a;
      bufpl.second /= a;
      oo[bufpl]++;
    } else {
      if (bufpl.first < 0) bufpl.first *= -1;
      if (bufpl.second < 0) bufpl.second *= -1;
      a = GCD(bufpl.first, bufpl.second);
      bufpl.first /= a;
      bufpl.second /= a;
      tt[bufpl]++;
    }
  }
  long long ans = RepeatSquaring(2, N, 1000000007);
  a = RepeatSquaring(2, zn.size(), 1000000007);
  a--;
  b = RepeatSquaring(2, nz.size(), 1000000007);
  b--;
  c = a * b;
  c %= 1000000007;
  c *= RepeatSquaring(2, N - zn.size() - nz.size(), 1000000007);
  c %= 1000000007;
  ans -= c;
  while (ans < 0) ans += 1000000007;
  for (auto itr : oo) {
    bufpl = itr.first;
    swap(bufpl.first, bufpl.second);
    x = itr.second;
    y = tt[bufpl];
    a = RepeatSquaring(2, x, 1000000007);
    a--;
    b = RepeatSquaring(2, y, 1000000007);
    b--;
    c = a * b;
    c %= 1000000007;
    c *= RepeatSquaring(2, N - x - y, 1000000007);
    c %= 1000000007;
    ans -= c;
    while (ans < 0) ans += 1000000007;
  }
  ans--;
  if (ans < 0) ans += 1000000007;
  cout << ans << endl;
  return 0;
}
