CodeEff/ECCO huggingface dataset:
# Original solution
import math,string,itertools,fractions,heapq,collections,re,array,bisect,sys,random,time,copy,functools

sys.setrecursionlimit(10**7)
inf = 10**20
eps = 1.0 / 10**15
mod = 10**9+7

def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI_(): return [int(x)-1 for x in sys.stdin.readline().split()]
def LF(): return [float(x) for x in sys.stdin.readline().split()]
def LS(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline())
def F(): return float(sys.stdin.readline())
def S(): return input()
def pf(s): return print(s, flush=True)


def main():
    start = time.time()
    n,m = LI()
    a = [LI() for _ in range(m)]
    e = [set() for _ in range(n)]
    for l,r,d in a:
        e[l-1].add((r-1,d))
        e[r-1].add((l-1,-d))

    b = [False] * n
    def g(i,c):
        gt = time.time() - start
        if gt > 1.9:
            return True
        if c == False:
            c = 0
        b[i] = c
        for r,d in e[i]:
            if b[r] != False:
                if b[r] != c+d:
                    return False
            else:
                if not g(r,c+d):
                    return False
        return True

    for i in range(n):
        if b[i] != False:
            continue
        if not g(i,False):
            return 'No'
    return 'Yes'


print(main())





# Optimized solution
import math,string,itertools,fractions,heapq,collections,re,array,bisect,sys,random,time,copy,functools

sys.setrecursionlimit(10**7)
inf = 10**20
eps = 1.0 / 10**15
mod = 10**9+7

def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI_(): return [int(x)-1 for x in sys.stdin.readline().split()]
def LF(): return [float(x) for x in sys.stdin.readline().split()]
def LS(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline())
def F(): return float(sys.stdin.readline())
def S(): return input()
def pf(s): return print(s, flush=True)


def main():
    start = time.time()
    n,m = LI()
    a = [LI() for _ in range(m)]
    e = [set() for _ in range(n)]
    for l,r,d in a:
        e[l-1].add((r-1,d))
        e[r-1].add((l-1,-d))

    b = [False] * n
    def g(i,c):
        gt = time.time() - start
        if gt > 1.0:
            return True
        if c == False:
            c = 0
        b[i] = c
        for r,d in e[i]:
            if b[r] != False:
                if b[r] != c+d:
                    return False
            else:
                if not g(r,c+d):
                    return False
        return True

    for i in range(n):
        if b[i] != False:
            continue
        if not g(i,False):
            return 'No'
    return 'Yes'


print(main())
# Original solution
import sys
sys.setrecursionlimit(100000)
N, M = list(map(int, input().split()))
union = list(range(N + 1))
dist = [0] * (N + 1)

def ascend(anc):
	tmp_d = dist[anc]
	if union[anc] == anc:
		return anc, tmp_d
	else:
		union[anc], d_sum = ascend(union[anc])
		dist[anc] = tmp_d + d_sum
	return union[anc], dist[anc]

def connect(l, r, d):
	l_anc, l_d = ascend(l)
	r_anc, r_d = ascend(r)
	if l_anc != r_anc:
		union[l_anc] = r_anc
		dist[l_anc] = dist[r] + d - dist[l]

def is_union(l, r):
	l_anc, l_d = ascend(l)
	r_anc, r_d = ascend(r) 
	return l_anc == r_anc

def check(l, r, d):
	l_anc, l_d = ascend(l)
	r_anc, r_d = ascend(r)
	return r_d + d == l_d

for i in range(M):
	l, r, d = list(map(int, input().split()))
	if is_union(l, r):
		if not check(l, r, d):
			print("No")
			break
	else:
		connect(l, r, d)
else:
	print("Yes")


# Optimized solution
import sys
sys.setrecursionlimit(100000)
N, M = list(map(int, input().split()))
union = list(range(N + 1))
dist = [0] * (N + 1)
rec = [0] * (N + 1)

def ascend(anc):
	count = 0
	while anc != union[anc]:
		rec[count] = anc
		anc = union[anc]
		count += 1
	rec[count] = anc
	for i in range(count - 1, -1, -1):
		dist[rec[i]] += dist[rec[i + 1]]
		union[rec[i]] = anc
	return anc

def connect(l, r, d):
	l_anc = ascend(l)
	r_anc = ascend(r)
	if l_anc != r_anc:
		union[l_anc] = r_anc
		dist[l_anc] = dist[r] + d - dist[l]

