#![allow(unused, non_snake_case, dead_code, non_upper_case_globals)]
use proconio::{fastout, input};
use std::cmp::*;
use std::collections::*;
use std::mem::*;

#[fastout]
fn main() {
  input! {
    n:usize,
    mut ab:[(i64,i64);n],
  }
  let mut zeroc = 0;
  let mut mp = BTreeMap::<(i64, i64), (i64, i64)>::new();
  for (xx, yy) in ab {
    let mut x = xx;
    let mut y = yy;

    if x == 0 && y == 0 {
      zeroc += 1;
      continue;
    }
    if x == 0 {
      mp.entry((0, 0)).or_insert((0, 0)).0 += 1;
    } else if y == 0 {
      mp.entry((0, 0)).or_insert((0, 0)).1 += 1;
    } else {
      if y < 0 {
        x = -x;
        y = -y;
      }
      let g = gcd(x.abs(), y.abs());
      x /= g;
      y /= g;
      if x < 0 {
        mp.entry((y, -x)).or_insert((0, 0)).0 += 1;
      } else {
        mp.entry((x, y)).or_insert((0, 0)).1 += 1;
      }
    }
  }
  let mut ans = 1i64;
  for (key, value) in mp {
    let a = mod_pow(2, value.0, MOD) + mod_pow(2, value.1, MOD) - 1;
    ans = (ans * a) % MOD;
  }
  ans = (ans + zeroc + MOD - 1) % MOD;
  println!("{}", ans);
}

pub fn gcd(a: i64, b: i64) -> i64 {
  if b == 0 {
    a
  } else {
    gcd(b, a % b)
  }
}
static MX: usize = 1010101;
static MOD: i64 = 1000000007;
static INF: i64 = std::i64::MAX >> 1;
/// (gcd, x, y)
pub fn extgcd(a: i64, b: i64) -> (i64, i64, i64) {
  if b == 0 {
    (a, 1, 0)
  } else {
    let (gcd, x, y) = extgcd(b, a % b);
    (gcd, y, x - (a / b) * y)
  }
}
/// x ^ n % m
pub fn mod_pow(x: i64, n: i64, m: i64) -> i64 {
  let mut res = 1;
  let mut x = x % m;
  let mut n = n;
  while n > 0 {
    if n & 1 == 1 {
      res = (res * x) % m;
    }
    x = (x * x) % m;
    n >>= 1;
  }
  res
}
pub fn mod_inverse(a: i64, m: i64) -> i64 {
  let (_, x, _) = extgcd(a as i64, m as i64);
  ((m as i64 + x) as i64 % m) % m
}
pub fn fact_table(len: usize, m: i64) -> Vec<i64> {
  let mut res = vec![1; len + 1];
  for i in 1..len + 1 {
    res[i] = (i as i64 * res[i - 1]) % m;
  }
  res
}
/// Factorial and Inverse factorial table
pub fn fact_inv_table(size: usize, m: i64) -> (Vec<i64>, Vec<i64>) {
  let mut fact = vec![1; size];
  let mut fact_inv = vec![1; size];
  for i in 2..size {
    fact[i] = fact[i - 1] * i as i64 % m;
    fact_inv[i] = m - ((m / i as i64) * fact_inv[(m % i as i64) as usize] % m);
  }
  for i in 1..size {
    fact_inv[i] = fact_inv[i - 1] * fact_inv[i] % m;
  }
  (fact, fact_inv)
}
/// (a mod p, e when n! = a p\^e)
pub fn mod_fact(n: i64, p: i64, fact: &[i64]) -> (i64, i64) {
  if n == 0 {
    (1, 0)
  } else {
    let (a, b) = mod_fact(n / p, p, fact);
    let pow = b + n / p;
    if n / p % 2 != 0 {
      (a * (p - fact[(n % p) as usize]) % p, pow)
    } else {
      (a * fact[(n % p) as usize] % p, pow)
    }
  }
}
/// C(n, k) % p
pub fn mod_comb(n: i64, k: i64, p: i64, fact: &[i64]) -> i64 {
  if n < k {
    0
  } else {
    let (a1, e1) = mod_fact(n, p, fact);
    let (a2, e2) = mod_fact(k, p, fact);
    let (a3, e3) = mod_fact(n - k, p, fact);
    if e1 > e2 + e3 {
      0
    } else {
      a1 * mod_inverse(a2 * a3 % p, p) % p
    }
  }
}
/// H(n, k) % p
pub fn mod_comb_repetition(n: i64, k: i64, p: i64, fact: &[i64]) -> i64 {
  mod_comb(n - 1 + k, n - 1, p, fact)
}


Measured memory_bytes: 23212