#include<stdio.h>

void QSort(long x[ ], int left, int right)
{
    int i, j;
    int pivot;

    i = left;                      /* ソートする配列の一番小さい要素の添字 */
    j = right;                     /* ソートする配列の一番大きい要素の添字 */

    pivot = x[(left + right) / 2]; /* 基準値を配列の中央付近にとる */

    while (1) {                    /* 無限ループ */

        while (x[i] < pivot)       /* pivot より大きい値が */
            i++;                   /* 出るまで i を増加させる */

        while (pivot < x[j])       /* pivot より小さい値が */
            j--;                   /*  出るまで j を減少させる */
        if (i >= j)                /* i >= j なら */
            break;                 /* 無限ループから抜ける */

        Swap(x, i, j);             /* x[i] と x[j]を交換 */
        i++;                       /* 次のデータ */
        j--;
    }

    if (left < i - 1)              /* 基準値の左に 2 以上要素があれば */
        QSort(x, left, i - 1);     /* 左の配列を Q ソートする */
    if (j + 1 <  right)            /* 基準値の右に 2 以上要素があれば */
        QSort(x, j + 1, right);    /* 右の配列を Q ソートする */
}

  /* 配列の要素を交換する */
void Swap(long x[ ], int i, int j)
{
    int temp;

    temp = x[i];
    x[i] = x[j];
    x[j] = temp;
}

int main(){
  int n,i,cnt=0,flag=0,j;
  long a[100000];
  scanf("%d",&n);
  for(i=0;i<n;i++)
    scanf("%ld",&a[i]);
  QSort(a, 0, n - 1);
  for(i=0;i<n;i++){
    flag=1;
    for(j=1+i;j<n;j++){
      if(a[i]==a[j])
        flag=(1+flag)%2;
      else break;
    }
    cnt+=flag;
    i=j-1;
  }
  printf("%d\n",cnt);
}


#include <stdio.h>
#include <stdlib.h>

typedef struct List {
	struct List *prev, *next;
	int id;
} list;

int main()
{
	int i, N, A[100001];
	scanf("%d", &N);
	for (i = 1; i <= N; i++) scanf("%d", &(A[i]));

	list *p, *hash[10007] = {}, data[100001];
	long long count = 0;
	for (i = 1; i <= N; i++) {
		for (p = hash[A[i] % 10007]; p != NULL; p = p->next) {
			if (p->id == A[i]) break;
		}
		if (p == NULL) {
			data[i].prev = NULL;
			data[i].next = hash[A[i] % 10007];
			if (data[i].next != NULL) (data[i].next)->prev = &(data[i]);
			data[i].id = A[i];
			hash[A[i] % 10007] = &(data[i]);
			count++;
		} else {
			if (p->prev != NULL) p->prev->next = p->next;
			else hash[A[i] % 10007] = p->next;
			if (p->next != NULL) p->next->prev = p->prev;
			count--;
		}
	}

	printf("%lld\n", count);
	fflush(stdout);
	return 0;
}


#include<stdio.h>

void QSort(long x[ ], int left, int right)
{
    int i, j;
    int pivot;

    i = left;                      /* ソートする配列の一番小さい要素の添字 */
    j = right;                     /* ソートする配列の一番大きい要素の添字 */

    pivot = x[(left + right) / 2]; /* 基準値を配列の中央付近にとる */

    while (1) {                    /* 無限ループ */

        while (x[i] < pivot)       /* pivot より大きい値が */
            i++;                   /* 出るまで i を増加させる */

        while (pivot < x[j])       /* pivot より小さい値が */
            j--;                   /*  出るまで j を減少させる */
        if (i >= j)                /* i >= j なら */
            break;                 /* 無限ループから抜ける */

        Swap(x, i, j);             /* x[i] と x[j]を交換 */
        i++;                       /* 次のデータ */
        j--;
    }

    if (left < i - 1)              /* 基準値の左に 2 以上要素があれば */
        QSort(x, left, i - 1);     /* 左の配列を Q ソートする */
    if (j + 1 <  right)            /* 基準値の右に 2 以上要素があれば */
        QSort(x, j + 1, right);    /* 右の配列を Q ソートする */
}

  /* 配列の要素を交換する */
void Swap(long x[ ], int i, int j)
{
    long temp;

    temp = x[i];
    x[i] = x[j];
    x[j] = temp;
}

int main(){
  long a[100000],n,i,cnt=0,cnt2=0,j,b,c=1;
  scanf("%ld",&n);
  for(i=0;i<n;i++)
    scanf("%ld",&a[i]);
  QSort(a, 0, n - 1);
  /*for(i=0;i<n;i++)
    printf("%d\n",a[i]);*/
  b=a[0];
  for(i=1;i<n;i++){
    if(a[i]==b){
      c=(c+1)%2;
      if(i==n-1)
        cnt+=c;
    }
    else{
      cnt+=c;
      c=1;
      b=a[i];
      if(i==n-1)
        cnt++;
    }
  }
  printf("%ld\n",cnt);
}


#include <stdio.h>
#include <stdlib.h>
typedef long long ll;

int compare_ll(const void *a, const void *b){
    return *(ll*)a - *(ll*)b;
}

int main(){
    int n, ans=0;
    ll a[100001]={0};
    scanf("%d",&n);
    for(int i=0; i<n; i++){ scanf("%lld",&a[i]); }
    qsort( a, n, sizeof(ll), compare_ll );

    int cnt=1, tmp=a[0];
    for(int i=1; i<n; i++){
        if( tmp==a[i] ){ cnt++; }
        else{
            if( cnt&1 ){ ans++; }
            tmp=a[i];
            cnt=1;
        }
    }
    printf("%d\n",ans);
    return 0;
}


#include <stdio.h>
#include <stdlib.h>
typedef long long ll;

int compare_ll(const void *a, const void *b){
    return *(ll*)a - *(ll*)b;
}

int main(){
    int n, ans=0;
    ll a[101]={0};
    scanf("%d",&n);
    for(int i=0; i<n; i++){ scanf("%lld",&a[i]); }
    qsort( a, n, sizeof(ll), compare_ll );

    int cnt=1, tmp=a[0];
    for(int i=1; i<n; i++){
        if( tmp==a[i] ){ cnt++; }
        else{
            if( cnt&1 ){ ans++; }
            tmp=a[i];
            cnt=1;
        }
    }
    printf("%d\n",ans);
    return 0;
}


#include <stdio.h>
#include <stdlib.h>

int compare_int(const void *a, const void *b){
    return *(int*)a - *(int*)b;
}

int main(void){
    int n;
    int a[100000+5];
    scanf("%d", &n);
    for(int i=0; i<n; i++)scanf("%d", &a[i]);
    qsort(a, n, sizeof(int), compare_int);

    int ans=0;
    for(int i=0; i<n-1; i++){
        int cnt=1;
        while(a[i]==a[i+1]){
            cnt++;
            i++;
        }
        ans+=cnt%2;
    }
    printf("%d\n", ans);
    return 0;
}


'use strict';

const main = arg => {

    const input = arg.split("\n");
    const n = parseInt(input[0]);

    let freq = new Map();
    for (let i = 0; i < n; i++) {
        const a = parseInt(input[i + 1].trim());
        if (freq.has(a)) {
            freq.set(a, freq.get(a) + 1);
        } else {
            freq.set(a, 1);
        }
    }

    let count = 0;
    for (const [key, value] of freq.entries()) {
        if (value % 2 == 1) {
            count++;
        }
    }
    console.log(count);
}

main(require('fs').readFileSync('/dev/stdin', 'utf8'));


console.log((args=>{
  const [N,...A] = args.trim().split`\n`.map(v=>v|0);
  const ex = new Map();
  for ( let i = 0; i < N; i++ ) ex.set( A[i], ex.get(A[i])^1 );
  let s = 0;
  for ( const [,v] of ex ) s += v;
  return `${s}`;
})(require('fs').readFileSync('/dev/stdin', 'utf8')));


"use strict"

function main(input) {
  input = input.split(/\n/).map(v => ~~v)
  let set = new Set()
  for (let i = 1; i < input.length-1; i++) {
    //
    if (set.has(input[i])) {
      set.delete(input[i])
    } else {
      set.add(input[i])
    }
  }
  console.log(set.size)
}
main(require("fs").readFileSync("/dev/stdin", "utf8"))


'use strict';

const main = arg => {

    const input = arg.split("\n");
    n = parseInt(input[0]);

    let freq = Map();
    for (let i = 0; i < n; i++) {
        const a = parseInt(input[i + 1]);
        if (freq.has(a)) {
            freq.set(freq.get(a) + 1);
        } else {
            freq.set(a, 1);
        }
    }

    let count = 0;
    for (let [key, value] of freq.entries()) {
        if (value % 2 == 1) {
            count++;
        }
    }
    console.log(count);
}

main(require('fs').readFileSync('/dev/stdin', 'utf8'));


function Main (input) {
  input = input.split("\n").map(Number);
  var N = input[0];
  input.pop();
  input.shift();
  var now_j = [input[0]];
  input.shift();
  input.forEach (i => {
    if (now_j.some(inp => inp == i)) {
      now_j.splice(now_j.indexOf(i), 1);
    }
    else {
      now_j.push(i);
    }
  });
  console.log(now_j.length);
}

Main(require("fs").readFileSync("/dev/stdin", "utf8"));


function Main (input) {
  input = input.split("\n").map(Number);
  var N = input[0];
  input.pop();
  input.shift();
  var now_j = [input[0]];
  input.shift();
  for (var i of input) {
    if (now_j.some(inp => inp == i)) {
      now_j.splice(now_j.indexOf(i), 1);
    }
    else {
      now_j.push(i);
    }
  }
  console.log(now_j.length);
}

Main(require("fs").readFileSync("/dev/stdin", "utf8"));


package main

import (
	"bufio"
	"fmt"
	"os"
)

var sc = bufio.NewScanner(os.Stdin)

func r() (n int) {
	sc.Scan()
	for _, v := range sc.Bytes() {
		n = n*10 + int(v-48)
	}
	return
}

func main() {
	sc.Split(bufio.ScanWords)
	n := r()

	a := make(map[int]int, n)
	var ans int
	for i := 0; i < n; i++ {
		t := r()
		if a[t] == 1 {
			a[t] = 0
			ans--
		} else {
			a[t] = 1
			ans++
		}
	}
	fmt.Println(ans)
}


