{-# OPTIONS_GHC -O2 #-}
{-# LANGUAGE BangPatterns, BinaryLiterals, CPP, DerivingStrategies  #-}
{-# LANGUAGE DerivingVia, FlexibleContexts, FlexibleInstances       #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, KindSignatures, LambdaCase #-}
{-# LANGUAGE MagicHash, MultiParamTypeClasses, MultiWayIf           #-}
{-# LANGUAGE NumericUnderscores, OverloadedStrings, PatternSynonyms #-}
{-# LANGUAGE RankNTypes, RecordWildCards, ScopedTypeVariables       #-}
{-# LANGUAGE StandaloneDeriving, TupleSections, TypeApplications    #-}
{-# LANGUAGE TypeFamilies, TypeInType, UnboxedTuples, ViewPatterns  #-}

import           Control.Applicative
import           Control.Exception
import           Control.Monad
import           Control.Monad.Primitive
import           Control.Monad.Reader
import           Control.Monad.ST
import           Control.Monad.State.Strict
import           Data.Bifunctor
import           Data.Bool
import qualified Data.ByteString               as B
import qualified Data.ByteString.Builder       as B
import qualified Data.ByteString.Char8         as C
import qualified Data.ByteString.Internal      as B
import qualified Data.ByteString.Unsafe        as B
import           Data.Char
import qualified Data.Foldable                 as F
import           Data.Function
import           Data.Functor.Identity
import qualified Data.IntMap.Strict            as IM
import qualified Data.IntSet                   as IS
import qualified Data.List                     as L
import qualified Data.Map.Strict               as M
import           Data.Monoid
import           Data.Ord
import           Data.Primitive
import           Data.Ratio
import           Data.Semigroup
import qualified Data.Set                      as S
import           Data.Tuple
import qualified Data.Vector                   as V
import qualified Data.Vector.Generic           as G
import qualified Data.Vector.Generic.Mutable   as GM
import qualified Data.Vector.Mutable           as VM
import qualified Data.Vector.Primitive         as P
import qualified Data.Vector.Primitive.Mutable as PM
import qualified Data.Vector.Unboxed           as U
import qualified Data.Vector.Unboxed.Mutable   as UM
import           Debug.Trace
import           Foreign                       hiding (void)
import           GHC.Exts
import qualified System.IO                     as IO
import           Unsafe.Coerce

main :: IO ()
main = do
    n <- readLn @Int
    xys <- U.unfoldr (runParser $ (,) <$> int <*> int) <$> C.getContents
    print $ solve n xys

#define MOD 1000000007

normalize :: (Int, Int) -> (Int, Int)
normalize (x, y)
    | x < 0 = (-quot x g, -quot y g)
    | otherwise = (quot x g, quot y g)
  where
    !g = gcd x y

orthogonal :: (Int, Int) -> (Int, Int)
orthogonal (x, y)
    | y < 0 = (-y, x)
    | otherwise = (y, -x)

solve :: Int -> U.Vector (Int, Int) -> IntMod
solve 1 _ = IntMod 1
solve n xys
    = finalize
    . M.foldl' step 1
    . getMultiSet
    . U.foldl' ins emptyMS
    . U.map normalize
    $ U.filter (\(x, y) -> x/=0 && y/=0) xys
  where
    !onOrigin = U.length $ U.filter (== (0,0)) xys
    !onXaxis = U.length (U.filter ((==0).snd) xys) - onOrigin
    !onYaxis = U.length (U.filter ((==0).fst) xys) - onOrigin

    offset = 0x100000
    ins ms (x, y)
        | y > 0 = insertMS (x, y) ms
        | otherwise = insertNtimesMS offset (orthogonal (x, y)) ms
    decode x = (x .&. 0xfffff, unsafeShiftR x 20)

    step :: IntMod -> Int -> IntMod
    step acc (decode -> (x, y)) = acc * calc x y

    finalize :: IntMod -> IntMod
    finalize acc
        | onXaxis + onYaxis > 0 = IntMod onOrigin + calc onXaxis onYaxis * acc - 1
        | otherwise = IntMod onOrigin + acc - 1

calc :: Int -> Int -> IntMod
calc x y = table U.! x + table U.! y - 1

table :: U.Vector IntMod
table = U.iterateN 200200 (*2) 1


newtype MultiSet a = MultiSet { getMultiSet :: M.Map a Int }
    deriving (Eq)

instance (Show a, Ord a) => Show (MultiSet a) where
    show = show . toList

instance (Ord a) => IsList (MultiSet a) where
    type Item (MultiSet a) = a
    fromList = L.foldl' (flip insertMS) emptyMS
    toList = concatMap (\(k, x) -> replicate x k)
        . M.toList
        . (coerce :: MultiSet a -> M.Map a Int)

emptyMS :: MultiSet a
emptyMS = coerce (M.empty :: M.Map a Int)

singletonMS :: a -> MultiSet a
singletonMS x = coerce $ M.singleton x (1 :: Int)

-- | /O(min(n,W))/
insertMS :: (Ord a) => a -> MultiSet a -> MultiSet a
insertMS x = coerce . M.insertWith (+) x (1 :: Int) . coerce

insertNtimesMS :: (Ord a) => Int -> a -> MultiSet a -> MultiSet a
insertNtimesMS n x = coerce . M.insertWith (+) x (n :: Int) . coerce

-- | /O(min(n,W))/
deleteMS :: (Ord a) => a -> MultiSet a -> MultiSet a
deleteMS x = (coerce :: M.Map a Int -> MultiSet a)
    . M.update (\y -> if y > 1 then Just $! y - 1 else Nothing) x
    . coerce

-- | /O(min(n,W))/
deleteAllMS :: (Ord a) => a -> MultiSet a -> MultiSet a
deleteAllMS x = (coerce :: M.Map a Int -> MultiSet a)
    . M.delete x
    . coerce

-- | /O(min(n,W))/
memberMS :: (Ord a) => a -> MultiSet a -> Bool
memberMS x = M.member x . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(min(n,W))/
notMemberMS :: (Ord a) => a -> MultiSet a -> Bool
notMemberMS x = not . memberMS x

-- | /P(min(n, W))/
countMS :: (Ord a) => a -> MultiSet a -> Int
countMS key = M.findWithDefault 0 key . coerce

-- | /O(log n)/
lookupLTMS :: (Ord a) => a -> MultiSet a -> Maybe a
lookupLTMS x = fmap fst . M.lookupLT x
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(log n)/
lookupGTMS :: (Ord a) => a -> MultiSet a -> Maybe a
lookupGTMS x = fmap fst . M.lookupGT x
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(log n)/
lookupLEMS :: (Ord a) => a -> MultiSet a -> Maybe a
lookupLEMS x = fmap fst . M.lookupLE x
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(log n)/
lookupGEMS :: (Ord a) => a -> MultiSet a -> Maybe a
lookupGEMS x = fmap fst . M.lookupGE x
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(1)/
nullMS :: MultiSet a -> Bool
nullMS = M.null
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(n)/
sizeMS :: MultiSet a -> Int
sizeMS = M.foldl' (+) 0
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(min(n,W))/
findMinMS :: (Ord a) => MultiSet a -> a
findMinMS = fst
    . M.findMin
    . (coerce :: MultiSet a -> M.Map a Int)
-- | /O(min(n,W))/
findMaxMS :: (Ord a) => MultiSet a -> a
findMaxMS = fst
    . M.findMax
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(min(n,W))/
deleteMinMS :: (Ord a) => MultiSet a -> MultiSet a
deleteMinMS = coerce
    . M.updateMin (\x -> if x > 1 then Just $! x - 1 else Nothing)
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(min(n,W))/
deleteMaxMS :: (Ord a) => MultiSet a -> MultiSet a
deleteMaxMS = coerce
    . M.updateMax (\x -> if x > 1 then Just $! x - 1 else Nothing)
    . (coerce :: MultiSet a -> M.Map a Int)

-- | /O(min(n,W))/
maxViewMS :: (Ord a) => MultiSet a -> Maybe (a, MultiSet a)
maxViewMS = maybe Nothing just
    . M.maxViewWithKey
    . (coerce :: MultiSet a -> M.Map a Int)
  where
    just ((k, x), m)
        | x > 1 = case M.insert k (x - 1) m of
            m' -> Just (k, coerce m')
        | otherwise = Just (k, coerce m)

-- | /O(min(n,W))/
minViewMS :: (Ord a) => MultiSet a -> Maybe (a, MultiSet a)
minViewMS = maybe Nothing just
    . M.minViewWithKey
    . (coerce :: MultiSet a -> M.Map a Int)
  where
    just ((k, x), m)
        | x > 1 = case M.insert k (x - 1) m of
            m' -> Just (k, coerce m')
        | otherwise = Just (k, coerce m)


-------------------------------------------------------------------------------
-- Utils
-------------------------------------------------------------------------------
rep :: Monad m => Int -> (Int -> m ()) -> m ()
rep !n = U.forM_ $ U.generate n id
{-# INLINE rep #-}
rev :: Monad m => Int -> (Int -> m ()) -> m ()
rev !n = U.forM_ $ U.iterateN n (subtract 1) (n - 1)
{-# INLINE rev #-}
infixl 8 `shiftRL`, `unsafeShiftRL`
shiftRL = unsafeShiftRL
{-# INLINE shiftRL #-}
unsafeShiftRL (I# x#) (I# i#) = I# (uncheckedIShiftRL# x# i#)
{-# INLINE unsafeShiftRL #-}
type Parser a = StateT C.ByteString Maybe a
runParser :: Parser a -> C.ByteString -> Maybe (a, C.ByteString)
runParser = runStateT
{-# INLINE runParser #-}
int :: Parser Int
int = coerce $ C.readInt . C.dropWhile isSpace
{-# INLINE int #-}
int1 :: Parser Int
int1 = fmap (subtract 1) int
{-# INLINE int1 #-}
char :: Parser Char
char = coerce C.uncons
{-# INLINE char #-}
byte :: Parser Word8
byte = coerce B.uncons
{-# INLINE byte #-}

-------------------------------------------------------------------------------
-- Data.IntMod
-------------------------------------------------------------------------------
modulus :: (Num a) => a
modulus = MOD
{-# INLINE modulus #-}
infixr 8 ^%
infixl 7 *%, /%
infixl 6 +%, -%
(+%) :: Int -> Int -> Int
(I# x#) +% (I# y#) = case x# +# y# of { r# -> I# (r# -# ((r# >=# MOD#) *# MOD#))}
{-# INLINE (+%) #-}
(-%) :: Int -> Int -> Int
(I# x#) -% (I# y#) = case x# -# y# of { r# -> I# (r# +# ((r# <# 0#) *# MOD#))}
{-# INLINE (-%) #-}
(*%) :: Int -> Int -> Int
(I# x#) *% (I# y#) = I# ((x# *# y#) `remInt#` MOD#)
{-# INLINE (*%) #-}
(/%) :: Int -> Int -> Int
(I# x#) /% (I# y#) = go# y# MOD# 1# 0# where { go# a# b# u# v# | isTrue# (b# ># 0#) = case a# `quotInt#` b# of { q# -> go# b# (a# -# (q# *# b#)) v# (u# -# (q# *# v#))} | otherwise = I# ((x# *# (u# +# MOD#)) `remInt#` MOD#)}
{-# INLINE (/%) #-}
(^%) :: Int -> Int -> Int
x ^% n | n > 0 = go 1 x n | n == 0 = 1 | otherwise = go 1 (1 /% x) (-n) where { go !acc !y !m | m .&. 1 == 0 = go acc (y *% y) (unsafeShiftR m 1) | m == 1 = acc *% y | otherwise = go (acc *% y) (y *% y) (unsafeShiftR (m - 1) 1)}
newtype IntMod = IntMod{getIntMod :: Int} deriving newtype (Eq, Ord, Read, Show, Real, Prim)
intMod :: (Integral a) => a -> IntMod
intMod x = fromIntegral $ mod (fromIntegral x) MOD
{-# INLINE intMod #-}
intModValidate :: IntMod -> Bool
intModValidate (IntMod x) = 0 <= x && x < MOD
{-# INLINE intModValidate #-}
instance Bounded IntMod where { minBound = IntMod 0; maxBound = IntMod $ modulus - 1}
instance Enum IntMod where { toEnum = intMod; fromEnum = coerce}
instance Integral IntMod where { quotRem x y = (x / y, x - x / y * y); toInteger = coerce (toInteger @Int)}
instance Num IntMod where { (+) = coerce (+%); (-) = coerce (-%); (*) = coerce (*%); abs = id; signum = const (IntMod 1); fromInteger x = coerce @Int @IntMod . fromInteger $ mod x modulus}
instance Fractional IntMod where { (/) = coerce (/%); fromRational q = fromInteger (numerator q) / fromInteger (denominator q)}
newtype instance  UM.MVector s IntMod = MV_IntMod (UM.MVector s Int)
newtype instance  U.Vector IntMod = V_IntMod (U.Vector Int)
instance U.Unbox IntMod
instance GM.MVector UM.MVector IntMod where { basicLength (MV_IntMod v) = GM.basicLength v; {-# INLINE basicLength #-}; basicUnsafeSlice i n (MV_IntMod v) = MV_IntMod $ GM.basicUnsafeSlice i n v; {-# INLINE basicUnsafeSlice #-}; basicOverlaps (MV_IntMod v1) (MV_IntMod v2) = GM.basicOverlaps v1 v2; {-# INLINE basicOverlaps #-}; basicUnsafeNew n = MV_IntMod `liftM` GM.basicUnsafeNew n; {-# INLINE basicUnsafeNew #-}; basicInitialize (MV_IntMod v) = GM.basicInitialize v; {-# INLINE basicInitialize #-}; basicUnsafeReplicate n x = MV_IntMod `liftM` GM.basicUnsafeReplicate n (coerce x); {-# INLINE basicUnsafeReplicate #-}; basicUnsafeRead (MV_IntMod v) i = coerce `liftM` GM.basicUnsafeRead v i; {-# INLINE basicUnsafeRead #-}; basicUnsafeWrite (MV_IntMod v) i x = GM.basicUnsafeWrite v i (coerce x); {-# INLINE basicUnsafeWrite #-}; basicClear (MV_IntMod v) = GM.basicClear v; {-# INLINE basicClear #-}; basicSet (MV_IntMod v) x = GM.basicSet v (coerce x); {-# INLINE basicSet #-}; basicUnsafeCopy (MV_IntMod v1) (MV_IntMod v2) = GM.basicUnsafeCopy v1 v2; {-# INLINE basicUnsafeCopy #-}; basicUnsafeMove (MV_IntMod v1) (MV_IntMod v2) = GM.basicUnsafeMove v1 v2; {-# INLINE basicUnsafeMove #-}; basicUnsafeGrow (MV_IntMod v) n = MV_IntMod `liftM` GM.basicUnsafeGrow v n; {-# INLINE basicUnsafeGrow #-}}
instance G.Vector U.Vector IntMod where { basicUnsafeFreeze (MV_IntMod v) = V_IntMod `liftM` G.basicUnsafeFreeze v; {-# INLINE basicUnsafeFreeze #-}; basicUnsafeThaw (V_IntMod v) = MV_IntMod `liftM` G.basicUnsafeThaw v; {-# INLINE basicUnsafeThaw #-}; basicLength (V_IntMod v) = G.basicLength v; {-# INLINE basicLength #-}; basicUnsafeSlice i n (V_IntMod v) = V_IntMod $ G.basicUnsafeSlice i n v; {-# INLINE basicUnsafeSlice #-}; basicUnsafeIndexM (V_IntMod v) i = coerce `liftM` G.basicUnsafeIndexM v i; {-# INLINE basicUnsafeIndexM #-}; basicUnsafeCopy (MV_IntMod mv) (V_IntMod v) = G.basicUnsafeCopy mv v; elemseq _ = seq; {-# INLINE elemseq #-}}


Measured memory_bytes: 56628