import std.conv, std.stdio;
import std.algorithm, std.array, std.range, std.string;
import std.math, std.numeric;
import std.typecons;
import std.functional;
enum mod = 10_0000_0007;
void main()
{
    immutable n = readln.chomp.to!size_t;
    Tuple!(long, long, bool)[] sardines;
    foreach (i; 0..n)
    {
        auto buf = readln.chomp.split.to!(long[]);
        try sardines ~= buf[0..2].reduce.pipe!(
            sardine => (0 < sardine[1])
                ? tuple(sardine[0], sardine[1], false)
                : tuple(-sardine[1], sardine[0], true));
        catch (Exception) {}
    }
    ulong answer = 1;
    foreach (sardineGroup;
            sardines
            .sort
            .group
            .chunkBy!((a, b) => a[0][0..2] == b[0][0..2]))
    {
        stderr.writeln(sardineGroup);
        auto f = sardineGroup.front;
        sardineGroup.popFront;
        if (sardineGroup.empty)
            answer *= f[1].p2;
        else
            answer *= f[1].p2 + sardineGroup.front[1].p2 - 1;
        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];
}