// ProblemURL : https://atcoder.jp/contests/abc073/tasks/abc073_c
// ---------------------------------------------
package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"sort"
	"strconv"
	"strings"
)

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}
func diff(a, b int) int {
	if a > b {
		return a - b
	}
	return b - a
}
func larger(a, b int) int {
	if a > b {
		return a
	}
	return b
}
func smaller(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func largest(a, b, c int) (lgst int) {
	if a > b {
		lgst = a
	} else {
		lgst = b
	}
	if c > lgst {
		lgst = c
	}
	return
}
func smallest(a, b, c int) (slst int) {
	if a < b {
		slst = a
	} else {
		slst = b
	}
	if c < slst {
		slst = c
	}
	return
}
func intsMax(a []int) (val int) {
	if len(a) == 0 {
		panic("func intsMax: zero length slice")
	}
	val = a[0]
	for _, aa := range a {
		if aa > val {
			val = aa
		}
	}
	return
}
func intsMaxIdx(a []int) (idx, val int) {
	if len(a) == 0 {
		panic("func intsMaxIdx: zero length slice")
	}
	val = a[0]
	for i, aa := range a {
		if aa > val {
			idx, val = i, aa
		}
	}
	return
}
func intsMin(a []int) (val int) {
	if len(a) == 0 {
		panic("func intsMin: zero length slice")
	}
	val = a[0]
	for _, aa := range a {
		if aa < val {
			val = aa
		}
	}
	return
}
func intsMinIdx(a []int) (idx, val int) {
	if len(a) == 0 {
		panic("func intsMinIdx: zero length slice")
	}
	val = a[0]
	for i, aa := range a {
		if aa < val {
			idx, val = i, aa
		}
	}
	return
}
func intsSum(a []int) int {
	res := 0
	for _, v := range a {
		res += v
	}
	return res
}
func intsAve(s []int) float64 {
	return float64(intsSum(s)) / float64(len(s))
}
func intsAdd(a *[]int, x int)    { *a = append(*a, x) }
func intsDelete(a *[]int, i int) { *a = append((*a)[:i], (*a)[i+1:]...) }
func intsJoin(a, b []int) []int  { return append(a, b...) }
func intsClear(a []int) []int    { return a[:0] }
func intsCopy(a []int) []int     { return append([]int(nil), a...) }
func intsCopySortAsc(a []int) []int {
	s := intsCopy(a)
	sort.Ints(s)
	return s
}
func intsCopySortDesc(a []int) []int {
	s := intsCopy(a)
	sort.Sort(sort.Reverse(sort.IntSlice(s)))
	return s
}
func intsReverse(a []int) {
	for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
		a[i], a[j] = a[j], a[i]
	}
}
func intsFill(a []int, val int) {
	for i := 0; i < len(a); i++ {
		a[i] = val
	}
}
func intsPeekBack(a []int) int {
	if len(a) == 0 {
		panic("func peekBack: zero length slice")
	}
	return a[len(a)-1]
}
func intsPeekFront(a []int) int {
	if len(a) == 0 {
		panic("func peekFront: zero length slice")
	}
	return a[0]
}
func intsPopBack(a *[]int) int {
	if len(*a) == 0 {
		panic("func popBack: zero length slice")
	}
	t := (*a)[len(*a)-1]
	*a = (*a)[:len(*a)-1]
	return t
}
func intsPopFront(a *[]int) int {
	if len(*a) == 0 {
		panic("func popFront: zero length slice")
	}
	h := (*a)[0]
	*a = (*a)[1:]
	return h
}
func intsPushBack(a *[]int, x int) { *a = append(*a, x) }
func intsPushFront(a *[]int, x int) {
	*a = append(*a, 0)
	copy((*a)[1:], (*a)[0:])
	(*a)[0] = x
}
func intsAppendSentinelHead(a *[]int, sentinel int) { intsPushFront(a, sentinel) }
func intsAppendSentinelTail(a *[]int, sentinel int) { intsPushBack(a, sentinel) }
func intsAppendSentinels(a *[]int, sentinel int) {
	intsPushFront(a, sentinel)
	intsPushBack(a, sentinel)
}
func intsIota(n int) []int {
	as := make([]int, n)
	for i := range as {
		as[i] = i
	}
	return as
}
func intsHaveDuplicateElm(a []int) bool {
	m := make(map[int]bool, len(a))
	for _, aa := range a {
		if _, exists := m[aa]; exists {
			return true
		} else {
			m[aa] = true
		}
	}
	return false
}
func isEven(n int) bool     { return n&1 == 0 }
func isOdd(n int) bool      { return n&1 == 1 }
func floor(x float64) int   { return int(x) }
func ceil(x float64) int    { return int(math.Ceil(x)) }
func ceilDiv(x, y int) int  { return ((x) + (y) - 1) / (y) }
func floorDiv(x, y int) int { return (x) / (y) }
func pow(x, y int) int {
	if y < 0 {
		panic("Exponent must be a natural number")
	}
	z := 1
	for ; y != 0; y >>= 1 {
		if y&1 == 1 {
			z *= x
		}
		x *= x
	}
	return z
}
func fact(x int) int {
	fact := 1
	for i := 1; i <= x; i++ {
		fact *= i
	}
	return fact
}
func sigmaK(n int) int  { return n * (n + 1) / 2 }
func sigmaKK(n int) int { return (n * (n + 1) / 2) * (2*n + 1) / 3 }
func euclideanDistance(a, b point) float64 {
	s := diff(a.x, b.x)
	s *= s
	t := diff(a.y, b.y)
	t *= t
	return math.Sqrt(float64(s) + float64(t))
}
func manhattanDistance(a, b point) int {
	s := diff(a.x, b.x)
	t := diff(a.y, b.y)
	return s + t
}
func swap(a, b *int) { *a, *b = *b, *a }
func chmax(a *int, b int) bool {
	if *a < b {
		*a = b
		return true
	}
	return false
}
func chmin(a *int, b int) bool {
	if *a > b {
		*a = b
		return true
	}
	return false
}
func cumsumBuild(a []int) []int {
	b := append([]int{0}, a...)
	for i := 1; i < len(b); i++ {
		b[i] += b[i-1]
	}
	return b
}
func bytesReverse(a []byte) {
	for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
		a[i], a[j] = a[j], a[i]
	}
}
func strIsUpperAlphabet(s string) bool {
	if len(s) != 1 {
		panic("wrong arg")
	}
	t := s[0]
	return 'A' <= t && t <= 'Z'
}
func strIsLowerAlphabet(s string) bool {
	if len(s) != 1 {
		panic("wrong arg")
	}
	t := s[0]
	return 'a' <= t && t <= 'z'
}
func strToUpperCase(s string) string { return strings.ToUpper(s) }
func strToLowerCase(s string) string { return strings.ToLower(s) }
func strAlphaUpperCaseToInt(s string) int {
	if len(s) != 1 {
		panic("wrong arg")
	}
	t := s[0]
	if !('A' <= t && t <= 'Z') {
		panic("arg must be upper case alphabet")
	}
	return int(t - 'A')
}
func strAlphaLowerCaseToInt(s string) int {
	if len(s) != 1 {
		panic("wrong arg")
	}
	t := s[0]
	if !('a' <= t && t <= 'z') {
		panic("arg must be lower case alphabet")
	}
	return int(t - 'a')
}
func strCountAlphabetsLower(alphaLower string) (ctAlphas [26]int) {
	ct := [26]int{}
	for _, ss := range alphaLower {
		if !('a' <= ss && ss <= 'z') {
			panic("func strCountAlphabetsLower: argument must be lowercase string")
		}
		ct[ss-'a']++
	}
	return ct
}
func strCountAlphabetsUpper(alphaUpper string) (ctAlphas [26]int) {
	ct := [26]int{}
	for _, ss := range alphaUpper {
		if !('A' <= ss && ss <= 'Z') {
			panic("func strCountAlphabetsUpper: argument must be uppercase string")
		}
		ct[ss-'A']++
	}
	return ct
}
func strReverse(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
func runeIsUpperAlphabet(r rune) bool { return 'A' <= r && r <= 'Z' }
func runeIsLowerAlphabet(r rune) bool { return 'a' <= r && r <= 'z' }
func runesReverse(a []rune) {
	for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
		a[i], a[j] = a[j], a[i]
	}
}
func strToInt(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		panic(err)
	}
	return i
}
func intToStr(i int) string     { return strconv.Itoa(i) }
func CalcMod(a, mod int) int    { return (a%mod + mod) % mod }
func ModSum(a *int, b, mod int) { *a = (((*a) % mod) + mod + (b % mod)) % mod }
func ModMul(a *int, b, mod int) { *a = (((*a) % mod) * (b % mod)) % mod }
func ModPow(x, y, mod int) int {
	if y < 0 {
		panic("Exponent must be a natural number")
	}
	z := 1
	for ; y != 0; y >>= 1 {
		if y&1 == 1 {
			z = (z * x) % mod
		}
		x = (x * x) % mod
	}
	return z
}
func ModFact(x, mod int) int {
	fact := 1
	for i := 1; i <= x; i++ {
		fact = fact * i % mod
	}
	return fact
}
func ModInv(x, mod int) int { return ModPow(x, mod-2, mod) }
func initMatrixBool(parentSize, childSize int, initialValue bool) *[][]bool {
	res := make([][]bool, parentSize)
	for i := range res {
		res[i] = make([]bool, childSize)
		for j := range res[i] {
			res[i][j] = initialValue
		}
	}
	return &res
}
func initMatrixInt(parentSize, childSize int, initialValue int) *[][]int {
	res := make([][]int, parentSize)
	for i := range res {
		res[i] = make([]int, childSize)
		for j := range res[i] {
			res[i][j] = initialValue
		}
	}
	return &res
}

var (
	sc = bufio.NewScanner(os.Stdin)
	bw = bufio.NewWriterSize(os.Stdout, bwBufSize)
)

func init() {
	sc.Split(bufio.ScanWords)
	sc.Buffer(make([]byte, 1e7), 1e7)
}
func ru() (n int) {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	for _, v := range sc.Bytes() {
		n = n*10 + int(v-48)
	}
	return
}
func ri() (n int) {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	b := sc.Bytes()
	neg := false
	if b[0] == 45 {
		neg = true
		b = b[1:]
	}
	for _, v := range b {
		n = n*10 + int(v-48)
	}
	if neg {
		n = -n
	}
	return
}
func ri64() (n int64) {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	b := sc.Bytes()
	neg := false
	if b[0] == 45 {
		neg = true
		b = b[1:]
	}
	for _, v := range b {
		n = n*10 + int64(v-48)
	}
	if neg {
		n = -n
	}
	return
}
func rf64() float64 {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	f, err := strconv.ParseFloat(sc.Text(), 64)
	if err != nil {
		panic(err)
	}
	return f
}
func rs() string {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	return sc.Text()
}
func rb() []byte {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	return sc.Bytes()
}
func rr() []rune {
	sc.Scan()
	if err := sc.Err(); err != nil {
		panic(err)
	}
	return []rune(sc.Text())
}
func ris(n int) []int {
	s := make([]int, n)
	for i := range s {
		s[i] = ri()
	}
	return s
}
func risZeroBased(n int) []int {
	s := make([]int, n)
	for i := range s {
		s[i] = ri() - 1
	}
	return s
}
func ri64s(n int) []int64 {
	s := make([]int64, n)
	for i := range s {
		s[i] = ri64()
	}
	return s
}
func riis(n int) ([]int, []int) {
	s := make([]int, n)
	t := make([]int, n)
	for i := range s {
		s[i] = ri()
		t[i] = ri()
	}
	return s, t
}
func rss(n int) []string {
	s := make([]string, n)
	for i := range s {
		s[i] = rs()
	}
	return s
}
func pf(format string, a ...interface{}) {
	if _, err := fmt.Fprintf(bw, format, a...); err != nil {
		panic(err)
	}
}
func pln(a ...interface{}) {
	if _, err := fmt.Fprintln(bw, a...); err != nil {
		panic(err)
	}
}
func pints(a []int) {
	for _, v := range a {
		fmt.Fprintln(bw, v)
	}
}
func pintsol(a []int) {
	s := fmt.Sprint(a)
	if _, err := fmt.Fprintln(bw, s[1:len(s)-1]); err != nil {
		panic(err)
	}
}
func dbg(a ...interface{}) {
	if _, err := fmt.Fprintln(os.Stderr, a...); err != nil {
		panic(err)
	}
}
func dbgMatrix(a [][]int) {
	for i := 0; i < len(a); i++ {
		dbg(a[i])
	}
}
func flush() {
	if err := bw.Flush(); err != nil {
		panic(err)
	}
}
func YESorNO(b bool) string {
	if b {
		return "YES"
	} else {
		return "NO"
	}
}
func YesOrNo(b bool) string {
	if b {
		return "Yes"
	} else {
		return "No"
	}
}
func main() {
	solve()
	flush()
}

const (
	bwBufSize = 1e6

	alphabetLower = "abcdefghijklmnopqrstuvwxyz"
	alphabetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

	// maxInt64  = 9223372036854775807  // 9e18
	// maxInt32  = 2147483647           // 2e9
	// maxUint64 = 18446744073709551615 // 1e19
	inf = 1 << 60
	mod = 1e9 + 7
)

type pair struct{ fi, se int }
type byFiSe []pair

func (p byFiSe) Len() int      { return len(p) }
func (p byFiSe) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p byFiSe) Less(i, j int) bool {
	if p[i].fi == p[j].fi {
		return p[i].se < p[j].se
	}
	return p[i].fi < p[j].fi
}

type point struct{ x, y int }

func solve() {
	n := ri()
	as := ris(n)

	m := make(map[int]int, n)
	for _, a := range as {
		m[a]++
	}

	ct := 0
	for _, v := range m {
		ct += v % 2
	}
	pln(ct)
}


package main

import (
	"bufio"
	"fmt"
	"os"
)

var sc = bufio.NewScanner(os.Stdin)

func r() (n int) {
	sc.Scan()
	for _, v := range sc.Bytes() {
		n = n*10 + int(v-48)
	}
	return
}

func main() {
	sc.Split(bufio.ScanWords)
	n := r()

	a := make(map[int]int, n)
	for i := 0; i < n; i++ {
		x := r()
		if _, ok := a[x]; !ok {
			a[x] = 1
		} else {
			a[x]++
		}
	}
	var ans int
	for _, v := range a {
		if v%2 != 0 {
			ans++
		}
	}
	fmt.Println(ans)
}


package main

import (
	"fmt"
)

func main() {
	var n int
	fmt.Scan(&n)

	as := map[int]bool
	for i := 0; i < n; i++ {
		var a int
		fmt.Scan(&a)
		as[a] = !as[a]
	}

	ans := 0
	for _, b := range as {
		if b {
			ans++
		}
	}

	fmt.Print(ans)
}


package main

import (
	"fmt"
	"sort"
)

func main() {
	var n int
	fmt.Scan(&n)

	as := make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Scan(&as[i])
	}
	sort.Sort(sort.IntSlice(as))

	ans := 0
	tmp := 0
	tmpnum := 0
	for _, a := range as {
		if tmp == a {
			tmpnum++
			continue
		}

		tmp = a
		if tmpnum%2 != 0 {
			ans++
		}
	}
	fmt.Print(ans)
}


package main

import (
	"fmt"
	"sort"
)

func main() {
	var n int
	fmt.Scan(&n)

	as := make([]int, n)
	for i := 0; i < n; i++ {
		fmt.Scan(&as[i])
	}
	sort.Sort(sort.IntSlice(as))

	ans := 0
	tmp := ""
	tmpnum := 0
	for _, a := range as {
		if tmp == a {
			tmpnum++
			continue
		}

		tmp = a
		if tmpnum%2 != 0 {
			ans++
		}
	}
	fmt.Print(ans)
}