def is_union(l, r):
	return ascend(l) == ascend(r)

def check(l, r, d):
	return dist[r] + d == dist[l]

for i in range(M):
	l, r, d = list(map(int, input().split()))
	if is_union(l, r):
		if not check(l, r, d):
			print("No")
			break
	else:
		connect(l, r, d)
else:
	print("Yes")
# Original solution
from collections import deque

class Edge:
	def __init__(self, t, w):
		self.to = t
		self.ew = w

class Graph:
	def __init__(self, N):
		self.nw = [-1] * N
		self.al = [[] for i in range(N)]

	def add_edge(self, fr, to, weight):
		self.al[fr].append(Edge(to, weight))
		self.al[to].append(Edge(fr, -weight))

	def dfs(self, s, used):
		self.nw[s] = 0
		q = deque([s])
		while q:
			h = q.popleft()
			if h in used:
				continue
			used.add(h)
			for e in self.al[h]:
				if e.to in used and self.nw[h] + e.ew != self.nw[e.to]:
					return False
				self.nw[e.to] = self.nw[h] + e.ew
				q.appendleft(e.to)
		return True


def main():
	N, M = list(map(int, input().split()))
	graph = Graph(N)
	for i in range(M):
		l, r, d = list(map(int, input().split()))
		graph.add_edge(l - 1, r - 1, d)

	used = set()
	for i in range(N):
		if i in used or graph.dfs(i, used):
			continue
		print("No")
		break
	else:
		print("Yes")


if __name__ == "__main__":
	main()


# Optimized solution
from collections import deque

class Edge:
	def __init__(self, t, w):
		self.to = t
		self.ew = w

class Graph:
	def __init__(self, N):
		self.nw = [-1] * N
		self.al = [[] for i in range(N)]

	def add_edge(self, fr, to, weight):
		self.al[fr].append(Edge(to, weight))
		self.al[to].append(Edge(fr, -weight))

	def dfs(self, s, used):
		self.nw[s] = 0
		q = deque([s])
		used.add(s)
		while q:
			h = q.popleft()
			for e in self.al[h]:
				if e.to in used:
					if self.nw[h] + e.ew != self.nw[e.to]:
						return False
					continue
				used.add(e.to)
				self.nw[e.to] = self.nw[h] + e.ew
				q.appendleft(e.to)
		return True


def main():
	N, M = list(map(int, input().split()))
	graph = Graph(N)
	for i in range(M):
		l, r, d = list(map(int, input().split()))
		graph.add_edge(l - 1, r - 1, d)

	used = set()
	for i in range(N):
		if i in used or graph.dfs(i, used):
			continue
		print("No")
		break
	else:
		print("Yes")


if __name__ == "__main__":
	main()
# Original solution
from collections import deque

class Edge:
	def __init__(self, t, w):
		self.to = t
		self.ew = w

class Graph:
	def __init__(self, N):
		self.nw = [-1] * N
		self.al = [[] for i in range(N)]

	def add_edge(self, fr, to, weight):
		self.al[fr].append(Edge(to, weight))
		self.al[to].append(Edge(fr, -weight))

	def dfs(self, s, used):
		self.nw[s] = 0
		q = deque([s])
		used.add(s)
		while q:
			h = q.popleft()
			for e in self.al[h]:
				if e.to in used:
					if self.nw[h] + e.ew != self.nw[e.to]:
						return False
					continue
				used.add(e.to)
				self.nw[e.to] = self.nw[h] + e.ew
				q.appendleft(e.to)
		return True


def main():
	N, M = list(map(int, input().split()))
	graph = Graph(N)
	for i in range(M):
		l, r, d = list(map(int, input().split()))
		graph.add_edge(l - 1, r - 1, d)

	used = set()
	for i in range(N):
		if i in used or graph.dfs(i, used):
			continue
		print("No")
		break
	else:
		print("Yes")


if __name__ == "__main__":
	main()


# Optimized solution
from collections import deque

def dfs(s, al, nw, used):
	nw[s] = 0
	q = deque([s])
	used.add(s)
	while q:
		h = q.popleft()
		for to, ew in al[h]:
			if to in used:
				if nw[h] + ew != nw[to]:
					return False
				continue
			used.add(to)
			nw[to] = nw[h] + ew
			q.appendleft(to)
	return True

