You are given a graph consisting of $n$ vertices and $m$ edges. It is not guaranteed that the given graph is connected. Some edges are already directed and you can't change their direction. Other edges are undirected and you have to choose some direction for all these edges.

You have to direct undirected edges in such a way that the resulting graph is directed and acyclic (i.e. the graph with all edges directed and having no directed cycles). Note that you have to direct all undirected edges.

You have to answer $t$ independent test cases.


-----Input-----

The first line of the input contains one integer $t$ ($1 \le t \le 2 \cdot 10^4$) — the number of test cases. Then $t$ test cases follow.

The first line of the test case contains two integers $n$ and $m$ ($2 \le n \le 2 \cdot 10^5$, $1 \le m \le min(2 \cdot 10^5, \frac{n(n-1)}{2})$) — the number of vertices and the number of edges in the graph, respectively.

The next $m$ lines describe edges of the graph. The $i$-th edge is described with three integers $t_i$, $x_i$ and $y_i$ ($t_i \in [0; 1]$, $1 \le x_i, y_i \le n$) — the type of the edge ($t_i = 0$ if the edge is undirected and $t_i = 1$ if the edge is directed) and vertices this edge connects (the undirected edge connects vertices $x_i$ and $y_i$ and directed edge is going from the vertex $x_i$ to the vertex $y_i$). It is guaranteed that the graph do not contain self-loops (i.e. edges from the vertex to itself) and multiple edges (i.e. for each pair ($x_i, y_i$) there are no other pairs ($x_i, y_i$) or ($y_i, x_i$)).

It is guaranteed that both sum $n$ and sum $m$ do not exceed $2 \cdot 10^5$ ($\sum n \le 2 \cdot 10^5$; $\sum m \le 2 \cdot 10^5$).


-----Output-----

For each test case print the answer — "NO" if it is impossible to direct undirected edges in such a way that the resulting graph is directed and acyclic, otherwise print "YES" on the first line and $m$ lines describing edges of the resulted directed acyclic graph (in any order). Note that you cannot change the direction of the already directed edges. If there are several answers, you can print any.


-----Example-----
Input
4
3 1
0 1 3
5 5
0 2 1
1 1 5
1 5 4
0 5 2
1 3 5
4 5
1 1 2
0 4 3
1 3 1
0 2 3
1 2 4
4 5
1 4 1
1 1 3
0 1 2
1 2 4
1 3 2

Output
YES
3 1
YES
2 1
1 5
5 4
2 5
3 5
YES
1 2
3 4
3 1
3 2
2 4
NO



-----Note-----

Explanation of the second test case of the example:

[Image]

Explanation of the third test case of the example:

[Image]

Source: codeforces | Difficulty: HARD
Tags: Graph algorithms

Graph traversal

Constructive algorithms


Solutions:
import sys
input = sys.stdin.readline

def read_list():
	return map(int, input().split())

def print_list(l):
	print(' '.join(map(str, l)))
from collections import defaultdict as dc
N = int(input())
for _ in range(N):
	(n, m) = read_list()
	res = []
	gress = [0] * (n + 1)
	dn = dc(list)
	for _ in range(m):
		(d, x, y) = read_list()
		if d == 1:
			dn[x].append(y)
			gress[y] += 1
		res.append([d, x, y])
	zp = [i for i in range(1, n + 1) if gress[i] == 0]
	count = 0
	p = [0] * (n + 1)
	while zp:
		u = zp.pop()
		count += 1
		p[u] = count
		for v in dn[u]:
			gress[v] -= 1
			if gress[v] == 0:
				zp.append(v)
	if count == n:
		print('YES')
		for i in range(m):
			if res[i][0] == 1:
				print(res[i][1], res[i][2])
			elif p[res[i][1]] < p[res[i][2]]:
				print(res[i][1], res[i][2])
			else:
				print(res[i][2], res[i][1])
	else:
		print('NO')


t = int(input())
Maxn = 210000
while t > 0:
	t -= 1
	(n, m) = map(int, input().split())
	to = [[]]
	for i in range(0, n):
		to.append([])
	d = [0] * (n + 1)
	ran = [0] * (n + 1)
	q = []
	cnt = 0
	to[0].append(1)
	for i in range(0, m):
		(bj, u, v) = map(int, input().split())
		if bj:
			to[u].append(v)
			d[v] += 1
			q.append((u, v))
		else:
			q.append((u, v))
	que = []
	for i in range(1, n + 1):
		if d[i] == 0:
			que.append(i)
	while len(que):
		now = que.pop()
		cnt += 1
		ran[now] = cnt
		for i in to[now]:
			d[i] -= 1
			if d[i] == 0:
				que.append(i)
	if cnt != n:
		print('NO')
		continue
	print('YES')
	for i in q:
		if ran[i[0]] < ran[i[1]]:
			print(i[0], i[1])
		else:
			print(i[1], i[0])


from collections import defaultdict, deque
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	gr0 = defaultdict(list)
	gr1 = defaultdict(list)
	edges = []
	indeg = [0] * (n + 1)
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t == 0:
			edges.append([x, y])
		else:
			gr0[x].append(y)
			indeg[y] += 1
	q = deque()
	cnt = 0
	top = []
	mp = {}
	for i in range(1, n + 1):
		if indeg[i] == 0:
			q.append(i)
	while q:
		s = q.popleft()
		top.append(s)
		for i in gr0[s]:
			indeg[i] -= 1
			if indeg[i] == 0:
				q.append(i)
		cnt += 1
	if cnt != n:
		print('NO')
		continue
	for i in range(n):
		mp[top[i]] = i
	for (x, y) in edges:
		if mp[x] < mp[y]:
			gr0[x].append(y)
		else:
			gr0[y].append(x)
	print('YES')
	for i in gr0.keys():
		for j in gr0[i]:
			print(i, j)


import os, sys
from io import BytesIO, IOBase

def topsort(n, path):
	(ans, poi, visi) = ([], [0] * n, [0] * n)
	for i in range(n):
		if visi[i]:
			continue
		visi[i] = 2
		(st, st1) = ([i], [])
		while len(st):
			x = st[-1]
			if poi[x] == len(path[x]):
				visi[x] = 1
				st1.append(st.pop())
			else:
				i = path[x][poi[x]]
				poi[x] += 1
				if visi[i] == 2:
					return -1
				if not visi[i]:
					st.append(i)
					visi[i] = 2
		ans += st1
	return ans[::-1]

def main():
	for _ in range(int(input())):
		(n, m) = map(int, input().split())
		(edg, edg1) = ([], [])
		path = [[] for _ in range(n)]
		for _ in range(m):
			(t, a1, b1) = map(lambda xx: int(xx) - 1, input().split())
			if not t:
				path[a1].append(b1)
				edg1.append((a1 + 1, b1 + 1))
			else:
				edg.append((a1, b1))
		top = topsort(n, path)
		if top == -1:
			print('NO')
			continue
		node = [0] * n
		for (ind, i) in enumerate(top):
			node[i] = ind
		print('YES')
		for i in edg1:
			print(*i)
		for i in edg:
			if node[i[0]] > node[i[1]]:
				print(i[1] + 1, i[0] + 1)
			else:
				print(i[0] + 1, i[1] + 1)
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


from collections import defaultdict, deque
from sys import stdin, stdout

def input():
	return stdin.buffer.readline().rstrip()

def out(var):
	stdout.write(str(var) + '\n')
t = int(input())
(stats, arr, unchosen) = ([], [], [])
for _ in range(t):
	(numv, nume) = map(int, input().split())
	d = defaultdict(list)
	u = []
	for _ in range(nume):
		(t, x, y) = map(int, input().split())
		if t == 1:
			d[x].append(y)
		elif t == 0:
			u.append((x, y))
	stats.append(numv)
	unchosen.append(u)
	arr.append(d)
for i in range(len(arr)):
	(d, u, numv) = (arr[i], unchosen[i], stats[i])

	def get_indegrees(graph):
		indegree = {node: 0 for node in range(1, numv + 1)}
		for nodes in graph.values():
			for node in nodes:
				indegree[node] += 1
		return indegree

	def top_sort_bfs(graph):
		ans = []
		indegree = get_indegrees(graph)
		q = deque([node for node in indegree if indegree[node] == 0])
		while q:
			node = q.popleft()
			ans.append(node)
			for neighbor in graph[node]:
				indegree[neighbor] -= 1
				if indegree[neighbor] == 0:
					q.append(neighbor)
		return [] if len(ans) != len(graph) else ans
	toporder = top_sort_bfs(d)
	if len(toporder) == 0:
		out('NO')
		continue
	out('YES')
	for head in d:
		for tail in d[head]:
			out(f'{head} {tail}')
	position = {node: idx for (idx, node) in enumerate(toporder)}
	for (v1, v2) in u:
		if position[v1] < position[v2]:
			out(f'{v1} {v2}')
		else:
			out(f'{v2} {v1}')


from collections import deque

def bfs(indegree):
	q = deque()
	for i in range(len(indegree)):
		if indegree[i] == 0:
			q.append(i)
	while q:
		curr = q.popleft()
		topo.append(curr)
		for neighbor in g[curr]:
			indegree[neighbor] -= 1
			if indegree[neighbor] == 0:
				q.append(neighbor)
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	g = [[] * n for _ in range(n)]
	edgeList = []
	for _ in range(m):
		(t, u, v) = map(int, input().split())
		u -= 1
		v -= 1
		if t == 1:
			g[u].append(v)
		edgeList.append([u, v])
	indegree = [0] * n
	for vertex in g:
		for j in vertex:
			indegree[j] += 1
	topo = []
	bfs(indegree)
	pos = [0] * n
	for i in range(len(topo)):
		pos[topo[i]] = i
	if len(topo) != n:
		print('NO')
	else:
		print('YES')
		for (x, y) in edgeList:
			if pos[x] < pos[y]:
				print(x + 1, y + 1)
			else:
				print(y + 1, x + 1)


from collections import defaultdict as dc
N = int(input())
for _ in range(N):
	(n, m) = map(int, input().split())
	res = []
	gress = [0] * (n + 1)
	dn = dc(list)
	for _ in range(m):
		(d, x, y) = map(int, input().split())
		if d == 1:
			dn[x].append(y)
			gress[y] += 1
		res.append([d, x, y])
	zp = [i for i in range(1, n + 1) if gress[i] == 0]
	count = 0
	p = [0] * (n + 1)
	while zp:
		u = zp.pop()
		count += 1
		p[u] = count
		for v in dn[u]:
			gress[v] -= 1
			if gress[v] == 0:
				zp.append(v)
	if count == n:
		print('YES')
		for (d, x, y) in res:
			if d == 1:
				print(x, y)
			elif p[x] < p[y]:
				print(x, y)
			else:
				print(y, x)
	else:
		print('NO')


(n, m) = (int(), int())
gr = []
order = []
was = []

def pseudo_dfs(ver):
	was[ver] = 1
	good = True
	stck = [ver]
	while len(stck) > 0:
		ver = stck[-1]
		while len(gr[ver]) > 0:
			to = gr[ver][-1]
			if was[to] == 1:
				good = False
			if was[to] == 0:
				stck.append(to)
				was[to] = 1
				break
			gr[ver].pop()
		if len(gr[ver]) == 0:
			was[ver] = 2
			order.append(ver)
			stck.pop()
	return good
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	gr = [[] for _ in range(n)]
	ed = []
	for _ in range(m):
		(type, x, y) = map(int, input().split())
		x -= 1
		y -= 1
		ed.append((type, x, y))
		if type == 1:
			gr[x].append(y)
	was = [0 for _ in range(n)]
	good = True
	order.clear()
	for i in range(n):
		if was[i] == 0:
			good &= pseudo_dfs(i)
	if good == False:
		print('NO')
		continue
	num_in_order = [0 for _ in range(n)]
	for (num, elem) in enumerate(order):
		num_in_order[elem] = num
	print('YES')
	for e in ed:
		if e[0] == 0 and num_in_order[e[1]] < num_in_order[e[2]]:
			print(e[2] + 1, e[1] + 1)
		else:
			print(e[1] + 1, e[2] + 1)


import itertools

def main():
	t = int(input())
	for _ in range(t):
		(n, m) = map(int, input().split())
		Z = []
		G = [[] for _ in range(n)]
		deg = [0] * n
		for i in range(m):
			(temp, x, y) = map(int, input().split())
			x -= 1
			y -= 1
			if temp == 0:
				Z.append((x, y))
			else:
				G[x].append(y)
				deg[y] += 1
				Z.append((x, y))
		topo_list = topological(G, deg)
		if len(topo_list) < n:
			print('NO')
			continue
		print('YES')
		dic = {}
		for (i, x) in enumerate(topo_list):
			dic[x] = i
		for (x, y) in Z:
			if dic[x] < dic[y]:
				ans = [x + 1, y + 1]
			else:
				ans = [y + 1, x + 1]
			print(*ans)

def topological(graph, deg):
	start = []
	n = len(deg)
	for i in range(n):
		if deg[i] == 0:
			start.append(i)
	topo = []
	while start:
		v = start.pop()
		topo.append(v)
		for u in graph[v]:
			deg[u] -= 1
			if deg[u] == 0:
				start.append(u)
	return topo
main()


for _ in range(int(input())):
	(n, m) = map(int, input().split())
	go = {i: set() for i in range(1, n + 1)}
	gi = {i: set() for i in range(1, n + 1)}
	eu = []
	ed = []
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t:
			go[x].add(y)
			gi[y].add(x)
			ed.append((x, y))
		else:
			eu.append((x, y))
	l = []
	s = [i for i in gi if len(gi[i]) == 0]
	while s:
		i = s.pop()
		l.append(i)
		for j in go[i]:
			gi[j].discard(i)
			if gi[j] == set():
				s.append(j)
		go[i].clear()
	if any(go.values()):
		print('NO')
	else:
		print('YES')
		x = {}
		for (i, j) in enumerate(l):
			x[j] = i
		for i in range(1, n + 1):
			if i not in x:
				x[i] = n + i
		for (i, j) in eu:
			if x[i] < x[j]:
				print(i, j)
			else:
				print(j, i)
		for (i, j) in ed:
			print(i, j)


def main():
	import sys
	from collections import deque, defaultdict
	from heapq import heappop, heappush
	sys.setrecursionlimit(10 ** 9)
	input = sys.stdin.readline
	for __ in [0] * int(input()):
		(N, M) = list(map(int, input().split()))
		deg = [0] * N
		directed = defaultdict(list)
		undirected = defaultdict(list)
		for _ in [0] * M:
			(t, a, b) = list(map(int, input().split()))
			a -= 1
			b -= 1
			if t:
				directed[a].append(b)
				deg[b] += 1
			else:
				undirected[a].append(b)
				undirected[b].append(a)
		q = deque([i for (i, d) in enumerate(deg) if d == 0])
		topological = []
		while q:
			v = q.popleft()
			topological.append(v)
			if v not in directed:
				continue
			for u in directed[v]:
				deg[u] -= 1
				if deg[u] == 0:
					q.append(u)
		if len(topological) != N:
			print('NO')
			continue
		del deg
		print('YES')
		used = [0] * N
		for v in topological:
			if v in directed:
				for u in directed[v]:
					print(v + 1, u + 1)
			if v in undirected:
				for u in undirected[v]:
					if used[u]:
						continue
					print(v + 1, u + 1)
			used[v] = 1
		del used

