#include <stdio.h>
#include <vector>
#include <queue>
using namespace std;
struct Edge{ //边的信息
    int u;
    int v;
    int weight;
    int cost;
    Edge( int u1 ,int v1 ,int weight1 ,int cost1)
    {
        u = u1;
        v = v1;
        weight = weight1;
        cost = cost1;
    }
};
vector<Edge> graph[1001]; //邻接表存储无向图的边,便于访问起始和终止顶点及其权值
struct PQueueNode{ //队列结点
    int u ; //顶点编号
    int distance; //从起始顶点到u顶点的距离
    PQueueNode(int u1 , int distance1)
    {
        u = u1;
        distance = distance1;
    }
};
bool operator < (PQueueNode lhs , PQueueNode rhs)
{
    return lhs.distance > rhs.distance; //按距离进行降序排序构造小根堆!
}
int Cost[1001]; //起点到顶点i的费用
int Dijksstra( int s ,int t) //起始顶点+终止顶点
{
    //定义一个优先队列
    priority_queue<PQueueNode> pqueue;
    int distance[1001]; //从起始顶点到i顶点的距离
    bool isVisited[1001]; //记录某个顶点是否已经访问过
    for(int i = 0 ; i < 1001 ; i++) //初始化距离和是否访问
    {
        distance[i] = -1; //初始均为-1 表示无穷均不可达
        isVisited[i] = false; //初始每个顶点均未访问
        Cost[i] = 0; //费用初始化0
    }
    distance[s] = 0; //起始点到自己距离为0
    PQueueNode node( s,0); //建立一个队列结点作为队首结点
    pqueue.push(node); //将第一个结点入队
    while ( !pqueue.empty() ) //优先队列不为空时
    {
        int u = pqueue.top().u; // 将当前队首结点的编号存起来
        pqueue.pop(); //队头出队
        //遍历u编号顶点的对应的边邻接表
        for( int i = 0 ; i < graph[u].size() ;i++)//遍历u号顶点的所有邻接边取出终止顶点
        {
            int v = graph[u][i].v; //将u到其他邻居的编号存起来
            int weight = graph[u][i].weight; //u->v的的权值(距离)
            int cost = graph[u][i].cost; //u->v的费用
            if( distance[v] == -1 //起点->v的距离如果为无穷 则只能先到u再到v
                || distance[v] > distance[u]+weight //起点到v的距离大于起点到u再到v的距离
                //起点到v和起点先到u再到v的距离一致且 起点到v的费用大于起点到u再到v的费用时
                //也可以更新费用为最小
                || (distance[v] == distance[u]+weight && Cost[v] > Cost[u]+cost )
                  )
            {
                distance[v] = distance[u] + weight; //更新距离起点到u再到v
                //将v顶点压入优先队列 继续执行
                //距离大费用就大,距离大的时候也要更新起点到v的距离为较小值
                Cost[v] = Cost[u] + cost; //更新起点到v的最小费用
                PQueueNode next(v,distance[v]); //创建队列结点
                pqueue.push(next);
            }
        }
    }//处理完整个图了从起点到其他顶点的最短距离均完成
    return distance[t] ; //返回s->t的最短距离
}
int main()
{
    int n,m;
    while (scanf("%d%d",&n,&m) != EOF)
    {
        if(n==0 && m==0)
        {
            break;
        }
        for(int i = 0 ; i <= n ;i++) //每组测试完成后重新清空一下1-n编号(0号舍弃)的边邻接表
        {
            graph[i].clear();
        }
        for(int i = 0 ; i < m ;i++)
        {
            //注意是无向图(迪杰斯特拉)
            int u,v,weight,cost; //顶点1,2对应权值(距离),费用
            scanf("%d%d%d%d",&u,&v,&weight,&cost);
            Edge e1(u,v,weight,cost);
            graph[u].push_back(e1);
            Edge e2(v,u,weight,cost);
            graph[v].push_back(e2);
        }
        int s,t;
        scanf("%d%d",&s,&t);
        int d = Dijksstra(s,t); //先执行迪杰斯特拉获取到最短距离及对应的最小费用
        int c = Cost[t]; //最小费用存在全局数组中 ,迪杰斯特拉只能返回一个值
        printf("%d %d\n", d,c);
    }
    return 0;
}