#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define ll long long
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()

using namespace std;
using namespace __gnu_pbds;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>; ///find_by_order(),order_of_key()
template<class T1, class T2> ostream& operator<<(ostream& os, const pair<T1,T2>& a) { os << '{' << a.f << ", " << a.s << '}'; return os; }
template<class T> ostream& operator<<(ostream& os, const vector<T>& a){os << '{';for(int i=0;i<sz(a);i++){if(i>0&&i<sz(a))os << ", ";os << a[i];}os<<'}';return os;}
template<class T> ostream& operator<<(ostream& os, const set<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T> ostream& operator<<(ostream& os, const multiset<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const map<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const gp_hash_table<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<int D, typename T> struct Vec : public vector<Vec<D - 1, T>> {static_assert(D >= 1, "Vector dimension must be greater than zero!");template<typename... Args>
Vec(int n = 0, Args... args) : vector<Vec<D - 1, T>>(n, Vec<D - 1, T>(args...)) {}};template<typename T> struct Vec<1, T> : public vector<T> {
Vec(int n = 0, const T& val = T()) : vector<T>(n, val) {}};
//istream& operator>>(istream& is,__int128& a){string s;is>>s;a=0;for(auto p:s)a=a*10+p-'0';return is;}
//ostream& operator<<(ostream& os,__int128 a){bool neg=false;if(a<0)neg=true,a*=-1;ll high=(a/(__int128)1e18);ll low=(a-(__int128)1e18*high);string res;if(neg)res+='-';if(high>0){res+=to_string(high);string temp=to_string(low);res+=string(18-temp.size(),'0');res+=temp;}else res+=to_string(low);os<<res;return os;}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int t;
    cin >> t;
    while(t--)
    {
        queue<int> dobri;
        int n, m;
        cin >> n >> m;
        vector<int> in(n+20);
        vector<int> un[n+20];
        vector<int> ne[n+20];

        for(int i = 1; i <= n; i++)
            in[i] = 0;

        vector<pair<int,int>> res;
        map<int, int> done[n+10];
        int q[n+10];
        for(int i = 1; i <= n; i++)
            q[i] = 0;
        for(int i = 0 ; i < m; i++)
        {
            int t,x,y;
            cin >> t >> x >> y;
            if(t==0)
            {
                un[x].pb(y);
                un[y].pb(x);
                done[x][y] = 0;
                done[y][x] = 0;
            }
            else
            {
                res.pb({x , y});
                in[y]++;
                ne[x].pb(y);
            }
        }

        for(int i = 1; i <= n; i++)
        {
            if(in[i] == 0)
                {dobri.push(i); q[i] = 1;}
        }
        int br = 0;

        while(!dobri.empty())
        {
            br++;
            int tr = dobri.front();
            dobri.pop();

            if(in[tr] == 0)
            {
                for(int x : un[tr])
                {
                    if(done[x][tr] == 0)
                    {
                        done[x][tr] = 1;
                        done[tr][x] = 1;
                        ne[tr].pb(x);
                        in[x]++;
                        res.pb({tr, x});
                    }
                }
                for(int x : ne[tr])
                {
                    in[x]--;
                    if(in[x] == 0)
                        if(q[x] != 1)
                            {dobri.push(x);q[x]=1;}
                }
            }
        }

        if(br == n)
        {
            cout << "YES" << endl;
            for(auto p : res)
            cout << p.first << " " << p.second << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }

    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define ll long long
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()

using namespace std;
using namespace __gnu_pbds;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>; ///find_by_order(),order_of_key()
template<class T1, class T2> ostream& operator<<(ostream& os, const pair<T1,T2>& a) { os << '{' << a.f << ", " << a.s << '}'; return os; }
template<class T> ostream& operator<<(ostream& os, const vector<T>& a){os << '{';for(int i=0;i<sz(a);i++){if(i>0&&i<sz(a))os << ", ";os << a[i];}os<<'}';return os;}
template<class T> ostream& operator<<(ostream& os, const set<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T> ostream& operator<<(ostream& os, const multiset<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const map<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const gp_hash_table<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<int D, typename T> struct Vec : public vector<Vec<D - 1, T>> {static_assert(D >= 1, "Vector dimension must be greater than zero!");template<typename... Args>
Vec(int n = 0, Args... args) : vector<Vec<D - 1, T>>(n, Vec<D - 1, T>(args...)) {}};template<typename T> struct Vec<1, T> : public vector<T> {
Vec(int n = 0, const T& val = T()) : vector<T>(n, val) {}};
//istream& operator>>(istream& is,__int128& a){string s;is>>s;a=0;for(auto p:s)a=a*10+p-'0';return is;}
//ostream& operator<<(ostream& os,__int128 a){bool neg=false;if(a<0)neg=true,a*=-1;ll high=(a/(__int128)1e18);ll low=(a-(__int128)1e18*high);string res;if(neg)res+='-';if(high>0){res+=to_string(high);string temp=to_string(low);res+=string(18-temp.size(),'0');res+=temp;}else res+=to_string(low);os<<res;return os;}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int t;
    cin >> t;
    while(t--)
    {
        queue<int> dobri;
        int n, m;
        cin >> n >> m;
        vector<int> in(n+2);
        vector<int> un[n+2];
        vector<int> ne[n+2];

        for(int i = 1; i <= n; i++)
            in[i] = 0;

        vector<pair<int,int>> res;
        bool done[n+10];

        for(int i = 1; i <= n; i++)
          {

           done[i]=false;}
        for(int i = 0 ; i < m; i++)
        {
            int t,x,y;
            cin >> t >> x >> y;
            if(t==0)
            {
                un[x].pb(y);
                un[y].pb(x);

            }
            else
            {
                res.pb({x , y});
                in[y]++;
                ne[x].pb(y);
            }
        }

        for(int i = 1; i <= n; i++)
        {
            if(in[i] == 0)
                {dobri.push(i);}
        }
        int br = 0;

        while(!dobri.empty())
        {
            br++;
            int tr = dobri.front();
            dobri.pop();

            if(in[tr] == 0)
            {
                for(int x : un[tr])
                {
                    if(done[x]== false)
                    {

                        res.pb({tr, x});
                    }
                }
                for(int x : ne[tr])
                {
                    in[x]--;
                    if(in[x] == 0)

                            {dobri.push(x);}
                }
            }
            done[tr]=true;
        }

        if(br == n)
        {
            cout << "YES" << endl;
            for(auto p : res)
            cout << p.first << " " << p.second << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }

    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define ll long long
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()

using namespace std;
using namespace __gnu_pbds;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>; ///find_by_order(),order_of_key()
template<class T1, class T2> ostream& operator<<(ostream& os, const pair<T1,T2>& a) { os << '{' << a.f << ", " << a.s << '}'; return os; }
template<class T> ostream& operator<<(ostream& os, const vector<T>& a){os << '{';for(int i=0;i<sz(a);i++){if(i>0&&i<sz(a))os << ", ";os << a[i];}os<<'}';return os;}
template<class T> ostream& operator<<(ostream& os, const set<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T> ostream& operator<<(ostream& os, const multiset<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const map<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const gp_hash_table<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<int D, typename T> struct Vec : public vector<Vec<D - 1, T>> {static_assert(D >= 1, "Vector dimension must be greater than zero!");template<typename... Args>
Vec(int n = 0, Args... args) : vector<Vec<D - 1, T>>(n, Vec<D - 1, T>(args...)) {}};template<typename T> struct Vec<1, T> : public vector<T> {
Vec(int n = 0, const T& val = T()) : vector<T>(n, val) {}};
//istream& operator>>(istream& is,__int128& a){string s;is>>s;a=0;for(auto p:s)a=a*10+p-'0';return is;}
//ostream& operator<<(ostream& os,__int128 a){bool neg=false;if(a<0)neg=true,a*=-1;ll high=(a/(__int128)1e18);ll low=(a-(__int128)1e18*high);string res;if(neg)res+='-';if(high>0){res+=to_string(high);string temp=to_string(low);res+=string(18-temp.size(),'0');res+=temp;}else res+=to_string(low);os<<res;return os;}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int t;
    cin >> t;
    while(t--)
    {
        queue<int> dobri;
        int n, m;
        cin >> n >> m;
        vector<int> in(n+20);
        vector<int> un[n+20];
        vector<int> ne[n+20];

        for(int i = 1; i <= n; i++)
            in[i] = 0;

        vector<pair<int,int>> res;
        bool done[n+10][n+10];
        int q[n+10];
        for(int i = 1; i <= n; i++)
            q[i] = 0;
        for(int i = 0 ; i < m; i++)
        {
            int t,x,y;
            cin >> t >> x >> y;
            if(t==0)
            {
                un[x].pb(y);
                un[y].pb(x);
                done[x][y] = false;
                done[y][x] = false;
            }
            else
            {
                res.pb({x , y});
                in[y]++;
                ne[x].pb(y);
            }
        }

        for(int i = 1; i <= n; i++)
        {
            if(in[i] == 0)
                {dobri.push(i); q[i] = 1;}
        }
        int br = 0;

        while(!dobri.empty())
        {
            br++;
            int tr = dobri.front();
            dobri.pop();

            if(in[tr] == 0)
            {
                for(int x : un[tr])
                {
                    if(done[x][tr]== false)
                    {
                        done[x][tr] = true;
                        done[tr][x] = true;
                        ne[tr].pb(x);
                        in[x]++;
                        res.pb({tr, x});
                    }
                }
                for(int x : ne[tr])
                {
                    in[x]--;
                    if(in[x] == 0)
                        if(q[x] != 1)
                            {dobri.push(x);q[x]=1;}
                }
            }
        }

        if(br == n)
        {
            cout << "YES" << endl;
            for(auto p : res)
            cout << p.first << " " << p.second << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }

    return 0;
}


#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define ll long long
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define sz(x) (int)x.size()
#define all(x) x.begin(), x.end()

using namespace std;
using namespace __gnu_pbds;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template<class T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>; ///find_by_order(),order_of_key()
template<class T1, class T2> ostream& operator<<(ostream& os, const pair<T1,T2>& a) { os << '{' << a.f << ", " << a.s << '}'; return os; }
template<class T> ostream& operator<<(ostream& os, const vector<T>& a){os << '{';for(int i=0;i<sz(a);i++){if(i>0&&i<sz(a))os << ", ";os << a[i];}os<<'}';return os;}
template<class T> ostream& operator<<(ostream& os, const set<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T> ostream& operator<<(ostream& os, const multiset<T>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const map<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<class T1,class T2> ostream& operator<<(ostream& os, const gp_hash_table<T1,T2>& a) {os << '{';int i=0;for(auto p:a){if(i>0&&i<sz(a))os << ", ";os << p;i++;}os << '}';return os;}
template<int D, typename T> struct Vec : public vector<Vec<D - 1, T>> {static_assert(D >= 1, "Vector dimension must be greater than zero!");template<typename... Args>
Vec(int n = 0, Args... args) : vector<Vec<D - 1, T>>(n, Vec<D - 1, T>(args...)) {}};template<typename T> struct Vec<1, T> : public vector<T> {
Vec(int n = 0, const T& val = T()) : vector<T>(n, val) {}};
//istream& operator>>(istream& is,__int128& a){string s;is>>s;a=0;for(auto p:s)a=a*10+p-'0';return is;}
//ostream& operator<<(ostream& os,__int128 a){bool neg=false;if(a<0)neg=true,a*=-1;ll high=(a/(__int128)1e18);ll low=(a-(__int128)1e18*high);string res;if(neg)res+='-';if(high>0){res+=to_string(high);string temp=to_string(low);res+=string(18-temp.size(),'0');res+=temp;}else res+=to_string(low);os<<res;return os;}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int t;
    cin >> t;
    while(t--)
    {
        queue<int> dobri;
        int n, m;
        cin >> n >> m;
        vector<int> in(n+20);
        vector<int> un[n+20];
        vector<int> ne[n+20];

        for(int i = 1; i <= n; i++)
            in[i] = 0;

        vector<pair<int,int>> res;
        map<int, int> done[n+10];
        for(int i = 0 ; i < m; i++)
        {
            int t,x,y;
            cin >> t >> x >> y;
            if(t==0)
            {
                un[x].pb(y);
                un[y].pb(x);
                done[x][y] = 0;
                done[y][x] = 0;
            }
            else
            {
                res.pb({x , y});
                in[y]++;
                ne[x].pb(y);
            }
        }

        for(int i = 1; i <= n; i++)
        {
            if(in[i] == 0)
                dobri.push(i);
        }

        while(!dobri.empty())
        {
            int tr = dobri.front();
            dobri.pop();

            if(in[tr] == 0)
            {
                for(int x : un[tr])
                {
                    if(done[x][tr] == 0)
                    {
                        done[x][tr] = 1;
                        done[tr][x] = 1;
                        ne[tr].pb(x);
                        in[x]++;
                        res.pb({tr, x});
                    }
                }
                for(int x : ne[tr])
                {
                    in[x]--;
                    if(in[x] == 0)
                        dobri.push(x);
                }
            }
        }

        if(res.size() == m)
        {
            cout << "YES" << endl;
            for(auto p : res)
            cout << p.first << " " << p.second << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }

    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    int cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , (cnt++)*(2e5+9)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
        rnk[u] = rnk[v] = -1;
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] <= rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }

    if (!last){
        rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = 0;
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
int rnk[N];

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
        rnk[u] = rnk[v] = -1;
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] <= rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }

    if (!last){
        rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = 0;
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    int cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , (cnt++)*(2e5+9)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    memset(rnk , -1 , (n+9) * sizeof(rnk[0]));
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] <= rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }

    if (!last){
        rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = 0;
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    ll cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , (cnt++)*(2e5+9)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1LL});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = -1;

    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] <= rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , id , rnk[N], in[N];
pair<int,pair<int,int>> a[N];
vector<int> adj[N];
set<int> same[N];
vector<pair<int,int>> ans;
queue<int> sameNode;

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( in[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> u = q.front(); q.pop(); rnk[u.F] = u.S;
        for ( int v : adj[u.F] ){
            if ( !--in[v] ){
                q.push({v , u.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    id = 0;
    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if (t) adj[u].pb(v) , in[v]++;
        a[id++] = {t , {u , v}};
    }
    memset(rnk , -1 , sizeof(rnk[0]) * (n + 9));
    topoSort();

    bool res = true;
    rep(i,1,n+1) if ( !~rnk[i] ) {
        res = false;
    }

    rep(i,0,id){
        auto& cur = a[i];
        if ( cur.F == 1 ) ans.pb(cur.S);
        else{
            if      ( rnk[cur.S.F] <  rnk[cur.S.S] ) ans.pb({cur.S.F , cur.S.S});
            else if ( rnk[cur.S.S] <  rnk[cur.S.F] ) ans.pb({cur.S.S , cur.S.F});
            else if ( rnk[cur.S.F] == rnk[cur.S.S] ){
                if (cur.S.F > cur.S.S) swap(cur.S.F , cur.S.S);
                ans.pb({cur.S.F , cur.S.S});
            }
        }
    }

    if (res){
        cout << "YES\n";
        for ( auto i : ans ) cout << i.F << " " << i.S << "\n";
    }
    else cout << "NO\n";

    rep(i,0,n+9){
        adj[i].clear(); same[i].clear(); in[i] = 0;
    }
    ans.clear();
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    int cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , (cnt++)*(2e5+9)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    memset(indeg , -1 , (n+9) * sizeof(indeg[0]));
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] <= rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }

    if (!last){
        rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = 0;
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , id , rnk[N], in[N];
pair<int,pair<int,int>> a[N];
vector<int> adj[N];
set<int> same[N];
vector<pair<int,int>> ans;
queue<int> sameNode;

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( in[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> u = q.front(); q.pop(); rnk[u.F] = u.S;
        for ( int v : adj[u.F] ){
            if ( !--in[v] ){
                q.push({v , u.S + 1});
            }
        }
    }
}

int vis[N] , vid;
void bfs(int u){
    queue<int> q;
    q.push(u);
    while ( q.size() ){
        int u = q.front(); q.pop();
        queue<int> nxt;
        for ( int v : same[u] ) nxt.push(v);
        while ( nxt.size() ){
            int v = nxt.front(); nxt.pop();
            if ( same[u].count(v) && same[v].count(u) ){
                ans.pb({u , v});
                same[u].erase(v);
                same[v].erase(u);
                q.push(v);
            }
        }
    }
}
void solveSame(){
    vid++;
    while (sameNode.size()){
        int u = sameNode.front(); sameNode.pop();
        if ( vis[u] != vid ) bfs(u);
    }
}

void Main(int cse , bool last){
    id = 0;
    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if (t) adj[u].pb(v) , in[v]++;
        a[id++] = {t , {u , v}};
    }
    memset(rnk , -1 , sizeof(rnk[0]) * (n + 9));
    topoSort();

    bool res = true;
    rep(i,1,n+1) if ( !~rnk[i] ) {
        res = false;
    }

    rep(i,0,id){
        auto& cur = a[i];
        if ( cur.F == 1 ) ans.pb(cur.S);
        else{
            if      ( rnk[cur.S.F] <  rnk[cur.S.S] ) ans.pb({cur.S.F , cur.S.S});
            else if ( rnk[cur.S.S] <  rnk[cur.S.F] ) ans.pb({cur.S.S , cur.S.F});
            else if ( rnk[cur.S.F] == rnk[cur.S.S] ){
                same[cur.S.F].insert(cur.S.S);
                same[cur.S.S].insert(cur.S.F);
                sameNode.push(cur.S.S);
            }
        }
    }
    solveSame();

    if (res){
        cout << "YES\n";
        for ( auto i : ans ) cout << i.F << " " << i.S << "\n";
    }
    else cout << "NO\n";

    rep(i,0,n+9){
        adj[i].clear(); same[i].clear(); in[i] = 0;
    }
    ans.clear();
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , id , rnk[N], in[N];
pair<int,pair<int,int>> a[N];
vector<int> adj[N];
set<int> same[N];
vector<pair<int,int>> ans;
queue<int> sameNode;

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( in[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> u = q.front(); q.pop(); rnk[u.F] = u.S;
        for ( int v : adj[u.F] ){
            if ( !--in[v] ){
                q.push({v , u.S + 1});
            }
        }
    }
}

void Main(int cse , bool last){
    id = 0;
    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if (t) adj[u].pb(v) , in[v]++;
        a[id++] = {t , {u , v}};
    }
    memset(rnk , -1 , sizeof(rnk[0]) * (n + 9));
    topoSort();

    bool res = true;
    rep(i,1,n+1) if ( !~rnk[i] ) {
        res = false;
    }

    rep(i,0,id){
        auto& cur = a[i];
        if ( cur.F == 1 ) ans.pb(cur.S);
        else{
            if      ( rnk[cur.S.F] <  rnk[cur.S.S] ) ans.pb({cur.S.F , cur.S.S});
            else if ( rnk[cur.S.S] <  rnk[cur.S.F] ) ans.pb({cur.S.S , cur.S.F});
            else if ( rnk[cur.S.F] == rnk[cur.S.S] ){
                if (cur.S.F > cur.S.S) swap(cur.S.F , cur.S.S);
                ans.pb({cur.S.F , cur.S.S});
            }
        }
    }
    solveSame();

    if (res){
        cout << "YES\n";
        for ( auto i : ans ) cout << i.F << " " << i.S << "\n";
    }
    else cout << "NO\n";

    rep(i,0,n+9){
        adj[i].clear(); same[i].clear(); in[i] = 0;
    }
    ans.clear();
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    ll cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , (cnt++)*(2e5+9)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1LL});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = -1;

    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] < rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , id , rnk[N], in[N];
pair<int,pair<int,int>> a[N];
vector<int> adj[N], same[N];
vector<pair<int,int>> ans;
queue<int> sameNode;

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( in[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> u = q.front(); q.pop(); rnk[u.F] = u.S;
        for ( int v : adj[u.F] ){
            if ( !--in[v] ){
                q.push({v , u.S + 1});
            }
        }
    }
}

int vis[N] , vid;
void solveSame(){
    vid++;
    while ( sameNode.size() ){
        int u = sameNode.front(); sameNode.pop();
        if ( vis[u] == vid ) continue;
        vis[u] = vid;
        queue<int> q; q.push(u);
        while (q.size()){
            int uu = q.front(); q.pop();
            vis[uu] = vid;
            for (int v : same[uu]){
                if (vis[v] != vid){
                    ans.pb({uu , v});
                    q.push(v);
                }
            }
        }
    }
}

void Main(int cse , bool last){
    id = 0;
    cin >> n >> m;
    rep(i,0,n+9){
        adj[i].clear(); same[i].clear(); in[i] = 0;
    }
    ans.clear();

    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if (t) adj[u].pb(v) , in[v]++;
        a[id++] = {t , {u , v}};
    }
    memset(rnk , -1 , sizeof(rnk[0]) * (n + 9));
    topoSort();

    rep(i,1,n+1) if ( !~rnk[i] ) {
        cout << "NO\n"; return;
    }

    rep(i,0,id){
        auto& cur = a[i];
        if ( cur.F == 1 ) ans.pb(cur.S);
        else{
            if      ( rnk[cur.S.F] <  rnk[cur.S.S] ) ans.pb({cur.S.F , cur.S.S});
            else if ( rnk[cur.S.S] <  rnk[cur.S.F] ) ans.pb({cur.S.S , cur.S.F});
            else if ( rnk[cur.S.F] == rnk[cur.S.S] ){
                same[cur.S.F].pb(cur.S.S);
                same[cur.S.S].pb(cur.S.F);
                sameNode.push(cur.S.S);
            }
        }
    }
        solveSame();
        cout << "YES\n";
        for ( auto i : ans ) cout << i.F << " " << i.S << "\n";

}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , indeg[N]; vector<int> adj[N];
ll rnk[N];

void topoSort(){
    queue<pair<int,ll>> q;

    ll cnt = 0;
    rep(i,1,n+1){
        if ( indeg[i] == 0 ) q.push({i , 1LL*(cnt++)*(200009)});
    }

    while ( q.size() ){
        pair<int,ll> cur = q.front(); q.pop(); rnk[cur.F] = cur.S;
        for ( int v : adj[cur.F] ){
            if ( !--indeg[v] ){
                q.push({v , cur.S + 1LL});
            }
        }
    }
}

void Main(int cse , bool last){
    vector<pair<int,pair<int,int>>> ed;

    cin >> n >> m;
    rep(i,0,n+9) indeg[i] = 0 , adj[i].clear() , rnk[i] = -1;

    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if ( t ) adj[u].pb(v) , indeg[v]++;
        ed.pb({t, {u , v}});
    }
    topoSort();

    rep(i,1,n+1){
        if ( !~rnk[i] ){
            cout << "NO\n"; return;
        }
    }

    cout << "YES\n";
    for ( auto i : ed ){
        if ( i.F == 1 ) cout << i.S.F << " " << i.S.S << "\n";
        else{
            if ( rnk[i.S.F] < rnk[i.S.S] ) cout << i.S.F << " " << i.S.S << "\n";
            else cout << i.S.S << " " << i.S.F << "\n";
        }
    }
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#ifndef CLion
//#pragma GCC optimize ("O3")
#endif

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>

#define F first
#define S second
#define pb push_back
#define ll long long
#define all(x) x.begin() , x.end()
#define mod(x,m) ((x%m + m)%m)
#define rep(i,s,e) for (int i = s; i < e; ++i)
#define mem(a,v) memset(a , v , sizeof a)
using namespace std;
using namespace __gnu_pbds;

template<typename T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag,
        tree_order_statistics_node_update>;

const int N = 2e5 + 99 , base = 26;
int n , m , id , rnk[N], in[N];
pair<int,pair<int,int>> a[N];
vector<int> adj[N], same[N];
vector<pair<int,int>> ans;
queue<int> sameNode;

void topoSort(){
    queue<pair<int,int>> q;
    rep(i,1,n+1){
        if ( in[i] == 0 ) q.push({i , 0});
    }

    while ( q.size() ){
        pair<int,int> u = q.front(); q.pop(); rnk[u.F] = u.S;
        for ( int v : adj[u.F] ){
            if ( !--in[v] ){
                q.push({v , u.S + 1});
            }
        }
    }
}

int vis[N] , vid;
void solveSame(){
    vid++;
    while ( sameNode.size() ){
        int u = sameNode.front(); sameNode.pop();
        if ( vis[u] == vid ) continue;
        vis[u] = vid;
        queue<int> q; q.push(u);
        while (q.size()){
            int uu = q.front(); q.pop();
            vis[uu] = vid;
            for (int v : same[uu]){
                if (vis[v] != vid){
                    ans.pb({uu , v});
                    q.push(v);
                }
            }
        }
    }
}

void Main(int cse , bool last){
    id = 0;
    cin >> n >> m;
    rep(i,0,m){
        int t, u , v;
        cin >> t >> u >> v;
        if (t) adj[u].pb(v) , in[v]++;
        a[id++] = {t , {u , v}};
    }
    memset(rnk , -1 , sizeof(rnk[0]) * (n + 9));
    topoSort();

    bool res = true;
    rep(i,1,n+1) if ( !~rnk[i] ) {
        res = false;
    }

    rep(i,0,id){
        auto& cur = a[i];
        if ( cur.F == 1 ) ans.pb(cur.S);
        else{
            if      ( rnk[cur.S.F] <  rnk[cur.S.S] ) ans.pb({cur.S.F , cur.S.S});
            else if ( rnk[cur.S.S] <  rnk[cur.S.F] ) ans.pb({cur.S.S , cur.S.F});
            else if ( rnk[cur.S.F] == rnk[cur.S.S] ){
                same[cur.S.F].pb(cur.S.S);
                same[cur.S.S].pb(cur.S.F);
                sameNode.push(cur.S.S);
            }
        }
    }
    solveSame();

    if (res){
        cout << "YES\n";
        for ( auto i : ans ) cout << i.F << " " << i.S << "\n";
    }
    else cout << "NO\n";

    rep(i,0,n+9){
        adj[i].clear(); same[i].clear(); in[i] = 0;
    }
    ans.clear();
}

int main(){
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifdef CLion
    freopen( "in.txt" , "r" , stdin  );
    //freopen( "out.txt", "w" , stdout );
#endif

    int t = 1;
    cin >> t;
    for ( int cse = 1; cse <= t; ++cse ) Main(cse, cse == t);
    return 0;
}


#include <bits/stdc++.h>
using namespace std;

//Compiler version g++ 6.3.0
const int MAXN = 2e5+5;

bool ok;
vector<pair<int,int>> ans;
bool dfs(int u, vector<bool> &vis, vector<bool> &instack, vector<vector<pair<int,int>>> &adj)
{
  vis[u] = true;
  instack[u] = true;

  for(auto v: adj[u])
  {
    if(!vis[v.first])
    {
      bool child = dfs(v.first, vis, instack, adj);
      if(child)
      {
          if(v.second == 0)
          {
            ans.push_back({v.first, u});
          }
          else
          {
            ans.push_back({u, v.first});
            return true;
          }
      }
      else
        ans.push_back({u, v.first});
    }
    else if(instack[v.first])
    {
      if(v.second == 0)
      {
          ans.push_back({v.first, u});
      }
      else
      {
        ans.push_back({u, v.first});
        return true;
      }
    }
    else
      ans.push_back({u, v.first});
  }
  instack[u] = false;
  return false;
}
int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
    int t;
    cin>>t;
    while(t--)
    {
      ok = true;
      ans.clear();
      int n, m;
      cin>>n>>m;
      vector<vector<pair<int,int>>> adj(n+1);
      vector<pair<int,int>> edges;
      for(int i=0;i<m;i++)
      {
        int ty, u, v;
        cin>>ty>>u>>v;
          adj[u].push_back({v, ty});
      }

      vector<bool> vis(n+1, false), instack(n+1, false);

      bool cyc = false;
      for(int i=1;i<=n;i++)
      {
        if(!vis[i])
        {
          bool me = dfs(i, vis, instack, adj)  ;
          cyc = cyc || me;
        }
      }

      if(!cyc)
      {
        cout<<"YES\n"  ;
        for(auto x: ans)
          cout<<x.first<<" "<<x.second<<endl;
      }
      else
        cout<<"NO\n";
    }
}


#include <bits/stdc++.h>
using namespace std;

//Compiler version g++ 6.3.0
const int MAXN = 2e5+5;

bool ok;
vector<int> topsort;
int start[MAXN], endt[MAXN];
int timestamp;

void top(int u, vector<bool> &vis, vector<bool> &instack, vector<vector<int>> &adj)
{
  vis[u] = true;
  instack[u] = true;
  for(int v: adj[u])
    if(!vis[v])
      top(v, vis, instack, adj);
    else if(instack[v])
      ok = false;

  instack[u] = false;
  topsort.push_back(u);
}

void dfs(int u, vector<bool> &vis, vector<vector<int>> &adj)
{
  vis[u] = true;
  start[u] = timestamp++;

  for(int v: adj[u])
    if(!vis[v])
      dfs(v, vis, adj);

  endt[u] = timestamp++;
}

bool anc(int u, int v)
{
  int su = start[u], eu = endt[u];
  int sv = start [v], ev = endt[v];

  return (su <= sv && ev <= eu);
}

int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
    int t;
    cin>>t;
    while(t--)
    {
      ok = true;
      timestamp = 0;
      topsort.clear();

      int n, m;
      cin>>n>>m;
      vector<vector<int>> adj(n+1);
      vector<pair<int,int>> bi, orig;
      for(int i=0;i<m;i++)
      {
        int ty, u, v;
        cin>>ty>>u>>v;
        if(ty)
          adj[u].push_back(v), orig.push_back({u,v});
        else
          bi.push_back({u, v});
      }
      // find topological order and cycle
      vector<bool> vis(n+1, false), instack(n+1, false);
      for(int i=1;i<=n;i++)
        if(!vis[i])
          top(i, vis, instack, adj);
      reverse(begin(topsort), end(topsort));
      if(!ok)
      {
        cout<<"NO\n";
        continue;
      }
      // find start and end times
      fill(begin(vis), end(vis), false);
      for(int u: topsort)
        if(!vis[u])
          dfs(u, vis, adj);

      // for every bidirectional edge, check if adding can cause cycle
      vector<pair<int,int>> uni;
      for(auto e: bi)
      {
        int u = e.first, v=e.second;
        if(!anc(u, v))
        {
            uni.push_back({u, v});
            start[u] = start[v];
            endt[u] = endt[v];
        }
        else if(!anc(v, u))
        {
            uni.push_back({v, u});
            start[v] = start[u];
            endt[v] = endt[u];
        }
        else
          ok = false;
      }

      if(ok)
      {
        cout<<"YES\n"  ;
        for(auto x: uni)
          cout<<x.first<<" "<<x.second<<endl;
        for(auto x: orig)
          cout<<x.first<<" "<<x.second<<endl;
      }
      else
        cout<<"NO\n";
    }
}


#include <bits/stdc++.h>
using namespace std;

//Compiler version g++ 6.3.0
const int MAXN = 2e5+5;

bool ok;
vector<pair<int,int>> ans;
bool dfs(int u, vector<bool> &vis, vector<bool> &instack, vector<vector<pair<int,int>>> &adj)
{
  vis[u] = true;
  instack[u] = true;

  for(auto v: adj[u])
  {
    if(!vis[v.first])
    {
      bool child = dfs(v.first, vis, instack, adj);
      if(child)
      {
          if(v.second == 0)
          {
            ans.push_back({v.first, u});
          }
          else
            return true;
      }
      else
        ans.push_back({u, v.first});
    }
    else if(instack[v.first])
    {
      if(v.second == 0)
      {
          ans.push_back({v.first, u});
      }
      else
        return true;
    }
  }
  instack[u] = false;
  return false;
}
int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
    int t;
    cin>>t;
    while(t--)
    {
      ok = true;
      ans.clear();
      int n, m;
      cin>>n>>m;
      vector<vector<pair<int,int>>> adj(n+1);
      vector<pair<int,int>> edges;
      for(int i=0;i<m;i++)
      {
        int ty, u, v;
        cin>>ty>>u>>v;
          adj[u].push_back({v, ty});
      }

      vector<bool> vis(n+1, false), instack(n+1, false);

      bool cyc = false;
      for(int i=1;i<=n;i++)
      {
        if(!vis[i])
        {
          bool me = dfs(i, vis, instack, adj)  ;
          cyc = cyc || me;
        }
      }

      if(!cyc)
      {
        cout<<"YES\n"  ;
        for(auto x: ans)
          cout<<x.first<<" "<<x.second<<endl;
      }
      else
        cout<<"NO\n";
    }
}


#include <bits/stdc++.h>
using namespace std;

//Compiler version g++ 6.3.0
const int MAXN = 2e5+5;

bool ok;
vector<pair<int,int>> ans;
bool dfs(int u, vector<bool> &vis, vector<bool> &instack, vector<vector<pair<int,int>>> &adj)
{
  vis[u] = true;
  instack[u] = true;

  for(auto v: adj[u])
  {
    if(!vis[v.first])
    {
      bool child = dfs(v.first, vis, instack, adj);
      if(child)
      {
          if(v.second == 0)
          {
            ans.push_back({v.first, u});
          }
          else
          {
            ans.push_back({u, v.first});
            return true;
          }
      }
      else
        ans.push_back({u, v.first});
    }
    else if(instack[v.first])
    {
      if(v.second == 0)
      {
          ans.push_back({v.first, u});
      }
      else
      {
        ans.push_back({u, v.first});
        return true;
      }
    }
  }
  instack[u] = false;
  return false;
}
int main()
{
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
    int t;
    cin>>t;
    while(t--)
    {
      ok = true;
      ans.clear();
      int n, m;
      cin>>n>>m;
      vector<vector<pair<int,int>>> adj(n+1);
      vector<pair<int,int>> edges;
      for(int i=0;i<m;i++)
      {
        int ty, u, v;
        cin>>ty>>u>>v;
          adj[u].push_back({v, ty});
      }

      vector<bool> vis(n+1, false), instack(n+1, false);

      bool cyc = false;
      for(int i=1;i<=n;i++)
      {
        if(!vis[i])
        {
          bool me = dfs(i, vis, instack, adj)  ;
          cyc = cyc || me;
        }
      }

      if(!cyc)
      {
        cout<<"YES\n"  ;
        for(auto x: ans)
          cout<<x.first<<" "<<x.second<<endl;
      }
      else
        cout<<"NO\n";
    }
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout<<#x<<": "<<x<<endl
#define clr(x,v) memset(x, v, sizeof(x))
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 2e5 + 5;

vector<int> topsort;
bool vis[MAXN],instack[MAXN];
bool ok;
void topological_order(int u, vector<vector<int>> &adj)
{
    vis[u] = true;
    instack[u] = true;
    for(auto v : adj[u])
        if(!vis[v])
            topological_order(v, adj);
        else if(instack[v])
          ok = false;
    topsort.pb(u);
    instack[u] = false;
}

void cp()
{
  topsort.clear();
  ok = true;
    int n,m;
    cin >> n>>m;
    vector<vector<int>> adj(n+1), rev_adj(n+1);
    map<pi, bool> bi;
    vector<pi> orig;
    for(int i = 1; i <= m; i++)
    {
        int ty,u,v;
        cin >> ty>>u>>v;
        if(ty)
          orig.pb({u,v}), adj[u].pb(v), rev_adj[v].pb(u);
        if(!ty)
          bi[{u,v}] = true;
    }
    // Find topological Order
    fill(vis+1, vis+n+1, false);
    fill(instack+1, instack+n+1, false);
    for(int i = 1; i <= n; i++)
        if(!vis[i])
            topological_order(i, adj);
    reverse(all(topsort));

    vector<int> pos(n+1);
    for(int i=0;i<sz(topsort);i++)
      pos[topsort[i]] = i;

    if(!ok)
    {
      cout<<"NO\n"  ;
      return;
    }

    cout<<"YES\n";
    for(auto x: orig)
      cout<<x.ff<<" "<<x.ss<<endl;
    for(auto x: bi){
      int u=x.ff.ff, v =x.ff.ss;
      if(pos[u]>pos[v])
        swap(u, v);

      cout<<u<<" "<<v<<endl;

    }

}

int main()
{
    FASTIO;
    int t;
    t = 1;
    cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout<<#x<<": "<<x<<endl
#define clr(x,v) memset(x, v, sizeof(x))
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 2e5 + 5;

vector<int> topsort;
bool vis[MAXN];

void topological_order(int u, vector<vector<int>> &adj)
{
    vis[u] = true;
    for(auto v : adj[u])
        if(!vis[v])
            topological_order(v, adj);
    topsort.pb(u);
}

void dfs(int u, vector<pi> &scc, vector<vector<int>> &adj)
{
    vis[u] = true;
    for(int v : adj[u])
        if(!vis[v])
            scc.pb({v,u}), dfs(v, scc, adj);
}

void cp()
{
  topsort.clear();
    int n,m;
    cin >> n>>m;
    vector<vector<int>> adj(n+1), rev_adj(n+1);
    map<pi, bool> bi;
    vector<pi> orig;
    for(int i = 1; i <= m; i++)
    {
        int ty,u,v;
        cin >> ty>>u>>v;
        if(ty)
          orig.pb({u,v}), adj[u].pb(v), rev_adj[v].pb(u);
        if(!ty)
          bi[{u,v}] = true;
    }
    // Find topological Order
    fill(vis+1, vis+n+1, false);
    for(int i = 1; i <= n; i++)
        if(!vis[i])
            topological_order(i, adj);
    reverse(all(topsort));

    fill(vis+1, vis+n+1, false);
    bool ok = true;
    for(int u : topsort)
    {
        // Find SCC
        if(!vis[u])
        {
            vector<pi> scc;
            dfs(u, scc, rev_adj);

            if(sz(scc))
                ok = false;
        }
    }

    if(!ok)
    {
      cout<<"NO\n";
      return;
    }

    topsort.clear();
    for(auto e: bi)
    {
        adj[e.ff.ff].pb(e.ff.ss), rev_adj[e.ff.ss].pb(e.ff.ff);
    }
    fill(vis+1, vis+n+1, false);
    for(int i = 1; i <= n; i++)
        if(!vis[i])
            topological_order(i, adj);
    reverse(all(topsort));

    fill(vis+1, vis+n+1, false);
    vector<pi> ans;

    for(int u : topsort)
    {
        // Find SCC
        if(!vis[u])
        {
            vector<pi> scc;
            dfs(u, scc, rev_adj);
            reverse(all(scc));
            bool flip = false;
            for(int i=0;i<sz(scc);i++)
            {
              if(bi.count(scc[i]))
              {
                //if(!flip)
                flip=true, ans.pb({scc[i].ss, scc[i].ff}), bi.erase(scc[i]);
              }
              //cout<<scc[i]<<" ";
            }
            //cout<<endl;
        }
    }

    cout<<"YES\n";
    for(auto x: orig)
      cout<<x.ff<<" "<<x.ss<<endl;
    for(auto x: ans)
      cout<<x.ff<<" "<<x.ss<<endl;
    for(auto x: bi)
      cout<<x.ff.ff<<" "<<x.ff.ss<<endl;

}

int main()
{
    FASTIO;
    int t;
    t = 1;
    cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#define ll long long int
#define ld long double
#define pi pair<int,int>
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define sz(x) ((int)x.size())
#define ln(x) ((int)x.length())
#define mp make_pair
#define pb push_back
#define ff first
#define ss second
#define endl '\n'
#define dbg(x) cout<<#x<<": "<<x<<endl
#define clr(x,v) memset(x, v, sizeof(x))
#define FASTIO ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.0);
const ll mod = 1e9 + 7;
const int MAXN = 2e5 + 5;

vector<int> topsort;
bool vis[MAXN];

void topological_order(int u, vector<vector<int>> &adj)
{
    vis[u] = true;
    for(auto v : adj[u])
        if(!vis[v])
            topological_order(v, adj);
    topsort.pb(u);
}

void dfs(int u, vector<pi> &scc, vector<vector<int>> &adj)
{
    vis[u] = true;
    for(int v : adj[u])
        if(!vis[v])
            scc.pb({v,u}), dfs(v, scc, adj);
}

void cp()
{
  topsort.clear();
    int n,m;
    cin >> n>>m;
    vector<vector<int>> adj(n+1), rev_adj(n+1);
    map<pi, bool> bi;
    vector<pi> orig;
    for(int i = 1; i <= m; i++)
    {
        int ty,u,v;
        cin >> ty>>u>>v;
        if(ty)
          orig.pb({u,v}), adj[u].pb(v), rev_adj[v].pb(u);
        if(!ty)
          bi[{u,v}] = true;
    }
    // Find topological Order
    fill(vis+1, vis+n+1, false);
    for(int i = 1; i <= n; i++)
        if(!vis[i])
            topological_order(i, adj);
    reverse(all(topsort));

    fill(vis+1, vis+n+1, false);
    bool ok = true;
    for(int u : topsort)
    {
        // Find SCC
        if(!vis[u])
        {
            vector<pi> scc;
            dfs(u, scc, rev_adj);

            if(sz(scc))
                ok = false;
        }
    }

    if(!ok)
    {
      cout<<"NO\n";
      return;
    }

    topsort.clear();
    for(auto e: bi)
    {
        adj[e.ff.ff].pb(e.ff.ss), rev_adj[e.ff.ss].pb(e.ff.ff);
    }
    fill(vis+1, vis+n+1, false);
    for(int i = 1; i <= n; i++)
        if(!vis[i])
            topological_order(i, adj);
    reverse(all(topsort));

    fill(vis+1, vis+n+1, false);
    vector<pi> ans;

    for(int u : topsort)
    {
        // Find SCC
        if(!vis[u])
        {
            vector<pi> scc;
            dfs(u, scc, rev_adj);
            reverse(all(scc));
            bool flip = false;
            for(int i=0;i<sz(scc);i++)
            {
              if(bi.count(scc[i]))
              {
                if(!flip)
                flip=true, ans.pb({scc[i].ss, scc[i].ff}), bi.erase(scc[i]);
              }
              //cout<<scc[i]<<" ";
            }
            //cout<<endl;
        }
    }

    cout<<"YES\n";
    for(auto x: orig)
      cout<<x.ff<<" "<<x.ss<<endl;
    for(auto x: ans)
      cout<<x.ff<<" "<<x.ss<<endl;
    for(auto x: bi)
      cout<<x.ff.ff<<" "<<x.ff.ss<<endl;

}

int main()
{
    FASTIO;
    int t;
    t = 1;
    cin >> t;
    while(t--)
    {
        cp();
    }
    return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], vis[200005];
vector<pii> *q;
vector<int> *g;
bool cycle_found;
int comp = 0;

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
			return;
		}
	}
	return;
}

void topo_sort(int u) {
	vis[u] = true;
	for (auto v : g[u]) {
		if (!vis[v]) {
			topo_sort(v);
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}

void reset(int n, int m) {
	cycle_found = false;
	comp = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	ptr = 0;
}

void solve()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			continue;
		}

		else {
			cout << "YES" << "\n";
		}

		/*for (int i = 0; i < m; i++) {
			if (type[i] == 0) {
				g[q[i][0].first].push_back(q[i][0].second);
				g[q[i][0].second].push_back(q[i][0].first);
			}
		}*/

		for (int i = 1; i <= n; i++)
			if (vis[i] == 0)
				topo_sort(i);

		// print topo
		/*for (int i = 1; i <= n; i++) {
			cout << topo[i] << " " ;
		}
		*/
		//cout << endl;

		// connect undirected edges
		for (int i = 0; i < m; i++) {
			if (type[i] == 1) {
				continue;
			}
			if (topo[q[i][0].first] > topo[q[i][0].second]) {
				//g[q[i][0].first].pb(q[i][0].second);
				//q[i].pb(mp(q[i][0].first, q[i][0].second));
			}
			else {
				//g[q[i][0].second].pb(q[i][0].first);
				swap(q[i][0].first, q[i][0].second);
				//q[i].pb(mp(q[i][0].second, q[i][0].first));
			}
		}
		// print m edges
		for (int i = 0; i < m; i++) {
			cout << q[i][0].first << " " << q[i][0].second << endl;
		}
	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[100005], topo[100005], col[100005], vis[100005];
vector<pii> *q;
vector<int> *g;
bool cycle_found;
int comp = 0;

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
			return;
		}
	}
	return;
}