def __starting_point():
	main()
__starting_point()


from sys import stdin, setrecursionlimit
input = stdin.readline

def solve():
	(n, m) = map(int, input().split())
	adj = [[] for _ in range(n)]
	edges = []
	deg = [0] * n
	for _ in range(m):
		(d, u, v) = map(int, input().split())
		u -= 1
		v -= 1
		edges.append((u, v))
		if d == 1:
			adj[u].append(v)
			deg[v] += 1
	pos = [0] * n
	idx = 0
	st = [i for i in range(n) if deg[i] == 0]
	while len(st) > 0:
		u = st.pop()
		pos[u] = idx
		idx += 1
		for v in adj[u]:
			deg[v] -= 1
			if deg[v] == 0:
				st.append(v)
	if idx < n:
		print('NO')
		return
	print('YES')
	for (x, y) in edges:
		if pos[x] < pos[y]:
			print(x + 1, y + 1)
		else:
			print(y + 1, x + 1)

def main():
	t = int(input())
	for _ in range(t):
		solve()
main()


import sys
import io, os
input = sys.stdin.buffer.readline
from collections import deque

def main():
	t = int(input())
	for _ in range(t):
		(n, m) = map(int, input().split())
		g = [[] for i in range(n)]
		ind = [0] * n
		edge1 = []
		edge0 = []
		for i in range(m):
			(t, x, y) = map(int, input().split())
			(x, y) = (x - 1, y - 1)
			if t == 1:
				g[x].append(y)
				ind[y] += 1
				edge1.append((x, y))
			else:
				edge0.append((x, y))
		q = deque([])
		order = []
		for i in range(n):
			if ind[i] == 0:
				q.append(i)
		while q:
			v = q.popleft()
			order.append(v)
			for u in g[v]:
				ind[u] -= 1
				if ind[u] == 0:
					q.append(u)
		if len(order) != n:
			print('NO')
			continue
		d = {}
		for (i, v) in enumerate(order):
			d[v] = i
		print('YES')
		for i in range(len(edge1)):
			(x, y) = edge1[i]
			print(x + 1, y + 1)
		for i in range(len(edge0)):
			(x, y) = edge0[i]
			if d[x] < d[y]:
				print(x + 1, y + 1)
			else:
				print(y + 1, x + 1)
main()


from sys import stdin
from collections import deque
tt = int(stdin.readline())
for loop in range(tt):
	Query = []
	ans = []
	(n, m) = list(map(int, stdin.readline().split()))
	lis = [[] for i in range(n)]
	inum = [0] * n
	ilis = inum
	index = [None] * n
	for i in range(m):
		(st, x, y) = list(map(int, stdin.readline().split()))
		x -= 1
		y -= 1
		if st == 1:
			ilis[y] += 1
			lis[x].append(y)
			ans.append((x, y))
		else:
			Query.append((x, y))
	endnum = 0
	q = deque([])
	for i in range(n):
		if ilis[i] == 0:
			q.append(i)
	while len(q) > 0:
		v = q.popleft()
		index[v] = endnum
		endnum += 1
		for nex in lis[v]:
			inum[nex] -= 1
			if inum[nex] == 0:
				q.append(nex)
	if endnum != n:
		print('NO')
	else:
		print('YES')
		for (x, y) in Query:
			if index[x] < index[y]:
				print(x + 1, y + 1)
			else:
				print(y + 1, x + 1)
		for (x, y) in ans:
			print(x + 1, y + 1)


import sys

def toposort(graph):
	res = []
	found = [0] * len(graph)
	stack = list(range(len(graph)))
	while stack:
		node = stack.pop()
		if node < 0:
			res.append(~node)
		elif not found[node]:
			found[node] = 1
			stack.append(~node)
			stack += graph[node]
	for node in res:
		if any((found[nei] for nei in graph[node])):
			return None
		found[node] = 0
	return res[::-1]
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	g = [[] for _ in range(n + 1)]
	ug = []
	for i in range(m):
		(t, u, v) = map(int, input().split())
		if t == 0:
			ug.append([u, v])
		else:
			g[u].append(v)
	sorted = toposort(g)
	if not sorted:
		print('NO')
	else:
		print('YES')
		index = [0] * (n + 1)
		for i in range(n + 1):
			index[sorted[i]] = i
		for i in range(n + 1):
			for u in g[i]:
				print(i, u)
		for (u, v) in ug:
			if index[u] < index[v]:
				print(u, v)
			else:
				print(v, u)


from collections import deque
import sys
input = sys.stdin.readline
t = int(input())
for i in range(t):
	(n, m) = (int(x) for x in input().split())
	undirected = []
	directed = []
	for j in range(m):
		(t, x, y) = (int(x) for x in input().split())
		if t == 0:
			undirected.append((x, y))
		else:
			directed.append((x, y))
	connected = [[] for _ in range(n + 1)]
	ori = [0] * (n + 1)
	for (x, y) in directed:
		connected[x].append(y)
		ori[y] += 1
	d = deque([j for j in range(1, n + 1) if ori[j] == 0])
	t_sorted = []
	while d:
		temp = d.popleft()
		t_sorted.append(temp)
		for edge in connected[temp]:
			ori[edge] -= 1
			if ori[edge] == 0:
				d.append(edge)
	if len(t_sorted) != n:
		print('NO')
	else:
		print('YES')
		ans = directed
		order = list(enumerate(t_sorted))
		order.sort(key=lambda x: x[1])
		for (x, y) in undirected:
			if order[x - 1][0] < order[y - 1][0]:
				ans.append((x, y))
			else:
				ans.append((y, x))
		for x in ans:
			print(*x)


from sys import stdin, gettrace
from collections import deque
if gettrace():
	inputi = input
else:

	def input():
		return next(stdin)[:-1]

	def inputi():
		return stdin.buffer.readline()

def solve():
	(n, m) = map(int, inputi().split())
	dadjs = [[] for _ in range(n)]
	dadjd = [set() for _ in range(n)]
	dedgec = 0
	res = []
	uedge = []
	for _ in range(m):
		(t, x, y) = map(int, inputi().split())
		if t == 0:
			uedge.append((x - 1, y - 1))
		else:
			res.append((x - 1, y - 1))
			dadjs[x - 1].append(y - 1)
			dadjd[y - 1].add(x - 1)
			dedgec += 1
	sources = deque((x for x in range(n) if not dadjd[x]))
	nlist = []
	while sources:
		s = sources.popleft()
		nlist.append(s)
		for a in dadjs[s]:
			dedgec -= 1
			dadjd[a].remove(s)
			if not dadjd[a]:
				sources.append(a)
		dadjs[s] = []
	if dedgec != 0:
		print('NO')
		return
	npos = [-1] * n
	for (i, j) in enumerate(nlist):
		npos[j] = i
	for (x, y) in uedge:
		if npos[x] < npos[y]:
			res.append((x, y))
		else:
			res.append((y, x))
	print('YES')
	for (x, y) in res:
		print(x + 1, y + 1)

def main():
	t = int(inputi())
	for _ in range(t):
		solve()
main()


import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def RL():
	return map(int, sys.stdin.readline().rstrip().split())

def RLL():
	return list(map(int, sys.stdin.readline().rstrip().split()))

def N():
	return int(input())

def comb(n, m):
	return factorial(n) / (factorial(m) * factorial(n - m)) if n >= m else 0

def perm(n, m):
	return factorial(n) // factorial(n - m) if n >= m else 0

def mdis(x1, y1, x2, y2):
	return abs(x1 - x2) + abs(y1 - y2)
mod = 998244353
INF = float('inf')
from math import factorial
from collections import Counter, defaultdict, deque
from heapq import heapify, heappop, heappush
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

def main():
	for _ in range(N()):
		(n, m) = RL()
		gp = [[[], []] for _ in range(n + 1)]
		ind = [0] * (n + 1)
		edgs = []
		q = []
		for _ in range(m):
			(t, x, y) = RL()
			if t == 0:
				gp[x][0].append(y)
				gp[y][0].append(x)
			else:
				gp[x][1].append(y)
				ind[y] += 1
			edgs.append([x, y])
		for i in range(1, n + 1):
			if ind[i] == 0:
				q.append(i)
		ct = 0
		sq = [0] * (n + 1)
		while q:
			nd = q.pop()
			ct += 1
			sq[nd] = ct
			for nex in gp[nd][1]:
				ind[nex] -= 1
				if ind[nex] == 0:
					q.append(nex)
		if ct == n:
			print('YES')
			for (a, b) in edgs:
				(sa, sb) = (sq[a], sq[b])
				if sa < sb:
					print(a, b)
				else:
					print(b, a)
		else:
			print('NO')
main()


from collections import defaultdict, deque

def topsort(n, adj, indeg):
	seen = 0
	top = []
	q = deque((v for v in map(str, range(1, n + 1)) if indeg[v] == 0))
	while q:
		v = q.popleft()
		seen += 1
		top.append(v)
		for nei in adj[v]:
			indeg[nei] -= 1
			if indeg[nei] == 0:
				q.append(nei)
	if seen != n:
		return (False, None)
	else:
		return (True, {v: i for (i, v) in enumerate(top)})
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	e = defaultdict(list)
	f = set()
	indeg = defaultdict(int)
	for _ in range(m):
		(t, u, v) = input().split()
		if t == '1':
			e[u].append(v)
			indeg[v] += 1
		else:
			f.add((u, v))
	(no_cycle, rank) = topsort(n, e, indeg)
	if no_cycle:
		print('YES')
		for (u, vs) in e.items():
			for v in vs:
				print(u, v)
		for (u, v) in f:
			if rank[u] < rank[v]:
				print(u, v)
			else:
				print(v, u)
	else:
		print('NO')


from collections import deque
from collections import defaultdict
for _ in range(int(input())):
	(n, m) = list(map(int, input().split()))
	ud = defaultdict(list)
	d = defaultdict(list)
	pi = [0] * (n + 1)
	v = [0] * (n + 1)
	pis = [0] * (n + 1)
	u = 0
	co = 0
	for _ in range(m):
		(t, a, b) = list(map(int, input().split()))
		if t == 0:
			ud[a].append(b)
			ud[b].append(a)
		else:
			d[a].append(b)
			pi[b] += 1
	queue = deque([])
	for i in range(1, n + 1):
		if pi[i] == 0:
			queue.append(i)
			pis[i] = co
			co += 1
			v[i] = 1
	while len(queue) != 0:
		a = queue.popleft()
		for i in d[a]:
			pi[i] -= 1
			if pi[i] == 0:
				queue.append(i)
				pis[i] = co
				co += 1
				v[i] = 1
	if sum(v) == n:
		print('YES')
	else:
		print('NO')
		continue
	for i in ud:
		for j in ud[i]:
			if pis[i] < pis[j]:
				print(i, j)
	for i in ud:
		for j in ud[i]:
			if pis[i] == pis[j] and i < j:
				print(i, j)
	for i in d:
		for j in d[i]:
			print(i, j)


import sys, math
import io, os
from bisect import bisect_left as bl, bisect_right as br, insort
from collections import defaultdict as dd, deque, Counter

def data():
	return sys.stdin.readline().strip()

def mdata():
	return list(map(int, data().split()))

def outl(var):
	sys.stdout.write('\n'.join(map(str, var)) + '\n')

def out(var):
	sys.stdout.write(str(var) + '\n')
from fractions import Fraction
INF = float('inf')
mod = int(1000000000.0) + 7

def toposort(graph):
	(res, found) = ([], [0] * len(graph))
	stack = list(range(len(graph)))
	while stack:
		node = stack.pop()
		if node < 0:
			res.append(~node)
		elif not found[node]:
			found[node] = 1
			stack.append(~node)
			stack += graph[node]
	for node in res:
		if any((found[nei] for nei in graph[node])):
			return None
		found[node] = 0
	return res[::-1]
for _ in range(int(data())):
	(n, m) = mdata()
	g = [[] for i in range(n)]
	l = []
	ans = []
	for i in range(m):
		(t, x, y) = mdata()
		x -= 1
		y -= 1
		if t == 0:
			l.append((x, y))
		else:
			g[x].append(y)
			ans.append(str(x + 1) + ' ' + str(y + 1))
	top = toposort(g)
	if top:
		val = [0] * (n + 1)
		for (i, j) in enumerate(top):
			val[j] = i
		out('YES')
		for (x, y) in l:
			if val[x] < val[y]:
				ans.append(str(x + 1) + ' ' + str(y + 1))
			else:
				ans.append(str(y + 1) + ' ' + str(x + 1))
		outl(ans)
	else:
		out('NO')


from sys import stdin, stdout
import functools
import sys, os, math
T = int(input())
for _ in range(T):
	(N, M) = map(int, input().split())
	DS = [0] * (N + 1)
	ES = []
	g = [[] for _ in range(N + 1)]
	for _ in range(M):
		(t, u, v) = map(int, input().split())
		ES.append([t, u, v])
		if t == 1:
			DS[u] += 1
		g[u].append(len(ES) - 1)
		g[v].append(len(ES) - 1)
	q = []
	for u in range(1, N + 1):
		if DS[u] == 0:
			q.append(u)
	while len(q) > 0:
		u = q.pop()
		if DS[u] > 0:
			continue
		for e in g[u]:
			(t, u0, v0) = ES[e]
			if t == 1:
				if v0 == u:
					DS[u0] -= 1
					if DS[u0] == 0:
						q.append(u0)
			elif t == 0:
				v = v0 if u0 == u else u0
				ES[e] = [1, v, u]
	md = max(DS)
	if md > 0:
		print('NO')
	else:
		print('YES')
		for e in ES:
			print(e[1], e[2])


from collections import deque

def topo(g):
	par = [0] * (n + 1)
	for now in g:
		for nex in now:
			par[nex] += 1
	q = deque([])
	order = []
	for i in range(1, n + 1):
		if par[i] == 0:
			q.append(i)
	while q:
		v = q.popleft()
		order.append(v)
		for nex in g[v]:
			par[nex] -= 1
			if par[nex] == 0:
				q.append(nex)
	return order
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	e = [[] for i in range(n + 1)]
	l = []
	for i in range(m):
		(a, x, y) = map(int, input().split())
		if a == 1:
			e[x].append(y)
		l.append([x, y])
	order = topo(e)
	if len(order) < n:
		print('NO')
	else:
		print('YES')
		ind = [-1] * (n + 1)
		for i in range(len(order)):
			ind[order[i]] = i
		for (i, j) in l:
			if ind[i] > ind[j]:
				(i, j) = (j, i)
			print(i, j)


import sys
input = sys.stdin.readline
from collections import defaultdict, deque
(T,) = map(int, input().split())
for _ in range(T):
	(N, M) = map(int, input().split())
	G = [set() for _ in range(N + 1)]
	es = []
	ees = []
	D = defaultdict(int)
	in_cnt = defaultdict(int)
	outs = defaultdict(int)
	for _ in range(M):
		(t, x, y) = map(int, input().split())
		if t:
			G[x].add(y)
			in_cnt[y] += 1
			ees.append((x, y))
		else:
			es.append((x, y))
	res = []
	queue = deque([i for i in range(1, N + 1) if in_cnt[i] == 0])
	v2i = dict()
	while len(queue) != 0:
		v = queue.popleft()
		res.append(v)
		for v2 in G[v]:
			in_cnt[v2] -= 1
			if in_cnt[v2] == 0:
				queue.append(v2)
	if len(res) != N:
		print('NO')
		continue
	print('YES')
	for i in range(N):
		v2i[res[i]] = i
	for (x, y) in es:
		if v2i[x] < v2i[y]:
			print(x, y)
		else:
			print(y, x)
	for (x, y) in ees:
		print(x, y)


