Problem:
Score: 500 points

Problem Statement

We have caught N sardines. The deliciousness and fragrantness of the i-th sardine is A_i and B_i, respectively.

We will choose one or more of these sardines and put them into a cooler. However, two sardines on bad terms cannot be chosen at the same time.

The i-th and j-th sardines (i \neq j) are on bad terms if and only if A_i \cdot A_j + B_i \cdot B_j = 0.

In how many ways can we choose the set of sardines to put into the cooler? Since the count can be enormous, print it modulo 1000000007.

Constraints

All values in input are integers.

1 \leq N \leq 2 \times 10^5

-10^{18} \leq A_i, B_i \leq 10^{18}

Input

Input is given from Standard Input in the following format:

N
A_1 B_1
:
A_N B_N

Output

Print the count modulo 1000000007.

Sample Input 1

3
1 2
-1 1
2 -1

Sample Output 1

5

There are five ways to choose the set of sardines, as follows:

The 1-st

The 1-st and 2-nd

The 2-nd

The 2-nd and 3-rd

The 3-rd

Sample Input 2

10
3 2
3 2
-1 1
2 -1
-3 -9
-8 12
7 7
8 1
8 2
8 4

Sample Output 2

479

Solution in C# (Mono-csc 3.5.0) (judge verdict: Accepted)

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

using E = System.Linq.Enumerable;

internal partial class Solver {
    public void Run() {
        var n = ni();
        var a = new long[n];
        var b = new long[n];
        for (int i = 0; i < n; i++) {
            a[i] = nl();
            b[i] = nl();
        }
        // a[i] * a[j] + b[i] * b[j] = 0
        // a[i] * a[j] = b[i] * b[j]
        // a[i] / b[i] = -b[j] / a[j]
        // a[i] = b[i] = 0 -> 0
        // a[i] = 0 and b[i] != 0 -> b[j] = 0
        // b[i] = 0 and a[i] != 0 -> a[j] = 0
        var mod = ModInt.SetModValue(1000000007);

        long a0b0 = E.Range(0, n).Count(i => a[i] == 0 && b[i] == 0); // only both zero
        long a0b1 = 1L * E.Range(0, n).Count(i => a[i] == 0 && b[i] != 0);
        long a1b0 = 1L * E.Range(0, n).Count(i => b[i] == 0 && a[i] != 0);

        var two = new ModInt[n * 2];
        two[0] = 1;
        for (int i = 1; i < two.Length; i++) {
            two[i] = two[i - 1] * 2;
        }


        ModInt a0b0pattern = a0b0;
        ModInt a0b1pattern = two[a0b1] - 1;
        ModInt a1b0pattern = two[a1b0] - 1;

        var cand = E.Range(0, n).Where(i => a[i] != 0 && b[i] != 0).ToArray();

        var hist = cand
            .Select(i => new Fraction(a[i], b[i]))
            .GroupBy(f => Tuple.Create(f.Numerator, f.Denominator))
            .Select(g => new { Key = g.Key, Count = g.LongCount() })
            .ToDictionary(g => g.Key, g => g.Count);
        ModInt a1b1pattern = 1;

        foreach (var h in hist) {
            var other = Tuple.Create(h.Key.Item2 * (h.Key.Item1 < 0 ? 1 : -1), Math.Abs(h.Key.Item1));
            //new { h, other }.Dump();
            long otherCount = 0;
            hist.TryGetValue(other, out otherCount);

            if (otherCount > 0 && h.Key.Item1 < 0) continue;

            ModInt sub = 0;
            sub += two[h.Value] - 1;
            sub += two[otherCount] - 1;
            sub++; // not select both
            a1b1pattern *= sub;
        }
        a1b1pattern--;

        //new { a1b1pattern }.Dump();

        ModInt ans = 0;
        ans += a0b0pattern;
        ans += a1b0pattern;
        ans += a0b1pattern;
        ans += a1b1pattern;
        ans += a0b1pattern * a1b1pattern;
        ans += a1b0pattern * a1b1pattern;

        cout.WriteLine(ans);
    }
}

public struct ModInt : IEquatable<ModInt> {
    private static int mod = 0;
    private long _value;

    public ModInt(long x) {
        _value = x;
        Normalize();
    }

    private static long RegularMod(long x, int mod) {
        if (x >= mod) {
            if (x < 2 * mod) {
                return x - mod;
            }

            return x % mod;
        }
        if (x >= 0) {
            return x;
        }

        x = mod - RegularMod(-x, mod);
        if (x == mod) {
            return 0;
        }

        return x;
    }

    public static int SetModValue(int m) {
        return mod = m;
    }

    private void Normalize() {
        _value = RegularMod(_value, mod);
    }

