pub struct Scanner {
	handle: std::io::Stdin,
	buffer: std::collections::VecDeque<String>
}

impl Scanner {
	pub fn new() -> Scanner {
		Scanner {
			handle: std::io::stdin(),
			buffer: std::collections::VecDeque::new()
		}
	}
	pub fn next<T: std::str::FromStr>(&mut self) -> Result<T, T::Err> {
		if self.buffer.len() == 0 {
			loop {
				let mut available = String::new();
				self.handle.read_line(&mut available).unwrap();
				self.buffer = available.split_whitespace().map(String::from).collect();
				if self.buffer.len() > 0 {
					break;
				}
			}
		}
		T::from_str(&self.buffer.pop_front().unwrap())
	}
}

fn gcd(m: i64, n: i64) -> i64 {
	if m == 0 {
		n.abs()
	} else {
		gcd(n % m, m)
	}
}

fn two_p(p: i32) -> i32 {
	if p == 0 {
		1
	} else {
		(2 * two_p(p - 1)) % 1000000007
	}
}

fn main() {
	let mut scanner = Scanner::new();
	let n: usize = scanner.next().unwrap();
	let mut size = std::collections::BTreeMap::<(i64,i64), i32>::new();
	for _ in 0..n {
		let a: i64 = scanner.next().unwrap();
		let b: i64 = scanner.next().unwrap();
		let g = if a == 0 && b == 0 {
			1
		} else {
			gcd(a,b)
		};
		let s = size.entry((a/g,b/g)).or_insert(0);
		*s += 1;
	}
	// println!("{:?}", size);
	let mut total = 1;
	let mut zero_total = 0;
	'outer: for ((a1,b1),v1) in size.iter() {
		// println!("{:?}", ((a1,b1),v1));
		if *a1 == 0 && *b1 == 0 {
			zero_total = (zero_total + 1) % 1000000007;
			continue;
		}
		for ((a2,b2),v2) in size.iter() {
			if *a2 == 0 && *b2 == 0 {
				continue;
			} else if a1*a2 + b1*b2 == 0 {
				if a1*b2 > a2*b1 {
					// println!("A {:?}, {:?}", ((a1,b1),v1), ((a2,b2),v2));
					total *= (two_p(*v1) + two_p(*v2) - 1) % 1000000007;
				}
				continue 'outer;
			}
		}
		// println!("B {:?}", ((a1,b1),v1));
		total = (total * two_p(*v1)) % 1000000007;
	}
	total = (total + zero_total - 1) % 1000000007;
	println!("{}", total);
}
