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 Rust (1.42.0) (judge verdict: Accepted)

#![allow(dead_code)]
use crate::counter::Counter;
use crate::misc::{arr, unzip, CONST_1E9_7};
use crate::modulo::*;
use crate::scanner::Scanner;
use itertools::Itertools;
use num_rational::Ratio;
modint!(CONST_1E9_7);
fn main() {
    let mut scan = Scanner::new();
    let n = scan.read::<usize>();
    let (a, b) = unzip(&arr(n, || (scan.read::<i64>(), scan.read::<i64>())));
    let mut solid = 0;
    let mut numer_zero = 0;
    let mut denom_zero = 0;
    let mut cnt = Counter::new();
    for i in 0..n {
        if a[i] == 0 && b[i] == 0 {
            solid += 1;
        } else if a[i] == 0 {
            numer_zero += 1;
        } else if b[i] == 0 {
            denom_zero += 1;
        } else {
            cnt[Ratio::new(a[i], b[i])] += 1;
        }
    }
    let mut ret = Z::new(1);
    let keys = cnt.keys().cloned().collect_vec();
    for key in keys {
        if !cnt.contains_key(&key) {
            continue;
        }
        let rev_key = Ratio::new(-*key.denom(), *key.numer());
        let c1 = cnt.peek(&key);
        let c2 = cnt.peek(&rev_key);
        cnt.remove(&key);
        cnt.remove(&rev_key);
        ret *= Z::new(2).pow(c1) + Z::new(2).pow(c2) - Z::new(1);
    }
    ret *= Z::new(2).pow(numer_zero) + Z::new(2).pow(denom_zero) - Z::new(1);
    println!("{}", ret + Z::new(solid) - Z::new(1));
}
mod scanner {
    use std::io::{stdin, BufReader, Bytes, Read, Stdin};
    use std::str::FromStr;
    pub struct Scanner {
        buf: Bytes<BufReader<Stdin>>,
    }
    impl Scanner {
        pub fn new() -> Scanner {
            Scanner {
                buf: BufReader::new(stdin()).bytes(),
            }
        }
        pub fn read_next<T: FromStr>(&mut self) -> Option<T> {
            let token = self
                .buf
                .by_ref()
                .map(|c| c.unwrap() as char)
                .skip_while(|c| c.is_whitespace())
                .take_while(|c| !c.is_whitespace())
                .collect::<String>();
            token.parse::<T>().ok()
        }
        pub fn read<T: FromStr>(&mut self) -> T {
            self.read_next().unwrap()
        }
        pub fn readn<T: FromStr>(&mut self, n: usize) -> Vec<T> {
            (0..n).map(|_| self.read::<T>()).collect()
        }
        pub fn chars(&mut self) -> Vec<char> {
            self.read::<String>().chars().collect()
        }
    }
}
mod tiny_num_traits {
    pub trait ZeroOne {
        fn zero() -> Self;
        fn one() -> Self;
    }
    macro_rules! impl_zero_one {
        ( $ tpe : ident ) => {
            impl ZeroOne for $tpe {
                fn zero() -> Self {
                    0
                }
                fn one() -> Self {
                    1
                }
            }
        };
    }
    impl_zero_one!(i64);
    impl_zero_one!(i32);
    impl_zero_one!(usize);
}
mod misc {
    use crate::tiny_num_traits::ZeroOne;
    use std::collections::{BTreeSet, HashMap};
    use std::ops::{Add, Div, Mul, Sub};
    pub const CONST_1E9_7: i64 = 1_000_000_007;
    pub fn arr<'a, S, T>(n: usize, mut f: S) -> Vec<T>
    where
        S: FnMut() -> T + 'a,
    {
        (0..n).map(|_| f()).collect::<Vec<T>>()
    }
    pub fn adjacent4(
        y: usize,
        x: usize,
        h: usize,
        w: usize,
    ) -> impl Iterator<Item = (usize, usize)> {
        const DYDX: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
        DYDX.iter().filter_map(move |&(dy, dx)| {
            let ny = y as isize + dy;
            let nx = x as isize + dx;
            if nx >= 0 && nx < w as isize && ny >= 0 && ny < h as isize {
                Some((ny as usize, nx as usize))
            } else {
                None
            }
        })
    }
    pub fn adjacent8(
        y: usize,
        x: usize,
        h: usize,
        w: usize,
    ) -> impl Iterator<Item = (usize, usize)> {
        const DYDX: [(isize, isize); 8] = [
            (-1, 0),
            (1, 0),
            (0, -1),
            (0, 1),
            (-1, -1),
            (-1, 1),
            (1, -1),
            (1, 1),
        ];
        DYDX.iter().filter_map(move |&(dy, dx)| {
            let ny = y as isize + dy;
            let nx = x as isize + dx;
            if nx >= 0 && nx < w as isize && ny >= 0 && ny < h as isize {
                Some((ny as usize, nx as usize))
            } else {
                None
            }
        })
    }
    pub fn run_length_encoding<T: Clone + PartialEq>(slice: &[T]) -> Vec<(i64, T)> {
        if slice.len() == 0 {
            return vec![];
        }
        let mut acc = vec![];
        let mut current = slice[0].clone();
        let mut cnt = 1i64;
        for i in 1..slice.len() {
            if current != slice[i] {
                acc.push((cnt, current));
                cnt = 0;
            }
            current = slice[i].clone();
            cnt += 1;
        }
        acc.push((cnt, current));
        acc
    }
    pub fn count<T: Eq + std::hash::Hash + Clone>(
        slice: &[T],
    ) -> std::collections::HashMap<T, usize> {
        let mut hmap = std::collections::HashMap::new();
        for item in slice {
            *hmap.entry(item.clone()).or_insert(0) += 1;
        }
        hmap
    }
    pub fn indices_by_elem<T: Eq + std::hash::Hash + Clone>(
        slice: &[T],
    ) -> std::collections::HashMap<T, Vec<usize>> {
        let mut hmap = std::collections::HashMap::new();
        for i in 0..slice.len() {
            hmap.entry(slice[i].clone()).or_insert(vec![]).push(i);
        }
        hmap
    }
    pub fn combine<S: Clone, T: Clone, U: std::iter::FromIterator<(S, T)>>(
        left: &[S],
        right: &[T],
    ) -> U {
        let mut ret = vec![];
        for i in 0..left.len() {
            for j in 0..right.len() {
                ret.push((left[i].clone(), right[j].clone()));
            }
        }
        ret.into_iter().collect::<U>()
    }
    pub fn uncount<T: Clone + Eq + std::hash::Hash>(
        hmap: &std::collections::HashMap<T, usize>,
    ) -> Vec<T> {
        hmap.iter()
            .map(|(k, v)| vec![k.clone(); *v])
            .flatten()
            .collect()
    }
    pub fn max_with_index<T: Ord>(slice: &[T]) -> (usize, &T) {
        let mut value: Option<&T> = None;
        let mut index = 0;
        for i in 0..slice.len() {
            if let Some(v) = value {
                if v < &slice[i] {
                    index = i;
                    value = Some(&slice[i]);
                }
            } else {
                index = i;
                value = Some(&slice[i]);
            }
        }
        (index, value.expect("not found max value"))
    }
    pub fn min_with_index<T: Ord>(slice: &[T]) -> (usize, &T) {
        let mut value: Option<&T> = None;
        let mut index = 0;
        for i in 0..slice.len() {
            if let Some(v) = value {
                if v > &slice[i] {
                    index = i;
                    value = Some(&slice[i]);
                }
            } else {
                index = i;
                value = Some(&slice[i]);
            }
        }
        (index, value.expect("not found min value"))
    }
    pub fn split<T: PartialEq + Clone>(slice: &[T], sep: T) -> Vec<Vec<T>> {
        slice.iter().fold(vec![vec![]], |mut acc, x| {
            if x == &sep {
                acc.push(vec![]);
                acc
            } else {
                let last = acc.len() - 1;
                acc[last].push(x.clone());
                acc
            }
        })
    }
    pub fn join<T: std::fmt::Display>(slice: &[T], sep: &str) -> String {
        let strings = slice.iter().map(|t| format!("{}", t)).collect::<Vec<_>>();
        strings.join(sep)
    }
    pub fn coord_comp(slice: &[i64]) -> (Vec<i64>, HashMap<i64, usize>) {
        let mut set = BTreeSet::new();
        for &item in slice {
            set.insert(item);
        }
        let mut hmap = HashMap::new();
        for (i, &v) in set.iter().enumerate() {
            hmap.insert(v, i);
        }
        (set.into_iter().collect::<Vec<_>>(), hmap)
    }
    pub fn shakutori(n: usize, k: i64, a: &[i64]) -> usize {
        let mut sum = 0;
        let mut right = 0;
        let mut ret = 0;
        for left in 0..n {
            while right < n && sum <= k {
                sum += a[right];
                right += 1;
            }
            ret += right - left;
            if right == left {
                right += 1;
            } else {
                sum -= a[left];
            }
        }
        ret
    }
    pub fn inverse(a: &[usize], n: usize) -> Vec<usize> {
        let mut inv = vec![0; n];
        for i in 0..a.len() {
            inv[a[i]] = i;
        }
        inv
    }
    pub fn pow(x: i64, y: i64) -> i64 {
        (x as f64).powi(y as i32) as i64
    }
    pub fn ceil_div<T: Copy + ZeroOne + Add<Output = T> + Sub<Output = T> + Div<Output = T>>(
        x: T,
        y: T,
    ) -> T {
        (x + y - T::one()) / y
    }
    pub fn ceil_mod<
        T: Copy + ZeroOne + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T>,
    >(
        x: T,
        y: T,
    ) -> T {
        ceil_div(x, y) * y
    }
    pub fn unzip<P: Clone, Q: Clone>(v: &[(P, Q)]) -> (Vec<P>, Vec<Q>) {
        (
            v.iter().map(|t| t.0.clone()).collect(),
            v.iter().map(|t| t.1.clone()).collect(),
        )
    }
    pub fn unzip3<P: Clone, Q: Clone, R: Clone>(v: &[(P, Q, R)]) -> (Vec<P>, Vec<Q>, Vec<R>) {
        (
            v.iter().map(|t| t.0.clone()).collect(),
            v.iter().map(|t| t.1.clone()).collect(),
            v.iter().map(|t| t.2.clone()).collect(),
        )
    }
    pub fn base(c: char, b: char) -> usize {
        c as usize - b as usize
    }
    pub fn base_signed(c: char, b: char) -> i64 {
        c as i64 - b as i64
    }
    pub fn is_palindrome<T: Clone + Eq>(chars: &[T]) -> bool {
        let s = chars.to_vec();
        let mut t = s.clone();
        t.reverse();
        (0..s.len()).filter(|&i| s[i] == t[i]).count() == s.len()
    }
}
mod counter {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::ops::*;
    #[derive(Clone, Debug)]
    pub struct Counter<K: Eq + Hash> {
        pub cnt: HashMap<K, i64>,
        pub d: i64,
    }
    impl<K: Eq + Hash> Counter<K> {
        pub fn new() -> Counter<K> {
            Counter {
                cnt: HashMap::new(),
                d: 0,
            }
        }
        #[doc = " Remove key when the value <= 0"]
        pub fn dec(&mut self, key: K, delta: i64) {
            if self.peek(&key) - delta <= 0 {
                self.remove(&key);
            } else {
                *self.cnt.get_mut(&key).unwrap() -= delta;
            }
        }
        pub fn peek(&self, key: &K) -> i64 {
            *self.cnt.get(key).unwrap_or(&self.d)
        }
    }
    impl<K: Eq + Hash> Deref for Counter<K> {
        type Target = HashMap<K, i64>;
        fn deref(&self) -> &Self::Target {
            &self.cnt
        }
    }
    impl<K: Eq + Hash> DerefMut for Counter<K> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.cnt
        }
    }
    impl<K: Eq + Hash> Index<K> for Counter<K> {
        type Output = i64;
        fn index(&self, index: K) -> &Self::Output {
            self.cnt.get(&index).unwrap_or(&self.d)
        }
    }
    impl<K: Eq + Hash + Clone> IndexMut<K> for Counter<K> {
        fn index_mut(&mut self, index: K) -> &mut i64 {
            if !self.cnt.contains_key(&index) {
                self.cnt.insert(index.clone(), self.d);
            }
            self.cnt.get_mut(&index).unwrap()
        }
    }
    impl<K: Eq + Hash> std::iter::FromIterator<K> for Counter<K> {
        fn from_iter<T: IntoIterator<Item = K>>(iter: T) -> Self {
            let mut cnt = HashMap::new();
            for i in iter {
                *cnt.entry(i).or_insert(0) += 1;
            }
            Counter { cnt, d: 0 }
        }
    }
    impl<T: Eq + Hash + Clone> Add for Counter<T> {
        type Output = Counter<T>;
        fn add(self, other: Counter<T>) -> Counter<T> {
            let mut ret = Counter::new();
            for (k, v) in self.iter().chain(other.iter()) {
                ret[k.clone()] += *v;
            }
            ret
        }
    }
    impl<T: Eq + Hash + Clone> AddAssign for Counter<T> {
        fn add_assign(&mut self, other: Self) {
            *self = self.clone().add(other);
        }
    }
}
mod modulo {
    use crate::tiny_num_traits::ZeroOne;
    use std::marker::PhantomData;
    use std::ops::*;
    #[derive(Debug, PartialEq, Copy, Clone)]
    pub struct ModInt<T>(pub i64, PhantomData<*const T>);
    impl<T: ConstValue> ModInt<T> {
        pub fn new<U: ModIntCastable>(a: U) -> ModInt<T> {
            ModInt((a.to_long() % T::M + T::M) % T::M, PhantomData)
        }
        pub fn pow(self, mut n: i64) -> Self {
            let mut a = self;
            let mut res = Self::new(1);
            while n > 0 {
                if n & 1 == 1 {
                    res *= a;
                }
                a = a * a;
                n >>= 1;
            }
            res
        }
        pub fn inv(self) -> Self {
            self.pow(T::M - 2)
        }
    }
    pub trait ConstValue: Clone + Copy {
        const M: i64;
    }
    impl<T> std::fmt::Display for ModInt<T> {
        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
            write!(f, "{}", self.0)
        }
    }
    impl<T: ConstValue, U: ModIntCastable> From<U> for ModInt<T> {
        fn from(n: U) -> Self {
            Self::new(n)
        }
    }
    impl<T: ConstValue> Add for ModInt<T> {
        type Output = ModInt<T>;
        fn add(self, other: ModInt<T>) -> ModInt<T> {
            Self::new(self.0 + other.0)
        }
    }
    impl<T: ConstValue> AddAssign for ModInt<T> {
        fn add_assign(&mut self, other: Self) {
            *self = self.add(other);
        }
    }
    impl<T: ConstValue> Sub for ModInt<T> {
        type Output = ModInt<T>;
        fn sub(self, other: ModInt<T>) -> ModInt<T> {
            Self::new(self.0 - other.0)
        }
    }
    impl<T: ConstValue> SubAssign for ModInt<T> {
        fn sub_assign(&mut self, other: Self) {
            *self = self.sub(other);
        }
    }
    impl<T: ConstValue> Mul for ModInt<T> {
        type Output = ModInt<T>;
        fn mul(self, other: ModInt<T>) -> ModInt<T> {
            Self::new(self.0 * other.0)
        }
    }
    impl<T: ConstValue> MulAssign for ModInt<T> {
        fn mul_assign(&mut self, other: Self) {
            *self = self.mul(other);
        }
    }
    impl<T: ConstValue> Div for ModInt<T> {
        type Output = ModInt<T>;
        fn div(self, other: ModInt<T>) -> ModInt<T> {
            self * other.inv()
        }
    }
    impl<T: ConstValue> DivAssign for ModInt<T> {
        fn div_assign(&mut self, other: Self) {
            *self = self.div(other);
        }
    }
    #[macro_export]
    macro_rules! modint {
        ( $ m : expr ) => {
            #[derive(Debug, PartialEq, Copy, Clone)]
            pub enum __M {}
            impl ConstValue for __M {
                const M: i64 = $m;
            }
            #[allow(dead_code)]
            type Z = ModInt<__M>;
        };
    }
    pub trait ModIntCastable {
        fn to_long(self) -> i64;
    }
    impl ModIntCastable for i64 {
        fn to_long(self) -> i64 {
            self
        }
    }
    impl ModIntCastable for usize {
        fn to_long(self) -> i64 {
            self as i64
        }
    }
    impl ModIntCastable for i32 {
        fn to_long(self) -> i64 {
            self as i64
        }
    }
    impl<T: ConstValue> ZeroOne for ModInt<T> {
        fn zero() -> Self {
            Self::new(0)
        }
        fn one() -> Self {
            Self::new(1)
        }
    }
}