def main():
	N, M = list(map(int, input().split()))
	nw = [-1] * N
	al = [[] for i in range(N)]
	for i in range(M):
		l, r, d = list(map(int, input().split()))
		al[l - 1].append((r - 1, d))
		al[r - 1].append((l - 1, -d))

	used = set()
	for i in range(N):
		if i in used or dfs(i, al, nw, used):
			continue
		print("No")
		break
	else:
		print("Yes")


if __name__ == "__main__":
	main()
# Original solution
class WeightedUnionFind():
    def __init__(self, n):
        self.parents = [-1] * n
        self.par_weight = [0] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            root = self.find(self.parents[x])
            self.par_weight[x] += self.par_weight[self.parents[x]]
            self.parents[x] = root
            return self.parents[x]

    def union(self, x, y, w):
        w = w + self.weight(x) - self.weight(y)
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            self.parents[y] += self.parents[x]
            self.parents[x] = y
            self.par_weight[x] = -w
        else:
            self.parents[x] += self.parents[y]
            self.parents[y] = x
            self.par_weight[y] = w

    def weight(self, x):
        if self.parents[x] < 0:
            return 0
        else:
            return self.par_weight[x] + self.weight(self.parents[x])

    def diff(self, x, y):
        if self.find(x) != self.find(y):
            raise Exception('"{}" belongs to a different tree from "{}"'.format(x, y))
        return self.weight(y) - self.weight(x)


N, M = list(map(int, input().split()))
uf = WeightedUnionFind(N)
for i in range(M):
    L, R, D = list(map(int, input().split()))
    L -= 1
    R -= 1
    if uf.find(L) == uf.find(R):
        if uf.diff(L, R) != D:
            res = 'No'
            break
    else:
        uf.union(L, R, D)
else:
    res = 'Yes'

print(res)


# Optimized solution
import sys
sys.setrecursionlimit(10 ** 6)


def dfs(p):
    for edge in G[p]:
        if X[edge[0]] is not None:
            if X[edge[0]] - X[p] != edge[1]:
                print('No')
                exit()
        else:
            X[edge[0]] = X[p] + edge[1]
            dfs(edge[0])


N, M = list(map(int, input().split()))
G = [[] for _ in range(N)]

for i in range(M):
    L, R, D = list(map(int, input().split()))
    L -= 1
    R -= 1
    G[L].append((R, D))
    G[R].append((L, -D))

X = [None] * N
for i in range(N):
    if X[i] is None:
        X[i] = 0
        dfs(i)

print('Yes')
# Original solution
N,M = list(map(int,input().split()))
es = [[] for i in range(N)]
for i in range(M):
    l,r,d = list(map(int,input().split()))
    l,r = l-1,r-1
    es[l].append((r,d))
    es[r].append((l,-d))

checked = [0] * N
for i in range(N):
    if checked[i]: continue
    dist = [None] * N
    dist[i] = 0
    stack = [i]
    while stack:
        v = stack.pop()
        checked[v] = 1
        for to,d in es[v]:
            if dist[to] is None:
                dist[to] = dist[v] + d
                if not checked[to]:
                    stack.append(to)
            else:
                if dist[to] != dist[v] + d:
                    print('No')
                    exit()
print('Yes')


# Optimized solution
N,M = list(map(int,input().split()))
es = [[] for i in range(N)]
for i in range(M):
    l,r,d = list(map(int,input().split()))
    l,r = l-1,r-1
    es[l].append((r,d))
    es[r].append((l,-d))

dist = [None] * N
for i in range(N):
    if dist[i] is not None: continue
    dist[i] = 0
    stack = [i]
    while stack:
        v = stack.pop()
        for to,d in es[v]:
            if dist[to] is None:
                dist[to] = dist[v] + d
                stack.append(to)
            else:
                if dist[to] != dist[v] + d:
                    print('No')
                    exit()
print('Yes')
# Original solution
# -*- coding: utf-8 -*-
from collections import  deque
def inpl(): return tuple(map(int, input().split()))
 
N, M = inpl()
 
adj = [[] for _ in range(N+1)]  
 
for _ in range(M):
    l, r, d = inpl()
    adj[l].append([r, d])
    adj[r].append([l, -d])
 