from sys import stdin, stdout

def directing_edges(n, m, ind, dic, seq):
	q = []
	res = []
	for i in range(n):
		if ind[i] == 0:
			q.append(i)
	while len(q) > 0:
		cur = q.pop()
		res.append(cur)
		if cur in dic:
			for next in dic[cur]:
				ind[next] -= 1
				if ind[next] == 0:
					q.append(next)
	if len(res) < n:
		stdout.write('NO\n')
	else:
		stdout.write('YES\n')
		pos = [0] * n
		for i in range(n):
			pos[res[i]] = i
		for sq in seq:
			if pos[sq[0]] < pos[sq[1]]:
				print(sq[0] + 1, sq[1] + 1)
			else:
				print(sq[1] + 1, sq[0] + 1)
t = int(stdin.readline())
for i in range(t):
	(n, m) = map(int, stdin.readline().split())
	dic = {}
	ind = [0] * n
	seq = []
	for j in range(m):
		(t, x, y) = map(int, stdin.readline().split())
		x -= 1
		y -= 1
		seq.append([x, y])
		if t == 1:
			if x not in dic:
				dic[x] = []
			dic[x].append(y)
			ind[y] += 1
	directing_edges(n, m, ind, dic, seq)


import sys, collections
input = sys.stdin.readline

def topo(G, S, que, ID):
	while len(que) != 0:
		u = que.pop()
		S.append(u)
		for i in G[u]:
			ID[i] = ID[i] - 1
			if ID[i] == 0:
				que.append(i)
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	DG = []
	UG = {}
	ID = [-1 for i in range(n + 1)]
	ce = 0
	FG = []
	for i in range(1, n + 1):
		UG[i] = []
	for _ in range(m):
		(a, b, c) = map(int, input().split())
		if a == 0:
			DG.append([b, c])
		else:
			UG[b].append(c)
			ID[c] = max(ID[c] + 1, 1)
			ID[b] = max(ID[b], 0)
			FG.append([b, c])
	que = collections.deque([])
	S = collections.deque([])
	for i in range(1, n + 1):
		if ID[i] == 0:
			que.append(i)
		if ID[i] != -1:
			ce = ce + 1
	topo(UG, S, que, ID)
	if len(S) != ce:
		print('NO')
	else:
		print('YES')
		for i in range(len(S)):
			ID[S[i]] = i
		for i in DG:
			(a, b) = (i[0], i[1])
			if ID[a] > ID[b]:
				FG.append([b, a])
			elif ID[a] == ID[b]:
				FG.append([min(a, b), max(a, b)])
			else:
				FG.append([a, b])
		for i in FG:
			print(*i)


from collections import deque, defaultdict
it = lambda : list(map(int, input().strip().split()))

def solve():
	(n, m) = it()
	indegree = defaultdict(int)
	decided = defaultdict(list)
	undecided = defaultdict(list)
	for _ in range(m):
		(a, x, y) = it()
		if a == 1:
			indegree[y] += 1
			decided[x].append(y)
		else:
			undecided[x].append(y)
			undecided[y].append(x)
	queue = deque()
	for x in range(1, n + 1):
		if indegree[x] == 0:
			queue.append(x)
	ans = set()
	while queue and len(ans) < m:
		x = queue.popleft()
		for y in decided[x]:
			ans.add((x, y))
			indegree[y] -= 1
			if indegree[y] == 0:
				queue.append(y)
		for y in undecided[x]:
			if not (x, y) in ans and (not (y, x) in ans):
				ans.add((x, y))
	if len(ans) < m:
		print('NO')
	else:
		print('YES')
		for (x, y) in ans:
			print(x, y)
T = int(input())
for _ in range(T):
	solve()


from collections import defaultdict, deque

class Graph:

	def __init__(self, nVertices, graphArr):
		self.graph = defaultdict(list)
		for (u, v) in graphArr:
			self.graph[u].append(v)
		self.V = nVertices

	def topologicalSort(self):
		in_degree = [0] * self.V
		for i in self.graph:
			for j in self.graph[i]:
				in_degree[j] += 1
		queue = deque()
		for i in range(self.V):
			if in_degree[i] == 0:
				queue.append(i)
		cnt = 0
		top_order = []
		while queue:
			u = queue.popleft()
			top_order.append(u)
			for i in self.graph[u]:
				in_degree[i] -= 1
				if in_degree[i] == 0:
					queue.append(i)
			cnt += 1
		if cnt != self.V:
			return []
		else:
			return top_order
import sys
input = sys.stdin.buffer.readline
t = int(input())
for _ in range(t):
	(n, m) = [int(x) for x in input().split()]
	de = []
	ude = [set() for __ in range(n)]
	for __ in range(m):
		(t, x, y) = [int(x) for x in input().split()]
		if t == 0:
			ude[x - 1].add(y - 1)
			ude[y - 1].add(x - 1)
		else:
			de.append([x - 1, y - 1])
	g = Graph(n, de)
	topo = g.topologicalSort()
	if len(topo) == 0:
		print('NO')
		continue
	nde = []
	for i in topo:
		if len(ude[i]) > 0:
			for j in ude[i]:
				nde.append([i, j])
				ude[j].remove(i)
			ude[i] = set()
	for i in range(n):
		for j in ude[i]:
			nde.append([i, j])
	for (i, j) in de:
		nde.append([i, j])
	print('YES')
	for (i, j) in nde:
		print('{} {}'.format(i + 1, j + 1))


def solve():
	(n, m) = map(int, input().split())
	edges = []
	adj = [[] for _ in range(n)]
	indeg = [0] * n
	for _ in range(m):
		(t, x, y) = map(int, input().split())
		x -= 1
		y -= 1
		edges.append((x, y))
		if t == 1:
			adj[x].append(y)
			indeg[y] += 1
	q = [i for i in range(n) if indeg[i] == 0]
	count = 0
	order = [0] * n
	while q:
		top = q.pop()
		order[top] = count
		count += 1
		for nei in adj[top]:
			indeg[nei] -= 1
			if indeg[nei] == 0:
				q.append(nei)
	if count < n:
		print('NO')
		return
	print('YES')
	for (x, y) in edges:
		if order[x] > order[y]:
			(x, y) = (y, x)
		print(x + 1, y + 1)
t = int(input())
for _ in range(t):
	solve()


from sys import stdin
input = lambda : stdin.readline().strip()
from math import ceil, sqrt, factorial, gcd, log
from collections import deque
from bisect import bisect_left, bisect_right

def dfs(x):
	stack = [x]
	a = 0
	while stack:
		if len(dire[stack[-1]]) == 0:
			z.append(stack.pop())
		else:
			stack.append(dire[stack[-1]].pop())
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	graph = []
	directed = {i: set() for i in range(1, n + 1)}
	dire = {i: [] for i in range(1, n + 1)}
	for i in range(m):
		(t, a, b) = map(int, input().split())
		if t == 0:
			graph.append([a, b])
		else:
			directed[a].add(b)
			dire[a].append(b)
	visited = set()
	z = []
	for i in range(1, n + 1):
		if i not in visited:
			dfs(i)
	pos = [0 for i in range(n + 1)]
	z.reverse()
	for i in range(len(z)):
		pos[z[i]] = i
	cycle = 0
	for i in range(1, n + 1):
		for j in directed[i]:
			if pos[i] > pos[j]:
				cycle += 1
	if cycle > 0:
		print('NO')
	else:
		print('YES')
		for i in graph:
			(a, b) = i
			if pos[a] < pos[b]:
				print(a, b)
			else:
				print(b, a)
		for i in directed:
			for j in directed[i]:
				print(i, j)


import queue

class Task:

	def __init__(self) -> None:
		self.nedges = 0
		self.nvertices = 0
		self.directed = {}
		self.undirected = []
		self.position = {}

	def pos(self, u: int) -> int:
		if u in self.position:
			return self.position[u]
		return 0

	def ReadInput(self) -> None:
		(self.nvertices, self.nedges) = map(int, input().split())
		for _ in range(self.nedges):
			(t, start, end) = map(int, input().split())
			if t == 1:
				if start in self.directed:
					self.directed[start].append(end)
				else:
					self.directed[start] = [end]
			else:
				self.undirected.append((start, end))

	def WriteOutput(self) -> None:
		if not self.Topsort():
			print('NO')
			return
		for u in range(1, self.nvertices + 1):
			if u in self.directed:
				for v in self.directed[u]:
					if self.pos(u) >= self.pos(v):
						print('NO')
						return
		print('YES')
		for u in self.directed:
			for v in self.directed[u]:
				print(u, v)
		for (u, v) in self.undirected:
			if self.pos(u) < self.pos(v):
				print(u, v)
			else:
				print(v, u)

	def Topsort(self) -> bool:
		Q = queue.Queue()
		ideg = {}
		position = 1
		for u in range(1, self.nvertices + 1):
			if u in self.directed:
				for v in self.directed[u]:
					if v in ideg:
						ideg[v] += 1
					else:
						ideg[v] = 1
		for u in range(1, self.nvertices + 1):
			if u not in ideg:
				Q.put(u)
		if Q.empty():
			return False
		while not Q.empty():
			u = Q.get()
			self.position[u] = position
			position += 1
			if u in self.directed:
				for v in self.directed[u]:
					ideg[v] -= 1
					if ideg[v] == 0:
						Q.put(v)
		return True

def main() -> None:
	ntests = int(input())
	for _ in range(ntests):
		task = Task()
		task.ReadInput()
		task.WriteOutput()
main()


from collections import defaultdict as dd, deque as dq
import sys
input = sys.stdin.readline

def topological(d, ind, pos, n, cou):
	q = dq([])
	for i in range(1, n + 1):
		if ind[i] == 0:
			q.appendleft(i)
	curr = 0
	while q:
		a = q.pop()
		pos[a] = curr
		curr += 1
		for j in d[a]:
			ind[j] -= 1
			if ind[j] == 0:
				q.appendleft(j)
	return curr == n
t = int(input())
while t:
	d = dd(list)
	(n, m) = map(int, input().split())
	l = []
	ind = [0] * (n + 1)
	cou = 0
	for i in range(m):
		(typ, u, v) = map(int, input().split())
		if typ == 1:
			d[u].append(v)
			ind[v] += 1
			cou += 1
		l.append((typ, u, v))
	pos = [0] * (n + 1)
	if not topological(d, ind, pos, n, cou):
		print('NO')
	else:
		print('YES')
		for i in range(m):
			if l[i][0] == 1:
				print(l[i][1], l[i][2])
			elif pos[l[i][1]] > pos[l[i][2]]:
				print(l[i][2], l[i][1])
			else:
				print(l[i][1], l[i][2])
	t -= 1


import time, math as mt, bisect, sys
from sys import stdin, stdout
from collections import deque
from fractions import Fraction
from collections import Counter
from collections import OrderedDict
pi = 3.141592653589793

def II():
	return int(stdin.readline())

def IO():
	return stdin.readline()

def IP():
	return map(int, stdin.readline().split())

def L():
	return list(map(int, stdin.readline().split()))

def P(x):
	return stdout.write(str(x) + '\n')

def PI(x, y):
	return stdout.write(str(x) + ' ' + str(y) + '\n')

def lcm(a, b):
	return a * b // gcd(a, b)

def gcd(a, b):
	if a == 0:
		return b
	elif b == 0:
		return a
	if a > b:
		return gcd(a % b, b)
	else:
		return gcd(a, b % a)

def bfs(adj, v):
	visited = [False] * (v + 1)
	q = deque()
	while q:
		pass

def sieve():
	li = [True] * 1000001
	(li[0], li[1]) = (False, False)
	for i in range(2, len(li), 1):
		if li[i] == True:
			for j in range(i * i, len(li), i):
				li[j] = False
	prime = []
	for i in range(1000001):
		if li[i] == True:
			prime.append(i)
	return prime

def setBit(n):
	count = 0
	while n != 0:
		n = n & n - 1
		count += 1
	return count
mx = 10 ** 7
spf = [mx] * (mx + 1)

def SPF():
	spf[1] = 1
	for i in range(2, mx + 1):
		if spf[i] == mx:
			spf[i] = i
			for j in range(i * i, mx + 1, i):
				if i < spf[j]:
					spf[j] = i
	return
mod = 10 ** 9 + 7

def readGraph(n, e):
	adj = [set() for i in range(n + 1)]
	edge = deque()
	for i in range(e):
		(t, u1, u2) = map(int, input().split())
		if t == 1:
			adj[u1].add(u2)
			in_degree[u2] += 1
		edge.append((u1, u2))
	return (adj, edge)

def kahn_topo_sort():
	q = deque()
	topo = []
	for i in range(n + 1):
		if in_degree[i] == 0:
			q.append(i)
	while q:
		ele = q.popleft()
		topo.append(ele)
		for ver in adj[ele]:
			in_degree[ver] -= 1
			if in_degree[ver] == 0:
				q.append(ver)
	if len(topo) != n:
		return [-1]
	else:
		return topo
t = II()
while t != 0:
	(n, e) = map(int, input().split())
	in_degree = [0] * (n + 1)
	in_degree[0] = -99
	(adj, edge) = readGraph(n, e)
	topo = kahn_topo_sort()
	if topo == [-1]:
		print('NO')
	else:
		print('YES')
		d = {}
		for i in range(len(topo)):
			d[topo[i]] = i
		for ele in edge:
			if d[ele[0]] < d[ele[1]]:
				print(ele[0], ele[1])
			else:
				print(ele[1], ele[0])
	t -= 1


from bisect import *
from collections import *
from math import gcd, ceil, sqrt, floor, inf
from heapq import *
from itertools import *
from operator import add, mul, sub, xor, truediv, floordiv
from functools import *
import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def RL():
	return map(int, sys.stdin.readline().rstrip().split())

def RLL():
	return list(map(int, sys.stdin.readline().rstrip().split()))

def N():
	return int(input())
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc
farr = [1]
ifa = []

def fact(x, mod=0):
	if mod:
		while x >= len(farr):
			farr.append(farr[-1] * len(farr) % mod)
	else:
		while x >= len(farr):
			farr.append(farr[-1] * len(farr))
	return farr[x]

def ifact(x, mod):
	global ifa
	ifa.append(pow(farr[-1], mod - 2, mod))
	for i in range(x, 0, -1):
		ifa.append(ifa[-1] * i % mod)
	ifa = ifa[::-1]

def per(i, j, mod=0):
	if i < j:
		return 0
	if not mod:
		return fact(i) // fact(i - j)
	return farr[i] * ifa[i - j] % mod

def com(i, j, mod=0):
	if i < j:
		return 0
	if not mod:
		return per(i, j) // fact(j)
	return per(i, j, mod) * ifa[j] % mod

def catalan(n):
	return com(2 * n, n) // (n + 1)

def linc(f, t, l, r):
	while l < r:
		mid = (l + r) // 2
		if t > f(mid):
			l = mid + 1
		else:
			r = mid
	return l

def rinc(f, t, l, r):
	while l < r:
		mid = (l + r + 1) // 2
		if t < f(mid):
			r = mid - 1
		else:
			l = mid
	return l

