#include <bits/stdc++.h>

using namespace std;
namespace sub {
    template<typename T>
    constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
    inf_sub() {
        return std::numeric_limits<T>::max() / 2 - 1000;
    }

    template<typename T>
    constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
    inf_sub() {
        return std::min(std::numeric_limits<T>::max() / 2 - 1000, T(1e50));
    }
}  // namespace sub

template<typename T>
constexpr T inf() {
    static_assert(std::is_arithmetic<T>::value, "T must be arithmetic value");
    return sub::inf_sub<T>();
}

template<typename T>
constexpr T zero() {
    static_assert(std::is_arithmetic<T>::value, "T must be arithmetic value");
    return T(0);
}

const long double pi = acos(-1.0);

template<class edge_t>
class graph_t {
    std::vector<std::vector<edge_t>> g;

public:
    using reference = std::vector<edge_t> &;
    using const_reference = const std::vector<edge_t> &;
    using iterator = typename std::vector<edge_t>::iterator;

    graph_t() : g(0) { ; }

    graph_t(int n) : g(n) { ; }

    int size() const { return g.size(); }

    void push_back(const std::vector<edge_t> &es) { g.push_back(es); }

    reference operator[](int x) { return g[x]; }

    const_reference operator[](int x) const { return g[x]; }

    iterator &begin() { return begin(g); }

    iterator &end() { return end(g); }
};


typedef __int128 int128;

/**
 * func: (long long) -> boolean
 * binary_search() returns the minimum element x which satisfies f(-∞) != f(x)
 * e.g.
 * i:    1 2 3 4 5 6 7 8 9
 * f(i): 0 0 0 0 0 0 1 1 1
 *                   ^
 *                   x
 */
template<class F>
int128 binary_search(F func) {
    int128 l = -1, r = 1;
    while (func(l) == func(r)) {
        l *= 2;
        r *= 2;
    }
    bool l_value = func(l);

    while (l != r) {
        int128 m = (l + r);
        if (m % 2 == 0) m /= 2;
        else m = (m - 1) / 2;
        if (func(m) != l_value) {
            r = m;
        } else {
            l = m + 1;
        }
    }
    return l;
}

int128 sqrt(int128 v) {
    return binary_search([v](int128 x) {
        if (x < 0) return false;
        if (x >= v) return true;
        return x * x >= v;
    });

}

vector<long long> best_time(long long t, long long a, long long b) {
    long long alpha = t + a;
    int128 B = 2 * alpha;
    int128 C = (int128) alpha * alpha - b;
    if (B * B - 4 * C < 0) {
        return {0};
    }
    int128 D = sqrt(B * B - 4 * C);

    return {0ll, (long long) ((-B + D) / 2), (long long) ((-B + D) / 2) + 1};
}

long long ceil(long long a, long long b) {
    return (a + b - 1) / b;
}

template<typename edge_t, typename cost_type = typename edge_t::cost_type>
std::vector<cost_type> dijkstra(const graph_t<edge_t> &g, int s) {
    std::vector<cost_type> d(g.size(), inf<cost_type>());
    d[s] = zero<cost_type>();
    using P = std::pair<cost_type, int>;
    std::priority_queue<P, std::vector<P>, std::greater<P>> que;
    que.push(P(zero<cost_type>(), s));
    while (!que.empty()) {
        cost_type dist = que.top().first;
        int v = que.top().second;
        que.pop();
        if (d[v] < dist) continue;
        for (const auto &e : g[v]) {
            for (auto k : best_time(dist, e.a, e.b)) {
                if (k < 0) continue;
//                while (k > 0) {
//                    long long cost = k + ceil(e.b, dist + e.a + k);
//                    long long cost2 = k-1 + ceil(e.b, dist + e.a + k-1);
//                    if( cost <= cost2 ){
//                        break;
//                    }
//                    --k;
//                }
//                while (true) {
//                    long long cost = k + ceil(e.b, dist + e.a + k);
//                    long long cost2 = k+1 + ceil(e.b, dist + e.a + k+1);
//                    if( cost <= cost2 ){
//                        break;
//                    }
//                    ++k;
//                }
                long long cost = k + ceil(e.b, dist + e.a + k);

                if (d[e.to] <= d[v] + cost) continue;
                d[e.to] = d[v] + cost;
                que.push(P(d[e.to], e.to));
            }
        }
    }
    return d;
}


template<typename Cost>
class WeightedEdge {
public:
    using cost_type = Cost;
    int to;
    cost_type cost;
    long long a, b;

    WeightedEdge(int, int t, cost_type c, long long a, long long b) : to(t), cost(c), a(a), b(b) { ; }
};

template<typename Cost> using WeightedGraph = graph_t<WeightedEdge<Cost>>;

template<typename Cost>
void add_edge(WeightedGraph<Cost> &g, int from, int to, Cost cost, long long a, long long b) {
    g[from].emplace_back(from, to, cost, a, b);
}

int main() {
    int N, M, S, G;
    cin >> N >> M >> S >> G;
    WeightedGraph<long long> g(N);

    for (int i = 0; i < M; i++) {
        long long u, v, a, b;
        cin >> u >> v >> a >> b;
        --u, --v;
        add_edge(g, u, v, 0ll, a, b);
        add_edge(g, v, u, 0ll, a, b);
    }
    --S, --G;

    auto ans = dijkstra(g, S)[G];
    if (ans != inf<long long>()) {
        cout << ans << endl;
    } else {
        cout << -1 << endl;
    }
}


Measured memory_bytes: 36024