#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
struct mint {
  static const long long MOD = 1000000007;
  long long v;
  mint() : v(0) {}
  mint(long long x) {
    x = x % MOD;
    if (x < 0) {
      x += MOD;
    }
    v = x;
  }
  mint& operator+=(mint a) {
    v += a.v;
    if (v >= MOD) {
      v -= MOD;
    }
    return *this;
  }
  mint& operator*=(mint a) {
    v *= a.v;
    v %= MOD;
    return *this;
  }
  mint& operator-=(mint a) {
    v -= a.v;
    if (v < 0) {
      v += MOD;
    }
    return *this;
  }
  mint& operator/=(mint a) { return (*this) *= a.inv(); }
  mint operator*(mint a) const { return mint(v) *= a; }
  mint operator+(mint a) const { return mint(v) += a; }
  mint operator-(mint a) const { return mint(v) -= a; }
  mint operator/(mint a) const { return mint(v) /= a; }
  mint pow(long long k) const {
    mint res(1), tmp(v);
    while (k) {
      if (k & 1) res *= tmp;
      tmp *= tmp;
      k >>= 1;
    }
    return res;
  }
  mint inv() { return pow(MOD - 2); }
  static mint comb(long long n, int k) {
    mint res(1);
    for (int i = 0; i < k; ++i) {
      res *= mint(n - i);
      res /= mint(i + 1);
    }
    return res;
  }
  static mint factorial(long long n) {
    mint res(1);
    for (int i = n; i > 1; --i) {
      res *= mint(i);
    }
    return res;
  }
  bool operator<(const mint& a) const { return v < a.v; };
};
mint operator*(long long l, mint r) { return r * l; }
std::ostream& operator<<(std::ostream& stream, const mint& m) {
  stream << m.v;
  return stream;
}
ll gcd(ll a, ll b) {
  if (a < b) {
    return gcd(b, a);
  }
  ll r;
  while ((r = a % b)) {
    a = b;
    b = r;
  }
  return b;
}
struct Frac {
  ll sign;
  ll a, b;
  Frac(ll sign, ll a, ll b) : sign(sign), a(a), b(b) {}
  Frac(ll a0, ll b0) {
    assert(a0 != 0 || b0 != 0);
    if (a0 != 0 && b0 != 0) {
      const ll g = gcd(abs(a0), abs(b0));
      a0 /= g;
      b0 /= g;
      if ((a0 > 0 && b0 > 0) || (a0 < 0 && b0 < 0)) {
        sign = 1;
      } else {
        sign = -1;
      }
      a = abs(a0);
      b = abs(b0);
    } else if (a0 != 0) {
      sign = 1;
      a = 1;
      b = 0;
    } else if (b0 != 0) {
      sign = 1;
      a = 0;
      b = 1;
    }
  }
  const Frac flip() const {
    if (a != 0 && b != 0) {
      return Frac(-sign, b, a);
    } else if (a == 0) {
      return Frac(1, 1, 0);
    } else {
      assert(b == 0);
      return Frac(1, 0, 1);
    }
  }
  const Frac normalize() const {
    if (a == 0 || sign < 0) {
      return flip();
    } else {
      return *this;
    }
  }
  bool operator==(const Frac& other) const {
    return sign == other.sign && a == other.a && b == other.b;
  }
  bool operator!=(const Frac& other) const { return !(*this == other); }
  bool operator<(const Frac& other) const {
    return sign * a * other.b < other.sign * other.a * b;
  }
};
int main() {
  ll n;
  cin >> n;
  mint num0 = 0;
  set<Frac> type;
  map<Frac, ll> count;
  for (ll i = 0; i < (n); ++i) {
    ll ai, bi;
    cin >> ai >> bi;
    if (ai == 0 && bi == 0) {
      num0 += 1;
    } else {
      Frac f(ai, bi);
      assert(f.a >= 0 && f.b >= 0);
      count[f]++;
      if (f.normalize() != f) {
        assert(f.normalize() == f.flip());
      }
      type.insert(f.normalize());
    }
  }
  vector<mint> pow2(n + 1);
  pow2.at(0) = 1;
  for (ll i = 1; i <= n; ++i) {
    pow2.at(i) = pow2.at(i - 1) * 2;
  }
  mint ans = 1;
  for (const Frac& p : type) {
    assert(p.flip().flip() == p);
    const ll c1 = count[p];
    const ll c2 = count[p.flip()];
    mint tmp = 1;
    tmp += pow2.at(c1) - 1;
    tmp += pow2.at(c2) - 1;
    ans *= tmp;
  }
  cout << ans - 1 + num0 << endl;
  return 0;
}
