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


//Khodaya aghideye man ra az dast oghde am masoon dar!
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
template <typename T> using ordered_set =  tree<T, null_type, greater<T>, rb_tree_tag, tree_order_statistics_node_update>;
template <typename T> using ordered_multiset =  tree<T, null_type, greater_equal<T>, rb_tree_tag, tree_order_statistics_node_update>;
typedef long long ll;
typedef long double ld;
#pragma optimize O3
const int MOD = (int)1e9 + 7;
const int MAXN = (int)5e5 + 7;
const int infint = (ll)1e9;
const ll inf = (ll)1e18;
struct edge{
	int u, v, c, t;
};
unordered_map<int, int> hsh_color;
edge e[MAXN];
int two[MAXN], cur[MAXN], visited[MAXN], scc[MAXN];
int n, m, compo = 0;
vector<pair<int, int> > clause;
vector<int> adj[MAXN], G[MAXN], rev[MAXN], bad[MAXN], col[MAXN];
int id[MAXN];
vector<int> topol;
int nt(int a)
{
	if(a < 3 * m)
		return a + 3 * m;
	else
		return a - 3 * m;
}
void add_default_clause()
{
	for (int i = 0; i < m; i++)
	{
		if(e[i].c == two[e[i].u])
			bad[e[i].u].push_back(i);
		if(e[i].c == two[e[i].v])
			bad[e[i].v].push_back(i);
	}
	for (int i = 1; i <= n; i++)
		if(bad[i].size() == 2)
			clause.push_back({bad[i][0], bad[i][1]});
	int aux = 0;
	for (int i = 1; i <= n; i++)
	{
		if(adj[i].size() == 0)
			continue;
		//az majmooe yalhaye in bayad max 1 ta bardasht!
		for (int j = 1; j < adj[i].size(); j++)
			clause.push_back({aux + m + j, nt(aux + m + j - 1)});
		for (int j = 0; j < adj[i].size(); j++)
			clause.push_back({aux + m + j, nt(adj[i][j])});
		for (int j = 0; j < adj[i].size() - 1; j++)
			clause.push_back({nt(aux + m + j), nt(adj[i][j + 1])});
		aux += adj[i].size();
	}
}
void make_graph()
{
	for (int i = 0; i < 6 * m; i++)
		G[i].clear(), rev[i].clear();
	for (auto u : clause)
	{
		G[u.first].push_back(nt(u.second));
		G[u.second].push_back(nt(u.first));
		rev[nt(u.second)].push_back(u.first);
		rev[nt(u.first)].push_back(u.second);
	}
}
void dfs(int u)
{
	visited[u] = 1;
	for (auto v : G[u])
		if(!visited[v])
			dfs(v);
	topol.push_back(u);
}
void dfs2(int u)
{
	scc[u] = compo;
	visited[u] = 1;
	for (auto v : rev[u])
		if(!visited[v])
			dfs2(v);
}
bool sat()
{
	make_graph();
	topol.clear();
	memset(scc, -1, sizeof scc);
	memset(visited, 0, sizeof visited);
	for (int i = 0; i < 6 * m; i++)
		if(!visited[i])
			dfs(i);
	memset(visited, 0, sizeof visited);
	compo = 0;
	for (int i = topol.size() - 1; i >= 0; i--)
		if(!visited[topol[i]])
			dfs2(topol[i]), compo++;
	for (int i = 0; i < 3 * m; i++)
		if(scc[i] == scc[nt(i)])
		{
			return 0;
		}
	return 1;
}
bool check(int tme)
{
	for (int i = 0; i < m; i++)
		if(e[i].t > tme)
			clause.push_back({nt(i), nt(i)});
	bool ans = sat();
	for (int i = 0; i < m; i++)
		if(e[i].t > tme)	
			clause.pop_back();
	return ans;
}
int main()
{	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> e[i].u >> e[i].v >> e[i].c >> e[i].t;
		adj[e[i].u].push_back(i);
		adj[e[i].v].push_back(i);
	}
	//no vertex with 3 same edge && no vertex with 2pair of same edge.
	memset(two, -1, sizeof two);
	set<int> diff_col;
	for (int i = 0; i < m; i++)
		diff_col.insert(e[i].c);
	int cmp_color = 0;
	for (auto u : diff_col)
		hsh_color[u] = cmp_color++;
	
	for (int i = 0; i < m; i++)
		e[i].c = hsh_color[e[i].c];
	for (int i = 0; i < m; i++)
		col[e[i].c].push_back(i);
	for (int i = 0; i < cmp_color; i++)
	{
		memset(cur, 0, sizeof cur);
		for (auto cnt : col[i])
		{
			cur[e[cnt].u]++;
			cur[e[cnt].v]++;
			if(cur[e[cnt].u] == 3)
				return cout << "No", 0;
			if(cur[e[cnt].v] == 3)
				return cout << "No", 0;
			if(cur[e[cnt].u] == 2 && two[e[cnt].u] != -1)
				return cout << "No", 0;
			if(cur[e[cnt].v] == 2 && two[e[cnt].v] != -1)
				return cout << "No", 0;
			if(cur[e[cnt].u] == 2)
				two[e[cnt].u] = i;
			if(cur[e[cnt].v] == 2)
				two[e[cnt].v] = i;
		}
	}
	//binary seach && 2-sat
	vector<int> diff_time;
	for (int i = 0; i < m; i++)
		diff_time.push_back(e[i].t);
	sort(diff_time.begin(), diff_time.end());
	add_default_clause();
	if(check(diff_time.back()) == 0)
		return cout << "No", 0;
	bool any_two = 0;
	for (int i = 1; i <= n; i++)
		if(two[i] != -1)
			any_two = 1;
	if(!any_two)
		return cout << "Yes\n0 0", 0;
	
	int l = -1, r = diff_time.size();
	while(r - l > 1)
	{
		int mid = (l + r) >> 1;
		if(check(diff_time[mid]))
			r = mid;
		else
			l = mid;
	}
	int ans = diff_time[r];
	cout << "Yes\n" << ans << " ";
	for (int i = 0; i < m; i++)
		if(e[i].t > ans)
			clause.push_back({nt(i), nt(i)});
	sat();
	vector<int> v;
	for (int i = 0; i < topol.size(); i++)
		id[topol[i]] = i;
	for (int i = 0; i < m; i++)
		if(id[i] > id[nt(i)])
			v.push_back(i + 1);
	cout << v.size() << "\n";
	for (auto u : v)
		cout << u << " ";
}
