3 5
2 1 608591421 720962564
3 2 608591421 641991574
3 2 718110706 353409763
2 3 482786015 858341723
3 2 825397574 357507279
 2 1
2 1 317425595 636156322
 50000 1
20845 28516 904146168 997637433

Yes
641991574 1
2 
 Yes
0 0

 Yes
0 0


#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
template <class T> using Tree = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>;

#define ll long long
#define F first
#define S second
#define pb push_back
#define all(x) x.begin(), x.end()
#define gotohell {cout << "No"; exit(0);}
#define fail(x) return x;

const int N = 5e4 + 5;
const int LOG = 20;
const int MOD = 1e9 + 7;
const ll inf = 1e9 + 5;


int n, m;
struct edge{
    int u = 0, v = 0, c = 0, w = 0;
} e[N];

int mrk[N];
int dead[N];
set < pair <int, int> > adj[N];
unordered_map <int, int> hide[N], deg[N];

int oth(int v, int i){
    return ((v == e[i].v) ? e[i].u : e[i].v);
}

queue <int> q;
int check(int u, int c){
    while (true){
        auto it = adj[u].lower_bound({c, -1});
        if (it == adj[u].end() || (*it).F != c) break;
        int id = (*it).S;
        int v = oth(u, id);
        if (mrk[id] && mrk[id] != 2)  fail(2)
        if (dead[u] && dead[u] != id) fail(3)
        if (dead[v] && dead[v] != id) fail(4)
        q.push(id);
        mrk[id] = 2;
        dead[u] = dead[v] = id;
        adj[u].erase(adj[u].lower_bound({c, id}));
        adj[v].erase(adj[v].lower_bound({c, id}));
    }
    return 1;
}

int check(int u){
    while (adj[u].size()){
        auto it = adj[u].begin();
        auto tmp = *it;
        int c = tmp.F;
        int id = tmp.S;
        int v = oth(u, id);
        if (mrk[id] && mrk[id] != 1) fail(5)
        if (hide[u].count(c) && hide[u][c] != id) fail(6)
        if (hide[v].count(c) && hide[v][c] != id) fail(7)
        q.push(id);
        mrk[id] = 1;
        hide[u][c] = hide[v][c] = id;
        adj[u].erase(adj[u].lower_bound({c, id}));
        adj[v].erase(adj[v].lower_bound({c, id}));
    }
    return 1;
}

inline int calc(){
    while (q.size()){
        int i = q.front();
        int u = e[i].u; 
        int v = e[i].v;
        int c = e[i].c;
        q.pop();
        if (mrk[i] == 1){
            int tmp = check(u, c);
            if (tmp != 1) fail(tmp)
            tmp = check(v, c);
            if (tmp != 1) fail(tmp)
        }
        else {
            int tmp = check(u);
            if (tmp != 1) fail(tmp)
            tmp = check(v);
            if (tmp != 1) fail(tmp)
        }
    }
    return 1;
}

bool ok(int mid){

    while (q.size()) q.pop();
    for (int i = 1; i <= n; i ++){
        dead[i] = 0;
        adj[i].clear();
        hide[i].clear();
    }
    for (int i = 1; i <= m; i ++){
        mrk[i] = 0;
    }

    for (int i = 1; i <= m; i ++){
        int u = e[i].u, v = e[i].v;
        int c = e[i].c, w = e[i].w;
        if (mid < w){
            if (hide[u].count(c)) fail(0)
            if (hide[v].count(c)) fail(0)
            q.push(i);
            mrk[i] = 1;
            hide[u][c] = i; hide[v][c] = i;
            continue;
        }
        adj[u].insert({c, i});
        adj[v].insert({c, i});
    }

    if (calc() != 1) fail(0)
    return 1;
}

bool dfs(int r, int v, int par, int c){
    auto V = adj[v].lower_bound({c, -1});
    if (V == adj[v].end() || (*V).F != c) return 1;
    int id = (*V).S;
    int u = oth(v, id);
    adj[v].erase(V);
    adj[u].erase(adj[u].lower_bound({c, id}));
    mrk[id] = 3 - mrk[par];
    if (u == r) return (mrk[id] != 1);
    return dfs(r, u, id, c);
}

vector <int> ans;

int main() {
    ios_base::sync_with_stdio(0), cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; i ++){
        int u, v, c, w;
        cin >> u >> v >> c >> w;
        e[i] = {u, v, c, w};      
        deg[u][c] ++; deg[v][c] ++;
        if (2 < deg[u][c]) gotohell
        if (2 < deg[v][c]) gotohell   
    }
    for (int v = 1; v <= n; v ++){
        int cnt = 0;
        for (auto it : deg[v]){
            cnt += (it.S == 2);
        }
        if (1 < cnt) gotohell
    }
    int l = -1, r = inf;
    while (l + 1 < r){
        int mid = (l + r) >> 1;
        (ok(mid) ? r : l) = mid;
    }
    if (r == inf) gotohell
    ok(r);

    mrk[0] = 2;
    for (int v = 1; v <= n; v ++){
        int cnt = 0;
        for (auto it : deg[v]){
            cnt += (it.S == 2);
        }
        if (cnt == 0) continue;
        for (auto it : deg[v]){
            if (it.S == 2) continue;
            if (!dfs(v, v, 0, it.F)){
                gotohell
            }
        }
    }
    for (int v = 1; v <= n; v ++){
        for (auto it : deg[v]){
            if (it.S == 1) continue;
            if (!dfs(v, v, 0, it.F)){
                gotohell
            }
        }
    }
    for (int i = 1; i <= m; i ++){
        if (mrk[i] == 2){
            ans.pb(i);
        }
    }
    cout << "Yes\n" << r << ' ' << (int)ans.size() << '\n'; 
    for (int it : ans) cout << it << ' ';

    return 0;
}