void topo_sort(int u) {
	vis[u] = true;
	for (auto v : g[u]) {
		if (!vis[v]) {
			topo_sort(v);
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}

void reset(int n, int m) {
	cycle_found = false;
	comp = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	ptr = 0;
}

void solve()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			continue;
		}

		else {
			cout << "YES" << "\n";
		}

		for (int i = 0; i < m; i++) {
			if (type[i] == 0) {
				g[q[i][0].first].push_back(q[i][0].second);
				g[q[i][0].second].push_back(q[i][0].first);
			}
		}

		for (int i = 1; i <= n; i++)
			if (vis[i] == 0)
				topo_sort(i);

		// connect undirected edges
		for (int i = 0; i < m; i++) {
			if (type[i] == 1) {
				continue;
			}
			if (topo[q[i][0].first] > topo[q[i][0].second]) {
				//g[q[i][0].first].pb(q[i][0].second);
				//q[i].pb(mp(q[i][0].first, q[i][0].second));
			}
			else {
				//g[q[i][0].second].pb(q[i][0].first);
				swap(q[i][0].first, q[i][0].second);
				//q[i].pb(mp(q[i][0].second, q[i][0].first));
			}
		}
		// print m edges
		for (int i = 0; i < m; i++) {
			cout << q[i][0].first << " " << q[i][0].second << endl;
		}
	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], vis[200005], comp = 0;