def ldec(f, t, l, r):
	while l < r:
		mid = (l + r) // 2
		if t < f(mid):
			l = mid + 1
		else:
			r = mid
	return l

def rdec(f, t, l, r):
	while l < r:
		mid = (l + r + 1) // 2
		if t > f(mid):
			r = mid - 1
		else:
			l = mid
	return l

def isprime(n):
	for i in range(2, int(n ** 0.5) + 1):
		if n % i == 0:
			return False
	return True

def binfun(x):
	c = 0
	for w in arr:
		c += ceil(w / x)
	return c

def lowbit(n):
	return n & -n

def inverse(a, m):
	a %= m
	if a <= 1:
		return a
	return (1 - inverse(m, a) * m) // a % m

class BIT:

	def __init__(self, arr):
		self.arr = arr
		self.n = len(arr) - 1

	def update(self, x, v):
		while x <= self.n:
			self.arr[x] += v
			x += x & -x

	def query(self, x):
		ans = 0
		while x:
			ans += self.arr[x]
			x &= x - 1
		return ans

class DSU:

	def __init__(self, n):
		self.c = [-1] * n

	def same(self, x, y):
		return self.find(x) == self.find(y)

	def find(self, x):
		if self.c[x] < 0:
			return x
		self.c[x] = self.find(self.c[x])
		return self.c[x]

	def union(self, u, v):
		(u, v) = (self.find(u), self.find(v))
		if u == v:
			return False
		if self.c[u] < self.c[v]:
			(u, v) = (v, u)
		self.c[u] += self.c[v]
		self.c[v] = u
		return True

	def size(self, x):
		return -self.c[self.find(x)]

class UFS:

	def __init__(self, n):
		self.parent = [i for i in range(n)]
		self.ranks = [0] * n

	def find(self, x):
		if x != self.parent[x]:
			self.parent[x] = self.find(self.parent[x])
		return self.parent[x]

	def union(self, u, v):
		(pu, pv) = (self.find(u), self.find(v))
		if pu == pv:
			return False
		if self.ranks[pu] >= self.ranks[pv]:
			self.parent[pv] = pu
			if self.ranks[pv] == self.ranks[pu]:
				self.ranks[pu] += 1
		else:
			self.parent[pu] = pv

def Prime(n):
	c = 0
	prime = []
	flag = [0] * (n + 1)
	for i in range(2, n + 1):
		if not flag[i]:
			prime.append(i)
			c += 1
		for j in range(c):
			if i * prime[j] > n:
				break
			flag[i * prime[j]] = prime[j]
			if i % prime[j] == 0:
				break
	return prime

def dij(s, graph):
	d = {}
	d[s] = 0
	heap = [(0, s)]
	seen = set()
	while heap:
		(dis, u) = heappop(heap)
		if u in seen:
			continue
		for v in graph[u]:
			if v not in d or d[v] > d[u] + graph[u][v]:
				d[v] = d[u] + graph[u][v]
				heappush(heap, (d[v], v))
	return d

def GP(it):
	return [[ch, len(list(g))] for (ch, g) in groupby(it)]

class DLN:

	def __init__(self, val):
		self.val = val
		self.pre = None
		self.next = None
t = N()
for i in range(t):
	(n, m) = RL()
	ans = []
	dg = [[] for i in range(n + 1)]
	ug = []
	ing = [0] * (n + 1)
	for i in range(m):
		(t, x, y) = RL()
		if t == 1:
			ans.append((x, y))
			dg[x].append(y)
			ing[y] += 1
		else:
			ug.append((x, y))
	q = deque()
	k = 0
	s = [-1] * (n + 1)
	for i in range(1, n + 1):
		if ing[i] == 0:
			q.append(i)
			s[i] = k
			k += 1
	while q:
		u = q.popleft()
		for v in dg[u]:
			ing[v] -= 1
			if ing[v] == 0:
				s[v] = k
				k += 1
				q.append(v)
	if n != k:
		print('NO')
	else:
		for (u, v) in ug:
			if s[u] < s[v]:
				ans.append((u, v))
			else:
				ans.append((v, u))
		print('YES')
		for i in range(m):
			print(*ans[i])


import sys
input = sys.stdin.readline
from collections import deque

class Graph(object):

	def __init__(self, n, d):
		self.n = n
		self.graph = [[] for i in range(n)]
		self.parent = [-1 for i in range(n)]
		self.directed = d

	def addEdge(self, x, y):
		self.graph[x].append(y)
		if not self.directed:
			self.graph[y].append(x)

	def bfs(self, root):
		queue = [root]
		queue = deque(queue)
		vis = [0] * self.n
		while len(queue) != 0:
			element = queue.popleft()
			vis[element] = 1
			for i in self.graph[element]:
				if vis[i] == 0:
					queue.append(i)
					self.parent[i] = element
					vis[i] = 1

	def dfs(self, root, ans):
		stack = [root]
		vis = [0] * self.n
		stack2 = []
		while len(stack) != 0:
			element = stack.pop()
			if vis[element]:
				continue
			vis[element] = 1
			stack2.append(element)
			for i in self.graph[element]:
				if vis[i] == 0:
					self.parent[i] = element
					stack.append(i)
		while len(stack2) != 0:
			element = stack2.pop()
			m = 0
			for i in self.graph[element]:
				if i != self.parent[element]:
					m += ans[i]
			ans[element] = m
		return ans

	def shortestpath(self, source, dest):
		self.bfs(source)
		path = [dest]
		while self.parent[path[-1]] != -1:
			path.append(parent[path[-1]])
		return path[::-1]

	def detect_cycle(self):
		indeg = [0] * self.n
		for i in range(self.n):
			for j in self.graph[i]:
				indeg[j] += 1
		q = deque()
		vis = 0
		ans = []
		for i in range(self.n):
			if indeg[i] == 0:
				q.append(i)
		while len(q) != 0:
			e = q.popleft()
			ans.append(e)
			vis += 1
			for i in self.graph[e]:
				indeg[i] -= 1
				if indeg[i] == 0:
					q.append(i)
		if vis != self.n:
			return (True, [])
		return (False, ans)

	def reroot(self, root, ans):
		stack = [root]
		vis = [0] * n
		while len(stack) != 0:
			e = stack[-1]
			if vis[e]:
				stack.pop()
				continue
			vis[e] = 1
			for i in graph[e]:
				if not vis[e]:
					stack.append(i)
			if self.parent[e] == -1:
				continue
for nt in range(int(input())):
	(n, m) = map(int, input().split())
	g = Graph(n, True)
	edges = []
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t == 1:
			g.addEdge(x - 1, y - 1)
		else:
			edges.append((x - 1, y - 1))
	x = g.detect_cycle()
	if x[0]:
		print('NO')
		continue
	top = x[1]
	loc = {}
	for i in range(n):
		loc[top[i]] = i
	for i in edges:
		if loc[i[0]] < loc[i[1]]:
			g.addEdge(i[0], i[1])
		else:
			g.addEdge(i[1], i[0])
	if g.detect_cycle()[0]:
		print('NO')
		continue
	print('YES')
	for i in range(n):
		for j in g.graph[i]:
			print(i + 1, j + 1)


for _ in range(int(input())):
	(n, m) = map(int, input().split())
	g = {i: [] for i in range(1, n + 1)}
	(indegree, rnk) = ([0] * (n + 1), [0] * (n + 1))
	edge = []
	for __ in range(m):
		(t, u, v) = map(int, input().split())
		if t:
			g[u].append(v)
			indegree[v] += 1
		edge.append((u, v))
	stack = []
	for i in range(1, n + 1):
		if indegree[i] == 0:
			stack.append(i)
	cnt = 0
	while stack:
		u = stack.pop()
		(rnk[u], cnt) = (cnt + 1, cnt + 1)
		for v in g[u]:
			indegree[v] -= 1
			if indegree[v] == 0:
				stack.append(v)
	if cnt != n:
		print('NO')
	else:
		print('YES')
		for x in edge:
			print(*x) if rnk[x[0]] < rnk[x[1]] else print(*x[::-1])


from math import *
from collections import *
from bisect import *
import sys
input = sys.stdin.readline
t = int(input())

def fa(a):
	indeg = [0 for i in range(n + 1)]
	r = []
	qu = deque([])
	for i in range(1, n + 1):
		for j in gr[i]:
			indeg[j] += 1
	for i in range(1, n + 1):
		if indeg[i] == 0:
			qu.append(i)
	while qu:
		x = qu.popleft()
		r.append(x)
		for i in gr[x]:
			indeg[i] -= 1
			if indeg[i] == 0:
				qu.append(i)
	return r
while t:
	t -= 1
	(n, m) = map(int, input().split())
	gr = [[] for i in range(n + 1)]
	ed = []
	for i in range(m):
		(ty, x, y) = map(int, input().split())
		ed.append([x, y])
		if ty:
			gr[x].append(y)
	l = fa(gr)
	if len(l) < n:
		print('NO')
	else:
		print('YES')
		ind = {}
		co = 0
		for i in l:
			ind[i] = co
			co += 1
		for i in ed:
			if ind[i[0]] > ind[i[1]]:
				print(i[1], i[0])
			else:
				print(i[0], i[1])


import sys, math
import io, os
from bisect import bisect_left as bl, bisect_right as br, insort
from collections import defaultdict as dd, deque, Counter

def data():
	return sys.stdin.readline().strip()

def mdata():
	return list(map(int, data().split()))

def outl(var):
	sys.stdout.write('\n'.join(map(str, var)) + '\n')

def out(var):
	sys.stdout.write(str(var) + '\n')
from fractions import Fraction
INF = float('inf')
mod = int(1000000000.0) + 7

def find_SCC(coupl):
	(SCC, S, P) = ([], [], [])
	depth = [0] * len(coupl)
	stack = list(range(len(coupl)))
	while stack:
		node = stack.pop()
		if node < 0:
			d = depth[~node] - 1
			if P[-1] > d:
				SCC.append(S[d:])
				del S[d:], P[-1]
				for node in SCC[-1]:
					depth[node] = -1
		elif depth[node] > 0:
			while P[-1] > depth[node]:
				P.pop()
		elif depth[node] == 0:
			S.append(node)
			P.append(len(S))
			depth[node] = len(S)
			stack.append(~node)
			stack += coupl[node]
	return SCC[::-1]
for _ in range(int(data())):
	(n, m) = mdata()
	g = [[] for i in range(n)]
	l = []
	ans = []
	for i in range(m):
		(t, x, y) = mdata()
		x -= 1
		y -= 1
		if t == 0:
			l.append((x, y))
		else:
			g[x].append(y)
			ans.append(str(x + 1) + ' ' + str(y + 1))
	comp = find_SCC(g)
	val = [0] * (n + 1)
	for (i, j) in enumerate(comp):
		if len(j) > 1:
			out('NO')
			break
		val[j[0]] = i
	else:
		out('YES')
		for (x, y) in l:
			if val[x] < val[y]:
				ans.append(str(x + 1) + ' ' + str(y + 1))
			else:
				ans.append(str(y + 1) + ' ' + str(x + 1))
		outl(ans)


import sys
input = sys.stdin.readline
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	g = [[] for i in range(n)]
	edges = []
	in_deg = [0] * n
	for __ in range(m):
		(ty, u, v) = map(int, input().split())
		edges.append([u - 1, v - 1])
		if ty == 1:
			g[u - 1].append(v - 1)
			in_deg[v - 1] += 1
	q = [i for i in range(n) if in_deg[i] == 0]
	cnt = 0
	order = [0] * n
	while q:
		v = q.pop()
		order[v] = cnt
		cnt += 1
		for to in g[v]:
			in_deg[to] -= 1
			if in_deg[to] == 0:
				q.append(to)
	if cnt < n:
		print('NO')
		continue
	print('YES')
	for (x, y) in edges:
		if order[x] > order[y]:
			print(y + 1, x + 1)
		else:
			print(x + 1, y + 1)


from sys import stdin, stdout, setrecursionlimit
from math import floor, gcd, fabs, factorial, fmod, sqrt, inf, log, log2
from random import seed, randint
from datetime import datetime
from collections import defaultdict as dd, deque
from heapq import merge, heapify, heappop, heappush, nsmallest
from bisect import bisect_left as bl, bisect_right as br, bisect
from collections import defaultdict as dd
mod = pow(10, 9) + 7
mod2 = 998244353

def inp():
	return stdin.readline().strip()

def iinp():
	return int(inp())

def out(var, end='\n'):
	stdout.write(str(var) + '\n')

def outa(*var, end='\n'):
	stdout.write(' '.join(map(str, var)) + end)

def lmp():
	return list(mp())

def mp():
	return map(int, inp().split())

def smp():
	return map(str, inp().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(m, val) for j in range(n)]

def remadd(x, y):
	return 1 if x % y else 0

def ceil(a, b):
	return (a + b - 1) // b

def def_value():
	return 0

def def_inf():
	return inf

def atoi(s):
	return ord(s) - ord('a')

def inc(s, n=1):
	return chr((ord(s) - ord('a') + n) % 26 + ord('a'))

class XY:

	def __init__(self, x, y):
		self.x = x
		self.y = y

def rotate(a, b, c):
	ang = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y)
	if ang == 0:
		return 0
	else:
		return ang / abs(ang)
for _ in range(iinp()):
	(n, m) = mp()
	indg = l1d(n + 1)
	outdg = l1d(n + 1)
	adjlist = [[] for i in range(n + 1)]
	dire = []
	undir = []
	for i in range(m):
		(t, x, y) = mp()
		if t == 0:
			undir.append([x, y])
		else:
			dire.append([x, y])
			adjlist[x].append(y)
			indg[y] += 1
			outdg[x] += 1
	top_order = dd(int)
	cnt = 0
	queue = []
	test = []
	for i in range(1, n + 1):
		if indg[i] == 0:
			queue.append(i)
		if outdg[i] == 0:
			test.append(i)
	if len(test) == 0 or len(queue) == 0:
		print('NO')
	else:
		while len(queue) > 0:
			w = queue.pop()
			top_order[w] = cnt
			cnt += 1
			for j in adjlist[w]:
				indg[j] -= 1
				if indg[j] == 0:
					queue.append(j)
		if cnt != n:
			print('NO')
		else:
			print('YES')
			for i in range(len(dire)):
				print(dire[i][0], dire[i][1])
			for (i, j) in undir:
				if top_order[i] < top_order[j]:
					print(i, j)
				else:
					print(j, i)


t = int(input())
while t:
	t -= 1
	(n, m) = map(int, input().split())
	ed = []
	graph = []
	for i in range(n + 1):
		graph.append([])
	deg = [0] * (n + 1)
	for i in range(m):
		(x, u, v) = map(int, input().split())
		if x == 1:
			graph[u].append(v)
			deg[v] += 1
		ed.append([u, v])
	q = []
	for i in range(1, n + 1):
		if deg[i] == 0:
			q.append(i)
	ord = [0] * (n + 1)
	cnt = 0
	while len(q) > 0:
		cur = q.pop()
		cnt += 1
		ord[cur] = cnt
		for i in graph[cur]:
			deg[i] -= 1
			if deg[i] == 0:
				q.append(i)
	if cnt != n:
		print('NO')
		continue
	print('YES')
	for i in ed:
		if ord[i[0]] > ord[i[1]]:
			print(i[1], i[0])
		else:
			print(i[0], i[1])


import sys

def rs():
	return sys.stdin.readline().rstrip()

