use proconio::{fastout, input};
// use proconio::marker::*;
//
fn gcd(a: i64, b: i64) -> i64 {
   if b == 0 {
      a
   } else {
      gcd(b, a % b)
   }
}

#[fastout]
fn main() {
   input! {
      mut n: usize,
      ab: [(i64, i64); n]
   }
   let mut pp = vec![];
   let mut np = vec![];
   let mut zz = 0;
   let mut zl = 0;
   let mut zr = 0;
   for (_a, _b) in ab {
      if _a == 0 && _b == 0 {
         zz += 1;
         n -= 1;
         continue;
      } else if _a == 0 {
         zl += 1;
         continue;
      } else if _b == 0 {
         zr += 1;
         continue;
      }
      let m = gcd(_a.abs(), _b.abs());
      let a = _a / m;
      let b = _b / m;
      if a * b > 0 {
         pp.push((a.abs(), b.abs()));
      } else {
         np.push((a.abs(), b.abs()));
      }
   }
   np.push((1e18 as i64, 1e18 as i64));
   pp.sort();
   np.sort();

   let mut lr = vec![(zl, zr)];
   let mut l = 0;
   let mut r = 0;
   for i in 0..pp.len() {
      if i != 0 && pp[i] == pp[i - 1] {
         l += 1;
      } else {
         if r != 0 {
            lr.push((l, r));
         }
         l = 1;
         let (a, b) = pp[i];
         r = np.upper_bound(&(b, a)) - np.lower_bound(&(b, a));
      }
   }
   if r != 0 {
      lr.push((l, r));
   }
   let mut tot = n;
   for &(l, r) in lr.iter() {
      tot -= l + r;
   }

   let mut ans = Mint::new(2).pow(tot);
   for &(l, r) in lr.iter() {
      ans *= Mint::new(2).pow(l) + Mint::new(2).pow(r) - Mint::new(1);
   }
   ans -= Mint::new(1);
   ans += Mint::new(zz);
   println!("{}", ans);
}

use std::ops::*;
const MOD: i64 = 1000000007;
#[derive(Copy, Clone, Debug)]
pub struct Mint {
   x: i64,
}
impl Mint {
   fn new(x: i64) -> Self { Mint { x: x } }
   fn pow(self, n: usize) -> Self {
      let mut x = Mint::new(1);
      let mut r = self;
      let mut n = n;
      while n > 0 {
         if n & 1 == 1 {
            x *= r;
         }
         r *= r;
         n >>= 1;
      }
      x
   }
   fn inv(self) -> Self { self.pow((MOD - 2) as usize) }
}
impl AddAssign for Mint {
   fn add_assign(&mut self, rhs: Self) {
      self.x += rhs.x;
      self.x %= MOD;
   }
}
impl Add for Mint {
   type Output = Self;
   fn add(self, other: Self) -> Self {
      let mut res = self;
      res += other;
      res
   }
}
impl SubAssign for Mint {
   fn sub_assign(&mut self, rhs: Self) {
      self.x -= rhs.x;
      if self.x < 0 {
         self.x += MOD;
      }
   }
}
impl Sub for Mint {
   type Output = Self;
   fn sub(self, other: Self) -> Self {
      let mut res = self;
      res -= other;
      res
   }
}
impl MulAssign for Mint {
   fn mul_assign(&mut self, rhs: Self) {
      self.x *= rhs.x;
      self.x %= MOD;
   }
}
impl Mul for Mint {
   type Output = Self;
   fn mul(self, other: Self) -> Self {
      let mut res = self;
      res *= other;
      res
   }
}
impl DivAssign for Mint {
   fn div_assign(&mut self, rhs: Self) { *self *= rhs.inv(); }
}
impl Div for Mint {
   type Output = Self;
   fn div(self, other: Self) -> Self {
      let mut res = self;
      res *= other;
      res
   }
}
impl From<i64> for Mint {
   fn from(num: i64) -> Self { Mint { x: num } }
}
impl From<i32> for Mint {
   fn from(num: i32) -> Self { Mint { x: num as i64 } }
}
impl From<usize> for Mint {
   fn from(num: usize) -> Self { Mint { x: num as i64 } }
}
use std::fmt;
impl fmt::Display for Mint {
   fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.x) }
}

use std::cmp::Ordering;
/// Equivalent to std::lowerbound and std::upperbound in c++
pub trait BinarySearch<T> {
   fn lower_bound(&self, x: &T) -> usize;
   fn upper_bound(&self, x: &T) -> usize;
}
#[allow(dead_code)]
impl<T: Ord> BinarySearch<T> for [T] {
   fn lower_bound(&self, x: &T) -> usize {
      let mut low = 0;
      let mut high = self.len();
      while low != high {
         let mid = (low + high) / 2;
         match self[mid].cmp(x) {
            Ordering::Less => {
               low = mid + 1;
            }
            Ordering::Equal | Ordering::Greater => {
               high = mid;
            }
         }
      }
      low
   }
   fn upper_bound(&self, x: &T) -> usize {
      let mut low = 0;
      let mut high = self.len();
      while low != high {
         let mid = (low + high) / 2;
         match self[mid].cmp(x) {
            Ordering::Less | Ordering::Equal => {
               low = mid + 1;
            }
            Ordering::Greater => {
               high = mid;
            }
         }
      }
      low
   }
}


Measured memory_bytes: 16068