n=gets.to_i
a=$<.map{|e|e.to_i}.sort
c=0
cc=false
t=a[0]
a.each do |e|
  if t != e
    c+=1 if cc
    t=e
    cc=true
  else
    cc = !cc
  end
end
c+=1 if cc
p c


N,*as=$<.read.split.map(&:to_i)

h = {}

as.each do |a|
    if h[a]
        h.delete(a)
    else
        h[a] = 1
    end
end

puts h.keys.count


a = []

gets.to_i.times do
  a << gets.to_i
end

prev_n = 0
os = false
res = 0

a.sort.each do |n|
  if n != prev_n
    if os
      res += 1
    end

    os = true
  else
    os = !os
  end

  prev_n = n
end

if os
  res += 1
end

puts res


n = gets.to_i
rec = []
n.times do
  a = gets.to_i
  if rec[a-1] = 1
    rec[a-1] = 0
  else
    rec[a-1] = 1
  end
end
rec.compact!
puts rec.sum


n = gets.to_i
arr = []
n.times do
  arr << gets.to_i
end
arr.sort!
ans = 1
count = 0
flag = 0
for i in 1..(n-1)
  if arr[i-1] != arr[i]
    ans += 1
  elsif arr[i-1] == arr[i] && flag == 0
    ans -= 1
    flag = 0
  else
    ans += 1
    flag = 1
  end
end
puts ans


n = gets.to_i
arr = []
n.times do
  arr << gets.to_i
end
arr.sort!
ans = 0
count = 0
flag = 0
for i in 1..(n-1)
  if arr[i-1] != arr[i]
    ans += 1
  elsif arr[i-1] == arr[i] && flag == 1
    ans -= 1
    flag = 0
  else
    ans += 1
    flag = 1
  end
end
puts ans - 1


use std::io::Read;
use std::collections::HashSet;

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 set: HashSet<usize> = HashSet::with_capacity(n);

    for _ in 0..n {
        let a: usize = iterator.next().unwrap().parse().unwrap();
        if set.contains(&a) {
            set.remove(&a);
        } else {
            set.insert(a);
        }
    }

    return set.len().to_string();
}


macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut iter = $ s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { let s = { use std :: io :: Read ; let mut s = String :: new ( ) ; std :: io :: stdin ( ) . read_to_string ( & mut s ) . unwrap ( ) ; s } ; let mut iter = s . split_whitespace ( ) ; input_inner ! { iter , $ ( $ r ) * } } ; }
macro_rules ! input_inner { ( $ iter : expr ) => { } ; ( $ iter : expr , ) => { } ; ( $ iter : expr , mut $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let mut $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , mut $ var : ident $ ( $ r : tt ) * ) => { let mut $ var = $ iter . next ( ) . unwrap ( ) . parse ::< usize > ( ) . unwrap ( ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ iter , $ t ) ; input_inner ! { $ iter $ ( $ r ) * } } ; ( $ iter : expr , $ var : ident $ ( $ r : tt ) * ) => { let $ var = $ iter . next ( ) . unwrap ( ) . parse ::< usize > ( ) . unwrap ( ) ; input_inner ! { $ iter $ ( $ r ) * } } ; }
macro_rules ! read_value { ( $ iter : expr , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ iter , $ t ) ) ,* ) } ; ( $ iter : expr , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ iter , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ iter : expr , chars ) => { read_value ! ( $ iter , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ iter : expr , usize1 ) => { read_value ! ( $ iter , usize ) - 1 } ; ( $ iter : expr , $ t : ty ) => { $ iter . next ( ) . unwrap ( ) . parse ::<$ t > ( ) . unwrap ( ) } ; }
use std::collections::HashSet;
fn main() {
    input! {
        n: u64,
        mut a: [u64; n]
    }
    let mut set = HashSet::with_capacity(100_000);

    for &x in &a {
        if !set.remove(&x) {
            set.insert(x);
        }
    }
    println!("{}", set.len());
}


use std::collections::HashSet;
use std::io::{self, BufRead, Read};

fn main() {
    let stdin = io::stdin();
    let mut stdin = stdin.lock();
    for _ in stdin.by_ref().bytes().take_while(|r| r.as_ref().unwrap() != &b'\n') {}

    let mut set = HashSet::with_capacity(100_000);
    for a in stdin.lines() {
    	let a: u32 = a.unwrap().parse().unwrap();
        if ! set.remove(&a) { set.insert(a); }
    }

    println!("{}", set.len());
}


use whiteread::parse_line;

fn main() {
    let n: usize = parse_line().unwrap();
    let mut i: usize = 0;
    let mut v: Vec<usize> = Vec::new();

    while i < n {
        let a: usize = parse_line().unwrap();
        if !v.contains(&a) {
            v.push(a);
        } else {
            v.retain(|&x| x != a);
        }
        i = i + 1;
    }

    println!("{}", v.len());
}


use std::io::*;
use std::str::FromStr;

fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("filed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

fn main() {
    let n:usize = read();
    let mut v = Vec::new();
    for _ in 0..n {
        let a:usize = read();
        if v.contains(&a) {
            v.retain(|&x| x != a);
        } else {
            v.push(a);
        }
    }
    println!("{}", v.len());
}


use std::io::{stdin, BufRead};
fn main() {
    let stdin = stdin();
    let mut stdinlock = stdin.lock();
    let mut n = String::new();
    let _ = stdinlock.read_line(&mut n);
    let n: usize = n.trim().parse().expect("N");
    let mut v: Vec<u32> = Vec::with_capacity(n);
    for line in stdinlock.lines() {
        v.push(line.expect("line").trim().parse().expect("parse"));
    }
    v.sort();
    let mut n: u32 = 0;
    let mut current = v[0];
    let mut oddness = true;
    for &ai in &v[1..] {
        if ai == current {
            oddness = !oddness;
        } else {
            if oddness {
                n += 1;
            }
            current = ai;
            oddness = true;
        }
    }
    println!("{}", n);
}


// 🐋💭
import Foundation

func readInts() -> [Int] {
    return readLine()!.split(separator: " ").map { Int($0)! }
}

func readInt() -> Int {
    return Int(readLine()!)!
}

func readDoubles () -> [Double] {
    return readLine()!.split(separator: " ").map { Double($0)! }
}

func readDouble() -> Double{
    return Double(readLine()!)!
}

func readLines() -> [String] {
    return readLine()!.map { String($0) }
}

///最大公約数を返す
func gcd(_ m:Int, _ n:Int) -> Int {
    if n == 0 {return m}
    return gcd(n, m % n)
}

///最大公約数を返す
func gcd(_ array: [Int]) -> Int {
    var value = array[0]
    if array.count > 1 {
        for i in 1..<array.count {
            value = gcd(value, array[i])
        }
    }
    return value
}

///最小公倍数を返す
func lcm(_ m:Int, _ n:Int) -> Int {
    return m * n / gcd(m, n)
}

///素数判定(試し割り)
func isPrime(_ x:Int) -> Bool {
    if x <= 1 || x % 2 == 0 { return false }
    if x == 2 { return true }
    for i in stride(from: 3, to: Int(ceil(sqrt(Double(x)))), by: 2){
        if x % i == 0 {
            return false
        }
    }
    return true
}

//https://program-life.com/669
///実行速度の計測
func exectionTime(action: () -> Void) {
    let startDate = Date()
    action()
    let endDate = Date()
    print(endDate.timeIntervalSince(startDate))
}

//https://riptutorial.com/ja/swift/example/14738/インデックスを知らずに配列から要素を削除する
extension Array where Element: Equatable {

    mutating func remove(_ element: Element) {
        _ = firstIndex(of: element).flatMap {
            self.remove(at: $0)
        }
    }
}

func binarySearch(rifgt:Int, key:Int, ary:[String]) -> Int{
    var left = 0
    var right = rifgt

    while abs(right - left) > 1 {
        let mid = left + (right - left) / 2

        if Int(ary[mid])! < key {
            left = mid

        } else {
            right = mid
        }
    }
    return left
}

func arrangement (prev:String, ary:[String], returnVal:[String]) -> [String] {
    var returnVal = returnVal
    if ary.count == 0 {
        returnVal.append(prev)
        return returnVal
    }

    for s in ary {
        let wprev = prev + s
        var wary = ary
        wary.remove(s)
        returnVal = arrangement(prev: wprev, ary: wary, returnVal: returnVal)
    }
    return returnVal
}
// https://algo-logic.info/divisor/
/// 約数の配列を返す
func divisor(_ n:Int) -> [Int] {
    var ret = [Int]()
    var i = 1
    while i * i <= n {
        if n % i == 0 {
            ret.append(i)
            if i * i != n {
                ret.append(n / i)
            }
        }
        i += 1
    }
    return ret.sorted()
}

func main() {
    let n = readInt()
    var aryA = [Int]()
    for _ in 0..<n {
        aryA.append(readInt())
    }
    //＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿ここまで入力
    aryA.sort()
    var left = 0
    var right = 1
    var ans = 0
    while right < n {
        while right < n && aryA[left] == aryA[right] {
            right += 1
        }
        if (right - left) % 2 == 1 {
            ans += 1
        }
        left = right
    }
    print(ans)
}
main()


var paper = [Int : Int]()
for _ in 0..<Int(readLine()!)! {
    let A = Int(readLine()!)!
    if paper[A] == nil {
        paper[A] = 1
    } else {
        paper[A] = nil
    }
}
print(paper.values.reduce(0){$0+$1})


// 🐋💭
import Foundation

func readInts() -> [Int] {
    return readLine()!.split(separator: " ").map { Int($0)! }
}

func readInt() -> Int {
    return Int(readLine()!)!
}

func readDoubles () -> [Double] {
    return readLine()!.split(separator: " ").map { Double($0)! }
}

func readDouble() -> Double{
    return Double(readLine()!)!
}

func readLines() -> [String] {
    return readLine()!.map { String($0) }
}

///最大公約数を返す
func gcd(_ m:Int, _ n:Int) -> Int {
    if n == 0 {return m}
    return gcd(n, m % n)
}

///最大公約数を返す
func gcd(_ array: [Int]) -> Int {
    var value = array[0]
    if array.count > 1 {
        for i in 1..<array.count {
            value = gcd(value, array[i])
        }
    }
    return value
}

///最小公倍数を返す
func lcm(_ m:Int, _ n:Int) -> Int {
    return m * n / gcd(m, n)
}

///素数判定(試し割り)
func isPrime(_ x:Int) -> Bool {
    if x <= 1 || x % 2 == 0 { return false }
    if x == 2 { return true }
    for i in stride(from: 3, to: Int(ceil(sqrt(Double(x)))), by: 2){
        if x % i == 0 {
            return false
        }
    }
    return true
}

//https://program-life.com/669
///実行速度の計測
func exectionTime(action: () -> Void) {
    let startDate = Date()
    action()
    let endDate = Date()
    print(endDate.timeIntervalSince(startDate))
}

//https://riptutorial.com/ja/swift/example/14738/インデックスを知らずに配列から要素を削除する
extension Array where Element: Equatable {

    mutating func remove(_ element: Element) {
        _ = firstIndex(of: element).flatMap {
            self.remove(at: $0)
        }
    }
}

func binarySearch(rifgt:Int, key:Int, ary:[String]) -> Int{
    var left = 0
    var right = rifgt

    while abs(right - left) > 1 {
        let mid = left + (right - left) / 2

        if Int(ary[mid])! < key {
            left = mid

        } else {
            right = mid
        }
    }
    return left
}

func arrangement (prev:String, ary:[String], returnVal:[String]) -> [String] {
    var returnVal = returnVal
    if ary.count == 0 {
        returnVal.append(prev)
        return returnVal
    }

    for s in ary {
        let wprev = prev + s
        var wary = ary
        wary.remove(s)
        returnVal = arrangement(prev: wprev, ary: wary, returnVal: returnVal)
    }
    return returnVal
}
// https://algo-logic.info/divisor/
/// 約数の配列を返す
func divisor(_ n:Int) -> [Int] {
    var ret = [Int]()
    var i = 1
    while i * i <= n {
        if n % i == 0 {
            ret.append(i)
            if i * i != n {
                ret.append(n / i)
            }
        }
        i += 1
    }
    return ret.sorted()
}

func main() {
    let n = readInt()
    var aryA = [Int]()
    for _ in 0..<n {
        aryA.append(readInt())
    }
    //＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿ここまで入力
    aryA.sort()
    var left = 0
    var right = 1
    var ans = 0
    while right < n {
        while right < n && aryA[left] == aryA[right] {
            right += 1
        }
        if (right - left) % 2 == 1 {
            ans += 1
        }
        left = right
        right += 1
    }
    print(ans)
}
main()


// 🐋💭
import Foundation

func readInts() -> [Int] {
    return readLine()!.split(separator: " ").map { Int($0)! }
}

func readInt() -> Int {
    return Int(readLine()!)!
}

func readDoubles () -> [Double] {
    return readLine()!.split(separator: " ").map { Double($0)! }
}

func readDouble() -> Double{
    return Double(readLine()!)!
}

func readLines() -> [String] {
    return readLine()!.map { String($0) }
}

///最大公約数を返す
func gcd(_ m:Int, _ n:Int) -> Int {
    if n == 0 {return m}
    return gcd(n, m % n)
}

///最大公約数を返す
func gcd(_ array: [Int]) -> Int {
    var value = array[0]
    if array.count > 1 {
        for i in 1..<array.count {
            value = gcd(value, array[i])
        }
    }
    return value
}

///最小公倍数を返す
func lcm(_ m:Int, _ n:Int) -> Int {
    return m * n / gcd(m, n)
}

///素数判定(試し割り)
func isPrime(_ x:Int) -> Bool {
    if x <= 1 || x % 2 == 0 { return false }
    if x == 2 { return true }
    for i in stride(from: 3, to: Int(ceil(sqrt(Double(x)))), by: 2){
        if x % i == 0 {
            return false
        }
    }
    return true
}

//https://program-life.com/669
///実行速度の計測
func exectionTime(action: () -> Void) {
    let startDate = Date()
    action()
    let endDate = Date()
    print(endDate.timeIntervalSince(startDate))
}

//https://riptutorial.com/ja/swift/example/14738/インデックスを知らずに配列から要素を削除する
extension Array where Element: Equatable {

    mutating func remove(_ element: Element) {
        _ = firstIndex(of: element).flatMap {
            self.remove(at: $0)
        }
    }
}

func binarySearch(rifgt:Int, key:Int, ary:[String]) -> Int{
    var left = 0
    var right = rifgt

    while abs(right - left) > 1 {
        let mid = left + (right - left) / 2

        if Int(ary[mid])! < key {
            left = mid

        } else {
            right = mid
        }
    }
    return left
}

func arrangement (prev:String, ary:[String], returnVal:[String]) -> [String] {
    var returnVal = returnVal
    if ary.count == 0 {
        returnVal.append(prev)
        return returnVal
    }

    for s in ary {
        let wprev = prev + s
        var wary = ary
        wary.remove(s)
        returnVal = arrangement(prev: wprev, ary: wary, returnVal: returnVal)
    }
    return returnVal
}
// https://algo-logic.info/divisor/
/// 約数の配列を返す
func divisor(_ n:Int) -> [Int] {
    var ret = [Int]()
    var i = 1
    while i * i <= n {
        if n % i == 0 {
            ret.append(i)
            if i * i != n {
                ret.append(n / i)
            }
        }
        i += 1
    }
    return ret.sorted()
}

func main() {
    let n = readInt()
    var aryA = [Int]()
    for _ in 0..<n {
        aryA.append(readInt())
    }
    //＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿＿ここまで入力
    aryA.sort()
    var left = 0
    var right = 1
    var ans = 0
    print(aryA)
    while right < n {
        while right < n && aryA[left] == aryA[right] {
            right += 1
        }
        if (right - left) % 2 == 1 {
            ans += 1
        }
        print(left, right)
        left = right
        right += 1
    }
    print(ans)
}
main()


"use strict";

main(require("fs").readFileSync("/dev/stdin", "utf8"));

function main(input) {
  const [n, ...a] = input.trim().split("\n").map(Number);

  const map = new Map();

  a.forEach(e => {
    if(map.has(e)) {
      map.delete(e);
    } else {
      map.set(e, true);
    }
  });
  const ans = map.size;
  console.log(ans);
}


"use strict";

main(require("fs").readFileSync("/dev/stdin", "utf8"));

function main(input) {
  const a = input.split("\n").map(Number).slice(1);

  const map = new Map();

  a.forEach(e => {
    if(map.has(e)) {
      map.delete(e);
    } else {
      map.set(e, true);
    }
  });

  const ans = map.size;
  console.log(ans);
}


"use strict";

main(require("fs").readFileSync("/dev/stdin", "utf8"));

function main(input) {
  const [n, ...a] = input.split("\n").map(Number);

  const map = new Map();

  a.forEach(e => {
    if(map.has(e)) {
      map.delete(e);
    } else {
      map.set(e, true);
    }
  });
  const ans = map.size;
  console.log(ans);
}


import sys, math

max_int = 1000000001  # 10^9+1
min_int = -max_int

n = int(sys.stdin.readline())
positive_counter = {}
negative_counter = {}
to_add = 0
a_zero_counter = b_zero_counter = 0
for _n in range(n):
    s = sys.stdin.readline()
    a, b = map(int, s.split())

    if a == 0 and b == 0:
        to_add += 1
        continue
    elif a == 0:
        a_zero_counter += 1
        continue
    elif b == 0:
        b_zero_counter += 1
        continue
    elif (a > 0) == (b > 0):
        a = abs(a)
        b = abs(b)
        gcd = math.gcd(a, b)
        definer = (a // gcd, b // gcd)

        if definer not in positive_counter:
            positive_counter[definer] = 1
        else:
            positive_counter[definer] += 1
    else:
        a = abs(a)
        b = abs(b)
        gcd = math.gcd(a, b)
        definer = (b // gcd, a // gcd)

        if definer not in negative_counter:
            negative_counter[definer] = 1
        else:
            negative_counter[definer] += 1

prod = 2 ** a_zero_counter + 2 ** b_zero_counter - 1
#print(positive_counter, negative_counter)
for k in positive_counter:
    if k in negative_counter:
        prod *= 2 ** positive_counter[k] + 2 ** negative_counter[k] - 1
        negative_counter.pop(k)
    else:
        prod *= 2 ** positive_counter[k]
    prod %= 1000000007

for k in negative_counter:
    prod *= 2 ** negative_counter[k]
    prod %= 1000000007

prod += to_add - 1

print(prod % 1000000007)


from collections import Counter
from math import gcd
import sys
input = sys.stdin.readline
n = int(input())
same, diff, zero_a, zero_b, zero_zero = 0, 0, 0, 0, 0
same_cnt = Counter()
diff_cnt = Counter()

for i in range(n):
    a, b = map(int, input().split())
    if a == b == 0:
        zero_zero += 1
    elif a == 0:
        zero_a += 1
    elif b == 0:
        zero_b += 1
    elif a * b > 0:
        x = gcd(a, b)
        a, b = a // x, b // x
        same += 1
        same_cnt[(abs(a), abs(b))] += 1
    else:
        x = gcd(a, b)
        a, b = a // x, b // x
        diff += 1
        diff_cnt[(abs(a), -abs(b))] += 1

ans = 1
mod = 10 ** 9 + 7

for (a, b) in diff_cnt.keys():
    s = same_cnt[(-b, a)]
    d = diff_cnt[(a, b)]
    if s > 0:
        same -= s
        diff -= d
        ans = ans * (pow(2, s, mod) + pow(2, d, mod) - 1) % mod

ans = ans * pow(2, same + diff, mod) % mod

ans = ans * (pow(2, zero_a, mod) + pow(2, zero_b, mod) - 1) - 1
print((ans + zero_zero) % mod)


mod = 1000000007
eps = 10**-9

def main():
    import sys
    from math import gcd
    input = sys.stdin.buffer.readline

    N = int(input())
    cnt = {}
    zz = 0
    for _ in range(N):
        a, b = map(int, input().split())
        if a == b == 0:
            zz += 1
            continue
        if a < 0:
            a = -a
            b = -b
        elif a == 0:
            if b < 0:
                b = -b
        g = gcd(abs(a), abs(b))
        x = (a // g, b // g)
        if x in cnt:
            cnt[x] += 1
        else:
            cnt[x] = 1

    ans = 1
    num = 0
    for x in cnt:
        if cnt[x] == 0:
            continue
        a, b = x
        if b > 0:
            y = (b, -a)
        else:
            y = (-b, a)
        if y in cnt:
            nx = cnt[x]
            ny = cnt[y]
            cnt[y] = 0
            ans = (ans * ((pow(2, nx, mod) - 1) + (pow(2, ny, mod) - 1) + 1)%mod)%mod
        else:
            num += cnt[x]
    ans = (ans * pow(2, num, mod))%mod
    ans = (ans - 1)%mod
    print((ans + zz)%mod)

if __name__ == '__main__':
    main()


#include<bits/stdc++.h>
using namespace std;
using ll=long long int;
ll mod=1e9+7;
ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }
int main()
{

    vector<ll> powe(1e6,0);
    powe[0]=1;
    for(int i=1;i<1e6;i++)
    {
        powe[i]=(powe[i-1]*2)%mod;
    }
    int n;
    cin>>n;
    vector<pair<ll,ll> > a(n);
    map<pair<ll,ll>,int> m;
    cout<<gcd(0,0)<<endl;
    int ct1=0;
    int ct2=0;
    int ct3=0;
    for(int i=0;i<n;i++)
    {
        cin>>a[i].first>>a[i].second;
        if(a[i].first==0&&a[i].second==0)
        {
            ct1++;
            continue;
        }
        else if(a[i].first==0||a[i].second==0)
        {
            ct2++;
            continue;
        }
        ll p=gcd(a[i].first,a[i].second);
        a[i].first/=p;
        a[i].second/=p;
         if(a[i].first>0&&a[i].second<0)
        {
            a[i].first=-a[i].first;
            a[i].second=-a[i].second;
        }
        if(a[i].second<0&&a[i].first<0)
        {
            a[i].second=-a[i].second;
            a[i].first=-a[i].first;
        }
        m[a[i]]++;
    }
    ll ans=1;
    for(auto it:m)
    {
        if(it.first.first<0)
        {
            if(m.find({it.first.second,-it.first.first})==m.end())
            ans=(ans*(it.second+1))%mod;
            continue;
        }
        ll p=it.second;
        ll q=0;
        if(m.find({-it.first.second,it.first.first})!=m.end());
        {
            q=m[{-it.first.second,it.first.first}];
        }

        ll res=(powe[p]-1+powe[q]-1+mod)%mod;
        ans=(ans*(res+1))%mod;
    }
    ans=(ans*(ct2+1))%mod;
    if(ct1>0)
    ans++;
    cout<<(ans-1+mod)%mod;

}


from collections import Counter
import sys
import math
from functools import cmp_to_key

sys.setrecursionlimit(10 ** 6)

mod = 1000000007
inf = int(1e18)

def main():
    n = int(input())
    p = [tuple(map(int, input().split())) for i in range(n)]
    a = {}
    b = {}
    c = 0
    for x, y in p:
        if x == y == 0:
            c += 1
            continue
        if y == 0:
            if x > 0:
                a[(0, 1)] = a.get((0, 1), 0) + 1
            else:
                b[(0, 1)] = b.get((0, 1), 0) + 1

        g = math.gcd(x, y)
        if x * y > 0:
            x = abs(x)
            y = abs(y)
            a[(x//g, y//g)] = a.get((x//g, y//g), 0) + 1
        else:
            x = abs(x)
            y = abs(y)
            b[(y//g, x//g)] = b.get((y//g, x//g), 0) + 1

    ans = 1
    s = 0
    for k, v in a.items():
        if k in b:
            s += v + b[k]
            ans *= pow(2, v) + pow(2, b[k]) - 1
            ans %= mod
    ans *= pow(2, n - s - c, mod)
    print((ans - 1 + c) % mod)

main()


from collections import Counter
import sys
import math
from functools import cmp_to_key

sys.setrecursionlimit(10 ** 6)

mod = 1000000007
inf = int(1e18)

def main():
    n = int(input())
    p = [tuple(map(int, input().split())) for i in range(n)]
    a = {}
    b = {}
    c = 0
    for x, y in p:
        if x == y == 0:
            c += 1
            continue
        if y == 0:
            if x > 0:
                a[(0, 1)] = a.get((0, 1), 0) + 1
            else:
                b[(0, 1)] = b.get((0, 1), 0) + 1

        g = math.gcd(x, y)
        if x * y > 0:
            x = abs(x)
            y = abs(y)
            a[(x//g, y//g)] = a.get((x//g, y//g), 0) + 1
        else:
            x = abs(x)
            y = abs(y)
            b[(y//g, x//g)] = b.get((y//g, x//g), 0) + 1

    ans = 1
    s = 0
    for k, v in a.items():
        if k in b:
            s += v + b[k]
            ans *= pow(2, v) + pow(2, b[k]) - 1
            ans %= mod
    ans *= pow(2, n - s - c, mod)
    print((ans - 1) % mod)

main()


import java.io.*;
import java.util.*;

class Direction {
  final long a, b;

  Direction(long a, long b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public boolean equals(Object object) {
    Direction direction = (Direction)object;
    return this.a == direction.a && this.b == direction.b;
  }

  @Override
  public int hashCode() {
    return Long.hashCode(a + 13 * b);
  }
}

public class Main {
  private static long gcd(long a, long b) {
    if (a == 0 || b == 0) {
      return a + b;
    }
    return a % b == 0 ? b : gcd(b, a % b);
  }

  private static Direction norm(long a, long b) {
    if (a == 0 && b == 0) {
      return new Direction(0, 0);
    }
    long g = gcd(Math.abs(a), Math.abs(b));
    a /= g;
    b /= g;
    if (b == 0) {
      return new Direction(1L, 0L);
    }
    if (a == 0) {
      return new Direction(0L, 1L);
    }
    if (b < 0) {
      a *= -1;
      b *= -1;
    }
    return new Direction(a, b);
  }

  private static long solve(int n, long[][] abs) {
    Map<Direction, Integer> map = new HashMap<>();
    for (long[] ab : abs) {
      long a = ab[0];
      long b = ab[1];
      Direction d = norm(a, b);
      map.put(d, map.getOrDefault(d, 0) + 1);
    }
    ModCalculator mc = new ModCalculator(1000000007);
    long answer = 0;
    long pattern = 1;
    for (Map.Entry<Direction, Integer> entry : map.entrySet()) {
      Direction key = entry.getKey();
      int value = entry.getValue();
      if (key.a == 0 && key.b == 0) {
        answer = mc.add(answer, value);
      } else {
        long otherA = -1 * key.b;
        long otherB = key.a;
        if (otherB < 0) {
          otherA *= -1;
          otherB *= -1;
        }
        if (otherA < 0 && otherB == 0) {
          otherA *= -1;
        }
        Direction otherKey = new Direction(otherA, otherB);
        Integer otherValue = map.get(otherKey);
        if (otherValue != null && key.a <= 0) {
          continue;
        }
        int otherValueInt = otherValue != null ? otherValue : 0;
        long v1 = mc.pow(2, value);
        long v2 = mc.pow(2, otherValueInt);
        long v3 = mc.sub(mc.add(v1, v2), 1);
        pattern = mc.mul(pattern, v3);
      }
    }
    pattern = mc.sub(pattern, 1);
    answer = mc.add(answer, pattern);
    return answer;
  }

  private static void execute(ContestReader reader, ContestWriter out) {
    int n = reader.nextInt();
    long[][] abs = reader.nextLong(n, 2);
    out.println(solve(n, abs));
  }

  public static void main(String[] args) {
    ContestReader reader = new ContestReader(System.in);
    ContestWriter out = new ContestWriter(System.out);
    execute(reader, out);
    out.flush();
  }
}

class ModCalculator {
  private final long mod;
  private final ModCombinationCache modCombinationCache;

  ModCalculator(long mod) {
    this.mod = mod;
    this.modCombinationCache = new ModCombinationCache();
  }

  public long norm(long v) {
    return v % mod;
  }

  public long add(long a, long b) {
    return norm(a + b);
  }

  public long sub(long a, long b) {
    return norm(a - b + mod);
  }

  public long mul(long a, long b) {
    return norm(a * b);
  }

  public long pow(long a, long b) {
    if (b == 0) {
      return 1;
    }
    long v = pow(mul(a, a), b / 2);
    if (b % 2 == 1) {
      return mul(v, a);
    } else {
      return v;
    }
  }

  public long inverse(long a) {
    return pow(a, mod - 2);
  }

  public long div(long a, long b) {
    return mul(a, inverse(b));
  }

  // Verify ARC 042 D
  // https://atcoder.jp/contests/arc042/tasks/arc042_d
  // a^x mod p === b
  // return -1 there is no such positive x
  public long log(long a, long b) {
    Map<Long, Long> map = new HashMap<>();
    long powA = 1;
    long rootP = 0;
    while (true) {
      if (powA == b && rootP != 0) {
        return rootP;
      }
      if (map.containsKey(powA)) {
        return -1;
      }
      map.put(powA, rootP);
      powA = mul(powA, a);
      rootP++;
      if (rootP * rootP > mod) {
        break;
      }
    }
    long inversePowA = inverse(powA);
    for (int i = 1; i <= rootP; i++) {
      b = mul(b, inversePowA);
      Long value = map.get(b);
      if (value != null && value + rootP * i > 0) {
        return value + rootP * i;
      }
    }
    return -1;
  }

  public long getF(int n) {
    return modCombinationCache.getF(n);
  }

  public long getP(int n, int r) {
    return modCombinationCache.getP(n, r);
  }

  public long getC(int n, int k) {
    return modCombinationCache.getC(n, k);
  }

  class ModCombinationCache {
    private final List<Long> factorialCache;
    private final List<Long> factorialInverseCache;

    public ModCombinationCache() {
      factorialCache = new ArrayList<>();
      factorialCache.add(1L);
      factorialInverseCache = new ArrayList<>();
      factorialInverseCache.add(1L);
    }

    private void resize(int n) {
      for (int i = factorialCache.size() - 1; i < n; i++) {
        long v = mul(factorialCache.get(i), i + 1);
        factorialCache.add(v);
        factorialInverseCache.add(inverse(v));
      }
    }

    long getF(int n) {
      resize(n);
      return factorialCache.get(n);
    }

    long getP(int n, int r) {
      resize(n);
      return mul(factorialCache.get(n), factorialInverseCache.get(n - r));
    }

    long getC(int n, int k) {
      resize(n);
      return mul(factorialCache.get(n), mul(factorialInverseCache.get(k), factorialInverseCache.get(n-k)));
    }
  }
}

class ContestWriter extends PrintWriter {
  ContestWriter(PrintStream printeStream) {
    super(printeStream);
  }

  public void printList(List<? extends Object> list) {
    for (Object object : list) {
      println(object);
    }
  }

  public void printListOneLine(List<? extends Object> list) {
    List<String> stringList = new ArrayList<>();
    for (Object object : list) {
      stringList.add(object.toString());
    }
    println(String.join(" ", stringList));
  }
}

class ContestReader {
  private static final int BUFFER_SIZE = 1024;

  private final InputStream stream;
  private final byte[] buffer;
  private int pointer;
  private int bufferLength;

  ContestReader(InputStream stream) {
    this.stream = stream;
    this.buffer = new byte[BUFFER_SIZE];
    this.pointer = 0;
    this.bufferLength = 0;
  }

  private boolean hasNextByte() {
    if (pointer < bufferLength) {
      return true;
    }

    pointer = 0;
    try {
      bufferLength = stream.read(buffer);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    return bufferLength > 0;
  }

  private int readByte() {
    if (hasNextByte()) {
      return buffer[pointer++];
    } else {
      return -1;
    }
  }

  private static boolean isPrintableChar(int c) {
    return 33 <= c && c <= 126;
  }

  public boolean hasNext() {
    while (hasNextByte() && !isPrintableChar(buffer[pointer])) {
      pointer++;
    }
    return hasNextByte();
  }

  public String next() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }
    StringBuilder sb = new StringBuilder();
    while(true) {
      int b = readByte();
      if (!isPrintableChar(b)) {
        break;
      }
      sb.appendCodePoint(b);
    }
    return sb.toString();
  }

  public String nextLine() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }
    StringBuilder sb = new StringBuilder();
    while(true) {
      int b = readByte();
      if (!isPrintableChar(b) && b != 0x20) {
        break;
      }
      sb.appendCodePoint(b);
    }
    return sb.toString();
  }

  public char nextChar() {
    return next().charAt(0);
  }

  public int nextInt() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }

    int n = 0;
    boolean minus = false;

    {
      int b = readByte();
      if (b == '-') {
        minus = true;
      } else if ('0' <= b && b <= '9') {
        n = b - '0';
      } else {
        throw new NumberFormatException();
      }
    }

    while(true){
      int b = readByte();
      if ('0' <= b && b <= '9') {
        n *= 10;
        n += b - '0';
      } else if (b == -1 || !isPrintableChar(b)) {
        return minus ? -n : n;
      } else {
        throw new NumberFormatException();
      }
    }
  }

  public long nextLong() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }

    long n = 0;
    boolean minus = false;

    {
      int b = readByte();
      if (b == '-') {
        minus = true;
      } else if ('0' <= b && b <= '9') {
        n = b - '0';
      } else {
        throw new NumberFormatException();
      }
    }

    while(true){
      int b = readByte();
      if ('0' <= b && b <= '9') {
        n *= 10;
        n += b - '0';
      } else if (b == -1 || !isPrintableChar(b)) {
        return minus ? -n : n;
      } else {
        throw new NumberFormatException();
      }
    }
  }

  public double nextDouble() {
    return Double.parseDouble(next());
  }

  public String[] next(int n) {
    String[] array = new String[n];
    for (int i = 0; i < n; i++) {
      array[i] = next();
    }
    return array;
  }

  public String[] nextLine(int n) {
    String[] array = new String[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextLine();
    }
    return array;
  }

  public char[] nextChar(int n) {
    char[] array = new char[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextChar();
    }
    return array;
  }

  public int[] nextInt(int n) {
    int[] array = new int[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextInt();
    }
    return array;
  }

  public long[] nextLong(int n) {
    long[] array = new long[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextLong();
    }
    return array;
  }

  public double[] nextDouble(int n) {
    double[] array = new double[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextDouble();
    }
    return array;
  }

  public char[] nextCharArray() {
    return next().toCharArray();
  }

  public String[][] next(int n, int m) {
    String[][] matrix = new String[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = next();
      }
    }
    return matrix;
  }

  public int[][] nextInt(int n, int m) {
    int[][] matrix = new int[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextInt();
      }
    }
    return matrix;
  }

  public char[][] nextChar(int n, int m) {
    char[][] matrix = new char[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextChar();
      }
    }
    return matrix;
  }

  public long[][] nextLong(int n, int m) {
    long[][] matrix = new long[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextLong();
      }
    }
    return matrix;
  }

  public double[][] nextDouble(int n, int m) {
    double[][] matrix = new double[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextDouble();
      }
    }
    return matrix;
  }

  public char[][] nextCharArray(int n) {
    char[][] matrix = new char[n][];
    for (int i = 0; i < n; i++) {
      matrix[i] = next().toCharArray();
    }
    return matrix;
  }
}


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;