X = [-1 for _ in range(N+1)]
searched = [False for _ in range(N+1)]
S = 0 # == sum(searched)
res = True
g = 1
Q = deque([])
while True:
    if S >= N:
        break
    g = g + searched[g:].index(False)
    X[g] = 0
    Q.append(g)
    searched[g] = True
    S += 1
    while Q:
        p = Q.popleft()
        for np, d in adj[p]:
            if searched[np]:
                if X[np] != X[p] + d:
                    Q = []
                    res = False
                    break
            else:
                X[np] = X[p] + d
                Q.append(np)
                searched[np] = True
                S += 1
    if not res:
        break
 
if res:
    print("Yes")
else:
    print("No")

# Optimized solution
# -*- coding: utf-8 -*-
from collections import  deque
def inpl(): return tuple(map(int, input().split()))
 
N, M = inpl()
 
adj = [[] for _ in range(N+1)]  
 
for _ in range(M):
    l, r, d = inpl()
    adj[l].append([r, d])
    adj[r].append([l, -d])
 
X = [-1 for _ in range(N+1)]
searched = [False for _ in range(N+1)]
S = 0 # == sum(searched)
res = True
g = 1
Q = deque([])
for g in range(1, N+1):
    if searched[g]:
        continue
    X[g] = 0
    Q.append(g)
    searched[g] = True
    while Q:
        p = Q.popleft()
        for np, d in adj[p]:
            if searched[np]:
                if X[np] != X[p] + d:
                    Q = []
                    res = False
                    break
            else:
                X[np] = X[p] + d
                Q.append(np)
                searched[np] = True
    if not res:
        break
 
if res:
    print("Yes")
else:
    print("No")
# Original solution
from collections import defaultdict

n,m = list(map(int,input().split()))
uf = [-1]*n
def root(x):
    if uf[x] < 0:
        return x
    uf[x] = root(uf[x])
    return uf[x]
def unite(x,y):
    rx, ry = root(x), root(y)
    if rx==ry: return False
    if rx > ry: rx, ry = ry, rx
    uf[rx] += uf[ry]
    uf[ry] = rx
    return True

edge = [[-1]*n for i in range(n)]
edge = defaultdict(list)
for i in range(m):
    l,r,d = list(map(int, input().split()))
    edge[l-1].append((r-1,d))
    edge[r-1].append((l-1,-d))
    unite(l-1, r-1)

dist = [0]*n
visited = [False]*n
ans = True

def bfs(p, d=0):
    global ans
    if not ans:
        return
    if not visited[p]:
        visited[p] = True
        dist[p] = d
        for ind, j in edge[p]:
            bfs(ind, d+j)
    else:
        if dist[p] != d:
            ans = False

for i in range(n):
    if uf[i] >= 0:
        continue
    bfs(i, 0)
    if not ans:
        break

if ans:
    print("Yes")
else:
    print("No")

# Optimized solution
from collections import defaultdict

n,m = list(map(int,input().split()))
uf = [-1]*n
def root(x):
    if uf[x] < 0:
        return x
    uf[x] = root(uf[x])
    return uf[x]
def unite(x,y):
    rx, ry = root(x), root(y)
    if rx==ry: return False
    if rx > ry: rx, ry = ry, rx
    uf[rx] += uf[ry]
    uf[ry] = rx
    return True

edge = [[-1]*n for i in range(n)]
edge = defaultdict(list)
for i in range(m):
    l,r,d = list(map(int, input().split()))
    edge[l-1].append((r-1,d))
    edge[r-1].append((l-1,-d))
    unite(l-1, r-1)

dist = [0]*n
visited = [False]*n

from collections import deque

for i in range(n):
    if uf[i] >= 0:
        continue
    q = deque()
    q.append((i,0))
    while q:
        to, d = q.popleft()
        if visited[to]:
            if dist[to] != d:
                print("No")
                exit()
        else:
            visited[to] = True
            dist[to] = d
            for ind, j in edge[to]:
                q.append((ind,d+j))

print("Yes")
# Original solution
from collections import defaultdict

n,m = list(map(int,input().split()))
uf = [-1]*n
def root(x):
    if uf[x] < 0:
        return x
    uf[x] = root(uf[x])
    return uf[x]
def unite(x,y):
    rx, ry = root(x), root(y)
    if rx==ry: return False
    if rx > ry: rx, ry = ry, rx
    uf[rx] += uf[ry]
    uf[ry] = rx
    return True

edge = [[-1]*n for i in range(n)]
edge = defaultdict(list)
for i in range(m):
    l,r,d = list(map(int, input().split()))
    edge[l-1].append((r-1,d))
    edge[r-1].append((l-1,-d))
    unite(l-1, r-1)

