using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;

class Program
{
    const string Yes = "Yes";
    const string No = "No";
    const char White = '.';
    const char Black = '#';
    const long Mod = 1000000007;

    static void Main(string[] args) {
#if DEBUG
        MyDebugger.Test(process, "../TextFile1.txt");
#else
        object ret = process(new StreamReader(Console.OpenStandardInput()));
        Console.WriteLine(ret);
#endif
    }

    static object process(TextReader input) {
        var n = int.Parse(input.ReadLine());
        
        var tbl = new int[n][];
        for (int i = 0; i < tbl.Length; i++) {
            tbl[i] = ToIntArray(input.ReadLine(), 2);
        }

        var dicAB = new DefaultDectionary<RationalNumber, long>(0);
        long Azero = 0;
        long Bzero = 0;
        long ABzero = 0;

        foreach (var ab in tbl) {
            var a = ab[0];
            var b = ab[1];
            if (a != 0 && b != 0) {
                dicAB[new RationalNumber(a, b)]++;
            }
            else if(a == 0 && b == 0) {
                ABzero++;
            }else if(a == 0) {
                Azero++;
            }else if(b == 0) {
                Bzero++;
            }
        }

        long sum = 1;

        HashSet<RationalNumber> counted = new HashSet<RationalNumber>();

        foreach(var x in dicAB) {
            //Debug.WriteLine(x);
            if(counted.Contains(x.Key)) continue;

            var z = x.Key.Inverse() * -1;
            counted.Add(z);

            var x1 = x.Value;
            var x2 = dicAB[z];

            long ss1 = ModPow(2, x1);
            long ss2 = ModPow(2, x2);
            long add = (ss1 + ss2 - 1) % Mod;
            sum *= add;
            sum %= Mod;
        }

        long zeroadd = (ModPow(2, Azero) + ModPow(2, ABzero) - 1) % Mod;
        sum *= zeroadd;
        sum %= Mod;

        sum += ABzero;
        sum %= Mod;

        sum -= 1; //All zero
        return sum;
    }

    static int[] ToIntArray(string strs, int n) {
        var ret = new int[n];
        var spl = strs.Split(' ');
        for (int i = 0; i < n; i++)
            ret[i] = int.Parse(spl[i]);
        return ret;
    }

    static long ModPow(long x1, long x2) {
        long ans = 1;
        while (x2 > 0) {
            if (x2 % 2 == 1) {
                ans = (ans * x1) % Mod;
            }
            x1 = (x1 * x1) % Mod;
            x2 /= 2;
        }
        return ans % Mod;
    }
}

class DefaultDectionary<Tkey, TValue> : Dictionary<Tkey, TValue>
{
    TValue DefaultValue;
    public DefaultDectionary(TValue defaultvalue) {
        this.DefaultValue = defaultvalue;
    }
    public new TValue this[Tkey key] {
        get {
            if (this.ContainsKey(key)) return base[key];
            else return DefaultValue;
        }
        set {
            base[key] = value;
        }
    }
}

class RationalNumber : IEquatable<RationalNumber>, IEquatable<long>
{
    private bool sign = true;
    private long a = 0;
    private long b = 1;

    private static Regex Pattern
        = new Regex("\\s*(-?[0-9]+)\\s*(/\\s*([0-9]+)\\s*)?");

    //--------------------

    /// <summary>
    /// 分子の絶対値を取得します。
    /// </summary>
    public long Numerator {
        get { return a; }
    }

    /// <summary>
    /// 分母の絶対値を取得します。
    /// </summary>
    public long Denominator {
        get { return b; }
    }

    /// <summary>
    /// 小数値を取得します。
    /// </summary>
    public double DoubleValue {
        get {
            var v = (double)a / (double)b;
            return SignValue * v;
        }
    }
    /// <summary>
    /// 値が0であるかを取得します。
    /// </summary>
    public bool IsZero {
        get { return a == 0; }
    }

    /// <summary>
    /// 正の数の場合1、負の数の場合-1を取得します。
    /// </summary>
    public int SignValue {
        get { return this.sign ? 1 : -1; }
    }

    //--------------------

    /// <summary>
    /// 0を生成します。
    /// </summary>
    public RationalNumber() {
    }

    /// <summary>
    /// 分子と分母を指定して数を生成します。
    /// </summary>
    /// <param name="a">分子</param>
    /// <param name="b">分母</param>
    public RationalNumber(long a, long b = 1) {
        if (b == 0) {
            throw new DivideByZeroException("分母が0に指定されました。");
        }
        if (a == 0) {
            sign = true;
            this.a = 0;
            this.b = 1;
        } else {
            sign = IsSign(a, b);
            this.a = Math.Abs(a);
            this.b = Math.Abs(b);
            Reduction();
        }
    }

    //--------------------

    /// <summary>
    /// 分数形式で表記されたの文字列から数を生成します。
    /// </summary>
    /// <param name="s">分数または整数式の文字列</param>
    public static RationalNumber Parse(string s) {
        var mc = Pattern.Match(s);
        if (!mc.Success) {
            throw new FormatException("文字列が分数の形式ではありません。");
        }
        var a = long.Parse(mc.Groups[1].Value);
        var b = mc.Groups[3].Success ? long.Parse(mc.Groups[3].Value) : 1;
        return new RationalNumber(a, b);
    }

    /// <summary>
    /// 符号を検証します。
    /// </summary>
    private bool IsSign(long a, long b) {
        if (a >= 0 && b >= 0) return true;
        if (a < 0 && b < 0) return true;
        return false;
    }

