import std;

alias P = Tuple!(long, "x", long, "y");
const MOD = 10^^9 + 7;

long calc(P[] ps, int zeros) {
    long[P] m;
    foreach (p; ps) m[p]++;

    bool[P] used;
    long[] group;
    foreach (p; ps) {
        if (used.get(p, false)) continue;

        // p に属するグループ数
        auto a1 = p;
        auto a2 = P(-p.x, -p.y);
        long acnt = m.get(a1, 0) + m.get(a2, 0);

        // p と垂直な位置関係にある点のグループ数
        auto b1 = P(-p.y, p.x);
        auto b2 = P(p.y, -p.x);
        long bcnt = m.get(b1, 0) + m.get(b2, 0);

        long cnt = 0;
        // s のグループからのみ選ぶ組み合わせ数
        cnt += modpow(2, acnt) - 1; // -1 は何も選ばない分を引いている
        // t のグループからのみ選ぶ組み合わせ数
        cnt += modpow(2, bcnt) - 1;
        // s からも t からも選ばない
        cnt += 1;
        cnt %= MOD;

        group ~= cnt;
        used[a1] = used[a2] = used[b1] = used[b2] = true;
    }

    long ans = 0;
    if (group.length > 0) {
        ans += group.fold!((a, b) => a * b % MOD)(1L);
        ans -= 1; // 全てのグループにおいて何も選ばなかった分の 1 を引く
    }
    ans += zeros;
    return (ans + MOD) % MOD;
}

void main() {
    int n; scan(n);
    P[] ps;
    int zeros = 0;
    foreach (_; 0..n) {
        long a, b; scan(a, b);
        if (a == 0 && b == 0) zeros++;
        else {
            long g = gcd(abs(a), abs(b));
            ps ~= P(a/g, b/g);
        }
    }
    writeln(calc(ps, zeros));
}

void scan(T...)(ref T a) {
    string[] ss = readln.split;
    foreach (i, t; T) a[i] = ss[i].to!t;
}
T read(T=string)() { return readln.chomp.to!T; }
T[] reads(T)() { return readln.split.to!(T[]); }
alias readints = reads!int;

long modpow(long x, long k) {
    if (k == 0) return 1;
    if (k % 2 == 0) return modpow(x * x % MOD, k / 2);
    return x * modpow(x, k - 1) % MOD;
}