vector<pii> *q;
vector<int> *g;
bool cycle_found;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0; ptr = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
}

void dfs(int u) {
	col[u] = 1;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == 1) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	ptr++;
	topo[u] = ptr;
	col[u] = 2;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			//continue;
		}
		else {
			cout << "YES" << "\n";
			// print m edges
			for (int i = 0; i < m; i++) {
				// make reverse edge
				if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
					swap(q[i][0].first, q[i][0].second);
				}

				cout << q[i][0].first << " " << q[i][0].second << endl;
			}
		}

	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], comp = 0;
vector<pii> *q;
vector<int> *g;
bool cycle_found;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0; ptr = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(col, -1, sizeof col);
	return;
}
// dfs to check cycle and topo sort on directed edges only
void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	ptr++;
	topo[u] = ptr;
	//col[u] = 2;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			//continue;
		}
		else {
			cout << "YES" << "\n";
			// print m edges
			for (int i = 0; i < m; i++) {
				// make reverse edge
				if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
					swap(q[i][0].first, q[i][0].second);
				}

				cout << q[i][0].first << " " << q[i][0].second << endl;
			}
		}

	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], vis[200005];
vector<pii> *q;
vector<int> *g;
bool cycle_found;
int comp = 0;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	ptr = 0;
}

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
			return;
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}

