import std.conv, std.stdio;
import std.algorithm, std.array, std.range, std.string;
import std.math, std.numeric;
import std.typecons;
enum mod = 10_0000_0007;
void main()
{
    immutable n = readln.chomp.to!size_t;
    long[2][] sardines;
    foreach (i; 0..n)
    {
        auto buf = readln.chomp.split.to!(long[]);
        try sardines ~= buf[0..2].reduce;
        catch (Exception) {}
    }
    ulong answer = 1;
    Tuple!(long[], size_t) last;
    foreach (sardineGroup;
            sardines
            .map!(sardine => (0 < sardine[1]) ? [sardine[0], sardine[1], 0] : [-sardine[1], sardine[0], 1])
            .array
            .sort
            .group
            .chunkBy!((a, b) => a[0][0..2] == b[0][0..2])
            .map!array)
    {
        stderr.writeln(sardineGroup);
        switch (sardineGroup.length)
        {
            case 1:
                answer *= sardineGroup[0][1].p2;
                break;
            case 2:
                answer *= sardineGroup[0][1].p2 + sardineGroup[1][1].p2 - 1;
                break;
            default: assert (false);
        }
        answer %= mod;
    }
    writeln((answer + mod - 1) % mod);
}

long p2(long n)
{
    if (n == 0) return 1;
    if (n == 1) return 2;
    return p2(n/2) * p2(n/2) * ((n & 1) + 1) % mod;
}

long[2] reduce(long[2] x)
{
    if (x[0] == 0 && x[1] == 0)
        throw new Exception("(0, 0)");
    auto g = gcd(x[0].abs, x[1].abs);
    if (x[0] < 0 || (x[0] == 0 && x[1] < 0))
        g *= -1;
    return [x[0] / g, x[1] / g];
}