def ri():
	return int(sys.stdin.readline())

def ria():
	return list(map(int, sys.stdin.readline().split()))

def ws(s):
	sys.stdout.write(s)
	sys.stdout.write('\n')

def wi(n):
	sys.stdout.write(str(n))
	sys.stdout.write('\n')

def wia(a, sep=' '):
	sys.stdout.write(sep.join([str(x) for x in a]))
	sys.stdout.write('\n')

def solve(n, m, e0, e1):
	g = [[] for _ in range(n)]
	for e in e1:
		g[e[0]].append(e[1])
	(res, found) = ([], [0] * n)
	stack = list(range(n))
	while stack:
		node = stack.pop()
		if node < 0:
			res.append(~node)
		elif not found[node]:
			found[node] = 1
			stack.append(~node)
			stack += g[node]
	for node in res:
		if any((found[nei] for nei in g[node])):
			ws('NO')
			return
		found[node] = 0
	topo_sorted = res[::-1]
	idx = [0] * n
	for i in range(n):
		idx[topo_sorted[i]] = i
	ws('YES')
	for e in e1:
		wia([e[0] + 1, e[1] + 1])
	for e in e0:
		if idx[e[0]] < idx[e[1]]:
			wia([e[0] + 1, e[1] + 1])
		else:
			wia([e[1] + 1, e[0] + 1])

def main():
	for _ in range(ri()):
		(n, m) = ria()
		e0 = []
		e1 = []
		for i in range(m):
			(ti, xi, yi) = ria()
			xi -= 1
			yi -= 1
			if ti == 0:
				e0.append((xi, yi))
			else:
				e1.append((xi, yi))
		solve(n, m, e0, e1)
main()


from sys import stdin
input = stdin.readline
INF = 10 ** 9 + 7
MAX = 10 ** 7 + 7
MOD = 10 ** 9 + 7
for Ti in range(int(input().strip())):
	(n, m) = [int(x) for x in input().strip().split()]
	graph = [[] for _ in range(n)]
	ulist = []
	for mi in range(m):
		(ti, xi, yi) = [int(x) for x in input().strip().split()]
		if ti:
			graph[xi - 1].append(yi - 1)
		else:
			ulist.append((xi - 1, yi - 1))
	visited = [0] * n
	topo = []

	def dfs(node):
		stack = []
		stack.append(node)
		ret = True
		while stack:
			cn = stack[-1]
			if visited[cn] == 1:
				topo.append(cn)
				stack.pop()
				visited[cn] = 2
				continue
			visited[cn] = 1
			for nei in graph[cn]:
				if not visited[nei]:
					stack.append(nei)
				elif visited[nei] == 1:
					return False
		return ret
	ans = True
	for ni in range(n):
		if not visited[ni]:
			ret = dfs(ni)
			if not ret:
				ans = False
				break
	if not ans:
		print('NO')
		continue
	ans = []
	topo = topo[::-1]
	topoOrder = [0] * n
	for (i, ti) in enumerate(topo):
		topoOrder[ti] = i + 1
	topo = topoOrder
	for (us, ue) in ulist:
		if topo[us] >= topo[ue]:
			ans.append((ue, us))
		else:
			ans.append((us, ue))
		if topo[us] == topo[ue]:
			topo[us] += 1
			topo[ue] -= 1
	print('YES')
	for (us, ue) in ans:
		print(us + 1, ue + 1)
	for (i, gi) in enumerate(graph):
		for ei in gi:
			print(i + 1, ei + 1)


def main():
	from collections import deque

	def check(N):
		par = [i for i in range(N)]
		rank = [0 for i in range(N)]

		def find_root(x):
			if par[x] == x:
				return x
			else:
				par[x] = find_root(par[x])
				return par[x]

		def is_same_group(x, y):
			return find_root(x) == find_root(y)

		def unite(x, y):
			x = find_root(x)
			y = find_root(y)
			if x == y:
				return
			if rank[x] < rank[y]:
				par[x] = y
			else:
				par[y] = x
				if rank[x] == rank[y]:
					rank[x] += 1
	T = int(input())
	for _ in range(T):
		(N, M) = (int(i) for i in input().split())
		edge = [[] for _ in range(N)]
		edges = []
		direct = [0] * M
		deg = [0] * N
		for m in range(M):
			(t, a, b) = (int(i) for i in input().split())
			a -= 1
			b -= 1
			if t == 1:
				edge[a].append(b)
				deg[b] += 1
				direct[m] = t
			edges.append((a, b))
		que = deque()
		order = 0
		ans = []
		for i in range(N):
			if deg[i] == 0:
				que.append(i)
				ans.append((i, order))
				order += 1
		while que:
			u = que.popleft()
			for v in edge[u]:
				deg[v] -= 1
				if deg[v] == 0:
					que.append(v)
					ans.append((v, order))
					order += 1
		ans.sort(key=lambda p: p[1])
		if len(ans) == N:
			print('YES')
			dic = {a: s for (a, s) in ans}
			for (m, (x, y)) in enumerate(edges):
				if direct[m] == 1:
					print(x + 1, y + 1)
				elif dic[x] < dic[y]:
					print(x + 1, y + 1)
				else:
					print(y + 1, x + 1)
		else:
			print('NO')
main()


from collections import deque
import sys
sys.setrecursionlimit(10 ** 5)
int1 = lambda x: int(x) - 1
p2D = lambda x: print(*x, sep='\n')

def II():
	return int(sys.stdin.readline())

def MI():
	return map(int, sys.stdin.readline().split())

def LI():
	return list(map(int, sys.stdin.readline().split()))

def LLI(rows_number):
	return [LI() for _ in range(rows_number)]

def SI():
	return sys.stdin.readline()[:-1]
for _ in range(II()):
	(n, m) = MI()
	to = [[] for _ in range(n)]
	ot = [[] for _ in range(n)]
	undir = [[] for _ in range(n)]
	outdeg = [0] * n
	uv = []
	ans = []
	for _ in range(m):
		(t, u, v) = MI()
		(u, v) = (u - 1, v - 1)
		if t:
			to[u].append(v)
			ot[v].append(u)
			ans.append((u, v))
			outdeg[u] += 1
		else:
			undir[u].append(v)
			undir[v].append(u)
			uv.append((u, v))
	topo = []
	fin = [-1] * n
	ng = False
	for u in range(n):
		if fin[u] == 1:
			continue
		stack = [u]
		while stack:
			u = stack.pop()
			if fin[u] == 1:
				continue
			if fin[u] == 0:
				topo.append(u)
				fin[u] = 1
			else:
				fin[u] = 0
				stack.append(u)
				for v in to[u]:
					if fin[v] == 0:
						ng = True
						break
					if fin[v] == 1:
						continue
					stack.append(v)
			if ng:
				break
	if ng:
		print('NO')
		continue
	rank = [-1] * n
	for (i, u) in enumerate(topo):
		rank[u] = i
	for (u, v) in uv:
		if rank[u] == -1:
			rank[u] = len(topo)
			topo.append(u)
		if rank[v] == -1:
			rank[v] = len(topo)
			topo.append(v)
		if rank[u] > rank[v]:
			ans.append((u, v))
		else:
			ans.append((v, u))
	print('YES')
	for (u, v) in ans:
		print(u + 1, v + 1)


import bisect as bi
import math as mt
import collections as cc
I = lambda : list(map(int, input().split()))

def topo(g):
	indg = [0] * (n + 1)
	for i in range(1, n + 1):
		for j in g[i]:
			indg[j] += 1
	st = cc.deque()
	for i in range(1, n + 1):
		if indg[i] == 0:
			st.append(i)
	ans = []
	while st:
		x = st.pop()
		ans.append(x)
		for y in g[x]:
			indg[y] -= 1
			if indg[y] == 0:
				st.append(y)
	return ans
for tc in range(int(input())):
	(n, m) = I()
	g = cc.defaultdict(list)
	ed = []
	for i in range(m):
		(f, x, y) = I()
		ed.append([x, y])
		if f == 1:
			g[x].append(y)
	top = topo(g)
	if len(top) < n:
		print('NO')
	else:
		print('YES')
		inn = {}
		for i in range(n):
			inn[top[i]] = i
		for i in range(len(ed)):
			(x, y) = ed[i]
			if inn[x] > inn[y]:
				(x, y) = (y, x)
			print(x, y)


from collections import deque

def topoSort(g):
	n = len(g)
	inedges = [0] * n
	for i in range(n):
		for v in g[i]:
			inedges[v] += 1
	q = deque()
	for i in range(n):
		if inedges[i] == 0:
			q.append(i)
	r = []
	while q:
		v = q.popleft()
		r.append(v)
		for u in g[v]:
			inedges[u] -= 1
			if inedges[u] == 0:
				q.append(u)
	return r
for t in range(int(input())):
	(n, m) = map(int, input().split())
	g = [[] for _ in range(n)]
	e = []
	h = {}
	for i in range(m):
		(t, x, y) = map(int, input().split())
		x -= 1
		y -= 1
		if t == 1:
			g[x].append(y)
		e.append([x, y])
		h[x] = True
		h[y] = True
	a = topoSort(g)
	if len(a) != n:
		print('NO')
		continue
	p = [0] * n
	for i in range(n):
		p[a[i]] = i
	print('YES')
	for (x, y) in e:
		if p[x] < p[y]:
			print(x + 1, y + 1)
		else:
			print(y + 1, x + 1)


import sys
input = sys.stdin.buffer.readline
Q = int(input())
for _ in range(Q):
	(N, M) = map(int, input().split())
	graph = [set() for _ in range(N)]
	Indeg = [0] * N
	Edges = []
	De = []
	for _ in range(M):
		(directed, a, b) = map(int, input().split())
		if directed:
			graph[a - 1].add(b - 1)
			Indeg[b - 1] += 1
			De.append((a - 1, b - 1))
		else:
			Edges.append((a - 1, b - 1))
	sortedEdges = []
	stack = []
	for n in range(N):
		if Indeg[n] == 0:
			stack.append(n)
	while stack:
		n = stack.pop()
		for v in graph[n]:
			Indeg[v] -= 1
			if Indeg[v] == 0:
				stack.append(v)
		sortedEdges.append(n)
	if len(sortedEdges) == N:
		print('YES')
		Seq = [-1] * N
		for (i, n) in enumerate(sortedEdges):
			Seq[n] = i
		for (a, b) in Edges:
			if Seq[a] > Seq[b]:
				(a, b) = (b, a)
			print(a + 1, b + 1)
		for (a, b) in De:
			print(a + 1, b + 1)
	else:
		print('NO')


import sys
from collections import deque
input = sys.stdin.buffer.readline
print = sys.stdout.write
T = int(input())
for _ in range(T):
	(n, m) = map(int, input().split())
	(e, inc) = ([[] for _ in range(n + 1)], [0] * (n + 1))
	edges = []
	for _ in range(m):
		(a, b, c) = map(int, input().split())
		edges.append((b, c))
		if a == 1:
			e[b].append(c)
			inc[c] += 1
	q = [u for u in range(1, n + 1) if inc[u] == 0]
	tp = []
	while q:
		u = q.pop()
		tp.append(u)
		for v in e[u]:
			inc[v] -= 1
			if inc[v] == 0:
				q.append(v)
	if len(tp) < n:
		print('NO\n')
		continue
	order = [0] * (n + 1)
	for (i, u) in enumerate(tp):
		order[u] = i
	print('YES\n')
	for (b, c) in edges:
		if order[b] < order[c]:
			print('%d %d\n' % (b, c))
		else:
			print('%d %d\n' % (c, b))


from collections import deque
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	adj = [[] for _ in range(n)]
	(ud, ans) = ([], [])
	for _ in range(m):
		(i, j, k) = map(int, input().split())
		if i == 1:
			adj[j - 1].append(k - 1)
			ans.append((j - 1, k - 1))
		else:
			ud.append((j - 1, k - 1))
	vis = [0] * n
	topsort = []
	q = deque()
	indeg = [0] * n
	for i in range(n):
		for j in adj[i]:
			indeg[j] += 1
	for i in range(n):
		if indeg[i] == 0:
			q.append(i)
	topsort = []
	while len(q):
		t = q.popleft()
		topsort.append(t)
		for i in adj[t]:
			indeg[i] -= 1
			if indeg[i] == 0:
				q.append(i)
	if len(topsort) != n:
		print('NO')
		continue
	idx = {}
	for (i, j) in enumerate(topsort):
		idx[j] = i
	for i in ud:
		if idx[i[0]] < idx[i[1]]:
			ans.append((i[0], i[1]))
		else:
			ans.append((i[1], i[0]))
	print('YES')
	for i in ans:
		print(i[0] + 1, i[1] + 1)


from collections import defaultdict as dc

def tpsort():
	q = [i for i in range(1, n + 1) if ins[i] == 0]
	tpcnt = 0
	while q:
		ele = q.pop()
		tp[ele] = tpcnt
		tpcnt += 1
		for each in di[ele]:
			ins[each] -= 1
			if ins[each] == 0:
				q.append(each)
	return tpcnt == n
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	di = dc(list)
	edges = []
	ins = [0] * (n + 1)
	tp = [0] * (n + 1)
	for i in range(m):
		(t, a, b) = map(int, input().split())
		edges.append([t, a, b])
		if t == 1:
			di[a].append(b)
			ins[b] += 1
	x = tpsort()
	if not x:
		print('NO')
	else:
		print('YES')
		for (t, a, b) in edges:
			if t == 0:
				if tp[a] > tp[b]:
					print(b, a)
				else:
					print(a, b)
			else:
				print(a, b)


from collections import deque
from sys import stdin
input = stdin.readline
INF = 10 ** 9 + 7
MAX = 10 ** 7 + 7
MOD = 10 ** 9 + 7
for Ti in range(int(input().strip())):
	(n, m) = [int(x) for x in input().strip().split()]
	graph = [[] for _ in range(n)]
	ulist = []
	inDegree = [0] * n
	for mi in range(m):
		(ti, xi, yi) = [int(x) for x in input().strip().split()]
		if ti:
			graph[xi - 1].append(yi - 1)
			inDegree[yi - 1] += 1
		else:
			ulist.append((xi - 1, yi - 1))
	visited = [0] * n
	topo = [0] * n
	q = deque()
	for ni in range(n):
		if inDegree[ni] == 0:
			q.append(ni)
	oi = 1
	cnt = 0
	while q:
		cn = q.popleft()
		topo[cn] = oi
		oi += 1
		cnt += 1
		for nei in graph[cn]:
			inDegree[nei] -= 1
			if inDegree[nei] == 0:
				q.append(nei)
	if cnt < n:
		print('NO')
		continue
	ans = []
	for (us, ue) in ulist:
		if topo[us] >= topo[ue]:
			ans.append((ue, us))
		else:
			ans.append((us, ue))
		if topo[us] == topo[ue]:
			topo[us] += 1
			topo[ue] -= 1
	print('YES')
	for (us, ue) in ans:
		print(us + 1, ue + 1)
	for (i, gi) in enumerate(graph):
		for ei in gi:
			print(i + 1, ei + 1)