/*void topo_sort(int u) {
	vis[u] = true;
	for (auto v : g[u]) {
		if (!vis[v]) {
			topo_sort(v);
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}*/

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			continue;
		}

		else {
			cout << "YES" << "\n";
		}

		/*for (int i = 0; i < m; i++) {
			if (type[i] == 0) {
				g[q[i][0].first].push_back(q[i][0].second);
				g[q[i][0].second].push_back(q[i][0].first);
			}
		}*/

		/*for (int i = 1; i <= n; i++)
			if (vis[i] == 0)
				topo_sort(i);*/

		// print topo
		/*for (int i = 1; i <= n; i++) {
			cout << topo[i] << " " ;
		}
		*/
		//cout << endl;

		// connect undirected edges
		for (int i = 0; i < m; i++) {
			if (type[i] == 1) {
				continue;
			}
			if (topo[q[i][0].first] > topo[q[i][0].second]) {
				//g[q[i][0].first].pb(q[i][0].second);
				//q[i].pb(mp(q[i][0].first, q[i][0].second));
			}
			else {
				//g[q[i][0].second].pb(q[i][0].first);
				swap(q[i][0].first, q[i][0].second);
				//q[i].pb(mp(q[i][0].second, q[i][0].first));
			}
		}
		// print m edges
		for (int i = 0; i < m; i++) {
			cout << q[i][0].first << " " << q[i][0].second << endl;
		}
	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], comp = 0;
