// (c) 2020, redotter
// The grass was greener...

#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#pragma comment(linker, "/stack:200000000")
#endif

#define pb push_back
#define the(a) (a).begin(), (a).end()
#define ft first
#define sd second
#define endl "\n"
#define forn(i, n) for (int i = 0; i < (n); i++)
#define forr(i, s, n) for (int i = (s); i < (n); i++)

using i64 = long long;
using i32 = int;
using f64 = long double;

template <class T> using vec = vector<T>;
using str = string; using p64 = pair<i64, i64>;
using v64 = vec<i64>; using vv64 = vec<v64>;
using s64 = set<i64>; using m64 = map<i64, i64>;
template <size_t Size1> using a64 = array<i64, Size1>;
template <size_t Size1, size_t Size2> using aa64 = array<a64<Size2>, Size1>;
template <class T> using hash_set = unordered_set<T>;
template <class T, class U> using hash_map = unordered_map<T, U>;

mt19937 gen(998244353);
const i64 inf = numeric_limits<i64>::max() / 2;

template <class T> inline i64 len(const T& a) { return (i64)(a.size()); }
inline void fast() { ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
template <class T, class S>
inline bool check_min(T& a, S b) { if (a > b) return a = b, 1; return 0; }
template <class T, class S>
inline bool check_max(T& a, S b) { if (a < b) return a = b, 1; return 0; }

void read() {}
template<class T, class ...U>
void read(T& a, U& ...b) { cin >> a; read(b...); }
void print() {}
template<class T, class ...U>
void print(const T& a, const U& ...b) { cout << a << " "; print(b...); }
void println() { cout << "\n"; }
template<class T, class ...U>
void println(const T& a, const U& ...b) { cout << a << " "; println(b...); }

struct Problem {
	void files(str in, str out) {
#ifdef ONLINE_JUDGE
		freopen(in.c_str(), "r", stdin), freopen(out.c_str(), "w", stdout);
#endif
	}
	void solve(), input();
	void many() { i64 t; cin >> t; while (t--) input(), solve(); }
	void make(bool mt = 0) { if (mt) many(); else input(), solve(); }
} problem;

signed main() {
	fast();
	problem.make();
#ifdef _DEBUG
	println(); println("[finished]"); system("pause > nul");
#endif
	return 0;
}

const i64 R = 1e9 + 7;

i64 n;
vec<p64> a;

i64 gcd(i64 a, i64 b) {
	while (b) {
		a %= b;
		swap(a, b);
	}
	return a;
}
i64 binary_power(i64 a, i64 n) {
	a %= R;
	if (n == 0) return 1;
	i64 ans = binary_power(a, n / 2);
	ans = ans * ans % R;
	if (n % 2 == 1) ans = ans * a % R;
	return ans;
}
i64 inv(i64 a) {
	return binary_power(a, R - 2);
}

i64 prisoner_zero() {
	i64 to_add = 0;
	vec<p64> b;
	forn(i, n) {
		if (a[i] != p64(0, 0)) {
			b.pb(a[i]);
		} else {
			++to_add;
		}
	}
	a = b;
	n = len(a);
	return to_add;
}

void Problem::solve() {
	i64 to_add = prisoner_zero();
	forn(i, n) {
		i64 g = gcd(a[i].ft, a[i].sd);
		a[i].ft /= g, a[i].sd /= g;
		if (a[i].ft < 0) {
			a[i].ft = -a[i].ft;
			a[i].sd = -a[i].sd;
		}
		if (a[i].ft == 0 && a[i].sd < 0) {
			a[i].sd = -a[i].sd;
		}
	}
	map<p64, i64> ms;
	forn(i, n) {
		i64 v = a[i].ft, u = a[i].sd;
		++ms[{v, u}];
	}
	i64 ans = 1;
	for (pair<p64, i64> p : ms) {
		if (!p.sd) continue;
		i64 v, u;
		tie(v, u) = p.ft;
		i64 nv = -u, nu = v;
		if (nv < 0) {
			nv = -nv, nu = -nu;
		}
		if (nv == 0 && nu < 0) {
			nu = -nu;
		}
		i64 bad = ms[{nv, nu}];
		i64 p1 = binary_power(2, p.sd);
		i64 p2 = binary_power(2, bad);
		i64 cur = ((p1 + p2) % R - 1 + R) % R;
		ans = ans * cur % R;
		ms[{nv, nu}] = 0;
	}
	ans = (ans + R - 1) % R;
	ans = (ans + to_add) % R;
	println(ans);
}

void Problem::input() {
	read(n);
	a.resize(n);
	forn(i, n) read(a[i].ft, a[i].sd);
}


Measured memory_bytes: 31332