public class Main {
    private static final Mod mod = new Mod(1_000_000_007);

    void solve(MyScanner in, MyWriter out) {
        int n = in.nextInt();
        long[][] ab = in.nextVerticalLongArrays(2, n);
        long[] a = ab[0];
        long[] b = ab[1];

        var plus = new HashMap<Rate, Long>();
        var minus = new HashMap<Rate, Long>();
        long zeros = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] == 0 && b[i] == 0) {
                zeros++;
            } else if (a[i] == 0) {
                plus.merge(new Rate(0, 1), 1L, Long::sum);
            } else if (b[i] == 0) {
                minus.merge(new Rate(0, 1), 1L, Long::sum);
            } else {
                long absA = Math.abs(a[i]);
                long absB = Math.abs(b[i]);
                long gcd = gcd(absA, absB);
                absA /= gcd;
                absB /= gcd;
                if ((a[i] >= 0 && b[i] >= 0) || (a[i] < 0 && b[i] < 0)) {
                    plus.merge(new Rate(absA, absB), 1L, Long::sum);
                } else {
                    minus.merge(new Rate(absB, absA), 1L, Long::sum);
                }
            }
        }
        Mod.Value answer = mod.value(1);
        for (Map.Entry<Rate, Long> e : plus.entrySet()) {
            if (minus.containsKey(e.getKey())) {
                Mod.Value v = mod.value(0);
                v.add(mod.pow(2, e.getValue()));
                v.add(mod.pow(2, minus.get(e.getKey())));
                v.subtract(1);
                answer.multiply(v);
                minus.remove(e.getKey());
            } else {
                answer.multiply(mod.pow(2, e.getValue()));
            }
        }
        for (Map.Entry<Rate, Long> e : minus.entrySet()) {
            answer.multiply(mod.pow(2, e.getValue()));
        }
        answer.add(zeros);
        answer.subtract(1);
        out.println(answer);
    }
    private static long gcd(long a, long b) {
        if (a < 0 || b < 0) {
            throw new IllegalArgumentException();
        }
        return b == 0 ? a : gcd(b, a % b);
    }
    static class Rate {
        final long a;
        final long b;
        Rate(long a, long b) {
            this.a = a;
            this.b = b;
        }
        @Override
        public int hashCode() {
            return Objects.hash(a, b);
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (!(obj instanceof Rate))
                return false;
            Rate other = (Rate)obj;
            return a == other.a && b == other.b;
        }
    }
    static final class Mod {
        final int divisor;

        Mod(int divisor) {
            if (divisor <= 0) {
                throw new IllegalArgumentException();
            }
            this.divisor = divisor;
        }
        long mod(long dividend) {
            return Math.floorMod(dividend, divisor);
        }
        long add(long a, long b) {
            return mod(mod(a) + mod(b));
        }
        long subtract(long a, long b) {
            return mod(mod(a) - mod(b));
        }
        long multiply(long a, long b) {
            return mod(mod(a) * mod(b));
        }
        long pow(long a, long b) {
            if (b < 0) {
                throw new IllegalArgumentException();
            }
            long r = 1;
            while (b > 0) {
                if ((b & 1) == 1) {
                    r = multiply(r, a);
                }
                a = multiply(a, a);
                b >>= 1;
            }
            return r;
        }
        long inverse(long a) {
            // assert isCoprime(a, divisor);
            if (a == 0) {
                throw new IllegalArgumentException();
            }
            a = mod(a);
            long b = divisor;
            long u = 1;
            long v = 0;
            while (b != 0) {
                long q = a / b;
                a -= q * b;
                u -= q * v;

                long tmp = a;
                a = b;
                b = tmp;

                tmp = u;
                u = v;
                v = tmp;
            }
            return mod(u);
        }
        long div(long a, long b) {
            return multiply(a, inverse(b));
        }
        Value value(long initialValue) {
            return new Value(this, initialValue);
        }

        static final class Value {
            private final Mod mod;
            private long value;

            private Value(Mod mod, long initialValue) {
                this.mod = mod;
                value = mod.mod(initialValue);
            }
            long longValue() {
                return value;
            }
            Value add(long a) {
                value = mod.add(value, a);
                return this;
            }
            Value add(Value a) {
                return add(a.value);
            }
            Value subtract(long a) {
                value = mod.subtract(value, a);
                return this;
            }
            Value subtract(Value a) {
                return subtract(a.value);
            }
            Value multiply(long a) {
                value = mod.multiply(value, a);
                return this;
            }
            Value multiply(Value a) {
                return multiply(a.value);
            }
            Value pow(long a) {
                value = mod.pow(value, a);
                return this;
            }
            Value inverse() {
                value = mod.inverse(value);
                return this;
            }
            Value div(long a) {
                value = mod.div(value, a);
                return this;
            }
            Value div(Value a) {
                return div(a.value);
            }
            @Override
            public String toString() {
                return Long.toString(value);
            }
        }
    }
    public static void main(String[] args) {
        MyWriter w = new MyWriter(System.out);
        new Main().solve(new MyScanner(System.in), w);
        w.flush();
    }
    static final class MyScanner {
        static final int BUFFER_SIZE = 8192;
        private final InputStream in;
        private final byte[] buffer = new byte[BUFFER_SIZE];
        private int point;
        private int readLength;

        MyScanner(InputStream in) {
            this.in = in;
        }
        private int readByte() {
            if (point < readLength) {
                int result = buffer[point];
                point += 1;
                return result;
            }
            try {
                readLength = in.read(buffer);
            } catch (IOException e) {
                throw new AssertionError(null, e);
            }
            if (readLength == -1) {
                return -1;
            }
            point = 1;
            return buffer[0];
        }
        private static boolean isVisibleChar(int c) {
            return 33 <= c && c <= 126;
        }
        char nextChar() {
            int c = readByte();
            while (!(c == -1 || isVisibleChar(c))) {
                c = readByte();
            }
            if (c == -1) {
                throw new NoSuchElementException();
            }
            return (char)c;
        }
        String next() {
            return next(16);
        }
        String next(int n) {
            int c = readByte();
            while (!(c == -1 || isVisibleChar(c))) {
                c = readByte();
            }
            if (c == -1) {
                throw new NoSuchElementException();
            }
            StringBuilder b = new StringBuilder(n);
            do {
                b.append((char)c);
                c = readByte();
            } while (c != -1 && isVisibleChar(c));
            return b.toString();
        }
        long nextLong() {
            int c = readByte();
            while (!(c == -1 || isVisibleChar(c))) {
                c = readByte();
            }
            if (c == -1) {
                throw new NoSuchElementException();
            }
            boolean minus = false;
            long limit = -Long.MAX_VALUE;
            if (c == '-') {
                minus = true;
                limit = Long.MIN_VALUE;
                c = readByte();
            }
            long n = 0L;
            do {
                if (c < '0' || c > '9') {
                    throw new InputMismatchException();
                }
                if (n < limit / 10L) {
                    throw new InputMismatchException();
                }
                n *= 10L;
                int digit = c - '0';
                if (n < limit + digit) {
                    throw new InputMismatchException();
                }
                n -= digit;
                c = readByte();
            } while (c != -1 && isVisibleChar(c));
            return minus ? n : -n;
        }
        int nextInt() {
            long n = nextLong();
            if (n < Integer.MIN_VALUE || n > Integer.MAX_VALUE) {
                throw new InputMismatchException();
            }
            return (int)n;
        }
        double nextDouble() {
            return Double.parseDouble(next());
        }
        int[] nextIntArray(int n) {
            int[] result = new int[n];
            for (int i = 0; i < n; i++) {
                result[i] = nextInt();
            }
            return result;
        }
        long[] nextLongArray(int n) {
            long[] result = new long[n];
            for (int i = 0; i < n; i++) {
                result[i] = nextLong();
            }
            return result;
        }
        char[] nextCharArray(int n) {
            char[] result = new char[n];
            for (int i = 0; i < n; i++) {
                result[i] = nextChar();
            }
            return result;
        }
        char[][] next2dCharArray(int n, int m) {
            char[][] result = new char[n][m];
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    result[i][j] = nextChar();
                }
            }
            return result;
        }
        int[][] nextVerticalIntArrays(int arrayCount, int arrayLength) {
            int[][] result = new int[arrayCount][arrayLength];
            for (int i = 0; i < arrayLength; i++) {
                for (int j = 0; j < arrayCount; j++) {
                    result[j][i] = nextInt();
                }
            }
            return result;
        }
        long[][] nextVerticalLongArrays(int arrayCount, int arrayLength) {
            long[][] result = new long[arrayCount][arrayLength];
            for (int i = 0; i < arrayLength; i++) {
                for (int j = 0; j < arrayCount; j++) {
                    result[j][i] = nextLong();
                }
            }
            return result;
        }
        char[][] nextVerticalCharArrays(int arrayCount, int arrayLength) {
            char[][] result = new char[arrayCount][arrayLength];
            for (int i = 0; i < arrayLength; i++) {
                for (int j = 0; j < arrayCount; j++) {
                    result[j][i] = nextChar();
                }
            }
            return result;
        }
    }
    static final class MyWriter extends PrintWriter {
        MyWriter(OutputStream out) {
            super(out);
        }
        void println(int[] x) {
            println(x, " ");
        }
        void println(int[] x, String delimiter) {
            if (x.length > 0) {
                print(x[0]);
                for (int i = 1; i < x.length; i++) {
                    print(delimiter);
                    print(x[i]);
                }
            }
            println();
        }
        void println(long[] x) {
            println(x, " ");
        }
        void println(long[] x, String delimiter) {
            if (x.length > 0) {
                print(x[0]);
                for (int i = 1; i < x.length; i++) {
                    print(delimiter);
                    print(x[i]);
                }
            }
            println();
        }
        void println(Iterable<?> iterable) {
            println(iterable, " ");
        }
        void println(Iterable<?> iterable, String delimiter) {
            Iterator<?> i = iterable.iterator();
            if (i.hasNext()) {
                print(i.next());
                while (i.hasNext()) {
                    print(delimiter);
                    print(i.next());
                }
            }
            println();
        }
        void printLines(int[] x) {
            println(x, System.lineSeparator());
        }
        void printLines(long[] x) {
            println(x, System.lineSeparator());
        }
        void printLines(Iterable<?> iterable) {
            println(iterable, System.lineSeparator());
        }
    }
}