vector<pii> *q;
vector<int> *g;
bool cycle_found;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0; ptr = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	//memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	return;
}

void dfs(int u) {
	col[u] = 1;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == 1) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	ptr++;
	topo[u] = ptr;
	col[u] = 2;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			//continue;
		}
		else {
			cout << "YES" << "\n";
			// print m edges
			for (int i = 0; i < m; i++) {
				// make reverse edge
				if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
					swap(q[i][0].first, q[i][0].second);
				}

				cout << q[i][0].first << " " << q[i][0].second << endl;
			}
		}

	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[100005], topo[100005], col[100005], vis[100005];
vector<pii> *q;
vector<int> *g;
bool cycle_found;
int comp = 0;

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
			return;
		}
	}
	return;
}

void topo_sort(int u) {
	vis[u] = true;
	for (auto v : g[u]) {
		if (!vis[v]) {
			topo_sort(v);
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}

void reset(int n, int m) {
	cycle_found = false;
	comp = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	ptr = 0;
}

void solve()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			continue;
		}

		else {
			cout << "YES" << "\n";
		}

		/*for (int i = 0; i < m; i++) {
			if (type[i] == 0) {
				g[q[i][0].first].push_back(q[i][0].second);
				g[q[i][0].second].push_back(q[i][0].first);
			}
		}*/

		for (int i = 1; i <= n; i++)
			if (vis[i] == 0)
				topo_sort(i);

		// print topo
		/*for (int i = 1; i <= n; i++) {
			cout << topo[i] << " " ;
		}
		*/
		//cout << endl;

		// connect undirected edges
		for (int i = 0; i < m; i++) {
			if (type[i] == 1) {
				continue;
			}
			if (topo[q[i][0].first] > topo[q[i][0].second]) {
				//g[q[i][0].first].pb(q[i][0].second);
				//q[i].pb(mp(q[i][0].first, q[i][0].second));
			}
			else {
				//g[q[i][0].second].pb(q[i][0].first);
				swap(q[i][0].first, q[i][0].second);
				//q[i].pb(mp(q[i][0].second, q[i][0].first));
			}
		}
		// print m edges
		for (int i = 0; i < m; i++) {
			cout << q[i][0].first << " " << q[i][0].second << endl;
		}
	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], vis[200005];