    public override string ToString() {
        return _value.ToString(CultureInfo.InvariantCulture);
    }

    public int ToInt() {
        return (int)_value;
    }

    public static bool operator ==(ModInt c1, ModInt c2) {
        return c1._value == c2._value;
    }

    public static bool operator !=(ModInt c1, ModInt c2) {
        return !(c1 == c2);
    }

    public static ModInt operator +(ModInt x, ModInt y) {
        return new ModInt(x._value + y._value);
    }
    public static ModInt operator -(ModInt x, ModInt y) {
        return new ModInt(x._value - y._value);
    }
    public static ModInt operator -(ModInt x) {
        return new ModInt(-x._value);
    }
    public static ModInt operator *(ModInt x, ModInt y) {
        return new ModInt(x._value * y._value);
    }
    public static ModInt operator /(ModInt x, ModInt y) {
        return new ModInt(x._value * Inverse(y._value, mod));
    }
    public static ModInt operator ++(ModInt x) {
        return x + 1;
    }
    public static ModInt operator --(ModInt x) {
        return x - 1;
    }
    public static ModInt Pow(ModInt x, long n) {
        ModInt r = 1;
        while (n > 0) {
            if ((n & 1) != 0) {
                r *= x;
            }

            x *= x;
            n >>= 1;
        }
        return r;
    }
    private static long ExtendedGcd(long a, long b, ref long x, ref long y) {
        if (b == 0) {
            x = 1; y = 0;
            return a;
        } else {
            var d = ExtendedGcd(b, a % b, ref y, ref x);
            y -= a / b * x;
            return d;
        }
    }

    private static long Inverse(long a, long mod) {
        long x = 0, y = 0;
        if (ExtendedGcd(a, mod, ref x, ref y) == 1) {
            return (x + mod) % mod;
        } else {
            throw new Exception("Invalid inverse " + a + " " + mod);
        }
    }

    public static implicit operator ModInt(long x) {
        return new ModInt(x);
    }

    public override bool Equals(object obj) {
        if (obj == null) {
            return false;
        }
        return _value.Equals(((ModInt)obj)._value);
    }

    public override int GetHashCode() {
        return _value.GetHashCode();
    }

    public bool Equals(ModInt other) {
        return _value.Equals(other._value);
    }
}
public struct Fraction : IComparable<Fraction>, IEquatable<Fraction> {
    public long Numerator { get; private set; }
    public long Denominator { get; private set; }

    private static long Gcd(long s, long t) {
        return t != 0 ? Gcd(t, s % t) : Math.Abs(s);
    }

    private static long Lcm(long s, long t) {
        return s / Gcd(s, t) * t;
    }

    private void Reduce() {
        if (Denominator < 0) {
            Numerator = -Numerator;
            Denominator = -Denominator;
        }

        if (Numerator == 0) {
            Denominator = 1;
        } else {
            long g = Gcd(Numerator, Denominator);
            Numerator /= g;
            Denominator /= g;
        }
    }

    public Fraction(long a, long b = 1)
        : this() {
        Numerator = a;
        Denominator = b;
        Reduce();
    }

    public static implicit operator double(Fraction r) {
        return r.Numerator * 1.0 / r.Denominator;
    }

    public static Fraction operator +(Fraction f1, Fraction f2) {
        long lcm = Lcm(f1.Denominator, f2.Denominator);
        return new Fraction(f1.Numerator * (lcm / f1.Denominator) + f2.Numerator * (lcm / f2.Denominator), lcm);
    }

    public static Fraction operator -(Fraction f1, Fraction f2) {
        long lcm = Lcm(f1.Denominator, f2.Denominator);
        return new Fraction(f1.Numerator * (lcm / f1.Denominator) - f2.Numerator * (lcm / f2.Denominator), lcm);
    }

    public static Fraction operator *(Fraction f1, Fraction f2) {
        return new Fraction(f1.Numerator * f2.Numerator, f1.Denominator * f2.Denominator);
    }

    public static Fraction operator /(Fraction f1, Fraction f2) {
        return new Fraction(f1.Numerator * f2.Denominator, f1.Denominator * f2.Numerator);
    }

    public static Fraction operator -(Fraction f) {
        return new Fraction(-f.Numerator, f.Denominator);
    }

    public int CompareTo(Fraction f) {
        return (Numerator * f.Denominator).CompareTo(f.Numerator * Denominator);
    }

    public static bool operator <(Fraction f1, Fraction f2) { return f1.CompareTo(f2) < 0; }
    public static bool operator <=(Fraction f1, Fraction f2) { return f1.CompareTo(f2) <= 0; }
    public static bool operator ==(Fraction f1, Fraction f2) { return f1.CompareTo(f2) == 0; }
    public static bool operator >(Fraction f1, Fraction f2) { return f1.CompareTo(f2) > 0; }
    public static bool operator >=(Fraction f1, Fraction f2) { return f1.CompareTo(f2) >= 0; }
    public static bool operator !=(Fraction f1, Fraction f2) { return f1.CompareTo(f2) != 0; }

