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>
using namespace std;
const int MAX_N = 50 * 1000 + 10;
int E, c[MAX_N], t[MAX_N], type[MAX_N], col[MAX_N], pl[4 * MAX_N], n, m;
vector<int> edges[MAX_N], adj[4 * MAX_N], rev[4 * MAX_N], topol;
map<int, int> color_cnt[MAX_N];
bool mark[4 * MAX_N], val[4 * MAX_N];
pair<int, int> same_edge[MAX_N];
void read_input() {
  cin >> n >> m;
  for (int i = 0; i < m; i++) {
    int u, v;
    cin >> u >> v >> c[i] >> t[i];
    u--, v--;
    edges[u].push_back(i);
    edges[v].push_back(i);
    color_cnt[u][c[i]]++;
    color_cnt[v][c[i]]++;
  }
}
void imp() {
  cout << "No";
  exit(0);
}
int NOT(int v) { return 4 * m - v - 1; }
void check_ver_color() {
  for (int v = 0; v < n; v++) {
    for (auto i : color_cnt[v]) {
      if (i.second > 2)
        imp();
      else if (i.second == 2 && col[v])
        imp();
      else if (i.second == 2)
        col[v] = i.first;
    }
    if (col[v]) {
      type[v] = 1;
      for (auto i : edges[v])
        if (c[i] == col[v]) {
          same_edge[v].first = i;
          swap(same_edge[v].first, same_edge[v].second);
        }
    } else
      type[v] = 2;
  }
}
void add_clause(int u, int v) {
  adj[u].push_back(v);
  adj[NOT(v)].push_back(NOT(u));
  rev[v].push_back(u);
  rev[NOT(u)].push_back(NOT(v));
}
void type1_fill_adj(int v) {
  int e1 = same_edge[v].first;
  int e2 = same_edge[v].second;
  add_clause(e1, NOT(e2));
  add_clause(NOT(e1), e2);
  for (auto e : edges[v])
    if (e != e1 && e != e2) add_clause(e, NOT(e));
}
void type2_fill_adj(int v) {
  for (int i = 0; i < (int)edges[v].size(); i++, E++) {
    add_clause(edges[v][i], E);
    if (i) add_clause(E - 1, E);
  }
}
void build_graph(int d) {
  E = m;
  for (int i = 0; i < 4 * m; i++) adj[i].clear(), rev[i].clear();
  for (int v = 0; v < n; v++)
    if (type[v] == 1)
      type1_fill_adj(v);
    else
      type2_fill_adj(v);
  for (int e = 0; e < m; e++)
    if (t[e] > d) add_clause(e, NOT(e));
}
void rev_dfs(int v, int k) {
  mark[v] = true;
  pl[v] = k;
  for (auto u : rev[v])
    if (!mark[u]) rev_dfs(u, k);
}
void dfs(int v) {
  mark[v] = true;
  for (auto u : adj[v])
    if (!mark[u]) dfs(u);
  topol.push_back(v);
}
bool scc() {
  memset(mark, false, sizeof mark);
  topol.clear();
  for (int v = 0; v < 4 * m; v++)
    if (!mark[v]) dfs(v);
  reverse(topol.begin(), topol.end());
  memset(mark, false, sizeof mark);
  int cnt = 0;
  for (auto v : topol)
    if (!mark[v]) rev_dfs(v, cnt++);
  memset(val, false, sizeof val);
  for (int v = 0; v < m; v++)
    if (pl[v] > pl[NOT(v)])
      val[v] = true;
    else if (pl[v] < pl[NOT(v)])
      val[NOT(v)] = true;
    else
      return false;
  return true;
}
bool is_ok(int d) {
  build_graph(d);
  return scc();
}
int find_ans() {
  int L = -1, R = 1e9 + 5;
  while (L < R - 1) {
    int mid = (L + R) / 2;
    if (is_ok(mid))
      R = mid;
    else
      L = mid;
  }
  if (!is_ok(R)) imp();
  return R;
}
void solve() {
  check_ver_color();
  int ans = find_ans();
  int cnt = 0;
  for (int i = 0; i < m; i++)
    if (val[i]) cnt++;
  cout << "Yes\n" << ans << ' ' << cnt << endl;
  for (int i = 0; i < m; i++)
    if (val[i]) cout << i + 1 << ' ';
}
int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  read_input();
  solve();
}