vector<pii> *q;
vector<int> *g;
bool cycle_found;
int comp = 0;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
	ptr = 0;
}

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	topo[u] = ptr;
	ptr++;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			continue;
		}

		else {
			cout << "YES" << "\n";
		}

		// print m edges
		for (int i = 0; i < m; i++) {
			// make reverse edge
			if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
				swap(q[i][0].first, q[i][0].second);
			}

			cout << q[i][0].first << " " << q[i][0].second << endl;
		}
	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], vis[200005], comp = 0;
vector<pii> *q;
vector<int> *g;
bool cycle_found;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0; ptr = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(vis, 0, sizeof vis);
	memset(col, -1, sizeof col);
}

void dfs(int u) {
	col[u] = comp;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	ptr++;
	topo[u] = ptr;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			//continue;
		}
		else {
			cout << "YES" << "\n";
			// print m edges
			for (int i = 0; i < m; i++) {
				// make reverse edge
				if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
					swap(q[i][0].first, q[i][0].second);
				}

				cout << q[i][0].first << " " << q[i][0].second << endl;
			}
		}

	}
}

int32_t main()
{
	solve();
	return 0;
}


#include<bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;

#define ff              first
#define ss              second
#define ll  			int
#define int             long long
#define pb              push_back
#define mp              make_pair
#define pii             pair<int,int>
#define vi              vector<int>
#define mii             map<int,int>
#define pqb             priority_queue<int>
#define pqs             priority_queue<int,vi,greater<int> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define mod             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define mk(arr,n,type)  type *arr=new type[n];
#define w(x)            int x; cin>>x; while(x--)
mt19937                 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