from collections import defaultdict as dc
from collections import deque as dq
import sys
input = sys.stdin.readline
T = int(input())
while T:
	(n, m) = map(int, input().split())
	g = dc(list)
	l = [0] * (n + 1)
	t = []
	for i in range(m):
		(x, u, v) = map(int, input().split())
		if x == 1:
			g[u].append(v)
			l[v] += 1
		t.append([x, u, v])
	p = [0] * (n + 1)
	c = 0
	q = dq([])
	for i in range(1, n + 1):
		if l[i] == 0:
			q.appendleft(i)
	while q:
		y = q.pop()
		p[y] = c
		c += 1
		for i in g[y]:
			l[i] -= 1
			if l[i] == 0:
				q.appendleft(i)
	if c != n:
		print('NO')
	else:
		print('YES')
		for i in range(m):
			if t[i][0] == 1:
				print(t[i][1], t[i][2])
			elif p[t[i][1]] <= p[t[i][2]]:
				print(t[i][1], t[i][2])
			else:
				print(t[i][2], t[i][1])
	T -= 1


import sys
input = sys.stdin.readline

def is_dag(graph: list) -> bool:
	n = len(graph)
	indegree = [0] * n
	for v in range(n):
		for nxt_v in graph[v]:
			indegree[nxt_v] += 1
	tp_order = [i for i in range(n) if indegree[i] == 0]
	stack = tp_order[:]
	while stack:
		v = stack.pop()
		for nxt_v in graph[v]:
			indegree[nxt_v] -= 1
			if indegree[nxt_v] == 0:
				stack.append(nxt_v)
				tp_order.append(nxt_v)
	return max(indegree) == 0

def topological_sort(graph: list) -> list:
	n = len(graph)
	indegree = [0] * n
	for v in range(n):
		for nxt_v in graph[v]:
			indegree[nxt_v] += 1
	tp_order = [i for i in range(n) if indegree[i] == 0]
	stack = tp_order[:]
	while stack:
		v = stack.pop()
		for nxt_v in graph[v]:
			indegree[nxt_v] -= 1
			if indegree[nxt_v] == 0:
				stack.append(nxt_v)
				tp_order.append(nxt_v)
	return tp_order
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	edges = [list(map(int, input().split())) for i in range(m)]
	di_graph = [[] for i in range(n)]
	edges_dict = {}
	for (i, (flag, u, v)) in enumerate(edges):
		u -= 1
		v -= 1
		if flag == 1:
			di_graph[u].append(v)
		else:
			if u not in edges_dict:
				edges_dict[u] = []
			if v not in edges_dict:
				edges_dict[v] = []
			edges_dict[u].append((i, v))
			edges_dict[v].append((i, u))
	if not is_dag(di_graph):
		print('NO')
		continue
	tp_order = topological_sort(di_graph)
	used = set()
	for v in tp_order:
		if v not in edges_dict:
			continue
		for (i, u) in edges_dict[v]:
			if i in used:
				continue
			else:
				used.add(i)
				edges[i] = [1, v + 1, u + 1]
	print('YES')
	for (i, u, v) in edges:
		print(u, v)


import io
import os
from collections import Counter, defaultdict, deque
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

def solve(N, M, edges):
	graph = [[] for i in range(N)]
	for (t, u, v) in edges:
		u -= 1
		v -= 1
		if t == 1:
			graph[u].append(v)
	order = {}
	seen = set()
	orderId = 0

	@bootstrap
	def topo(path, pathSet):
		nonlocal orderId
		u = path[-1]
		if u in seen:
			yield False
			return
		seen.add(u)
		for v in graph[u]:
			if v in pathSet:
				yield True
				return
			path.append(v)
			pathSet.add(v)
			ret = (yield topo(path, pathSet))
			if ret:
				yield ret
				return
			path.pop()
			pathSet.remove(v)
		order[u] = orderId
		orderId += 1
		yield False
	for u in range(N):
		if topo([u], {u}):
			return 'NO'
	for (t, u, v) in edges:
		u -= 1
		v -= 1
		if t == 0:
			if order[u] > order[v]:
				graph[u].append(v)
			else:
				graph[v].append(u)
	roots = set(range(N))
	for u in range(N):
		for v in graph[u]:
			roots.discard(v)
	if not roots:
		return 'NO'
	order = {}
	seen = set()
	orderId = 0
	for u in roots:
		if topo([u], {u}):
			return 'NO'
	return 'YES\n' + '\n'.join((str(u + 1) + ' ' + str(v + 1) for u in range(N) for v in graph[u]))