dist = [0]*n
visited = [False]*n

from collections import deque

for i in range(n):
    if uf[i] >= 0:
        continue
    q = deque()
    q.append((i,0))
    while q:
        to, d = q.popleft()
        if visited[to]:
            if dist[to] != d:
                print("No")
                exit()
        else:
            visited[to] = True
            dist[to] = d
            for ind, j in edge[to]:
                q.append((ind,d+j))

print("Yes")

# Optimized solution
from collections import defaultdict, deque

n,m = list(map(int,input().split()))
edge = [[] for i in range(n)]
for i in range(m):
    l,r,d = list(map(int, input().split()))
    edge[l-1].append((r-1,d))
    edge[r-1].append((l-1,-d))

dist = [0]*n
visited = [False]*n

for i in range(n):
    if visited[i]:
        continue
    q = deque()
    q.append((i,0))
    while q:
        to, d = q.popleft()
        if visited[to]:
            if dist[to] != d:
                print("No")
                exit()
        else:
            visited[to] = True
            dist[to] = d
            for ind, j in edge[to]:
                q.append((ind,d+j))

print("Yes")
# Original solution
def solve(N, M, lrd):
    vs = {}
    def calc(l, r):
        cs = set([l])
        xs = [(l, 0)]
        while xs:
            x = xs.pop()
            if x[0] in vs:
                for y, d in list(vs[x[0]].items()):
                    if y == r:
                        return d + x[1]
                    if y not in cs: 
                        xs.append((y, d + x[1]))
                        cs.add(y)
        return None
    for l, r, d in lrd:
        x = calc(l, r)
        if not x is None and x != d:
            return False
        if l not in vs:
            vs[l] = {}
        if r not in vs:
            vs[r] = {}
        vs[l][r] = d
        vs[r][l] = -d
    return True

if __name__ == "__main__":
    N, M = [int(x) for x in input().split()]
    lrd = []
    for i in range(M):
        lrd.append([int(x) for x in input().split()])
    if solve(N, M, lrd):
        print("Yes")
    else:
        print("No")


# Optimized solution
def solve(N, M, lrd):
    edges = {}
    nodes = {}
    for l, r, d in lrd:
        if not l in edges:
            edges[l] = {}
        if not r in edges:
            edges[r] = {}
        edges[l][r] = d
        edges[r][l] = -d
        nodes[l] = None
        nodes[r] = None
    for node in nodes:
        if nodes[node] is not None:
            continue
        nodes[node] = 0
        stack = []
        stack.append(node)
        while stack:
            node = stack.pop()
            for n, d in list(edges[node].items()):
                if nodes[n] is None:
                    stack.append(n)
                    nodes[n] = nodes[node] + edges[node][n]
                else:
                    if nodes[n] != nodes[node] + edges[node][n]:
                        return False
    return True

if __name__ == "__main__":
    N, M = [int(x) for x in input().split()]
    lrd = []
    for i in range(M):
        lrd.append([int(x) for x in input().split()])
    if solve(N, M, lrd):
        print("Yes")
    else:
        print("No")
# Original solution
"""
unionFind木を使う。
根に対する距離を記録していく。
"""
import sys
sys.setrecursionlimit(2000000)
input = sys.stdin.readline
class UnionFind():
    def __init__(self,n):
        self.n=n
        self.parents = [i for i in range(n+1)]
        self.distance = [0]*(n+1)

    def find(self,x):
        if self.parents[x]==x:
            return x,0
        else:
            self.parents[x],dist2=self.find(self.parents[x])
            self.distance[x] += dist2
            return self.parents[x],self.distance[x]

    def union(self,l,r,dist):
        lRoot,lDist = self.find(l)
        rRoot,rDist = self.find(r)
        #まず、位置関係に矛盾がないかを調べる
        #lRootはlよりlDist右にいる
        #rRootはrよりrDist右にいる
        #lRoot=rRootのとき、lDist-rDist != distなら矛盾。
        if lRoot==rRoot and lDist-rDist != dist:
            print("No")
            exit()

        #より左にいる根を右にいる根にマージする
        if lDist < dist+rDist:
            #lRootがrRootよりも左の場合
            self.parents[lRoot]=rRoot
            self.distance[lRoot]=(dist+rDist)-lDist
        else:
            self.parents[rRoot]=lRoot
            self.distance[rRoot]=lDist-(dist+rDist)