    public override string ToString() {
        return string.Format("{0}/{1}", Numerator, Denominator);
    }

    public override bool Equals(object obj) {
        if (obj == null) {
            return false;
        }

        var instance = (Fraction)obj;
        return this == instance;
    }

    public bool Equals(Fraction other) {
        return this == other;
    }

    public override int GetHashCode() {
        return Numerator.GetHashCode() ^ Denominator.GetHashCode();
    }
}
// PREWRITEN CODE BEGINS FROM HERE
internal partial class Solver : Scanner {
    public static void Main() {
#if LOCAL
        byte[] inputBuffer = new byte[1000000];
        var inputStream = Console.OpenStandardInput(inputBuffer.Length);
        using (var reader = new StreamReader(inputStream, Console.InputEncoding, false, inputBuffer.Length)) {
            Console.SetIn(reader);
            new Solver(Console.In, Console.Out).Run();
        }
#else
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        new Solver(Console.In, Console.Out).Run();
        Console.Out.Flush();
#endif
    }

#pragma warning disable IDE0052
    private readonly TextReader cin;
    private readonly TextWriter cout;
#pragma warning restore IDE0052

    public Solver(TextReader reader, TextWriter writer)
        : base(reader) {
        cin = reader;
        cout = writer;
    }
    public Solver(string input, TextWriter writer)
        : this(new StringReader(input), writer) {
    }

#pragma warning disable IDE1006
#pragma warning disable IDE0051
    private int ni() { return NextInt(); }
    private int[] ni(int n) { return NextIntArray(n); }
    private long nl() { return NextLong(); }
    private long[] nl(int n) { return NextLongArray(n); }
    private double nd() { return NextDouble(); }
    private double[] nd(int n) { return NextDoubleArray(n); }
    private string ns() { return Next(); }
    private string[] ns(int n) { return NextArray(n); }
#pragma warning restore IDE1006
#pragma warning restore IDE0051
}

internal static class LinqPadExtension {
    [Conditional("DEBUG")]
    public static void Dump<T>(this T obj) {
#if DEBUG
        LINQPad.Extensions.Dump(obj);
#endif
    }
}

public class Scanner {
    private readonly TextReader Reader;
    private readonly CultureInfo ci = CultureInfo.InvariantCulture;

    private readonly char[] buffer = new char[2 * 1024];
    private int cursor = 0, length = 0;
    private string Token;
    private readonly StringBuilder sb = new StringBuilder(1024);

    public Scanner()
        : this(Console.In) {
    }

    public Scanner(TextReader reader) {
        Reader = reader;
    }

    public int NextInt() { return checked((int)NextLong()); }
    public long NextLong() {
        var s = Next();
        long r = 0;
        int i = 0;
        bool negative = false;
        if (s[i] == '-') {
            negative = true;
            i++;
        }
        for (; i < s.Length; i++) {
            r = r * 10 + (s[i] - '0');
#if DEBUG
            if (!char.IsDigit(s[i])) throw new FormatException();
#endif
        }
        return negative ? -r : r;
    }
    public double NextDouble() { return double.Parse(Next(), ci); }
    public string[] NextArray(int size) {
        string[] array = new string[size];
        for (int i = 0; i < size; i++) {
            array[i] = Next();
        }

        return array;
    }
    public int[] NextIntArray(int size) {
        int[] array = new int[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextInt();
        }

        return array;
    }

    public long[] NextLongArray(int size) {
        long[] array = new long[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextLong();
        }

        return array;
    }

    public double[] NextDoubleArray(int size) {
        double[] array = new double[size];
        for (int i = 0; i < size; i++) {
            array[i] = NextDouble();
        }

        return array;
    }

    public string Next() {
        if (Token == null) {
            if (!StockToken()) {
                throw new Exception();
            }
        }
        var token = Token;
        Token = null;
        return token;
    }

    public bool HasNext() {
        if (Token != null) {
            return true;
        }

        return StockToken();
    }

    private bool StockToken() {
        while (true) {
            sb.Clear();
            while (true) {
                if (cursor >= length) {
                    cursor = 0;
                    if ((length = Reader.Read(buffer, 0, buffer.Length)) <= 0) {
                        break;
                    }
                }
                var c = buffer[cursor++];
                if (33 <= c && c <= 126) {
                    sb.Append(c);
                } else {
                    if (sb.Length > 0) break;
                }
            }

            if (sb.Length > 0) {
                Token = sb.ToString();
                return true;
            }

            return false;
        }
    }
}