if False:
	import random
	random.seed(0)
	for tc in range(100):
		N = random.randint(2, 10)
		M = random.randint(1, N * (N - 1) // 2)
		edges = set(((random.randint(1, N), random.randint(1, N)) for i in range(M)))
		edges = [(random.randint(0, 1), u, v) for (u, v) in edges if u != v]
		M = len(edges)
		solve(N, M, edges)
		print()
	exit()
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
T = int(input())
for tc in range(T):
	(N, M) = [int(x) for x in input().split()]
	edges = [[int(x) for x in input().split()] for i in range(M)]
	ans = solve(N, M, edges)
	print(ans)


import sys
input = sys.stdin.readline
f = lambda : map(int, input().split())
for _ in range(int(input())):
	(n, m) = f()
	g = {}
	edges = []
	for __ in range(m):
		(t, a, b) = f()
		if t:
			try:
				g[a].append(b)
			except:
				g[a] = [b]
		else:
			edges.append((a, b))
	clr = [0] * (n + 1)
	cyc = False
	tpo = []
	for i in range(1, n + 1):
		if clr[i] == 2:
			continue
		stack = [i]
		while stack:
			u = stack.pop()
			if clr[u] == 1:
				clr[u] = 2
				tpo.append(u)
			elif clr[u] == 0:
				clr[u] = 1
				stack.append(u)
				try:
					for v in g[u]:
						if clr[v] == 1:
							cyc = True
						if clr[v] == 0:
							stack.append(v)
				except:
					continue
	if cyc:
		print('NO')
		continue
	print('YES')
	ind = [0] * (n + 1)
	tpo.reverse()
	for (i, v) in enumerate(tpo):
		ind[v] = i
	for (u, v) in edges:
		if ind[u] < ind[v]:
			print(u, v)
		else:
			print(v, u)
	for u in range(1, n + 1):
		try:
			for v in g[u]:
				print(u, v)
		except:
			continue


from collections import defaultdict
from sys import setrecursionlimit
setrecursionlimit(10 ** 9)

def topological_sort(graph, start):
	seen = set()
	stack = []
	order = []
	q = [start]
	while q:
		v = q.pop()
		if v not in seen:
			seen.add(v)
			q.extend(graph[v])
			while stack and v not in graph[stack[-1]]:
				order.append(stack.pop())
			stack.append(v)
	return stack + order[::-1]

class Graph:

	def __init__(self, vertices):
		self.graph = defaultdict(list)
		self.V = vertices
		self.visited = [False] * self.V
		self.recStack = [False] * self.V

	def addEdge(self, u, v):
		self.graph[u].append(v)

	def isCyclicUtil(self, v):
		try:
			self.visited[v] = True
			self.recStack[v] = True
			for neighbour in self.graph[v]:
				if not self.visited[neighbour]:
					if self.isCyclicUtil(neighbour):
						return True
				elif self.recStack[neighbour]:
					return True
			self.recStack[v] = False
			return False
		except Exception as e:
			pass

	def isCyclic(self):
		for node in range(self.V):
			if not self.visited[node]:
				if self.isCyclicUtil(node):
					return True
		return False
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	graph = [[] for _ in range(n)]
	degrees = [0] * n
	dg = Graph(n)
	undirected = []
	for _ in range(m):
		(w, u, v) = map(int, input().split())
		u -= 1
		v -= 1
		if w:
			dg.addEdge(u, v)
			graph[u].append(v)
			degrees[v] += 1
		else:
			undirected.append((u, v))
	if dg.isCyclic():
		print('NO')
		continue
	top = []
	for u in range(n):
		if degrees[u] == 0:
			top.extend(topological_sort(graph, u))
	if not top:
		print('NO')
		continue
	d = {}
	for i in range(len(top)):
		d[top[i]] = i
	for (u, v) in undirected:
		try:
			if d[u] < d[v]:
				dg.addEdge(u, v)
				graph[u].append(v)
			else:
				dg.addEdge(v, u)
				graph[v].append(u)
		except:
			dg.addEdge(u, v)
			graph[u].append(v)
	if dg.isCyclic():
		print('NO')
		continue
	print('YES')
	for u in range(n):
		for v in graph[u]:
			print(u + 1, v + 1)


import math
import time
from collections import defaultdict, deque
from sys import stdin, stdout
from bisect import bisect_left, bisect_right
import sys

class Graph:

	def __init__(self, vertices):
		self.graph = defaultdict(list)
		self.V = vertices

	def addEdge(self, u, v):
		self.graph[u].append(v)

	def topologicalSort(self):
		in_degree = [0] * (self.V + 10)
		for i in self.graph:
			for j in self.graph[i]:
				in_degree[j] += 1
		queue = deque()
		for i in range(1, self.V + 1):
			if in_degree[i] == 0:
				queue.append(i)
		cnt = 0
		top_order = []
		while queue:
			u = queue.popleft()
			top_order.append(u)
			for i in self.graph[u]:
				in_degree[i] -= 1
				if in_degree[i] == 0:
					queue.append(i)
			cnt += 1
		if cnt != self.V:
			return [-1]
		else:
			return top_order
t = int(stdin.readline())
for _ in range(t):
	(n, m) = map(int, stdin.readline().split())
	g = Graph(n)
	undir = []
	for _ in range(m):
		(ty, a, b) = map(int, stdin.readline().split())
		if ty == 1:
			g.addEdge(a, b)
		else:
			undir.append([a, b])
	topo = g.topologicalSort()
	if topo == [-1]:
		print('NO')
	else:
		print('YES')
		index = defaultdict(lambda : 0)
		for i in range(n):
			index[topo[i]] = i
		for (a, b) in undir:
			if index[a] < index[b]:
				print(a, b)
			else:
				print(b, a)
		for i in g.graph.keys():
			for j in g.graph[i]:
				print(i, j)


import sys
from functools import lru_cache, cmp_to_key
from heapq import merge, heapify, heappop, heappush, nsmallest
from math import ceil, floor, gcd, fabs, factorial, fmod, sqrt, inf
from collections import defaultdict as dd, deque, Counter as C
from itertools import combinations as comb, permutations as perm
from bisect import bisect_left as bl, bisect_right as br, bisect
from time import perf_counter
from fractions import Fraction
from decimal import Decimal
mod = pow(10, 9) + 7
mod2 = 998244353

def data():
	return sys.stdin.readline().strip()

def out(var):
	sys.stdout.write(str(var) + '\n')

def outa(*var, end='\n'):
	sys.stdout.write(' '.join(map(str, var)) + end)

def l():
	return list(sp())

def sl():
	return list(ssp())

def sp():
	return map(int, data().split())

def ssp():
	return map(str, data().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(n, val) for j in range(m)]

def kahn():
	q = []
	for i in range(1, n + 1):
		if not indeg[i]:
			q.append(i)
	time = 0
	while q:
		source = q.pop()
		order[source] = time
		for child in graph[source]:
			indeg[child] -= 1
			if not indeg[child]:
				q.append(child)
		time += 1
	for i in indeg:
		if i:
			return False
	return True
for _ in range(int(data())):
	(n, m) = sp()
	graph = [set() for i in range(n + 1)]
	edges = []
	(indeg, vis) = ([0] * (n + 1), [0] * (n + 1))
	for i in range(m):
		(f, u, v) = sp()
		edges.append([u, v])
		if f:
			graph[u].add(v)
			indeg[v] += 1
	order = [0] * (n + 1)
	if not kahn():
		out('NO')
		continue
	out('YES')
	for (a, b) in edges:
		if order[a] > order[b]:
			out('{} {}'.format(b, a))
			continue
		out('{} {}'.format(a, b))


from collections import defaultdict
import sys, math
f = None
try:
	f = open('q1.input', 'r')
except IOError:
	f = sys.stdin
if 'xrange' in dir(__builtins__):
	range = xrange

def print_case_iterable(case_num, iterable):
	print('Case #{}: {}'.format(case_num, ' '.join(map(str, iterable))))

def print_case_number(case_num, iterable):
	print('Case #{}: {}'.format(case_num, iterable))

def print_iterable(A):
	print(' '.join(A))

def read_int():
	return int(f.readline().strip())

def read_int_array():
	return [int(x) for x in f.readline().strip().split(' ')]

def rns():
	a = [x for x in f.readline().split(' ')]
	return (int(a[0]), a[1].strip())

def read_string():
	return list(f.readline().strip())

def ri():
	return int(f.readline().strip())

def ria():
	return [int(x) for x in f.readline().strip().split(' ')]

def rns():
	a = [x for x in f.readline().split(' ')]
	return (int(a[0]), a[1].strip())

def rs():
	return list(f.readline().strip())

def bi(x):
	return bin(x)[2:]
from collections import deque
import math
NUMBER = 10 ** 9 + 7

def factorial(n):
	M = NUMBER
	f = 1
	for i in range(1, n + 1):
		f = f * i % M
	return f

def mult(a, b):
	return a * b % NUMBER

def minus(a, b):
	return (a - b) % NUMBER

def plus(a, b):
	return (a + b) % NUMBER

def egcd(a, b):
	if a == 0:
		return (b, 0, 1)
	else:
		(g, y, x) = egcd(b % a, a)
		return (g, x - b // a * y, y)

def modinv(a):
	m = NUMBER
	(g, x, y) = egcd(a, m)
	if g != 1:
		raise Exception('modular inverse does not exist')
	else:
		return x % m

def choose(n, k):
	if n < k:
		assert false
	return mult(factorial(n), modinv(mult(factorial(k), factorial(n - k)))) % NUMBER
from collections import deque, defaultdict
import heapq
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc

def dfs(g, timeIn, timeOut, depths, parents):
	cnt = 0
	stack = [[1, 0, 0, 0]]
	while stack:
		(v, neig_i, parent, depth) = stack[-1]
		parents[v] = parent
		depths[v] = depth
		if neig_i == 0:
			timeIn[v] = cnt
			cnt += 1
		while neig_i < len(g[v]):
			u = g[v][neig_i]
			if u == parent:
				neig_i += 1
				continue
			stack[-1][1] = neig_i + 1
			stack.append([u, 0, v, depth + 1])
			break
		if neig_i == len(g[v]):
			stack.pop()
			timeOut[v] = cnt
			cnt += 1
cnt = 0

@bootstrap
def dfs(v, adj, timeIn, timeOut, depths, parents, parent=0, depth=0):
	global cnt
	parents[v] = parent
	depths[v] = depth
	timeIn[v] = cnt
	cnt += 1
	for u in adj[v]:
		if u == parent:
			continue
		yield dfs(u, adj, timeIn, timeOut, depths, parents, v, depth + 1)
	timeOut[v] = cnt
	cnt += 1
	yield

def gcd(a, b):
	if a == 0:
		return b
	return gcd(b % a, a)

def lcm(a, b):
	return a * b / gcd(a, b)

def get_num_2_5(n):
	twos = 0
	fives = 0
	while n > 0 and n % 2 == 0:
		n //= 2
		twos += 1
	while n > 0 and n % 5 == 0:
		n //= 5
		fives += 1
	return (twos, fives)

def shift(a, i, num):
	for _ in range(num):
		(a[i], a[i + 1], a[i + 2]) = (a[i + 2], a[i], a[i + 1])

def equal(x, y):
	return abs(x - y) <= 1e-09

def getAngle(a, b, c):
	ang = math.degrees(math.atan2(c[1] - b[1], c[0] - b[0]) - math.atan2(a[1] - b[1], a[0] - b[0]))
	return ang + 360 if ang < 0 else ang

def getLength(a, b):
	return math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
from heapq import heapify, heappush, heappop

def bfs(adj):
	s = set()
	d = defaultdict(set)
	q = deque([])
	s.add(0)
	d[0].add(0)
	q.append((0, 0))
	while len(q) > 0:
		(v, depth) = q.popleft()
		for u in adj[v]:
			if u not in s:
				s.add(u)
				d[depth + 1].add(u)
				q.append((u, depth + 1))
	return d

@bootstrap
def dfs(u, top, adj, vis):
	vis[u] = 1
	for (v, d) in adj[u]:
		if vis[v] == 2 or d == 0:
			continue
		if vis[v] == 1:
			yield 'No'
		x = (yield dfs(v, top, adj, vis))
		if x == 'No':
			yield 'No'
	vis[u] = 2
	top.append(u)
	yield

def solution(adj, n, m):
	top = []
	vis = [0] * n
	for v in range(n):
		if vis[v] == 0:
			if dfs(v, top, adj, vis) == 'No':
				return 'No'
	top = top[::-1]
	done = set()
	ans = []
	for v in top:
		for (u, d) in adj[v]:
			if (min(u, v), max(u, v)) in done:
				continue
			ans.append(f'{v + 1} {u + 1}')
			done.add((min(u, v), max(u, v)))
	print('YES')
	return '\n'.join(ans)

def main():
	T = 1
	T = ri()
	for i in range(T):
		(n, m) = ria()
		adj = [list() for _ in range(n)]
		for _ in range(m):
			(d, a, b) = ria()
			a -= 1
			b -= 1
			adj[a].append((b, d))
			if d == 0:
				adj[b].append((a, d))
		x = solution(adj, n, m)
		if 'xrange' not in dir(__builtins__):
			print(x)
		else:
			(print >> output, str(x))
	if 'xrange' in dir(__builtins__):
		print(output.getvalue())
		output.close()
if 'xrange' in dir(__builtins__):
	import cStringIO
	output = cStringIO.StringIO()
main()


import sys
from functools import lru_cache, cmp_to_key
from heapq import merge, heapify, heappop, heappush
from collections import defaultdict as dd, deque, Counter as C
from itertools import combinations as comb, permutations as perm
from bisect import bisect_left as bl, bisect_right as br, bisect, insort
from time import perf_counter
from fractions import Fraction
import copy
import time
starttime = time.time()
mod = int(pow(10, 9) + 7)
mod2 = 998244353

def data():
	return sys.stdin.readline().strip()

def out(*var, end='\n'):
	sys.stdout.write(' '.join(map(str, var)) + end)

def L():
	return list(sp())

def sl():
	return list(ssp())

def sp():
	return map(int, data().split())

def ssp():
	return map(str, data().split())

def l1d(n, val=0):
	return [val for i in range(n)]

def l2d(n, m, val=0):
	return [l1d(n, val) for j in range(m)]
try:
	sys.stdin = open('input.txt', 'r')
except:
	pass

def pmat(A):
	for ele in A:
		print(*ele, end='\n')

def topo():
	visited = []
	indegree = [0 for i in range(n + 1)]
	for i in range(1, n + 1):
		for neigh in g[i]:
			indegree[neigh] += 1
	q = deque()
	for i in range(1, n + 1):
		if indegree[i] == 0:
			q.append(i)
	while q:
		u = q.popleft()
		visited.append(u)
		for neigh in g[u]:
			indegree[neigh] -= 1
			if indegree[neigh] == 0:
				q.append(neigh)
	return visited
for _ in range(L()[0]):
	(n, m) = L()
	A = [L() for i in range(m)]
	g = [[] for i in range(n + 1)]
	for (i, x, y) in A:
		if i == 0:
			continue
		g[x].append(y)
	T = topo()
	if len(T) != n:
		print('NO')
	else:
		print('YES')
		d = {}
		for i in range(n):
			d[T[i]] = i
		for (i, x, y) in A:
			if i == 1:
				print(x, y)
				continue
			if d[x] < d[y]:
				print(x, y)
			else:
				print(y, x)
endtime = time.time()


import sys

def toposort(graph):
	res = []
	found = [0] * len(graph)
	stack = list(range(len(graph)))
	while stack:
		node = stack.pop()
		if node < 0:
			res.append(~node)
		elif not found[node]:
			found[node] = 1
			stack.append(~node)
			stack += graph[node]
	for node in res:
		if any((found[nei] for nei in graph[node])):
			return None
		found[node] = 0
	return res[::-1]
inp = [int(x) for x in sys.stdin.read().split()]
ii = 0
t = inp[0]
ii += 1
for _ in range(t):
	(n, m) = inp[ii:ii + 2]
	ii += 2
	graph = [[] for _ in range(n + 1)]
	undir = []
	for i in range(m):
		(type, u, v) = inp[ii:ii + 3]
		ii += 3
		if type == 0:
			undir.append([u, v])
		else:
			graph[u].append(v)
	order = toposort(graph)
	if not order:
		print('NO')
		continue
	print('YES')
	index = [0] * (n + 1)
	for i in range(n + 1):
		index[order[i]] = i
	for i in range(n + 1):
		for u in graph[i]:
			print(i, u)
	for (u, v) in undir:
		if index[u] < index[v]:
			print(u, v)
		else:
			print(v, u)


import os
import sys
from io import BytesIO, IOBase
from collections import defaultdict, deque

def kahns_topo(graph, in_degree, vertices):
	queue = deque()
	for i in range(vertices):
		if in_degree[i] == 0:
			queue.append(i)
	cnt = 0
	top_order = []
	while queue:
		u = queue.popleft()
		top_order.append(u)
		for i in graph[u]:
			in_degree[i] -= 1
			if in_degree[i] == 0:
				queue.append(i)
		cnt += 1
	if cnt != vertices:
		return []
	else:
		return top_order

def main():
	for _ in range(int(input())):
		g = defaultdict(list)
		(n, ver) = map(int, input().split())
		in_degree = [0] * n
		edges = []
		for i in range(ver):
			(t, a, b) = map(int, input().split())
			a -= 1
			b -= 1
			if t == 1:
				g[a].append(b)
				in_degree[b] += 1
			edges.append([a, b])
		c = kahns_topo(g, in_degree, n)
		if c == []:
			print('NO')
		else:
			pos = [0] * len(c)
			for i in range(len(c)):
				pos[c[i]] = i
			print('YES')
			for i in edges:
				if pos[i[0]] < pos[i[1]]:
					print(i[0] + 1, i[1] + 1)
				else:
					print(i[1] + 1, i[0] + 1)
main()


import os
import sys
from io import BytesIO, IOBase
from collections import defaultdict

def kahn_top_sort(g, n):
	from collections import deque
	order = []
	indeg = [0] * n
	for i in range(n):
		for nbr in g[i]:
			indeg[nbr] += 1
	q = deque()
	for i in range(n):
		if indeg[i] == 0:
			q.append(i)
	while q:
		node = q.popleft()
		order.append(node)
		for nbr in g[node]:
			indeg[nbr] -= 1
			if indeg[nbr] == 0:
				q.append(nbr)
	return order

def main():
	t = int(input())
	for _ in range(t):
		(n, m) = map(int, input().split())
		dg_edgelist = []
		all_edgelist = []
		for _ in range(m):
			(t, x, y) = map(int, input().split())
			all_edgelist.append([x - 1, y - 1])
			if t == 1:
				dg_edgelist.append([x - 1, y - 1])
		g = defaultdict(list)
		for (a, b) in dg_edgelist:
			g[a].append(b)
		topsort = kahn_top_sort(g, n)
		if len(topsort) == n:
			print('YES')
			order = [0] * n
			for (i, node) in enumerate(topsort):
				order[node] = i
			for i in range(m):
				(x, y) = all_edgelist[i]
				if order[x] > order[y]:
					all_edgelist[i] = [y, x]
			for edge in all_edgelist:
				print(edge[0] + 1, edge[1] + 1)
		else:
			print('NO')
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


for _ in range(int(input())):
	(n, m) = map(int, input().split())
	edge = []
	cnt = [0 for i in range(n + 1)]
	h = [[] for i in range(n + 1)]
	for i in range(m):
		(k, x, y) = map(int, input().split())
		edge.append((x, y))
		if k == 1:
			h[x].append(y)
			cnt[y] += 1
	pos = [0 for i in range(n + 1)]
	temp = 0
	arr = []
	for i in range(1, n + 1):
		if cnt[i] == 0:
			arr.append(i)
	while arr != []:
		node = arr.pop()
		pos[node] = temp
		temp += 1
		for i in h[node]:
			cnt[i] -= 1
			if cnt[i] == 0:
				arr.append(i)
	if temp != n:
		print('NO')
	else:
		print('YES')
		for (x, y) in edge:
			if pos[x] < pos[y]:
				print(x, y)
				continue
			print(y, x)


from collections import deque

def tsort(In, G, N):
	que = deque([i for (i, c) in enumerate(In) if c == 0])
	l = []
	while que:
		v = que.popleft()
		l.append(v)
		for nv in G[v]:
			In[nv] -= 1
			if In[nv] == 0:
				que.append(nv)
	if len(l) != N:
		return [-1]
	return l

def solve():
	(n, m) = map(int, input().split())
	G = [[] for _ in range(n)]
	In = [0] * n
	und_edges = []
	for _ in range(m):
		(t, u, v) = map(int, input().split())
		(u, v) = (u - 1, v - 1)
		if t == 1:
			G[u].append(v)
			In[v] += 1
		else:
			und_edges.append((u, v))
	T_sorted = tsort(In, G, n)
	if T_sorted[0] == -1:
		print('NO')
		return
	P = [-1] * n
	for i in range(n):
		P[T_sorted[i]] = i
	for (u, v) in und_edges:
		if P[u] < P[v]:
			G[u].append(v)
		else:
			G[v].append(u)
	print('YES')
	for v in range(n):
		for nv in G[v]:
			print(v + 1, nv + 1)
	return
t = int(input())
for i in range(t):
	solve()


import sys
input = sys.stdin.readline

def canon(e):
	return tuple(sorted(e))

def solve():
	(n, m) = map(int, input().split())
	adj = [[] for _ in range(n)]
	un = []
	indeg = [0] * n
	for _ in range(m):
		(t, x, y) = map(int, input().split())
		x -= 1
		y -= 1
		if t == 0:
			un.append((x, y))
		else:
			adj[x].append(y)
			indeg[y] += 1
	O = []
	Q = [i for i in range(n) if indeg[i] == 0]
	for i in Q:
		O.append(i)
		for j in adj[i]:
			indeg[j] -= 1
			if indeg[j] == 0:
				Q.append(j)
	if len(O) != n:
		print('NO')
		return
	print('YES')
	order = [-1] * n
	for (i, v) in enumerate(O):
		order[v] = i
	for (x, y) in un:
		if order[x] < order[y]:
			print(x + 1, y + 1)
		else:
			print(y + 1, x + 1)
	for i in range(n):
		for j in adj[i]:
			print(i + 1, j + 1)
for _ in range(int(input())):
	solve()


import math
import time
from collections import defaultdict, deque
from sys import stdin, stdout
from bisect import bisect_left, bisect_right
import sys

class Graph:

	def __init__(self, n):
		sys.setrecursionlimit(200005)
		self.graph = defaultdict(lambda : [])
		self.vertices = n
		self.toposorted = []

	def addEdge(self, a, b):
		self.graph[a].append(b)

	def cycleUntill(self, visited, curr, parent):
		visited[curr] = True
		for i in self.graph[curr]:
			if not visited[i]:
				if self.cycleUntill(visited, i, curr):
					return True
			elif i != parent:
				return True
		return False

	def cycle(self):
		n = self.vertices
		visited = [False] * (n + 2)
		for i in range(1, n + 1):
			if not visited[i]:
				if self.cycleUntill(visited, i, -1):
					return True
		return False

	def topologicalSort(self):
		in_degree = [0] * (self.vertices + 10)
		for i in self.graph:
			for j in self.graph[i]:
				in_degree[j] += 1
		queue = deque()
		for i in range(1, self.vertices + 1):
			if in_degree[i] == 0:
				queue.append(i)
		cnt = 0
		while queue:
			u = queue.popleft()
			self.toposorted.append(u)
			for i in self.graph[u]:
				in_degree[i] -= 1
				if in_degree[i] == 0:
					queue.append(i)
			cnt += 1
		if cnt != self.vertices:
			return False
		else:
			return True
t = int(stdin.readline())
for _ in range(t):
	(n, m) = map(int, stdin.readline().split())
	g = Graph(n)
	undir = []
	for _ in range(m):
		(ty, a, b) = map(int, stdin.readline().split())
		if ty == 1:
			g.addEdge(a, b)
		else:
			undir.append([a, b])
	if not g.topologicalSort():
		print('NO')
	else:
		print('YES')
		index = defaultdict(lambda : 0)
		for i in range(n):
			index[g.toposorted[i]] = i
		for (a, b) in undir:
			if index[a] < index[b]:
				print(a, b)
			else:
				print(b, a)
		for i in g.graph.keys():
			for j in g.graph[i]:
				print(i, j)


import collections
import os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	graph1 = []
	graph2 = []
	graph3 = [0] * (n + 1)
	undirected = []
	for i in range(n + 1):
		graph1.append([])
		graph2.append([])
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t == 0:
			undirected.append([x, y])
		else:
			graph1[x].append(y)
			graph2[y].append(x)
	s = collections.deque([])
	for i in range(1, n + 1):
		if len(graph1[i]) == 0:
			s.append(i)
	l = []
	while s:
		p = s.popleft()
		l.append(p)
		for i in graph2[p]:
			m = i
			graph3[i] += 1
			if graph3[i] == len(graph1[i]):
				s.append(i)
	flag = 0
	for i in range(1, n + 1):
		if graph3[i] != len(graph1[i]):
			flag = 1
			break
	if flag == 1:
		print('NO')
		continue
	print('YES')
	for i in range(1, n + 1):
		for j in graph1[i]:
			print(i)
			print(j)
	positions = [0] * (n + 1)
	for i in range(len(l)):
		positions[l[i]] = i
	for i in range(len(undirected)):
		x = undirected[i][0]
		y = undirected[i][1]
		if positions[x] < positions[y]:
			print(y)
			print(x)
		else:
			print(x)
			print(y)


from __future__ import division, print_function
from fractions import Fraction
import sys
import os
from io import BytesIO, IOBase
from itertools import *
import bisect
from heapq import *
from math import *
from copy import *
from collections import deque
from collections import Counter as counter
from itertools import combinations as comb
from itertools import permutations as permutate
from bisect import bisect_left as bl
from bisect import bisect_right as br
from bisect import bisect
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')

def print(*args, **kwargs):
	(sep, file) = (kwargs.pop('sep', ' '), kwargs.pop('file', sys.stdout))
	at_start = True
	for x in args:
		if not at_start:
			file.write(sep)
		file.write(str(x))
		at_start = False
	file.write(kwargs.pop('end', '\n'))
	if kwargs.pop('flush', False):
		file.flush()
if sys.version_info[0] < 3:
	(sys.stdin, sys.stdout) = (FastIO(sys.stdin), FastIO(sys.stdout))
else:
	(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
mod = 1000000007

def inp():
	return sys.stdin.readline().rstrip('\r\n')

def out(var):
	sys.stdout.write(str(var))

def lis():
	return list(map(int, inp().split()))

def stringlis():
	return list(map(str, inp().split()))

def sep():
	return map(int, inp().split())

def strsep():
	return map(str, inp().split())

def zerolist(n):
	return [0] * n

def nextline():
	out('\n')

def testcase(t):
	for p in range(t):
		solve()

def printlist(a):
	for p in range(0, len(a)):
		out(str(a[p]) + ' ')

def lcm(a, b):
	return a * b // gcd(a, b)

def power(a, b):
	ans = 1
	while b > 0:
		if b % 2 == 1:
			ans *= a
		a *= a
		b //= 2
	return ans

def ncr(n, r):
	return factorial(n) // (factorial(r) * factorial(max(n - r, 1)))

def isPrime(n):
	if n <= 1:
		return False
	if n <= 3:
		return True
	if n % 2 == 0 or n % 3 == 0:
		return False
	i = 5
	while i * i <= n:
		if n % i == 0 or n % (i + 2) == 0:
			return False
		i = i + 6
	return True

def toposort(g, dependency):
	queue = deque([])
	for i in range(1, len(dependency)):
		if dependency[i] == 0:
			queue.append(i)
	new = []
	while len(queue):
		s = queue.pop()
		new.append(s)
		for i in g[s]:
			dependency[i] -= 1
			if dependency[i] == 0:
				queue.append(i)
	return new

def solve():
	(n, m) = sep()
	graph = [[] for i in range(n + 1)]
	toassign = []
	storage = []
	dependency = [0] * (n + 1)
	for i in range(m):
		(t, a, b) = sep()
		if t == 0:
			toassign.append([a, b])
		else:
			storage.append([a, b])
			graph[a].append(b)
			dependency[b] += 1
	order = toposort(graph, dependency)
	if len(order) < n:
		print('NO')
	else:
		print('YES')
		ind = [-1] * (n + 1)
		for i in range(len(order)):
			ind[order[i]] = i
		for i in range(len(toassign)):
			ff = toassign[i][0]
			ss = toassign[i][1]
			if ind[ff] < ind[ss]:
				storage.append([ff, ss])
			else:
				storage.append([ss, ff])
		for i in range(len(storage)):
			print(*storage[i])
testcase(int(inp()))


from collections import deque

def solve():
	(n, m) = map(int, input().split())
	G = [[] for i in range(n + 1)]
	edge = []
	In = [0] * (n + 1)
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t == 1:
			G[x].append(y)
			In[y] += 1
		edge.append((t, x, y))
	visited = [False] * (n + 10)
	stack = deque([])
	order = []
	Order = [-1] * (n + 1)
	for i in range(1, n + 1):
		if In[i] == 0:
			stack.append(i)
	while stack:
		x = stack.popleft()
		order.append(x)
		for node in G[x]:
			In[node] -= 1
			if In[node] == 0:
				stack.append(node)
	for (i, v) in enumerate(order):
		Order[v] = i + 1
	not_dag = any((Order[i] < 0 for i in range(1, n + 1)))
	ans = []
	for (t, x, y) in edge:
		if t == 1 and Order[x] > Order[y]:
			not_dag = True
			break
		elif t == 1:
			ans.append((x, y))
		elif Order[x] > Order[y]:
			ans.append((y, x))
		else:
			ans.append((x, y))
	if not_dag:
		print('NO')
	else:
		print('YES')
		for (x, y) in ans:
			print(x, y)

def main():
	t = int(input())
	for i in range(t):
		solve()
	return
main()


import threading, sys
threading.stack_size(2 ** 26)
sys.setrecursionlimit(10 ** 6)
startTime = []
finishTime = []
time = 0
cycle = False

def main():
	global startTime, finishTime, time, cycle
	t = int(input())
	for ii in range(t):
		(n, m) = map(int, input().split())
		undirected = set()
		directed = []
		extra = []
		for i in range(n):
			directed.append([])
		for i in range(m):
			(t, u, v) = map(int, input().split())
			if t == 0:
				undirected.add((u - 1, v - 1))
			else:
				directed[u - 1].append(v - 1)
				extra.append((u, v))
		startTime = [-1] * n
		finishTime = [-1] * n
		time = 0
		cycle = False
		sortedList = []

		def dfsVisit(node):
			global time
			global cycle
			startTime[node] = time
			time += 1
			for i in directed[node]:
				if startTime[i] == -1:
					dfsVisit(i)
				elif finishTime[i] == -1:
					cycle = True
			finishTime[node] = time
			time += 1
			sortedList.append(node)

		def dfs():
			global cycle
			for i in range(n):
				if startTime[i] == -1:
					dfsVisit(i)
				if cycle:
					break
		dfs()
		if cycle:
			print('NO')
			continue
		print('YES')
		count = 0
		final = [-1] * n
		for i in sortedList:
			final[i] = count
			count += 1
		for i in undirected:
			u = i[0]
			v = i[1]
			if final[u] != -1 and final[v] != -1:
				if final[u] > final[v]:
					print(u + 1, v + 1)
				else:
					print(v + 1, u + 1)
			elif final[u] != -1:
				final[v] = count
				count += 1
				print(v + 1, u + 1)
			elif final[v] != -1:
				final[u] = count
				count += 1
				print(u + 1, v + 1)
			else:
				final[u] = count
				count += 1
				final[v] = count
				count += 1
				print(v + 1, u + 1)
		for i in extra:
			print(i[0], i[1])
threading.Thread(target=main).start()


import os
import sys
from io import BytesIO, IOBase
from collections import defaultdict, deque

def kahns_topo(graph, in_degree, vertices):
	queue = deque()
	for i in range(vertices):
		if in_degree[i] == 0:
			queue.append(i)
	cnt = 0
	top_order = []
	while queue:
		u = queue.popleft()
		top_order.append(u)
		for i in graph[u]:
			in_degree[i] -= 1
			if in_degree[i] == 0:
				queue.append(i)
		cnt += 1
	if cnt != vertices:
		return []
	else:
		return top_order

def main():
	for _ in range(int(input())):
		g = defaultdict(list)
		(n, ver) = map(int, input().split())
		in_degree = [0] * n
		edges = []
		for i in range(ver):
			(t, a, b) = map(int, input().split())
			a -= 1
			b -= 1
			if t == 1:
				g[a].append(b)
				in_degree[b] += 1
			edges.append([a, b])
		c = kahns_topo(g, in_degree, n)
		if c == []:
			print('NO')
		else:
			pos = [0] * len(c)
			for i in range(len(c)):
				pos[c[i]] = i
			print('YES')
			for i in edges:
				if pos[i[0]] < pos[i[1]]:
					print(i[0] + 1, i[1] + 1)
				else:
					print(i[1] + 1, i[0] + 1)
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')
main()


import sys
from collections import deque
input = sys.stdin.readline
for _ in range(int(input())):
	(n, m) = map(int, input().split())
	IN = [[] for i in range(n)]
	OUT = [[] for i in range(n)]
	Edge = []
	for i in range(m):
		(t, x, y) = map(int, input().split())
		if t == 1:
			IN[y - 1].append(x - 1)
			OUT[x - 1].append(y - 1)
		else:
			Edge.append((x - 1, y - 1))
	ver = [i for i in range(n) if len(IN[i]) or len(OUT[i])]
	check = set(ver)
	V = len(ver)
	deg = [0 for i in range(n)]
	for v in check:
		for nv in IN[v]:
			deg[v] += nv in check
	res = [v for v in check if deg[v] == 0]
	deq = deque(res)
	used = [0] * V
	while deq:
		v = deq.popleft()
		for nv in OUT[v]:
			if nv in check:
				deg[nv] -= 1
				if deg[nv] == 0:
					deq.append(nv)
					res.append(nv)
	if len(res) != len(check):
		print('NO')
		continue
	print('YES')
	data = {i: e for (e, i) in enumerate(res)}
	for (x, y) in Edge:
		if x in check and y in check:
			if data[x] > data[y]:
				print(y + 1, x + 1)
			else:
				print(x + 1, y + 1)
		elif x in check:
			print(x + 1, y + 1)
		elif y in check:
			print(y + 1, x + 1)
		else:
			print(min(x + 1, y + 1), max(x + 1, y + 1))
	for i in range(n):
		for nv in OUT[i]:
			print(i + 1, nv + 1)


from collections import defaultdict, deque
import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
input = lambda : sys.stdin.readline().rstrip('\r\n')
from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc
t = int(input())
for _ in range(t):
	(n, m) = map(int, input().split())
	in_deg = defaultdict(set)
	out_deg = defaultdict(set)
	bi_deg = defaultdict(set)
	di_edges = []
	vis = [0 for i in range(n + 1)]
	for i in range(m):
		(di, u, v) = map(int, input().split())
		if di:
			out_deg[u].add(v)
			in_deg[v].add(u)
			di_edges.append((u, v))
		else:
			bi_deg[u].add(v)
			bi_deg[v].add(u)
	ans = []
	q = deque()
	for i in range(1, n + 1):
		if not in_deg[i]:
			q.append(i)
	has = True
	while q:
		pro = q.popleft()
		if vis[pro]:
			continue
		vis[pro] = 1
		for child in out_deg[pro]:
			if vis[child]:
				has = False
				break
			in_deg[child].remove(pro)
			if not in_deg[child]:
				q.append(child)
		for child in bi_deg[pro]:
			ans.append((pro, child))
		for child in list(bi_deg[pro]):
			bi_deg[pro].remove(child)
			bi_deg[child].remove(pro)
	for i in range(1, n + 1):
		if in_deg[i]:
			has = False
			break
	if has:
		print('YES')
		for each in ans:
			print(each[0], each[1])
		for edge in di_edges:
			print(edge[0], edge[1])
	else:
		print('NO')


import sys
input = sys.stdin.readline
t = int(input())
for xx in range(t):
	(n, m) = list(map(int, input().split()))
	E = []
	N = [[] for _ in range(n)]
	IN = [0 for _ in range(n)]
	for _ in range(m):
		(a, b, c) = list(map(int, input().split()))
		b -= 1
		c -= 1
		E.append((b, c))
		if a == 1:
			N[b].append(c)
			IN[c] += 1
	Q = []
	for i in range(n):
		if IN[i] == 0:
			Q.append(i)
	count = 0
	OR = [0 for _ in range(n)]
	while Q:
		curr = Q.pop()
		OR[curr] = count
		count += 1
		for child in N[curr]:
			IN[child] -= 1
			if IN[child] == 0:
				Q.append(child)
	if count < n:
		print('NO')
	else:
		print('YES')
		for edge in E:
			(x, y) = (edge[0], edge[1])
			if OR[x] > OR[y]:
				(x, y) = (y, x)
			print(x + 1, y + 1)


from types import GeneratorType

def bootstrap(f, stack=[]):

	def wrappedfunc(*args, **kwargs):
		if stack:
			return f(*args, **kwargs)
		else:
			to = f(*args, **kwargs)
			while True:
				if type(to) is GeneratorType:
					stack.append(to)
					to = next(to)
				else:
					stack.pop()
					if not stack:
						break
					to = stack[-1].send(to)
			return to
	return wrappedfunc
import sys
input = sys.stdin.buffer.readline
t = int(input())
for _ in range(t):
	(n, m) = [int(x) for x in input().split()]
	de = [[] for __ in range(n + 1)]
	ude = [set() for __ in range(n + 1)]
	for __ in range(m):
		(t, x, y) = [int(x) for x in input().split()]
		if t == 0:
			ude[x].add(y)
			ude[y].add(x)
		else:
			de[x].append(y)

	@bootstrap
	def dfs(node):
		explored.add(node)
		path.add(node)
		for nex in de[node]:
			if nex in path:
				yield False
			if nex not in explored:
				if (yield dfs(nex)) == False:
					yield False
		path.pop()
		topo.append(node)
		yield True
	explored = set()
	path = set()
	topo = []
	ok = True
	for i in range(1, n + 1):
		if i not in explored:
			if dfs(i) == False:
				ok = False
				break
	if ok == False:
		print('NO')
		continue
	topo.reverse()
	nde = []
	for i in topo:
		if len(ude[i]) > 0:
			for j in ude[i]:
				nde.append([i, j])
				ude[j].remove(i)
			ude[i] = set()
	for i in range(1, n + 1):
		for j in ude[i]:
			nde.append([i, j])
		for j in de[i]:
			nde.append([i, j])
	print('YES')
	for (i, j) in nde:
		print('{} {}'.format(i, j))


import sys
input = sys.stdin.readline
for f in range(int(input())):
	(n, m) = map(int, input().split())
	neig = [0] * n
	upn = [0] * n
	for i in range(n):
		neig[i] = [0]
		upn[i] = []
	lown = [0] * n
	edges = []
	for i in range(m):
		(t, a, b) = map(int, input().split())
		a -= 1
		b -= 1
		edges.append([a, b])
		neig[a][0] += 1
		neig[b][0] += 1
		neig[a].append(b)
		neig[b].append(a)
		if t == 1:
			lown[b] += 1
			upn[a].append(b)
	levs = [0] * n
	for i in range(n):
		levs[i] = []
	level = [0] * n
	tod = []
	for i in range(n):
		if lown[i] == 0:
			tod.append(i)
	while len(tod) > 0:
		v = tod.pop()
		levs[level[v]].append(v)
		for u in upn[v]:
			level[v] = level[u] + 1
			lown[u] -= 1
			if lown[u] == 0:
				tod.append(u)
	poss = True
	for i in range(n):
		if lown[i] > 0:
			poss = False
	if poss:
		print('YES')
		height = [0] * n
		i = 0
		for l in levs:
			for u in l:
				height[u] = i
				i += 1
		for e in edges:
			(a, b) = (e[0], e[1])
			if height[a] < height[b]:
				print(a + 1, b + 1)
			else:
				print(b + 1, a + 1)
	else:
		print('NO')


from sys import setrecursionlimit
setrecursionlimit(10 ** 9)
import threading
threading.stack_size(67108864)

def dfs(v):
	global used, ts, ok, u
	used[v] = 1
	for i in u[v]:
		if used[i] == 0:
			dfs(i)
		elif used[i] == 1:
			ok = False
	used[v] = 2
	ts.append(v)

def main():
	global used, ts, ok, u
	ans = []
	for _ in range(int(input())):
		(n, m) = map(int, input().split())
		u = []
		w = []
		for i in range(n):
			u.append([])
			w.append([])
		for i in range(m):
			(t, a, b) = map(lambda x: int(x) - 1, input().split())
			if t == -1:
				w[a].append(b)
				w[b].append(a)
			else:
				u[a].append(b)
		ok = True
		ts = []
		used = [0] * n
		for i in range(n):
			if used[i] == 0:
				dfs(i)
		if not ok:
			ans.append('NO')
			continue
		ans.append('YES')
		ts.reverse()
		pr = [False] * n
		for i in range(len(ts)):
			for j in w[ts[i]]:
				if not pr[j]:
					ans.append(str(ts[i] + 1) + ' ' + str(j + 1))
			pr[ts[i]] = True
		for i in range(n):
			for j in u[i]:
				ans.append(str(i + 1) + ' ' + str(j + 1))
	print('\n'.join(ans))
thread = threading.Thread(target=main)
thread.start()
thread.join()