/*
HDU 3062
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<algorithm>
using namespace std;
const int MAXE=100000002;
const int MAX=6009;
struct EDGE
{
int v; // 从u点出发能到达的点v
int next; // 从u点出发能到达的下一条边的编号
}edge[MAXE];
int first[MAX]; // first[u] = e:从点u出发的最后一条边的编号是e(“最后”是指最后输入)
int dfn[MAX]; // dfn[u]:节点u搜索的次序编号(时间戳)
int low[MAX];// low[u]:是u或u的子树能够追溯到的最早的栈中节点的次序号
int ins[MAX];// 是否在栈中
int scc[MAX]; // scc[i] = j:第i个点所在的强连通分量的编号 (此题可有可无)
int t,n,m;
int num; // 强连通分量的数目
int index; // 次序编号
int s[MAX];
int top,e;
int a,b,c,d,nn;
int DFS(int x)
{
low[x]=dfn[x]=index++;
s[++top]=x;
ins[x]=1;
// 枚举每一条边:u-->v
for(int k=first[x];k!=-1;k=edge[k].next)
{
int v=edge[k].v;
if(dfn[v]==0)
{
DFS(v);
low[x]=min(low[x],low[v]);
}
else if(ins[v])
{
low[x]=min(dfn[v],low[x]);
}
}
// 如果节点u是强连通分量的根
if(low[x]==dfn[x])
{
int v;
num++;
do{
v=s[top--];
ins[v]=0;
scc[v]=num;
}while(v!=x);
}
return 1;
}
inline void add(int u,int v)
{
edge[e].v=v;
edge[e].next=first[u];
first[u]=e++;
}
inline int turn(int x)
{
return x>n?x-n:x+n;
}
void init()
{
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(first,-1,sizeof(first));
// memset(ins,0,sizeof(ins));
index=1;
top=-1;
num=0;
e=0;
int aa,bb;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
a++;
aa=a+c*n;
b++;
bb=b+d*n;
add(aa,turn(bb));
add(bb,turn(aa));
}
}
bool check()
{
for(int i=1;i<=n;i++)
if(scc[i]==scc[i+n])
return false;
return true;
}
void solve()
{
for(int i=1;i<=2*n;i++)
{
if(dfn[i]==0)
{
DFS(i);
}
}
if(check())puts("YES");
else puts("NO");
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
scanf("%d",&m);
init();
solve();
}
return 0;
}
/*
HDU 1824
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<algorithm>
using namespace std;
const int MAXE=1000002;
const int MAX=6009;
struct EDGE
{
int v; // 从u点出发能到达的点v
int next; // 从u点出发能到达的下一条边的编号
}edge[MAXE];
int first[MAX]; // first[u] = e:从点u出发的最后一条边的编号是e(“最后”是指最后输入)
int dfn[MAX]; // dfn[u]:节点u搜索的次序编号(时间戳)
int low[MAX];// low[u]:是u或u的子树能够追溯到的最早的栈中节点的次序号
int ins[MAX];// 是否在栈中
int scc[MAX]; // scc[i] = j:第i个点所在的强连通分量的编号 (此题可有可无)
int t,n,m;
int num; // 强连通分量的数目
int index; // 次序编号
int s[MAX];
int top,e;
int a,b,c;
int DFS(int x)
{
low[x]=dfn[x]=index++;
s[++top]=x;
ins[x]=1;
// 枚举每一条边:u-->v
for(int k=first[x];k!=-1;k=edge[k].next)
{
int v=edge[k].v;
if(dfn[v]==0)
{
DFS(v);
low[x]=min(low[x],low[v]);
}
else if(ins[v])
{
low[x]=min(dfn[v],low[x]);
}
}
// 如果节点u是强连通分量的根
if(low[x]==dfn[x])
{
int v;
num++;
do{
v=s[top--];
ins[v]=0;
scc[v]=num;
}while(v!=x);
}
return 1;
}
inline void add(int u,int v)
{
edge[e].v=v;
edge[e].next=first[u];
first[u]=e++;
}
void init()
{
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(first,-1,sizeof(first));
// memset(ins,0,sizeof(ins));
index=1;
top=-1;
num=0;
e=0;
for(int i=1;i<=t;i++)
{
scanf("%d%d%d",&a,&b,&c);
a++;
b++;
c++;
add(a+n,b);
add(a+n,c);
add(b+n,a);
add(c+n,a);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
a++;
b++;
add(a,b+n);
add(b,a+n);
}
}
bool check()
{
for(int i=1;i<=n;i++)
if(scc[i]==scc[i+n])
return false;
return true;
}
void solve()
{
for(int i=1;i<=2*n;i++)
{
if(dfn[i]==0)
{
DFS(i);
}
}
if(check())puts("yes");
else puts("no");
}
int main()
{
while(scanf("%d%d",&t,&m)!=EOF)
{
n=3*t;
init();
solve();
}
return 0;
}