#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 110000
#define MAXM 210000
#define MAXT MAXN*2
#define INF 0x3f3f3f3f
int n,m;
int ch[MAXT][2],pnt[MAXT],val[MAXT],mx[MAXT],mt[MAXT];
bool rev[MAXT];
int stack[MAXT],tops=-1;
inline bool is_root(int x)
{
return !pnt[x] || (ch[pnt[x]][0]!=x && ch[pnt[x]][1]!=x);
}
void update(int now)
{
if (mx[ch[now][0]]>=mx[ch[now][1]])
{
mx[now]=mx[ch[now][0]];
mt[now]=mt[ch[now][0]];
}else
{
mx[now]=mx[ch[now][1]];
mt[now]=mt[ch[now][1]];
}
if (val[now]>mx[now])
{
mx[now]=val[now];
mt[now]=now;
}
}
void reverse(int now)
{
swap(ch[now][0],ch[now][1]);
rev[now]^=1;
}
void down(int now)
{
if (rev[now])
{
reverse(ch[now][0]);
reverse(ch[now][1]);
rev[now]=false;
}
}
void rotate(int now)
{
int p=pnt[now],anc=pnt[p];
int dir=ch[p][0]==now;
if (!is_root(p))
ch[anc][ch[anc][1]==p]=now;
pnt[now]=anc;
pnt[ch[now][dir]]=p;
ch[p][1-dir]=ch[now][dir];
pnt[p]=now;
ch[now][dir]=p;
update(p);
update(now);
}
void splay(int now)
{
int x=now;
stack[++tops]=x;
while (!is_root(x))
{
x=pnt[x];
stack[++tops]=x;
}
while (~tops)down(stack[tops--]);
while (!is_root(now))
{
int p=pnt[now],anc=pnt[p];
if (is_root(p))
rotate(now);
else if ((ch[anc][0]==p) == (ch[p][0]==now))
rotate(p),rotate(now);
else
rotate(now),rotate(now);
}
}
int access(int now)
{
int son=0;
while (now)
{
splay(now);
ch[now][1]=son;
update(now);
son=now;
now=pnt[now];
}
return son;
}
void make_root(int now)
{
access(now);
splay(now);
reverse(now);
}
void Create_edge(int x,int y)
{
//cout<<"Add:"<<x<<" "<<y<<endl;
make_root(x);
make_root(y);
pnt[y]=x;
ch[x][0]=y;
update(x);
}
void Erase_edge(int x,int y)
{
//cout<<"Del:"<<x<<" "<<y<<endl;
make_root(x);
access(y);
splay(x);
if (ch[x][0]==y)
{
splay(y);
ch[y][1]=pnt[x]=0;
update(y);
}else if (ch[x][1]==y)
{
ch[x][1]=pnt[y]=0;
update(x);
}else throw 1;
}
pair<int,int> Query_path(int x,int y)
{
//cout<<"Qry:"<<x<<" "<<y<<endl;
make_root(x);
int t=access(y);
return make_pair(mx[t],mt[t]-n);
}
struct aaa
{
int x,y,a,b,id;;
bool flag;
}l[MAXM],l0[MAXN];;
bool cmp_a(aaa a1,aaa a2)
{
return a1.a<a2.a;
}
bool cmp_b(aaa a1,aaa a2)
{
return a1.b<a2.b;
}
int uf[MAXN];
int get_fa(int now)
{
return uf[now]==now ? now : uf[now]=get_fa(uf[now]);
}
bool comb_uf(int x,int y)
{
x=get_fa(x);
y=get_fa(y);
if (x==y)return false;
uf[x]=y;
return true;
}
int main()
{
freopen("input.txt","r",stdin);
int i,j,k,x,y,z;
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++)
uf[i]=i;
for (i=1;i<=m;i++)
{
scanf("%d%d%d%d",&l[i].x,&l[i].y,&l[i].a,&l[i].b);
l[i].id=i;
val[i+n]=l[i].b;
l0[i]=l[i];
}
sort(l+1,l+m+1,cmp_a);
int ans=INF;
for (i=1;i<=m;i++)
{
if (comb_uf(l[i].x,l[i].y))
{
l[i].flag=true;
Create_edge(l[i].x,l[i].id+n);
Create_edge(l[i].y,l[i].id+n);
}else
{
pair<int,int> pr;
pr=Query_path(l[i].x,l[i].y);
if (pr.first<=l[i].b)continue;
Erase_edge(l0[pr.second].x,pr.second+n);
Erase_edge(l0[pr.second].y,pr.second+n);
Create_edge(l[i].x,l[i].id+n);
Create_edge(l[i].y,l[i].id+n);
}
if (get_fa(1)==get_fa(n))
{
ans=min(ans,l[i].a+Query_path(1,n).first);
// cout<<ans<<endl;
}
}
if (ans==INF)
printf("-1\n");
else
printf("%d\n",ans);
}