/**
 * @author derrick20
 */
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        FastScanner sc = new FastScanner();
        PrintWriter out = new PrintWriter(System.out);

        int N = sc.nextInt();

        freq = new HashMap<>();
        int zeroes = 0;
        for (int i = 0; i < N; i++) {
            long num = sc.nextLong();
            long denom = sc.nextLong();
            if (num == 0 && denom == 0) {
                zeroes++;
                continue;
            }
            else if (num == 0) {
                int[] val = freq.computeIfAbsent("0_1", key -> new int[2]);
                val[0]++;
            }
            else if (denom == 0) {
                int[] val = freq.computeIfAbsent("0_1", key -> new int[2]);
                val[1]++;
            }
            else {
                long g = gcd(Math.abs(num), Math.abs(denom));
                if (g != 0) {
                    num /= g;
                    denom /= g;
                }
                if (denom < 0) {
                    num *= -1;
                    denom *= -1;
                }
                // let's hash based on the positive version. If it's negative,
                // add one to the negative bucket.
                Fish f = new Fish(num, denom);
                int sign = (Math.signum(num) * Math.abs(denom) > 0 ? 0 : 1);
                String hash = f.toString();
                int[] val = freq.computeIfAbsent(hash, key -> new int[2]);
                val[sign]++;
            }
        }

        long[] pow = new long[(int) 2e5 + 1];
        pow[0] = 1;
        for (int i = 1; i < pow.length; i++) {
            pow[i] = (2 * pow[i - 1]) % mod;
        }

        long ans = 1;
        for (var e : freq.entrySet()) {
            String key = e.getKey();
            int[] buckets = e.getValue();

//            System.out.println(key + ": " + Arrays.toString(buckets));
            long a = pow[buckets[0]];
            long b = pow[buckets[1]];
            ans += (ans * (a + b - 1 - 1 + mod) % mod) % mod;
            ans %= mod;
        }

        out.println((ans - 1 + mod + zeroes) % mod);
        out.close();
    }

    static long mod = 1000000007;

    static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    static HashMap<String, int[]> freq;

    static class Fish {
        long num, denom;
        public Fish(long n, long d) {
            num = n; denom = d;
        }

        public String toString() {
            // return the positive version
            long n1 = Math.abs(num);
            long d1 = Math.abs(denom);
            long sign = (long) (Math.signum(num) * Math.signum(denom));
            if (sign > 0) {
                return n1 + "_" + d1;
            }
            else {
                return d1 + "_" + n1;
            }
        }
    }

    static class FastScanner {
        private int BS = 1<<16;
        private char NC = (char)0;
        private byte[] buf = new byte[BS];
        private int bId = 0, size = 0;
        private char c = NC;
        private double cnt = 1;
        private BufferedInputStream in;

        public FastScanner() {
            in = new BufferedInputStream(System.in, BS);
        }

        public FastScanner(String s) {
            try {
                in = new BufferedInputStream(new FileInputStream(new File(s)), BS);
            }
            catch (Exception e) {
                in = new BufferedInputStream(System.in, BS);
            }
        }

        private char getChar(){
            while(bId==size) {
                try {
                    size = in.read(buf);
                }catch(Exception e) {
                    return NC;
                }
                if(size==-1)return NC;
                bId=0;
            }
            return (char)buf[bId++];
        }

        public int nextInt() {
            return (int)nextLong();
        }

        public int[] nextInts(int N) {
            int[] res = new int[N];
            for (int i = 0; i < N; i++) {
                res[i] = (int) nextLong();
            }
            return res;
        }

        public long[] nextLongs(int N) {
            long[] res = new long[N];
            for (int i = 0; i < N; i++) {
                res[i] = nextLong();
            }
            return res;
        }

        public long nextLong() {
            cnt=1;
            boolean neg = false;
            if(c==NC)c=getChar();
            for(;(c<'0' || c>'9'); c = getChar()) {
                if(c=='-')neg=true;
            }
            long res = 0;
            for(; c>='0' && c <='9'; c=getChar()) {
                res = (res<<3)+(res<<1)+c-'0';
                cnt*=10;
            }
            return neg?-res:res;
        }

        public double nextDouble() {
            double cur = nextLong();
            return c!='.' ? cur:cur+nextLong()/cnt;
        }

        public String next() {
            StringBuilder res = new StringBuilder();
            while(c<=32)c=getChar();
            while(c>32) {
                res.append(c);
                c=getChar();
            }
            return res.toString();
        }

        public String nextLine() {
            StringBuilder res = new StringBuilder();
            while(c<=32)c=getChar();
            while(c!='\n') {
                res.append(c);
                c=getChar();
            }
            return res.toString();
        }

        public boolean hasNext() {
            if(c>32)return true;
            while(true) {
                c=getChar();
                if(c==NC)return false;
                else if(c>32)return true;
            }
        }
    }
}


import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static final long MOD = 1000000007L;

	class Node {
		long a, b;

		public Node(long a, long b) {
			this.a = a;
			this.b = b;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (o == null || getClass() != o.getClass()) return false;
			Node node = (Node) o;
			return a == node.a &&
					b == node.b;
		}

		@Override
		public int hashCode() {
			return Objects.hash(a, b);
		}
	}

	Main() {
		FastScanner scanner = new FastScanner();
		int N = scanner.nextInt();

		HashMap<Node, Integer> P = new HashMap<>();
		HashMap<Node, Integer> M = new HashMap<>();
		int aZero = 0, bZero = 0;

		for (int i = 0; i < N; i++) {
			long a = scanner.nextLong();
			long b = scanner.nextLong();

			if ( a == 0 && b == 0) {
				continue;
			} else if ( a == 0 ) {
				aZero++;
				continue;
			} else if ( b == 0 ) {
				bZero++;
				continue;
			}

			if ( (a > 0 && b > 0) || (a < 0 && b < 0) ) {
				a = Math.abs(a);
				b = Math.abs(b);
				long gcd = gcd(a, b);
				Node n = new Node(a/gcd, b/gcd);
				P.put( n, P.getOrDefault(n, 0)+1);
			} else {
				a = Math.abs(a);
				b = Math.abs(b);
				long gcd = gcd(a, b);
				Node n = new Node(b/gcd, a/gcd);
				M.put( n, M.getOrDefault(n, 0)+1);
			}
		}

		long Ans = 1L;
		for ( Node key : P.keySet() ) {
			if ( M.containsKey(key) ) {
				long c = ( modPow(P.get(key)) + modPow(M.get(key)) ) % MOD - 1;
				if (c < 0) c += MOD;
				Ans = Ans*c%MOD;
				M.remove(key);
			} else {
				Ans = Ans*modPow(P.get(key))%MOD;
			}
		}

		for ( Node key : M.keySet() ) {
			Ans = Ans*modPow(M.get(key))%MOD;
		}

		long c = ( modPow(aZero) + modPow(bZero) ) % MOD - 1;
		if (c < 0) c += MOD;
		Ans = Ans*c%MOD;

		//全てを選ばないことは禁止されている
		Ans -= 1;
		if (Ans < 0) Ans += MOD;

		System.out.println(Ans);

	}

	long gcd(long a, long b) {
		if ( a % b == 0) return b;
		return gcd(b,a%b);
	}

	public static void main(String[] args) {
		new Main();
	}

	long modPow(long n) {
		long res = 1;
		long a = 2L;
		while (n > 0) {
			if ( (n & 1) == 1) res = res * a % MOD;
			a = a * a % MOD;
			n >>= 1;
		}
		return res;
	}
}

