// Author: Tyler Wang

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cctype>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdlib>
#include <deque>
#include <functional>
#include <initializer_list>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <numeric>
#include <optional>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

using namespace std;

template <class C>
constexpr auto ssize(const C& c)
    -> std::common_type_t<std::ptrdiff_t,
                          std::make_signed_t<decltype(c.size())>> {
  return (std::common_type_t<std::ptrdiff_t,
                             std::make_signed_t<decltype(c.size())>>)c.size();
}

template <class T, std::ptrdiff_t N>
constexpr std::ptrdiff_t ssize(const T (&)[N]) noexcept {
  return N;
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, bool> has_single_bit(
    T x) noexcept {
  return x != 0 && (x & (x - 1)) == 0;
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, int> countl_zero(
    T x) noexcept {
  if constexpr (sizeof(T) <= sizeof(unsigned)) {
    return __builtin_clz(x) - (std::numeric_limits<unsigned>::digits -
                               std::numeric_limits<T>::digits);
  } else if constexpr (sizeof(T) <= sizeof(unsigned long)) {
    return __builtin_clzl(x) - (std::numeric_limits<unsigned long>::digits -
                                std::numeric_limits<T>::digits);
  } else {
    static_assert(sizeof(T) <= sizeof(unsigned long long));
    return __builtin_clzll(x) -
           (std::numeric_limits<unsigned long long>::digits -
            std::numeric_limits<T>::digits);
  }
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, int> countr_zero(
    T x) noexcept {
  if constexpr (sizeof(T) <= sizeof(unsigned)) {
    return __builtin_ctz(x);
  } else if constexpr (sizeof(T) <= sizeof(unsigned long)) {
    return __builtin_ctzl(x);
  } else {
    static_assert(sizeof(T) <= sizeof(unsigned long long));
    return __builtin_ctzll(x);
  }
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, int> popcount(T x) noexcept {
  if constexpr (sizeof(T) <= sizeof(unsigned)) {
    return __builtin_popcount(x);
  } else if constexpr (sizeof(T) <= sizeof(unsigned long long)) {
    return __builtin_popcountl(x);
  } else {
    static_assert(sizeof(T) <= sizeof(unsigned long long));
    return __builtin_popcountll(x);
  }
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T> bit_width(T x) noexcept {
  return (T)(std::numeric_limits<T>::digits - countl_zero(x));
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T> bit_ceil(T x) noexcept {
  return (T)(x <= 1 ? 1 : (T)1 << bit_width((T)(x - 1)));
}

template <typename T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T> bit_floor(T x) noexcept {
  return (T)(x == 0 ? 0 : (T)1 << (bit_width(x) - 1));
}

template <class Fun>
class y_combinator_result {
 public:
  template <class T>
  explicit y_combinator_result(T&& fun) : fun_(std::forward<T>(fun)) {}

  template <class... Args>
  decltype(auto) operator()(Args&&... args) {
    return fun_(std::ref(*this), std::forward<Args>(args)...);
  }

 private:
  Fun fun_;
};

template <class Fun>
decltype(auto) y_combinator(Fun&& fun) {
  return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun));
}

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::optional<T>& opt) {
  return opt ? os << *opt : os << "nullopt";
}

template <typename T1, typename T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& p) {
  return os << '{' << p.first << ", " << p.second << '}';
}

template <typename Container,
          std::enable_if_t<!std::is_convertible_v<Container, std::string_view>,
                           typename Container::const_iterator>* = nullptr>
std::ostream& operator<<(std::ostream& os, const Container& c) {
  os << '{';
  for (auto it = c.begin(); it != c.end(); ++it) {
    if (it != c.begin()) {
      os << ", ";
    }
    os << *it;
  }
  return os << '}';
}

template <typename T, typename Container>
std::ostream& operator<<(std::ostream& os, std::stack<T, Container> s) {
  std::vector<T> v(s.size());
  for (auto it = v.rbegin(); it != v.rend(); ++it) {
    *it = s.top();
    s.pop();
  }
  return os << v;
}

template <typename T, typename Container>
std::ostream& operator<<(std::ostream& os, std::queue<T, Container> q) {
  os << '{';
  if (!q.empty()) {
    os << q.front();
    q.pop();
    while (!q.empty()) {
      os << ", " << q.front();
      q.pop();
    }
  }
  return os << '}';
}

template <typename T, typename Container, typename Compare>
std::ostream& operator<<(std::ostream& os,
                         std::priority_queue<T, Container, Compare> pq) {
  os << '{';
  if (!pq.empty()) {
    os << pq.top();
    pq.pop();
    while (!pq.empty()) {
      os << ", " << pq.top();
      pq.pop();
    }
  }
  return os << '}';
}

namespace detail {

template <int I, typename... Ts>
void tuple_print(std::ostream& os, const std::tuple<Ts...>& t) {
  static_assert(0 <= I && I <= sizeof...(Ts));
  if constexpr (I != sizeof...(Ts)) {
    if constexpr (I != 0) {
      os << ", ";
    }
    os << std::get<I>(t);
    tuple_print<I + 1>(os, t);
  }
}

}  // namespace detail

template <typename... Ts>
std::ostream& operator<<(std::ostream& os, const std::tuple<Ts...>& t) {
  os << '{';
  detail::tuple_print<0>(os, t);
  return os << '}';
}

namespace detail {

template <typename T1, typename... Ts>
void debug_print(std::string_view keys, const T1& val1, const Ts&... vals) {
  if constexpr (sizeof...(Ts) == 0) {
    std::cerr << keys << " = " << val1 << std::endl;
  } else {
    for (int i = 0, paren = 0; i < (int)keys.size(); ++i) {
      if (keys[i] == '(' || keys[i] == '{') {
        ++paren;
      } else if (keys[i] == ')' || keys[i] == '}') {
        --paren;
      } else if (keys[i] == ',' && paren == 0) {
        std::cerr << keys.substr(0, i) << " = " << val1 << ',';
        return debug_print(keys.substr(i + 1), vals...);
      }
    }
  }
}

template <typename... Ts>
void debug_print(int line, const Ts&... keyvals) {
  if constexpr (sizeof...(Ts) <= 1) {
    std::cerr << "  " << line << " |" << std::endl;
  } else {
    std::cerr << "  " << line << " | ";
    debug_print(keyvals...);
  }
}

}  // namespace detail

#if defined(TYLER) && defined(__GNUC__) && __GNUC__ >= 8
#define debug(...) \
  detail::debug_print(__LINE__, #__VA_ARGS__ __VA_OPT__(, ) __VA_ARGS__)
#elif defined(TYLER)
#define debug(...) detail::debug_print(__LINE__, #__VA_ARGS__, __VA_ARGS__)
#else
#define debug(...) ((void)0)
#endif

template <const int& Modulus>
class mod_int {
 public:
  constexpr mod_int() : val_(0) {}

  template <typename T>
  constexpr mod_int(T val) : val_((int)(val % Modulus)) {
    static_assert(std::is_integral_v<T>);
    if (val_ < 0) {
      val_ += Modulus;
    }
  }

  static constexpr int modulus() { return Modulus; }

  template <typename T>
  constexpr explicit operator T() const {
    return (T)val_;
  }

  constexpr mod_int& operator++() {
    if (++val_ == Modulus) {
      val_ = 0;
    }
    return *this;
  }

  constexpr mod_int operator++(int) {
    mod_int t = *this;
    ++*this;
    return t;
  }

  constexpr mod_int& operator--() {
    if (val_ == 0) {
      val_ = Modulus;
    }
    --val_;
    return *this;
  }

  constexpr mod_int operator--(int) {
    mod_int t = *this;
    --*this;
    return t;
  }

  constexpr mod_int operator+=(mod_int other) {
    if ((val_ += other.val_) >= Modulus) {
      val_ -= Modulus;
    }
    return *this;
  }

  constexpr mod_int operator-=(mod_int other) {
    if ((val_ -= other.val_) < 0) {
      val_ += Modulus;
    }
    return *this;
  }

  constexpr mod_int operator*=(mod_int other) {
    val_ = (int)((long long)val_ * other.val_ % Modulus);
    return *this;
  }

  constexpr mod_int operator/=(mod_int other) { return *this *= inv(other); }

  friend constexpr mod_int operator+(mod_int mi) { return mi; }

  friend constexpr mod_int operator-(mod_int mi) {
    if (mi.val_ != 0) {
      mi.val_ = Modulus - mi.val_;
    }
    return mi;
  }

  friend constexpr mod_int operator+(mod_int lhs, mod_int rhs) {
    return lhs += rhs;
  }

  friend constexpr mod_int operator-(mod_int lhs, mod_int rhs) {
    return lhs -= rhs;
  }

  friend constexpr mod_int operator*(mod_int lhs, mod_int rhs) {
    return lhs *= rhs;
  }

  friend constexpr mod_int operator/(mod_int lhs, mod_int rhs) {
    return lhs /= rhs;
  }

  friend constexpr bool operator==(mod_int lhs, mod_int rhs) {
    return lhs.val_ == rhs.val_;
  }

  friend constexpr bool operator!=(mod_int lhs, mod_int rhs) {
    return lhs.val_ != rhs.val_;
  }

  friend constexpr bool operator<(mod_int lhs, mod_int rhs) {
    return lhs.val_ < rhs.val_;
  }

  friend constexpr bool operator<=(mod_int lhs, mod_int rhs) {
    return lhs.val_ <= rhs.val_;
  }

  friend constexpr bool operator>(mod_int lhs, mod_int rhs) {
    return lhs.val_ > rhs.val_;
  }

  friend constexpr bool operator>=(mod_int lhs, mod_int rhs) {
    return lhs.val_ >= rhs.val_;
  }

  friend std::ostream& operator<<(std::ostream& os, mod_int mi) {
    return os << mi.val_;
  }

  friend std::istream& operator>>(std::istream& is, mod_int& mi) {
    long long val;
    is >> val;
    mi = mod_int(val);
    return is;
  }

  friend constexpr mod_int inv(mod_int mi) {
    int a = mi.val_, b = Modulus;
    int x = 0, xx = 1;
    while (a != 0) {
      int q = b / a;
      int t = a;
      a = b - q * a;
      b = t;
      t = xx;
      xx = x - q * xx;
      x = t;
    }
    return mod_int(x);
  }

  template <typename T>
  friend constexpr mod_int pow(mod_int base, T expo) {
    static_assert(std::is_integral_v<T>);
    if (expo < 0) {
      base = inv(base);
      expo = -expo;
    }
    mod_int power = 1;
    for (; expo != 0; expo >>= 1) {
      if (expo & 1) {
        power *= base;
      }
      base *= base;
    }
    return power;
  }

 private:
  int val_;
};

constexpr int MODULUS = 1'000'000'007;
using m_int = mod_int<MODULUS>;

/*
template <typename T>
class fraction {
  static_assert(std::is_integral_v<T>);

 public:
  constexpr fraction(T num = 0) : num_(num), den_(1) {}

  constexpr fraction(T num, T den) {
    if (den == 0) {
      num_ = num < 0 ? -1 : 1;
      den_ = 0;
    } else {
      T g = std::gcd(num, den);
      if (den < 0) {
        g = -g;
      }
      num_ = num / g;
      den_ = den / g;
    }
  }

  fraction(const std::vector<T>& cont) : num_(cont.back()), den_(1) {
    for (int i = (int)cont.size() - 2; i >= 0; --i) {
      std::swap(num_, den_);
      num_ += cont[i] * den_;
    }
  }

  static constexpr fraction inf() { return fraction(1, 0); }

  constexpr T num() const { return num_; }

  constexpr T den() const { return den_; }

  template <typename U>
  constexpr explicit operator U() const {
    return (U)num_ / (U)den_;
  }

  constexpr fraction& operator+=(const fraction& other) {
    return *this = *this + other;
  }

  constexpr fraction& operator-=(const fraction& other) {
    return *this = *this - other;
  }

  constexpr fraction& operator*=(const fraction& other) {
    if (den_ == 0 || other.den_ == 0) {
      return *this = (num_ < 0) == (other.num_ < 0) ? inf() : -inf();
    }
    T g1 = std::gcd(num_, other.den_), g2 = std::gcd(den_, other.num_);
    num_ = (num_ / g1) * (other.num_ / g2);
    den_ = (den_ / g2) * (other.den_ / g1);
    return *this;
  }

  constexpr fraction& operator/=(const fraction& other) {
    if (den_ == 0 || other.num_ == 0) {
      return *this = (num_ < 0) == (other.num_ < 0) ? inf() : -inf();
    }
    T g1 = std::gcd(num_, other.num_), g2 = std::gcd(den_, other.den_);
    if (other.num_ < 0) {
      g1 = -g1;
    }
    num_ = (num_ / g1) * (other.den_ / g2);
    den_ = (den_ / g2) * (other.num_ / g1);
    return *this;
  }

  friend constexpr fraction operator+(fraction f) { return f; }

  friend constexpr fraction operator-(fraction f) {
    f.num_ = -f.num_;
    return f;
  }

  friend constexpr fraction operator+(const fraction& lhs,
                                      const fraction& rhs) {
    T g = std::gcd(lhs.den_, rhs.den_);
    return fraction(rhs.den_ / g * lhs.num_ + lhs.den_ / g * rhs.num_,
                    lhs.den_ / g * rhs.den_);
  }

  friend constexpr fraction operator-(const fraction& lhs,
                                      const fraction& rhs) {
    T g = std::gcd(lhs.den_, rhs.den_);
    return fraction(rhs.den_ / g * lhs.num_ - lhs.den_ / g * rhs.num_,
                    lhs.den_ / g * rhs.den_);
  }

  friend constexpr fraction operator*(fraction lhs, const fraction& rhs) {
    return lhs *= rhs;
  }

  friend constexpr fraction operator/(fraction lhs, const fraction& rhs) {
    return lhs /= rhs;
  }

  friend constexpr bool operator==(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ == rhs.num_ && lhs.den_ == rhs.den_;
  }

  friend constexpr bool operator!=(const fraction& lhs, const fraction& rhs) {
    return !(lhs == rhs);
  }

  friend constexpr bool operator<(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ < rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator<=(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ <= rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator>(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ > rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator>=(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ >= rhs.num_ * lhs.den_;
  }

  friend std::ostream& operator<<(std::ostream& os, const fraction& f) {
    return os << f.num_ << '/' << f.den_;
  }

  friend constexpr fraction inv(fraction f) {
    if (T t = f.num_; t < 0) {
      f.num_ = -f.den_;
      f.den_ = -t;
    } else {
      f.num_ = f.den_;
      f.den_ = t;
    }
    return f;
  }

  friend std::vector<T> continued(fraction f) {
    T q = f.num_ < 0 ? (f.num_ - f.den_ + 1) / f.den_ : f.num_ / f.den_;
    f.num_ -= q * f.den_;
    std::vector<T> cont = {q};
    while (f.num_ != 0) {
      std::swap(f.num_, f.den_);
      q = f.num_ / f.den_;
      f.num_ -= q * f.den_;
      cont.push_back(q);
    }
    return cont;
  }

 private:
  T num_, den_;
};
*/

template <typename T>
class fraction {
  static_assert(std::is_integral_v<T>);

 public:
  constexpr fraction(T num = 0) : num_(num), den_(1) {}

  constexpr fraction(T num, T den) {
    assert(den != 0);
    T g = std::gcd(num, den);
    if (den < 0) {
      g = -g;
    }
    num_ = num / g;
    den_ = den / g;
  }

  fraction(const std::vector<T>& cont) : num_(cont.back()), den_(1) {
    for (int i = (int)cont.size() - 2; i >= 0; --i) {
      std::swap(num_, den_);
      num_ += cont[i] * den_;
    }
  }

  constexpr T num() const { return num_; }

  constexpr T den() const { return den_; }

  template <typename U>
  constexpr explicit operator U() const {
    return (U)num_ / (U)den_;
  }

  constexpr fraction& operator+=(const fraction& other) {
    return *this = *this + other;
  }

  constexpr fraction& operator-=(const fraction& other) {
    return *this = *this - other;
  }

  constexpr fraction& operator*=(const fraction& other) {
    T g1 = std::gcd(num_, other.den_), g2 = std::gcd(den_, other.num_);
    num_ = (num_ / g1) * (other.num_ / g2);
    den_ = (den_ / g2) * (other.den_ / g1);
    return *this;
  }

  constexpr fraction& operator/=(const fraction& other) {
    assert(other.num_ != 0);
    T g1 = std::gcd(num_, other.num_), g2 = std::gcd(den_, other.den_);
    if (other.num_ < 0) {
      g1 = -g1;
    }
    num_ = (num_ / g1) * (other.den_ / g2);
    den_ = (den_ / g2) * (other.num_ / g1);
    return *this;
  }

  friend constexpr fraction operator+(fraction f) { return f; }

  friend constexpr fraction operator-(fraction f) {
    f.num_ = -f.num_;
    return f;
  }

  friend constexpr fraction operator+(const fraction& lhs,
                                      const fraction& rhs) {
    T g = std::gcd(lhs.den_, rhs.den_);
    return fraction(rhs.den_ / g * lhs.num_ + lhs.den_ / g * rhs.num_,
                    lhs.den_ / g * rhs.den_);
  }

  friend constexpr fraction operator-(const fraction& lhs,
                                      const fraction& rhs) {
    T g = std::gcd(lhs.den_, rhs.den_);
    return fraction(rhs.den_ / g * lhs.num_ - lhs.den_ / g * rhs.num_,
                    lhs.den_ / g * rhs.den_);
  }

  friend constexpr fraction operator*(fraction lhs, const fraction& rhs) {
    return lhs *= rhs;
  }

  friend constexpr fraction operator/(fraction lhs, const fraction& rhs) {
    return lhs /= rhs;
  }

  friend constexpr bool operator==(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ == rhs.num_ && lhs.den_ == rhs.den_;
  }

  friend constexpr bool operator!=(const fraction& lhs, const fraction& rhs) {
    return !(lhs == rhs);
  }

  friend constexpr bool operator<(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ < rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator<=(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ <= rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator>(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ > rhs.num_ * lhs.den_;
  }

  friend constexpr bool operator>=(const fraction& lhs, const fraction& rhs) {
    return lhs.num_ * rhs.den_ >= rhs.num_ * lhs.den_;
  }

  friend std::ostream& operator<<(std::ostream& os, const fraction& f) {
    return os << f.num_ << '/' << f.den_;
  }

  friend constexpr fraction inv(fraction f) {
    assert(f.num_ != 0);
    if (T t = f.num_; t < 0) {
      f.num_ = -f.den_;
      f.den_ = -t;
    } else {
      f.num_ = f.den_;
      f.den_ = t;
    }
    return f;
  }

  friend std::vector<T> continued(fraction f) {
    T q = f.num_ < 0 ? (f.num_ - f.den_ + 1) / f.den_ : f.num_ / f.den_;
    f.num_ -= q * f.den_;
    std::vector<T> cont = {q};
    while (f.num_ != 0) {
      std::swap(f.num_, f.den_);
      q = f.num_ / f.den_;
      f.num_ -= q * f.den_;
      cont.push_back(q);
    }
    return cont;
  }

 private:
  T num_, den_;
};

template <>
struct std::is_integral<__int128> : public std::true_type {};

template <>
constexpr __int128 std::gcd(__int128 a, __int128 b) {
  a = std::abs(a);
  b = std::abs(b);
  while (a != 0) {
    __int128 t = a;
    a = b % a;
    b = t;
  }
  return b;
}

std::ostream& operator<<(std::ostream& os, __int128 x) {
  static constexpr std::int64_t BASE = 1'000'000'000'000'000'000LL;
  char f = os.fill();
  int w = (int)os.width();
  if (__int128 mid = x / BASE; mid != 0) {
    x = std::abs(x - mid * BASE);
    if (int high = (int)(mid / BASE); high != 0) {
      mid = std::abs(mid - (__int128)high * BASE);
      os << high << std::setfill('0') << std::setw(18);
    }
    os << (std::int64_t)mid << std::setfill('0') << std::setw(18);
  }
  return os << (std::int64_t)x << std::setfill(f) << std::setw(w);
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cout.precision(12);
  std::cerr.precision(12);
  std::mt19937 rng((std::uint_fast32_t)std::chrono::high_resolution_clock::now()
                       .time_since_epoch()
                       .count());
  // ai * aj = -bi * bj
  // ai / bi = -bj / aj
  int n;
  cin >> n;
  map<fraction<__int128>, int> mp;
  int zero = 0, num_zero = 0, den_zero = 0;
  for (int i = 0; i < n; ++i) {
    long long a, b;
    cin >> a >> b;
    if (a == 0 && b == 0) {
      ++zero;
    } else if (a == 0) {
      ++num_zero;
    } else if (b == 0) {
      ++den_zero;
    } else {
      ++mp[fraction<__int128>(a, b)];
    }
  }
  constexpr m_int two = 2;
  m_int ans = 1;
  for (auto& [k, v] : mp) {
    auto f = -inv(k);
    if (auto it = mp.find(f); it == mp.end()) {
      ans *= pow(two, v);
    } else if (k > 0) {
      ans *= pow(two, v) + pow(two, it->second) - 1;
    }
  }
  ans *= pow(two, num_zero) + pow(two, den_zero) - 1;
  ans += zero - 1;
  cout << ans << '\n';
  return 0;
}

Measured memory_bytes: 22308