//  [rust]
use std::io::Read;
use std::collections::HashMap;
use num_integer::gcd_lcm;

fn main() {
    let mut buf = String::new();
    std::io::stdin().read_to_string(&mut buf).unwrap();

    let answer = solve(&buf);

    println!("{}", answer);
}

fn solve(input: &str) -> String {
    let mut iterator = input.split_whitespace();

    let n: usize = iterator.next().unwrap().parse().unwrap();
    let mut zero: usize = 0;
    let mut mp: HashMap<(isize, isize), (isize, isize)> = HashMap::new();
    for _ in 0..n {
        let mut x: isize = iterator.next().unwrap().parse().unwrap();
        let mut y: isize = iterator.next().unwrap().parse().unwrap();
        if x == 0 && y == 0 {
            zero += 1;
            continue;
        }
        let g: isize = gcd_lcm(x, y).0;
        x /= g;
        y /= g;
        if y < 0 {
            x = -x;
            y = -y;
        }
        if y == 0 && x < 0 {
            x = -x;
            y = -y;
        }
        let rot90: bool = x <= 0;
        if rot90 {
            let tmp = y;
            y = -x;
            x = tmp;
        }
        if rot90 {
            (*mp.entry((x, y)).or_insert((0, 0))).0 += 1;
        } else {
            (*mp.entry((x, y)).or_insert((0, 0))).1 += 1;
        }
    }

    let mut ans: usize = 1;
    for x in mp {
        let s = (x.1).0;
        let t = (x.1).1;
        let mut now = 1;
        now += mod_pow(2, s as usize) - 1;
        now %= MOD;
        now += mod_pow(2, t as usize) - 1;
        now %= MOD;
        ans = ans * now % MOD;
    }
    if ans == 0 {
        ans = MOD - 1;
    } else {
        ans -= 1;
    }
    ans = ans + zero % MOD;
    return ans.to_string();

    // let mut signs: Vec<isize> = Vec::new();
    // let mut a: Vec<usize> = Vec::new();
    // let mut b: Vec<usize> = Vec::new();
    // for _ in 0..n {
    //     let mut sign = 1;
    //     let mut ai: isize = iterator.next().unwrap().parse().unwrap();
    //     if ai < 0 {
    //         sign *= -1;
    //         ai *= -1;
    //     }
    //     let mut bi: isize = iterator.next().unwrap().parse().unwrap();
    //     if bi < 0 {
    //         sign *= -1;
    //         bi *= -1;
    //     }
    //     a.push(ai as usize);
    //     b.push(bi as usize);
    //     signs.push(sign);
    // }
    //
    // let mut counts: HashMap<(isize, usize, usize), usize> = HashMap::new();
    // for i in 0..n {
    //     let g = gcd(a[i], b[i]);
    //     let p = (signs[i], a[i] / g, b[i] / g);
    //     *counts.entry(p).or_insert(0) += 1;
    // }
    // println!("{:?}", counts);
    //
    // let mc = ModComb::init(n);
    //
    // let mut ans: usize = 0;
    // for i in 0..n {
    //     ans = (ans + 1) % MOD;
    //     let g = gcd(a[i], b[i]);
    //     let d: usize= *counts.get(&(-signs[i], b[i] / g, a[i] / g)).unwrap_or(&0);
    //     println!("{} {}", i, d);
    //     for r in 2..=(n - d - 1) {
    //         ans = (ans + mc.calc(n - d - 1, r - 1)) % MOD;
    //     }
    // }
    // return ans.to_string();
}

const MOD: usize = 1_000_000_007;

fn mod_pow(a: usize, b: usize) -> usize {
    let mut result = 1;
    for _ in 0..b {
        result = result * a % MOD;
    }
    return result
}