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;
struct edge {
  int a, b, t, index;
};
struct Constraint {
  int a, b, best0, best1;
  vector<int> sol0, sol1;
  int cons;
};
void nope() {
  cout << "No\n";
  exit(0);
}
void dfs(int node, vector<vector<edge>> &G, vector<int> &seen, int &end,
         int par, vector<int> &best, vector<vector<int>> &e, int from = -1) {
  seen[node] = 1;
  end = node;
  for (auto edge : G[node]) {
    int to = edge.b;
    if (to == node) to = edge.a;
    if (edge.index == from) continue;
    best[par] = max(best[par], edge.t);
    e[par].push_back(edge.index);
    if (seen[to]) continue;
    dfs(to, G, seen, end, 1 - par, best, e, edge.index);
    break;
  }
}
bool match(int node, vector<vector<int>> &G, vector<int> &seen, vector<int> &L,
           vector<int> &R) {
  if (seen[node]) return false;
  seen[node] = 1;
  for (auto temp : G[node])
    if (R[temp] == -1) {
      L[node] = temp;
      R[temp] = node;
      return true;
    }
  for (auto temp : G[node])
    if (match(R[temp], G, seen, L, R)) {
      L[node] = temp;
      R[temp] = node;
      return true;
    }
  return false;
}
vector<int> getMatching(int n, int m, vector<vector<int>> &G) {
  vector<int> L(n, -1), R(m, -1);
  int ans = 0;
  while (true) {
    bool ok = false;
    vector<int> seen(n, 0);
    for (int i = 0; i < n; ++i)
      if (L[i] == -1 && match(i, G, seen, L, R)) {
        ok = true;
        ans++;
      }
    if (!ok) break;
  }
  if (ans < m) return vector<int>();
  return L;
}
int main() {
  ios_base ::sync_with_stdio(false);
  int n, m;
  cin >> n >> m;
  vector<vector<edge>> G(n, vector<edge>());
  vector<vector<edge>> C(m, vector<edge>());
  unordered_map<int, int> norm;
  vector<int> seen(n, 0);
  vector<int> hasCycle(n, 0);
  int cnt = 0;
  for (int i = 0; i < m; ++i) {
    int a, b, color, t;
    cin >> a >> b >> color >> t;
    a--, b--;
    if (norm.count(color) == 0) norm[color] = cnt++;
    C[norm[color]].push_back({a, b, t, i});
  }
  int cons = 0;
  vector<Constraint> odd, even;
  int ans = 0;
  vector<int> sol;
  for (int color = 0; color < cnt; ++color) {
    unordered_set<int> nodes;
    for (auto temp : C[color]) {
      nodes.insert(temp.a);
      nodes.insert(temp.b);
      G[temp.a].push_back(temp);
      G[temp.b].push_back(temp);
    }
    for (auto node : nodes) {
      if (G[node].size() > 2) {
        nope();
      }
      if (G[node].size() == 2 && hasCycle[node]) {
        nope();
      }
      if (G[node].size() == 2) {
        hasCycle[node] = 1;
      }
    }
    for (auto node : nodes)
      if (G[node].size() == 1 && !seen[node]) {
        int start = node, end = -1;
        vector<int> best(2, 0);
        vector<vector<int>> e(2, vector<int>());
        dfs(node, G, seen, end, 0, best, e);
        int length = e[0].size() + e[1].size();
        if (length == 1) continue;
        if (length % 2 == 1) {
          odd.push_back({start, end, best[0], best[1], e[0], e[1], 0});
          continue;
        }
        even.push_back({start, end, best[0], best[1], e[0], e[1], cons++});
      }
    for (auto node : nodes) {
      if (seen[node]) continue;
      vector<vector<int>> e(2, vector<int>());
      vector<int> best(2, 0);
      int bla = 0;
      if (G[node].size() == 2) dfs(node, G, seen, bla, 0, best, e, -1);
      int length = e[0].size() + e[1].size();
      if (length % 2 == 1) {
        for (int i = 0; i < 2; ++i)
          for (auto temp : e[i]) cerr << i << " " << temp << " ";
        nope();
      }
      int x = 0;
      if (best[0] > best[1]) x = 1;
      for (auto temp : e[x]) sol.push_back(temp);
      ans = max(ans, best[x]);
    }
    for (auto node : nodes) {
      G[node].clear();
      seen[node] = 0;
    }
  }
  vector<int> aux = sol;
  sol.clear();
  int rAns = 0;
  for (int lf = ans, rt = 1e9; lf <= rt;) {
    int mid = (lf + rt) / 2;
    bool can = true;
    vector<int> tempSol = aux;
    vector<int> dead = hasCycle;
    vector<vector<int>> M(n, vector<int>());
    for (auto temp : odd) {
      if (temp.best0 > mid && temp.best1 > mid) can = false;
      if (temp.best1 <= mid) {
        for (auto edge : temp.sol1) tempSol.push_back(edge);
        continue;
      }
      if (dead[temp.a] || dead[temp.b]) can = false;
      dead[temp.a] = dead[temp.b] = 1;
      for (auto edge : temp.sol0) tempSol.push_back(edge);
    }
    for (auto temp : even) {
      if (!dead[temp.a] && temp.best0 <= mid) M[temp.a].push_back(temp.cons);
      if (!dead[temp.b] && temp.best1 <= mid) M[temp.b].push_back(temp.cons);
    }
    vector<int> whoMatches = getMatching(n, cons, M);
    if (whoMatches.empty())
      can = false;
    else {
      for (int i = 0; i < n; ++i)
        if (whoMatches[i] >= 0) {
          int r = whoMatches[i];
          if (even[r].a == i) {
            for (auto temp : even[r].sol0) tempSol.push_back(temp);
          } else {
            for (auto temp : even[r].sol1) tempSol.push_back(temp);
          }
        }
    }
    if (can) {
      rAns = mid;
      rt = mid - 1;
      sol = tempSol;
    } else {
      lf = mid + 1;
    }
  }
  cout << "Yes\n";
  cout << rAns << " " << sol.size() << "\n";
  for (auto temp : sol) cout << temp + 1 << " ";
}
