#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#define MAXN 1000+5
#define INF (int)1e9+5
#define DEBUG 1
using namespace std;
int G[MAXN][MAXN] = {INF}; //图
int COST[MAXN][MAXN] = {0}; //代价
int N,M;
int cost_d[MAXN] = {INF}; //距离数组
int cost_p[MAXN] = {0}; //花费数组
int vis[MAXN]={0};
struct Node{
int v;
int d,p;
};
vector<Node> Vec_G[MAXN];
void init(){
memset(G,INF,sizeof(G));
memset(COST,0,sizeof(COST));
memset(cost_d,INF,sizeof(cost_d));
memset(cost_p,0,sizeof(cost_p));
memset(vis,0,sizeof(vis));
}
void sovle1(){
while(scanf("%d%d",&N,&M)==2 && N&&M){
init();
int a,b,d,p,start,endd;
for(int i=0;i<M;i++){
scanf("%d%d%d%d",&a,&b,&d,&p);
//判断是否有重复的边,按最小的距离和花费更新
if(G[a][b] == INF || G[a][b]>d){
G[a][b] =G[b][a] = d;
COST[a][b]=COST[b][a] = p;
}else if(G[a][b]==d && COST[a][b] >p){
COST[a][b]=COST[b][a] = p;
}
}
scanf("%d%d",&start,&endd);
vis[start] = 1;
//首先更新cost数组
for(int i=1;i<=N;i++){
if(i == start){
cost_d[i] = 0;
cost_p[i] = 0;
}
else{
cost_d[i] = G[start][i];
cost_p[i] = COST[start][i];
}
}
//遍历其他未访问的节点
for(int i=1;i<N;i++){
int minn = INF,next;
//找出未访问过且最小路径的节点
for(int j=1;j<=N;j++){
if(!vis[j]&& cost_d[j] <minn){
minn = cost_d[j];
next = j;
}
}
vis[next] = 1;
//根据找出的next 更新cost数组
for(int j=1;j<=N;j++){
if(!vis[j]&&G[next][j]<INF){
if(G[next][j] + cost_d[next] < cost_d[j]){
cost_d[j] = G[next][j] + cost_d[next];
cost_p[j] = COST[next][j] + cost_p[next];
}else if(G[next][j] + cost_d[next] == cost_d[j]){
//如果距离相等,则根据 p来更新
if(COST[next][j] + cost_p[next] < cost_p[j])
cost_p[j] = COST[next][j] + cost_p[next];
}
}
}
}
printf("%d %d\n",cost_d[endd],cost_p[endd]);
}
}
int main()
{
if(DEBUG)
freopen("data.txt","r",stdin);
//vector_sovle();
sovle1();
return 0;
}