#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

using namespace std;
#define rep(i, n) for (ll i = 0; i < (ll)(n); (i)++)
#define per(i, n) for (ll i = n - 1; i >= 0; (i)--)
#define FOR(i, a, b) for (ll i = (a); i < (b); i++)
#define ROF(i, a, b) for (ll i = (b) - 1; i >= (a); i--)
#define ALL(x) std::begin(x), std::end(x)

typedef long long ll;
struct Edge{
    int to;
    ll weight;
    ll K;
    Edge(int t, ll w, ll k): to(t), weight(w), K(k) {}
};

// ダイクストラ法. Edgeを読み込む必要あり. sからstartして各頂点までの最短距離をdistに格納する.
// もし経路が存在しない場合はINF == 1LL << 60を入れる.
void dijkstra(const vector<vector<Edge>>& G, const int& s, const int& g, vector<ll>& dist){
    const ll INF = 1LL << 60;
    const int V = G.size();
    dist.assign(V, INF); dist[s] = 0;
    vector<bool> visited(V, false);
    using P = pair<ll, int>; //距離、頂点の順
    priority_queue<P, vector<P>, greater<P>> pq;

    pq.push(make_pair(0, s));
    while(!pq.empty()){
        int v = pq.top().second; pq.pop();
        // if(v == g) return;
        if(visited[v] == true) continue;
        visited[v] = true;
        for(auto e: G[v]){
            int nv = e.to;
            int r = dist[v] % e.K;
            r = (e.K - r) % e.K;
            if(dist[nv] > dist[v] + r + e.weight){
                dist[nv] = dist[v] + r + e.weight;
                pq.push(make_pair(dist[nv], nv));
            }
        }
    }
}


int main(){
    //input, initialize
    int N, M, X, Y; cin >> N >> M >> X >> Y;
    X--; Y--;
    vector<vector<Edge>> G(N);
    rep(i, M){
        int A, B; ll T, K;
        cin >> A >> B >> T >> K; A--; B--;
        G[A].emplace_back(Edge(B, T, K));
        G[B].emplace_back(Edge(A, T, K));
    }
    //solve
    vector<ll> dist;
    const ll INF = 1LL << 60;
    dijkstra(G, X, Y, dist);
    //output
    if(dist[Y] == INF) cout << -1 << '\n';
    else cout << dist[Y] << '\n';
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 7;

struct node {
    ll u, d;
    bool operator < (const node x) const {
        return d > x.d;
    }
};

struct edge{
    ll to, w, k;
};

vector<edge> g[N];
int vis[N];
priority_queue<node>q;

ll dj(int s, int t) {
    q.push({s, 0});
    while (q.size()) {
        node cd = q.top();
        q.pop();
        if (cd.u == t) {
            return cd.d;
        }
        if (vis[cd.u]) continue;
        vis[cd.u] = 1;
     //   cout << " u " << cd.u << endl;
        for (edge it: g[cd.u]) {
            ll to = it.to;
            ll w = it.w;
            ll k = it.k;
            ll cat = cd.d;
          //  cout << cat  << " " << k << endl;
            if (cat % k) {
               // cout << "AA" <<  (cat % k) << endl;
                cat = (k - cat % k) + cat;
               // cout << "cat " << cat << endl;
            }
            q.push({to, cat + w});
        }
    }
    return -1;
}

int main() {
    ios::sync_with_stdio(0);
    ll n, m, x, y;
    cin >> n >> m >> x >> y;
    for (int i = 1; i <= m; i++) {
        ll a, b, t, k;
        cin >> a >> b >> t >> k;
        g[a].push_back({b, t, k});
        g[b].push_back({a, t, k});
    }
    cout << dj(x, y) << endl;
}