N,M = list(map(int,input().split()))
tree = UnionFind(N)
for i in range(M):
    L,R,D = list(map(int,input().split()))
    tree.union(L,R,D)

print('Yes')

# Optimized solution
import sys
sys.setrecursionlimit(2000000)
input = sys.stdin.readline
class UnionFind():
    def __init__(self,n):
        self.n=n
        self.parents = [i for i in range(n+1)]
        #dist[x]はxとその親との間の距離を保持している
        self.dist = [0]*(n+1)
    def find(self,x):
        if self.parents[x]==x:
            return x
        else:
            a = self.parents[x]
            self.parents[x]=self.find(self.parents[x])
            self.dist[x] += self.dist[a]
            return self.parents[x]
    def unite(self,l,r,d):
        rRoot = self.find(r)
        lRoot = self.find(l)
        if rRoot == lRoot:
            if self.dist[l] - self.dist[r] != d:
                print("No")
                exit()
        else:
            #rRootとlRoot、どちらが親になるべきか決める
            newLDist = d + self.dist[r]
            if newLDist>=self.dist[l]:
                self.parents[lRoot] = rRoot
                self.dist[lRoot] = newLDist-self.dist[l]
            else:
                self.parents[rRoot] = lRoot
                self.dist[rRoot] = self.dist[l]-newLDist

N,M = list(map(int,input().split()))
tree = UnionFind(N)
for _ in range(M):
    l,r,d = list(map(int,input().split()))
    tree.unite(l,r,d)
print("Yes")
# Original solution
from collections import defaultdict
def solve():
	Base = 2000
	MAX = Base*2
	p = 10**9+7
	F = [1]; Finv = [0]*(MAX*2+1)
	for i in range(MAX*2): F.append(F[i]*(i+1)%p)
	Finv[MAX*2] = pow(F[MAX*2],p-2,p)
	for i in range(MAX*2,0,-1): Finv[i-1] = Finv[i]*i%p
	N = int(input())
	dup = A_MAX = B_MAX = 0
	dp = [[0]*(MAX+2) for i in range(MAX+2)]
	goals = defaultdict(int)
	for i in range(N):
		A,B = list(map(int,input().split()))
		dup = (dup + F[A*2+B*2]*Finv[A*2]*Finv[B*2]%p)%p
		if A > A_MAX: A_MAX = A
		if B > B_MAX: B_MAX = B
		goals[(Base+A,Base+B)] += 1
		dp[Base-B][Base-A] += 1
	for j in range(Base-B_MAX,Base+B_MAX+1):
		for i in range(Base-A_MAX,Base+A_MAX+1):
			dp[j+1][i] = (dp[j+1][i] + dp[j][i]) % p
			dp[j][i+1] = (dp[j][i+1] + dp[j][i]) % p
	ans = 0
	for (i,j),v in goals.items():
		ans = (ans + dp[j][i]*v) % p
	print(((ans - dup)*pow(2,p-2,p) + p) % p)
solve()

# Optimized solution
from collections import defaultdict
def solve():
	Base = 2000
	MAX = Base*2
	p = 10**9+7
	F = [1]; Finv = [0]*(MAX*2+1)
	for i in range(MAX*2): F.append(F[i]*(i+1)%p)
	Finv[MAX] = pow(F[MAX],p-2,p)
	for i in range(MAX,0,-1): Finv[i-1] = Finv[i]*i%p
	N = int(input())
	dup = A_MAX = B_MAX = 0
	dp = [[0]*(MAX+2) for i in range(MAX+2)]
	goals = defaultdict(int)
	for i in range(N):
		A,B = list(map(int,input().split()))
		dup = (dup + F[A*2+B*2]*Finv[A*2]*Finv[B*2])%p
		if A > A_MAX: A_MAX = A
		if B > B_MAX: B_MAX = B
		goals[(Base+A,Base+B)] += 1
		dp[Base-B][Base-A] += 1
	for j in range(Base-B_MAX,Base+B_MAX+1):
		for i in range(Base-A_MAX,Base+A_MAX+1):
			dp[j+1][i] = (dp[j+1][i] + dp[j][i]) % p
			dp[j][i+1] = (dp[j][i+1] + dp[j][i]) % p
	ans = 0
	for (i,j),v in goals.items():
		ans = (ans + dp[j][i]*v) % p
	print((ans - dup + p)*pow(2,p-2,p) % p)
solve()