class FastScanner {
	private final InputStream in = System.in;
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;

	private boolean hasNextByte() {
		if (ptr < buflen) {
			return true;
		} else {
			ptr = 0;
			try {
				buflen = in.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (buflen <= 0) {
				return false;
			}
		}
		return true;
	}

	private int readByte() {
		if (hasNextByte()) return buffer[ptr++];
		else return -1;
	}

	private static boolean isPrintableChar(int c) {
		return 33 <= c && c <= 126;
	}

	public boolean hasNext() {
		while (hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++;
		return hasNextByte();
	}

	public String next() {
		if (!hasNext()) throw new NoSuchElementException();
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (isPrintableChar(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	public long nextLong() {
		if (!hasNext()) throw new NoSuchElementException();
		long n = 0;
		boolean minus = false;
		int b = readByte();
		if (b == '-') {
			minus = true;
			b = readByte();
		}
		if (b < '0' || '9' < b) {
			throw new NumberFormatException();
		}
		while (true) {
			if ('0' <= b && b <= '9') {
				n *= 10;
				n += b - '0';
			} else if (b == -1 || !isPrintableChar(b)) {
				return minus ? -n : n;
			} else {
				throw new NumberFormatException();
			}
			b = readByte();
		}
	}

	public int nextInt() {
		long nl = nextLong();
		if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException();
		return (int) nl;
	}

	public double nextDouble() {
		return Double.parseDouble(next());
	}
}


import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class Main {
	static final long MOD = 1000000007L;

	class Node {
		long a, b;

		public Node(long a, long b) {
			this.a = a;
			this.b = b;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o) return true;
			if (o == null || getClass() != o.getClass()) return false;
			Node node = (Node) o;
			return a == node.a &&
					b == node.b;
		}

		@Override
		public int hashCode() {
			return Objects.hash(a, b);
		}
	}

	Main() {
		FastScanner scanner = new FastScanner();
		int N = scanner.nextInt();

		HashMap<Node, Integer> P = new HashMap<>();
		HashMap<Node, Integer> M = new HashMap<>();
		int aZero = 0, bZero = 0;

		for (int i = 0; i < N; i++) {
			long a = scanner.nextLong();
			long b = scanner.nextLong();

			if ( a == 0 && b == 0) {
				continue;
			} else if ( a == 0 ) {
				aZero++;
				continue;
			} else if ( b == 0 ) {
				bZero++;
				continue;
			}

			if ( (a > 0 && b > 0) || (a < 0 && b < 0) ) {
				a = Math.abs(a);
				b = Math.abs(b);
				long gcd = gcd(a, b);
				Node n = new Node(a/gcd, b/gcd);
				P.put( n, P.getOrDefault(n, 0)+1);
			} else {
				a = Math.abs(a);
				b = Math.abs(b);
				long gcd = gcd(a, b);
				Node n = new Node(b/gcd, a/gcd);
				M.put( n, M.getOrDefault(n, 0)+1);
			}
		}

		long Ans = 1;
		for ( Node key : P.keySet() ) {
			if ( M.containsKey(key) ) {
				long c = ( modPow(P.get(key)) + modPow(M.get(key)) ) % MOD - 1;
				if (c < 0) c += MOD;
				Ans = Ans*c%MOD;
				M.remove(key);
			} else {
				Ans = Ans*modPow(P.get(key))%MOD;
			}
		}

		for ( Node key : M.keySet() ) {
			Ans = Ans*modPow(M.get(key))%MOD;
		}

		long c = ( modPow(aZero) + modPow(bZero) ) % MOD - 1;
		if (c < 0) c += MOD;
		Ans = Ans*c%MOD;

		System.out.println(Ans-1);

	}

	long gcd(long a, long b) {
		if ( a % b == 0) return b;
		return gcd(b,a%b);
	}

	public static void main(String[] args) {
		new Main();
	}

	long modPow(long n) {
		long res = 1;
		long a = 2L;
		while (n > 0) {
			if ( (n & 1) == 1) res = res * a % MOD;
			a = a * a % MOD;
			n >>= 1;
		}
		return res;
	}
}

class FastScanner {
	private final InputStream in = System.in;
	private final byte[] buffer = new byte[1024];
	private int ptr = 0;
	private int buflen = 0;

	private boolean hasNextByte() {
		if (ptr < buflen) {
			return true;
		} else {
			ptr = 0;
			try {
				buflen = in.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (buflen <= 0) {
				return false;
			}
		}
		return true;
	}

	private int readByte() {
		if (hasNextByte()) return buffer[ptr++];
		else return -1;
	}

	private static boolean isPrintableChar(int c) {
		return 33 <= c && c <= 126;
	}

	public boolean hasNext() {
		while (hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++;
		return hasNextByte();
	}

	public String next() {
		if (!hasNext()) throw new NoSuchElementException();
		StringBuilder sb = new StringBuilder();
		int b = readByte();
		while (isPrintableChar(b)) {
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	public long nextLong() {
		if (!hasNext()) throw new NoSuchElementException();
		long n = 0;
		boolean minus = false;
		int b = readByte();
		if (b == '-') {
			minus = true;
			b = readByte();
		}
		if (b < '0' || '9' < b) {
			throw new NumberFormatException();
		}
		while (true) {
			if ('0' <= b && b <= '9') {
				n *= 10;
				n += b - '0';
			} else if (b == -1 || !isPrintableChar(b)) {
				return minus ? -n : n;
			} else {
				throw new NumberFormatException();
			}
			b = readByte();
		}
	}

	public int nextInt() {
		long nl = nextLong();
		if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException();
		return (int) nl;
	}

	public double nextDouble() {
		return Double.parseDouble(next());
	}
}


import java.util.*;

public class Main {
    static long MOD = 1000000007;

    public static void main(String[] args) throws Exception {
        // Your code here!
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        // ans:
        // n個の中から1個以上選ぶ選び方
        // (ただし、仲の悪いペアを含まない)
        // 仲が悪い＝「AiAj + BiBj = 0」

        // どうやって数える？:
        // 仲の悪いペアをSi、Tiとする
        // -> S1T1,S2T2,...は独立に考えてOKになる
        // 「Siから1つ以上かつTiから1つ以上」でNG
        // = 「Siからのみ1つ以上 + Tiからのみ1つ以上 + SiTiから1つも選ばない」はOK

        // ex. 123 45
        // 123->2^3-1 45->2^2-1
        // ok = 2^3-1 + 2^2-1 + 1 = 11

        // AiAj + BiBj = 0
        // Ai/Bi = -Bj/Aj
        // ex. (1/3,-3/1)はNG
        // -> こういう集合S,Tを持ちたい。
        // -> 1/3が来たら1/3のSに、-3/1が来たら1/3のTに入れる。
        // Sは分子分母が非負、Tは分子が負分母が正にする。

        // maps, mapt: 仲が悪いペアの集合
        // ex. 1/3 -3/1 1/3
        // -> maps: 1/3=2, mapt: 1/3=1
        Map<String, Integer> maps = new HashMap<String, Integer>();
        Map<String, Integer> mapt = new HashMap<String, Integer>();

        // zero: 0/0の数（zeroは誰とも組めない）
        int zero = 0;

        // 集合S, Tに格納する
        for (int i = 0; i < n; i++) {
            long a = sc.nextLong();
            long b = sc.nextLong();

            // S or Tのフォーマットにする
            if (a == 0 && b == 0) {
                zero++;
                continue;
            }
            if (a < 0 && b < 0) {
                a *= -1;
                b *= -1;
            }
            if (a >= 0 && b < 0) {
                a *= -1;
                b *= -1;
            }
            // System.out.println(a + " " + b);

            // 約分する
            long g = gcd(a, b);
            a /= g;
            b /= g;
            // System.out.println(a + " " + b);

            boolean s = (a > 0 && b >= 0);
            if (s) {
                String key = a + " " + b;
                int val = maps.getOrDefault(key, 0);
                maps.put(key, ++val);
                val = mapt.getOrDefault(key, 0);
                mapt.put(key, val);
            } else {
                String key = b + " " + -a;
                int val = mapt.getOrDefault(key, 0);
                mapt.put(key, ++val);
                val = maps.getOrDefault(key, 0);
                maps.put(key, val);
            }
        }
        // System.out.println(maps.toString());
        // System.out.println(mapt.toString());

        long ans = 1;
        for (String key : maps.keySet()) {
            long now = 0;
            int vals = maps.getOrDefault(key, 0);
            int valt = mapt.getOrDefault(key, 0);

            now += modpow(2, vals) - 1;
            now %= MOD;
            now += modpow(2, valt) - 1;
            now %= MOD;
            now += 1;
            now %= MOD;

            ans *= now;
            ans %= MOD;
        }
        ans += zero;
        ans %= MOD;
        ans -= 1;
        if (ans < 0) ans += MOD;
        System.out.println(ans);
    }

    public static long gcd(long a, long b) {
        if (b == 0) return a;

        return gcd(b, a % b);
    }

    public static long modpow(long num, long n) {
        // ex. 3^10
        // 3^10 = 3^(0b1010)
        // = 3^8が1個 * 3^4が0個 * 3^2が1個 * 3^1が0個
        // (次の桁の値は(前の桁)^2になる)

        long res = 1;
        long digit = num;

        while (n > 0) {
            long lsb = n & 1;
            if (lsb == 1) {
                res *= digit;
                res %= MOD;
            }

            digit = digit * digit;
            digit %= MOD;
            n = n >> 1;
        }

        return res;
    }
}


#pragma GCC optimize ("O3")
#pragma GCC target ("avx2")
//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
#define rep(i, n) for(int i = 0; i < (n); i++)
#define rep1(i, n) for(int i = 1; i <= (n); i++)
#define co(x) cout << (x) << "\n"
#define cosp(x) cout << (x) << " "
#define ce(x) cerr << (x) << "\n"
#define cesp(x) cerr << (x) << " "
#define pb push_back
#define mp make_pair
#define chmin(x, y) x = min(x, y)
#define chmax(x, y) x = max(x, y)
#define Would
#define you
#define please

//こんなクソコードを生み出してしまうなんて趣味は罪ね

const int mod = 1e9 + 7;
const int IMAX = 200000;
const int D[9] = { 1,10,100,1000,10000,100000,1000000,10000000,100000000 };
class unko {
public:
	int f[IMAX + 1];
	//働いてない？
	constexpr unko() : f() {
		f[0] = 1;
		rep1(i, IMAX) f[i] = ll(f[i - 1]) * 2 % mod;
	}
} f;

void pakuri_sort(int N, ull A[]) {
	const int b = 8;
	ull tmp[200001];
	rep(k, 8) {
		int kazu[1 << b] = {}, kazu2[1 << b] = {};
		rep(i, N) kazu[A[i] >> k * b & ((1 << b) - 1)]++;
		rep(i, (1 << b) - 1) kazu[i + 1] += kazu[i];
		for (int i = N - 1; i >= 0; i--) tmp[--kazu[A[i] >> k * b & ((1 << b) - 1)]] = A[i];
		k++;
		rep(i, N) kazu2[tmp[i] >> k * b & ((1 << b) - 1)]++;
		rep(i, (1 << b) - 1) kazu2[i + 1] += kazu2[i];
		for (int i = N - 1; i >= 0; i--) A[--kazu2[tmp[i] >> k * b & ((1 << b) - 1)]] = tmp[i];
	}
}

const int CM = 1 << 17, CL = 22;
char cn[CM + CL], * ci = cn + CM + CL, * owa = cn + CM, ct;
const ll ma0 = 1157442765409226768;
const ll ma1 = 1085102592571150095;
const ll ma2 = 71777214294589695;
const ll ma3 = 281470681808895;
const ll ma4 = 4294967295;
inline ll getint() {
	if (ci - owa > 0) {
		memcpy(cn, owa, CL);
		ci -= CM;
		fread(cn + CL, 1, CM, stdin);
	}
	int pn = 1;
	if (*ci == '-') {
		pn = -pn;
		ci++;
	}
	ll tmp = *(ll*)ci;
	int dig = ((tmp & ma0) ^ ma0) ? 68 - __builtin_ctzll((tmp & ma0) ^ ma0) : 0;
	tmp = tmp << dig & ma1;
	tmp = tmp * 10 + (tmp >> 8) & ma2;
	tmp = tmp * 100 + (tmp >> 16) & ma3;
	tmp = tmp * 10000 + (tmp >> 32) & ma4;
	ci += (64 - dig >> 3);
	ll tmp2 = *(ll*)ci;
	dig = ((tmp2 & ma0) ^ ma0) ? 68 - __builtin_ctzll((tmp2 & ma0) ^ ma0) : 0;
	tmp2 = dig == 64 ? 0 : tmp2 << dig & ma1;
	tmp2 = tmp2 * 10 + (tmp2 >> 8) & ma2;
	tmp2 = tmp2 * 100 + (tmp2 >> 16) & ma3;
	tmp2 = tmp2 * 10000 + (tmp2 >> 32) & ma4;
	int d = (64 - dig >> 3);
	tmp = tmp * D[d] + tmp2;
	ci += d;
	while ((ct = *ci++) >= '0') tmp = tmp * 10 + ct - '0';
	return pn * tmp;
}

const ull ma = (1ull << 52) - 1;
int main() {
	//cin.tie(0);
	//ios::sync_with_stdio(false);

	int N = getint();
	ull AB[200001];
	int a0 = 0, b0 = 0;
	int ab00 = 0;
	int n = 0;
	rep(i, N) {
		ll a = getint(), b = getint();

		if (a == 0 && b == 0) {
			ab00++;
			continue;
		}
		if (a == 0) {
			a0++;
			continue;
		}
		if (b == 0) {
			b0++;
			continue;
		}

		if (a < 0) {
			a = -a;
			b = -b;
		}
		bool p = (b > 0);
		if (!p) {
			b = -b;
			swap(a, b);
		}

		ull tmp = 0;
		rep(i, 3) {
			tmp *= 3;
			if (b) {
				int k = __builtin_clzll(a) - __builtin_clzll(b);
				if (k > 0) a <<= k;
				else b <<= -k;
				if (a > b) {
					b <<= 1;
					k--;
				}
				double d = double(b) / a;
				ull tmp1 = *(ull*)& d;
				ull c = tmp1 & ma | (1ull << 52);
				lll sa = (lll(b) << 52) - lll(a) * c;
				while (sa >= 0) {
					sa -= a;
					c++;
				}
				while (sa < 0) {
					sa += a;
					c--;
				}
				tmp += ull(k + 63) << 53 | c;
				b = sa;
			}
		}

		if (p) AB[n++] = tmp << 1 | 1ull;
		else AB[n++] = tmp << 1;
	}
	pakuri_sort(n, AB);

	ll kotae = 1;
	ull mae = AB[0] >> 1;
	int k1 = 0, k2 = 0;
	rep(i, n) {
		if (mae != AB[i] >> 1) {
			kotae = kotae * (f.f[k1] + f.f[k2] - 1) % mod;
			k1 = 0;
			k2 = 0;
			mae = AB[i] >> 1;
		}
		if (AB[i] & 1ull) k1++;
		else k2++;
	}
	kotae = kotae * (f.f[k1] + f.f[k2] - 1) % mod;

	kotae = kotae * (f.f[a0] + f.f[b0] - 1) % mod;

	printf("%lld\n", (kotae - 1 + ab00 + mod) % mod);

	Would you please return 0;
}


#include <bits/stdc++.h>

using namespace std;

using LL = long long;
const LL MOD = (LL)1e9 + 7;
const LL NS = (LL)2e5 + 4;
LL N;
LL A[NS], B[NS], tpow[NS];
map < pair < LL, LL >, LL > cnt;

int main(){
    tpow[0] = 1;
    for(int i = 1; i < NS; ++i){
        tpow[i] = tpow[i - 1] * 2 % MOD;
    }
    for(int i = 0; i < NS; ++i){
        --tpow[i];
        if(tpow[i] < 0) tpow[i] += MOD;
    }
    scanf("%lld", &N);
    LL ans = 1;
    for(LL i = 0; i < N; ++i){
        scanf("%lld %lld", A + i, B + i);
        if(A[i] == B[i] && !A[i]){
            ++ans;
            continue;
        }
        if(B[i] == 0){
            ++cnt[{(LL)3e18, (LL)3e18}];
            continue;
        }
        LL absA = abs(A[i]), absB = abs(B[i]);
        LL gcd = __gcd(absA, absB);
        if(B[i] < 0){
            B[i] *= -1, A[i] *= -1;
        }
        if(A[i] == 0){
            B[i] = 1; gcd = 1;
        }
        ++cnt[{A[i] / gcd, B[i] / gcd}];
    }
    for(auto&div:cnt){
        if(!div.second){
            continue;
        }
        if(!div.first.first){
            ans *= tpow[div.second] + tpow[cnt[{(LL)3e18, (LL)3e18}]] + 1;
            ans %= MOD;
            div.second = cnt[{(LL)3e18, (LL)3e18}] = 0;
            continue;
        }
        pair < LL, LL > now = div.first;
        pair < LL, LL > op = {now.second, -now.first};
        if(op.second < 0){
            op.first *= -1, op.second *= -1;
        }
        LL absA = abs(op.first), absB = abs(op.second);
        LL gcd = __gcd(absA, absB);
        op = {op.first / gcd, op.second / gcd};
        ans *= tpow[div.second] + tpow[cnt[op]] + 1;
        div.second = cnt[op] = 0;
        ans %= MOD;
    }
    --ans;
    if(ans < 0) ans += MOD;
    printf("%lld\n", ans);
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

using LL = long long;
const LL MOD = (LL)1e9 + 7;
const LL NS = (LL)2e5 + 4;
LL N;
LL A[NS], B[NS], tpow[NS];
map < pair < LL, LL >, LL > cnt;

int main(){
    tpow[0] = 1;
    for(int i = 1; i < NS; ++i){
        tpow[i] = tpow[i - 1] * 2 % MOD;
    }
    for(int i = 0; i < NS; ++i){
        --tpow[i];
        if(tpow[i] < 0) tpow[i] += MOD;
    }
    scanf("%lld", &N);
    LL ans = 1;
    for(LL i = 0; i < N; ++i){
        scanf("%lld %lld", A + i, B + i);
        if(A[i] == B[i] && !A[i]){
            ++ans;
            continue;
        }
        if(B[i] == 0){
            ++cnt[{(LL)3e18, (LL)3e18}];
            continue;
        }
        LL absA = abs(A[i]), absB = abs(B[i]);
        LL gcd = __gcd(absA, absB);
        if(B[i] < 0){
            B[i] *= -1, A[i] *= -1;
        }
        if(A[i] == 0){
            B[i] = 1;
        }
        ++cnt[{A[i] / gcd, B[i] / gcd}];
    }
    for(auto&div:cnt){
        if(!div.first.second){
            continue;
        }
        if(!div.first.first){
            ans *= tpow[div.second] + tpow[cnt[{(LL)3e18, (LL)3e18}]] + 1;
            ans %= MOD;
            div.second = cnt[{MOD, MOD}] = 0;
            continue;
        }
        pair < LL, LL > now = div.first;
        pair < LL, LL > op = {now.second, -now.first};
        if(op.second < 0){
            op.first *= -1, op.second *= -1;
        }
        LL absA = abs(op.first), absB = abs(op.second);
        LL gcd = __gcd(absA, absB);
        op = {op.first / gcd, op.second / gcd};
        ans *= tpow[div.second] + tpow[cnt[op]] + 1;
        div.second = cnt[op] = 0;
        ans %= MOD;
    }
    --ans;
    if(ans < 0) ans += MOD;
    printf("%lld\n", ans);
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

using LL = long long;
const LL MOD = (LL)1e9 + 7;
const LL NS = (LL)2e5 + 4;
LL N;
LL A[NS], B[NS], tpow[NS];
map < pair < LL, LL >, LL > cnt;

int main(){
    tpow[0] = 1;
    for(int i = 1; i < NS; ++i){
        tpow[i] = tpow[i - 1] * 2 % MOD;
    }
    for(int i = 0; i < NS; ++i){
        --tpow[i];
        if(tpow[i] < 0) tpow[i] += MOD;
    }
    scanf("%lld", &N);
    LL ans = 1;
    for(LL i = 0; i < N; ++i){
        scanf("%lld %lld", A + i, B + i);
        if(A[i] == B[i] && !A[i]){
            ++ans;
            continue;
        }
        if(B[i] == 0){
            ++cnt[{(LL)3e18, (LL)3e18}];
            continue;
        }
        LL absA = abs(A[i]), absB = abs(B[i]);
        LL gcd = __gcd(absA, absB);
        if(B[i] < 0){
            B[i] *= -1, A[i] *= -1;
        }
        ++cnt[{A[i] / gcd, B[i] / gcd}];
    }
    for(auto&div:cnt){
        if(!div.first.second){
            continue;
        }
        if(!div.first.first){
            ans *= tpow[div.second] + tpow[cnt[{(LL)3e18, (LL)3e18}]] + 1;
            ans %= MOD;
            div.second = cnt[{MOD, MOD}] = 0;
            continue;
        }
        pair < LL, LL > now = div.first;
        pair < LL, LL > op = {now.second, -now.first};
        if(op.second < 0){
            op.first *= -1, op.second *= -1;
        }
        LL absA = abs(op.first), absB = abs(op.second);
        LL gcd = __gcd(absA, absB);
        op = {op.first / gcd, op.second / gcd};
        ans *= tpow[div.second] + tpow[cnt[op]] + 1;
        div.second = cnt[op] = 0;
        ans %= MOD;
    }
    --ans;
    if(ans < 0) ans += MOD;
    printf("%lld\n", ans);
    return 0;
}


#pragma GCC optimize("O3", "unroll-loops")
#pragma GCC target("avx")
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE (1 << 17)
char BUFFER_A[BUFFER_SIZE];
char *input = BUFFER_A + BUFFER_SIZE, *end_ptr = BUFFER_A + BUFFER_SIZE;

char get_char(void){
    if(input == end_ptr){fread(BUFFER_A, sizeof(char), BUFFER_SIZE, stdin); input = BUFFER_A;}
    return *(input++);
}

int64_t get_int(void){
    int64_t res = 0; char c = get_char();
    if(c == '-'){ while((c = get_char() - '0') >= 0) res = 10 * res + c; return -res; }
    res = c - '0'; while((c = get_char() - '0') >= 0) res = 10 * res + c;
    return res;
}

typedef struct{
    int64_t x;
    int64_t y;
} Fraction;

int64_t gcd(int64_t x, int64_t y){
    if(x < 0) x = -x; if(y < 0) y = -y;
    int64_t tmp; while(y){ tmp = x % y; x = y; y = tmp; }
    return x;
}

int equal(Fraction* p, Fraction* q){
    return ((p -> x) == (q -> x)) && ((p -> y) == (q -> y));
}
int compare_f(const void* pp, const void* qq){
    const Fraction* p = pp, * q = qq;
    if((p -> x) < (q -> x)) return -1;
    else if((p -> x) > (q -> x)) return 1;
    if((p -> y) < (q -> y)) return -1;
    else if((p -> y) > (q -> y)) return 1;
    return 0;
}

#define N 200000
Fraction Point[N];
int count[N];

const int mod = 1000000007;
int power(int n){
    int res = 1, a = 2;
    while(n){
        if(n & 1) res = (int64_t)(res) * a % mod;
        a = (int64_t)(a) * a % mod; n >>= 1;
    }
    return res;
}

int main(void){
    int n = get_int();
    for(int i = 0; i < n; ++i){
        Point[i].x = get_int(); Point[i].y = get_int();
        if(!(Point[i].x | Point[i].y)) continue;
        const int64_t g = gcd(Point[i].x, Point[i].y); Point[i].x /= g; Point[i].y /= g;
        if(Point[i].x == 0){ if(Point[i].y < 0) Point[i].y = -Point[i].y; }
        else if(Point[i].x < 0){ Point[i].x = -Point[i].x; Point[i].y = -Point[i].y; }
    }
    qsort(Point, n, sizeof(Fraction), compare_f);
    int len = 0;
    {
        int idx = 0; while(idx < n){
            int cnt = 1;
            while(idx + cnt < n && equal(Point + idx, Point + idx + cnt)) ++cnt;
            Point[len] = Point[idx]; count[len] = cnt; ++len; idx += cnt;
        }
    }
    int64_t res = 1; int plus = 0;
    for(int i = 0; i < len; ++i){
        if(Point[i].x || Point[i].y){
            Fraction point = {Point[i].y, -Point[i].x};
            if(point.x < 0){ point.x = -point.x; point.y = -point.y; }
            else if(point.x == 0 && point.y < 0) point.y = -point.y;

            int lwr = -1, upr = len; int find = 0;
            while(upr - lwr > 1){
                const int mid = (lwr + upr) >> 1;
                const int flag = compare_f(Point + mid, &point);
                if(flag < 0) lwr = mid;
                else if(flag > 0) upr = mid;
                else{ lwr = mid; find = 1; break; }
            }
            if(find){
                if(compare_f(&point, Point + i) < 0) res = (res * (power(count[i]) + power(count[lwr]) - 1)) % mod;
            }
            else res = (res * power(count[i])) % mod;
        }
        else plus = count[i];
    }
    res += plus; res += mod - 1; res %= mod;
    printf("%lld\n", res);
    return 0;
}