int ptr = 0, type[200005], topo[200005], col[200005], comp = 0;
vector<pii> *q;
vector<int> *g;
bool cycle_found;

void reset(int n, int m) {
	cycle_found = false;
	comp = 0; ptr = 0;
	q = new vector<pii> [m + 1];
	g = new vector<int> [n + 2];
	memset(topo, 0, sizeof topo);
	memset(col, -1, sizeof col);
	return;
}
// dfs to check cycle and topo sort on directed edges only
void dfs(int u) {
	col[u] = 1;
	for (auto v : g[u]) {
		if (col[v] == -1) {
			dfs(v);
		}
		else if (col[v] == col[u]) {
			//cout << u << " " << v << endl;
			cycle_found = true;
		}
	}

	ptr++;
	topo[u] = ptr;
	col[u] = 2;
	return;
}

void solve() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	int t, n, m, u, v;
	cin >> t;

	while (t--) {
		cin >> n >> m;
		reset(n, m);

		for (int i = 0; i < m; i++) {
			cin >> type[i] >>  u >> v;
			q[i].pb(mp(u, v));
			if (type[i] == 1)
				g[u].pb(v);
		}

		for (int i = 1; i <= n; i++) {
			// if uncoloured
			if (col[i] == -1) {
				dfs(i);
				comp++;
			}
		}
		// if there is already cycle in given graph ans will always be no
		// otherwise yes
		if (cycle_found) {
			cout << "NO" << "\n";
			//continue;
		}
		else {
			cout << "YES" << "\n";
			// print m edges
			for (int i = 0; i < m; i++) {
				// make reverse edge
				if (type[i] == 0 && (topo[q[i][0].first] < topo[q[i][0].second])) {
					swap(q[i][0].first, q[i][0].second);
				}

				cout << q[i][0].first << " " << q[i][0].second << endl;
			}
		}

	}
}

int32_t main()
{
	solve();
	return 0;
}


#include <bits/stdc++.h>

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops")

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

#define f first
#define s second
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define len(x) ((int)x.size())
#define pb push_back
//#define mp make_pair
#define lwb lower_bound
#define upb upper_bound
#define fch(elem, x) for (const auto &elem : x)
#define fchs(elem, x) for (auto &elem : x)

using namespace std;

void fast_io() {
    ios::sync_with_stdio(0);
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
}