    /// <summary>
    /// 約分します。
    /// </summary>
    private void Reduction() {
        var c = Gcd(a, b);
        a /= c;
        b /= c;
    }

    /// <summary>
    /// 逆数を取得します。
    /// </summary>
    /// <returns>この有理数の逆数を表すオブジェクト</returns>
    public RationalNumber Inverse() {
        return new RationalNumber(b * SignValue, a);
    }

    /// <summary>
    /// 約分された分数の文字列表現を取得します。
    /// </summary>
    public override string ToString() {
        return $"{(sign ? "" : "-")}{a}{(b > 1 ? $"/{b}" : "")}";
    }

    /// <summary>
    /// 数値の一致を評価します。
    /// </summary>
    public bool Equals(RationalNumber other) {
        return sign == other.sign
            && a == other.a && b == other.b;
    }

    /// <summary>
    /// 数値の一致を評価します。
    /// </summary>
    public bool Equals(long v) {
        if (b != 1) return false;
        var aa = SignValue * a;
        return aa == v;
    }

    public override int GetHashCode() {
        return sign.GetHashCode()
            ^ a.GetHashCode() 
            ^ b.GetHashCode();
    }

    //--------------------

    //演算
    public static RationalNumber operator +(RationalNumber x1, RationalNumber x2) {
        var c = Lcm(x1.b, x2.b);
        var x1a = x1.a * (c / x1.b) * x1.SignValue;
        var x2a = x2.a * (c / x2.b) * x2.SignValue;
        return new RationalNumber(x1a + x2a, c);
    }
    public static RationalNumber operator -(RationalNumber x1, RationalNumber x2) {
        var c = Lcm(x1.b, x2.b);
        var x1a = x1.a * (c / x1.b) * x1.SignValue;
        var x2a = x2.a * (c / x2.b) * x2.SignValue;
        return new RationalNumber(x1a - x2a, c);
    }
    public static RationalNumber operator *(RationalNumber x1, RationalNumber x2) {
        var x1a = x1.a * x1.SignValue;
        var x2a = x2.a * x2.SignValue;
        return new RationalNumber(x1a * x2a, x1.b * x2.b);
    }
    public static RationalNumber operator /(RationalNumber x1, RationalNumber x2) {
        var x1a = x1.a * x1.SignValue;
        var x2a = x2.a * x2.SignValue;
        return new RationalNumber(x1a * x2.b, x2a * x1.b);
    }
    public static bool operator ==(RationalNumber x1, RationalNumber x2) {
        return x1.Equals(x2);
    }
    public static bool operator !=(RationalNumber x1, RationalNumber x2) {
        return !x1.Equals(x2);
    }
    public static bool operator >(RationalNumber x1, RationalNumber x2) {
        var c = Lcm(x1.b, x2.b);
        var x1a = x1.a * x1.SignValue * c;
        var x2a = x2.a * x2.SignValue * c;
        return x1a > x2a;
    }
    public static bool operator >=(RationalNumber x1, RationalNumber x2) {
        return x1 == x2 || x1 > x2;
    }
    public static bool operator <(RationalNumber x1, RationalNumber x2) {
        return !(x1 >= x2);
    }
    public static bool operator <=(RationalNumber x1, RationalNumber x2) {
        return !(x1 > x2);
    }

    //整数との演算
    public static RationalNumber operator +(RationalNumber x1, long i) {
        var x1a = x1.a * x1.SignValue;
        return new RationalNumber(x1a + i * x1.b, x1.b);
    }
    public static RationalNumber operator -(RationalNumber x1, long i) {
        var x1a = x1.a * x1.SignValue;
        return new RationalNumber(x1a - i * x1.b, x1.b);
    }
    public static RationalNumber operator *(RationalNumber x1, long i) {
        var x1a = x1.a * x1.SignValue;
        return new RationalNumber(x1a * i, x1.b);
    }
    public static RationalNumber operator /(RationalNumber x1, long i) {
        if (i == 0) throw new DivideByZeroException("0で除算を行いました。");
        var x1a = x1.a * x1.SignValue;
        return new RationalNumber(x1a, x1.b * i);
    }
    public static bool operator ==(RationalNumber x1, long i) {
        return x1.Equals(i);
    }
    public static bool operator !=(RationalNumber x1, long i) {
        return !x1.Equals(i);
    }
    public static bool operator >(RationalNumber x1, long i) {
        return x1.a * x1.SignValue > i * x1.b;
    }
    public static bool operator >=(RationalNumber x1, long i) {
        return x1 == i || x1 > i;
    }
    public static bool operator <(RationalNumber x1, long i) {
        return !(x1 >= i);
    }
    public static bool operator <=(RationalNumber x1, long i) {
        return !(x1 > i);
    }

    /// <summary>
    /// 最大公約数
    /// </summary>
    private static long Gcd(long m, long n) {
        var x1 = Math.Max(m, n);
        var x2 = Math.Min(m, n);

        while (true) {
            var mod = x1 % x2;
            if (mod == 0) return x2;
            x1 = x2;
            x2 = mod;
        }
    }

    /// <summary>
    /// 最小公倍数
    /// </summary>
    private static long Lcm(long m, long n) {
        var x1 = Math.Max(m, n);
        var x2 = Math.Min(m, n);

        var gcd = Gcd(x1, x2);
        return x1 / gcd * x2;
    }
}