void my_file_in_out() {
    freopen("inp.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
}

inline void OK(string t = "") { cerr << "OK " << t << '\n'; }
inline ld time() { return clock() / (ld)CLOCKS_PER_SEC; }
inline void stop(string t = "PROCESS FINISHED") { cerr << t << '\n'; exit(0); }

const ll MODD = 998244353;
const ll M = 1e9 + 7;

ll bp(ll c, ll st) {
    if (!st)
        return 1;
    if (st & 1)
        return (c * bp(c, st - 1)) % M;
    return bp((c * c) % M, st >> 1);
}

namespace VECTOR_MAGIC {
    template <typename T> istream &operator >>(istream &inp, vector <T>& a) {
        for (auto &elem : a)
            inp >> elem;
        return inp;
    }
    template <typename T> ostream &operator <<(ostream &out, vector <T>& a) {
        for (auto &elem : a)
            out << elem << ' ';
        out << endl;
        return out;
    }
    template <typename T> ostream &operator <<(ostream &out, vector <vector <T>>& a) {
        for (auto &elem : a)
            out << elem;
        return out;
    }
    template <typename T> void operator ++(vector <T> &a) {
        for (auto &el : a) el++;
    }
    template <typename T> void operator --(vector <T> &a) {
        for (auto &el : a) el--;
    }
}

using namespace VECTOR_MAGIC;

mt19937 rnd(time(0));

int rndc(int x) {
    int u = rnd();
    if (u < 0) u = -u;
    return u % x;
}

const int N = 3e5 + 7;
const int INF = 1e9 + 7;
const ll LINF = 4e17 + 9;

vector <vector <int>> g1, g2;
vector <int> used;
vector <pair <int, int>> edge;
bool flag;

void dfs1(int u) {
    used[u] = 1;
    fch (v, g1[u]) {
        if (used[v])
            continue;
        edge.push_back({u, v});
        dfs1(v);
    }
}

void dfs2(int u) {
    used[u] = 1;
    fch (v, g2[u]) {
        if (used[v] == 2)
            continue;
        if (used[v] == 1) {
            flag = false;
            continue;
        }
        dfs2(v);
    }
    used[u] = 2;
}

void solve() {
    g1.clear();
    g2.clear();
    edge.clear();
    used.clear();
    int n, m;
    cin >> n >> m;
    g1.resize(n);
    g2.resize(n);
    vector <int> deg(n, 0);
    used.resize(n, 0);
    for (int i = 0; i < m; i++) {
        int t, u, v;
        cin >> t >> u >> v;
        u--; v--;
        if (t == 0) {
            g1[u].push_back(v);
            g1[v].push_back(u);
        } else {
            edge.push_back({u, v});
            deg[v]++;
        }
    }
    vector <int> way(n, 0);
    iota(all(way), 0);
    sort(all(way), [&](int u, int v) {
        return deg[u] < deg[v];
    });
    fch (u, way)
        if (!used[u])
            dfs1(u);
    used.assign(n, 0);
    flag = true;
    fch (el, edge)
        g2[el.f].push_back(el.s);
    for (int i = 0; i < n; i++)
        if (used[i] == 0)
            dfs2(i);
    if (flag) {
        cout << "YES\n";
        fch (el, edge)
            cout << el.f + 1 << ' ' << el.s + 1 << '\n';
    } else {
        cout << "NO\n";
    }
}

signed main()
{
    fast_io();

    #ifdef LOCAL
        my_file_in_out();
    #endif

    int t = 1;
    cin >> t;

    while (t--)
        solve();

    #ifdef LOCAL
        cerr << "\n\n\ntime: " << time() << endl;
    #endif

    return 0;
}


#include <bits/stdc++.h>

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops")

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

#define f first
#define s second
#define all(x) x.begin(), x.end()
#define rall(x) x.rbegin(), x.rend()
#define len(x) ((int)x.size())
#define pb push_back
//#define mp make_pair
#define lwb lower_bound
#define upb upper_bound
#define fch(elem, x) for (const auto &elem : x)
#define fchs(elem, x) for (auto &elem : x)

using namespace std;

void fast_io() {
    ios::sync_with_stdio(0);
    ios_base::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
}

void my_file_in_out() {
    freopen("inp.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
}

inline void OK(string t = "") { cerr << "OK " << t << '\n'; }
inline ld time() { return clock() / (ld)CLOCKS_PER_SEC; }
inline void stop(string t = "PROCESS FINISHED") { cerr << t << '\n'; exit(0); }

const ll MODD = 998244353;
const ll M = 1e9 + 7;

ll bp(ll c, ll st) {
    if (!st)
        return 1;
    if (st & 1)
        return (c * bp(c, st - 1)) % M;
    return bp((c * c) % M, st >> 1);
}

namespace VECTOR_MAGIC {
    template <typename T> istream &operator >>(istream &inp, vector <T>& a) {
        for (auto &elem : a)
            inp >> elem;
        return inp;
    }
    template <typename T> ostream &operator <<(ostream &out, vector <T>& a) {
        for (auto &elem : a)
            out << elem << ' ';
        out << endl;
        return out;
    }
    template <typename T> ostream &operator <<(ostream &out, vector <vector <T>>& a) {
        for (auto &elem : a)
            out << elem;
        return out;
    }
    template <typename T> void operator ++(vector <T> &a) {
        for (auto &el : a) el++;
    }
    template <typename T> void operator --(vector <T> &a) {
        for (auto &el : a) el--;
    }
}

using namespace VECTOR_MAGIC;

mt19937 rnd(time(0));

int rndc(int x) {
    int u = rnd();
    if (u < 0) u = -u;
    return u % x;
}

const int N = 3e5 + 7;
const int INF = 1e9 + 7;
const ll LINF = 4e17 + 9;

vector <vector <int>> g1, g2;
vector <int> used;
vector <pair <int, int>> edge;
bool flag;

void dfs1(int u, int p = -1) {
    used[u] = 1;
    fch (v, g1[u]) {
        if (v == p)
            continue;
        edge.push_back({u, v});
        if (used[v])
            continue;
        dfs1(v, u);
    }
}

void dfs2(int u) {
    used[u] = 1;
    fch (v, g2[u]) {
        if (used[v] == 2)
            continue;
        if (used[v] == 1) {
            flag = false;
            continue;
        }
        dfs2(v);
    }
    used[u] = 2;
}

void solve() {
    g1.clear();
    g2.clear();
    edge.clear();
    used.clear();
    int n, m;
    cin >> n >> m;
    g1.resize(n);
    g2.resize(n);
    vector <int> deg(n, 0);
    used.resize(n, 0);
    for (int i = 0; i < m; i++) {
        int t, u, v;
        cin >> t >> u >> v;
        u--; v--;
        if (t == 0) {
            g1[u].push_back(v);
            g1[v].push_back(u);
        } else {
            edge.push_back({u, v});
            deg[v]++;
        }
    }
    vector <int> way(n, 0);
    iota(all(way), 0);
    sort(all(way), [&](int u, int v) {
        return deg[u] < deg[v];
    });
    fchs (elem, g1) {
        sort(all(elem), [&](int u, int v) {
            return deg[u] < deg[v];
        });
    }
    fch (u, way)
        if (!used[u])
            dfs1(u);
    used.assign(n, 0);
    flag = true;
    fch (el, edge)
        g2[el.f].push_back(el.s);
    for (int i = 0; i < n; i++)
        if (used[i] == 0)
            dfs2(i);
    if (flag) {
        cout << "YES\n";
        fch (el, edge)
            cout << el.f + 1 << ' ' << el.s + 1 << '\n';
    } else {
        cout << "NO\n";
    }
}

signed main()
{
    fast_io();

    #ifdef LOCAL
        my_file_in_out();
    #endif

    int t = 1;
    cin >> t;

    while (t--)
        solve();

    #ifdef LOCAL
        cerr << "\n\n\ntime: " << time() << endl;
    #endif

    return 0;
}