#include <bits/stdc++.h>
#define int int64_t

#define FOR(i, a, b) for (int i = (a); i < int(b); ++i)
#define RFOR(i, a, b) for (int i = (b)-1; i >= int(a); --i)
#define rep(i, n) FOR(i, 0, n)
#define rep1(i, n) FOR(i, 1, int(n) + 1)
#define rrep(i, n) RFOR(i, 0, n)
#define rrep1(i, n) RFOR(i, 1, int(n) + 1)
#define all(c) begin(c), end(c)
namespace io {
#ifdef LOCAL
#define dump(...)                                         \
    do {                                                  \
        std::ostringstream os;                            \
        os << __LINE__ << ":\t" << #__VA_ARGS__ << " = "; \
        io::print_to(os, ", ", "\n", __VA_ARGS__);        \
        std::cerr << io::highlight(os.str());             \
    } while (0)
#define dump_(cntnr)                                 \
    do {                                             \
        std::ostringstream os;                       \
        os << __LINE__ << ":\t" << #cntnr << " = ["; \
        io::print_to_(os, ", ", "]\n", all(cntnr));  \
        std::cerr << io::highlight(os.str());        \
    } while (0)
#define dumpf(fmt, ...)                             \
    do {                                            \
        const int N = 4096;                         \
        auto b = new char[N];                       \
        int l = snprintf(b, N, "%d:\t", __LINE__);  \
        snprintf(b + l, N - l, fmt, ##__VA_ARGS__); \
        std::cerr << io::highlight(b) << std::endl; \
        delete[] b;                                 \
    } while (0)
#else
#define dump(...)
#define dump_(...)
#define dumpf(...)
#endif
std::string highlight(std::string s) {
#ifdef _MSC_VER
    return s;
#else
    return "\033[33m" + s + "\033[0m";
#endif
}
template <typename T>
void print_to(std::ostream &os, std::string, std::string tail, const T &first) {
    os << first << tail;
}
template <typename F, typename... R>
void print_to(std::ostream &os, std::string del, std::string tail, const F &first,
              const R &... rest) {
    os << first << del;
    print_to(os, del, tail, rest...);
}
template <typename I>
void print_to_(std::ostream &os, std::string del, std::string tail, I begin, I end) {
    for (I it = begin; it != end;) {
        os << *it;
        os << (++it != end ? del : tail);
    }
}
template <typename F, typename... R>
void println(const F &first, const R &... rest) {
    print_to(std::cout, "\n", "\n", first, rest...);
}
template <typename F, typename... R>
void print(const F &first, const R &... rest) {
    print_to(std::cout, " ", "\n", first, rest...);
}
template <typename I>
void println_(I begin, I end) {
    print_to_(std::cout, "\n", "\n", begin, end);
}
template <typename I>
void print_(I begin, I end) {
    print_to_(std::cout, " ", "\n", begin, end);
}
template <typename C>
void println_(const C &cntnr) {
    println_(std::begin(cntnr), std::end(cntnr));
}
template <typename C>
void print_(const C &cntnr) {
    print_(std::begin(cntnr), std::end(cntnr));
}
int _ = (
#ifndef LOCAL
    std::cin.tie(nullptr), std::ios::sync_with_stdio(false),
#endif
    std::cout.precision(10), std::cout.setf(std::ios::fixed));
}
using io::print;
using io::println;
using io::println_;
using io::print_;
template <typename T>
using vec = std::vector<T>;
using vi = vec<int>;
using vvi = vec<vi>;
using pii = std::pair<int, int>;
using ll = long long;
using ld = long double;
using namespace std;
// const int MOD = 1000000007;

using Weight = int;
using Flow = int;
struct Edge {
    int src, dst;
    Weight weight;
    Flow cap;
    Edge() : src(0), dst(0), weight(0) {}
    Edge(int s, int d, Weight w) : src(s), dst(d), weight(w) {}
};
using Edges = std::vector<Edge>;
using Graph = std::vector<Edges>;
using Array = std::vector<Weight>;
using Matrix = std::vector<Array>;

void add_edge(Graph &g, int a, int b, Weight w = 1) {
    g[a].emplace_back(a, b, w);
    g[b].emplace_back(b, a, w);
}
void add_arc(Graph &g, int a, int b, Weight w = 1) { g[a].emplace_back(a, b, w); }

using flow_type = int;

struct dinic {
    struct edge {
        int src, dst;
        flow_type cap, flow;
        int rev_idx;
        bool is_rev;
    };
    int n, s, t;
    std::vector<std::vector<edge>> g;
    std::vector<int> level, prog, que;
    std::vector<std::pair<std::pair<int, int>, flow_type>> edges;

    dinic(int n_ = 0) : n(n_) {}

    // Compute the maximum-flow from `s_` to `t_` by Dinic's algorithm.
    flow_type maximum_flow(int s_, int t_) {
        s = s_;
        t = t_;
        make_graph();
        que.resize(n);
        flow_type res = 0;
        while (levelize()) {
            prog.assign(n, 0);
            res += augment(s, std::numeric_limits<flow_type>::max());
        }
        return res;
    }

    // Add an edge from `u` to `v` with capacity `c`. Note that it will be added
    // to `g` when `make_graph` is called instead of just after calling this
    // function.
    void add_edge(int u, int v, flow_type c) {
        if (u != v && c != 0) {
            edges.emplace_back(std::make_pair(u, v), c);
        }
    }

    void make_graph() {
        g.assign(n, {});
        if (true) {
            std::sort(edges.begin(), edges.end());
            for (auto it = edges.begin(); it != edges.end();) {
                flow_type c = 0;
                auto uv = it->first;
                while (it != edges.end() && it->first == uv) {
                    c += it->second;
                    ++it;
                }
                int u = uv.first, v = uv.second;
                g[u].push_back({u, v, c, 0, (int)g[v].size(), false});
                g[v].push_back({v, u, c, c, (int)g[u].size() - 1, true});
            }
        } else {
            for (auto &e : edges) {
                auto uv = e.first;
                int u = uv.first, v = uv.second;
                flow_type c = e.second;
                g[u].push_back({u, v, c, 0, (int)g[v].size(), false});
                g[v].push_back({v, u, c, c, (int)g[u].size() - 1, true});
            }
        }
    }

    bool levelize() {
        int fst = 0, lst = 0;
        que[lst++] = s;
        level.assign(n, -1);
        level[s] = 0;
        while (fst != lst) {
            int v = que[fst++];
            if (v == t) break;
            for (auto &e : g[v]) {
                if (level[e.dst] == -1 && residue(e) != 0) {
                    level[e.dst] = level[v] + 1;
                    que[lst++] = e.dst;
                }
            }
        }
        return level[t] != -1;
    }

    flow_type augment(int v, flow_type lim) {
        flow_type res = 0;
        if (v == t) return lim;
        for (int &i = prog[v]; i < (int)g[v].size(); ++i) {
            if (lim == 0) break;
            auto &e = g[v][i];
            if (level[v] < level[e.dst] && residue(e) != 0) {
                flow_type aug = augment(e.dst, std::min(lim, residue(e)));
                if (aug == 0) continue;
                e.flow += aug;
                reverse(e).flow -= aug;
                res += aug;
                lim -= aug;
            }
        }
        return res;
    }

    flow_type residue(const edge &e) { return e.cap - e.flow; }

    edge &reverse(const edge &e) { return g[e.dst][e.rev_idx]; }

    // Output current flow by graphviz dot language.
    // Run `dot $filename -o out.png -T png` on shell.
    void show(const std::string &filename = "out.dot") {
        int fst = 0, lst = 0;
        que[lst++] = s;
        level.assign(n, -1);
        level[s] = 0;
        while (fst != lst) {
            int v = que[fst++];
            for (auto &e : g[v]) {
                if (!e.is_rev && level[e.dst] == -1 && e.flow) {
                    level[e.dst] = level[v] + 1;
                    que[lst++] = e.dst;
                }
            }
        }
        std::map<int, std::vector<int>> rank_to_vertices;
        for (int i = 0; i < n; ++i) {
            rank_to_vertices[level[i]].push_back(i);
        }

        std::ostringstream oss;
        oss << "digraph {" << '\n';
        for (int i = 0; i < n; ++i) {
            oss << "\t" << i;
            if (i == s || i == t) {
                oss << " [ peripheries = 2 ]";
            }
            oss << ";\n";
            for (auto &e : g[i]) {
                if (!e.is_rev) {
                    const char *color = e.flow ? "black" : "gray";
                    oss << "\t" << e.src << " -> " << e.dst << "\t[ "
                        << "label = \"" << e.flow << "/" << e.cap << "\", "
                        << "color = \"" << color << "\", "
                        << "fontcolor = \"" << color << "\"];\n";
                }
            }
        }

        for (auto &p : rank_to_vertices) {
            const char *rank = p.first != -1 ? "same" : "sink";
            oss << "\t{ rank = " << rank << "; ";
            for (int v : p.second) {
                if (v != s && v != t) {
                    oss << v << "; ";
                }
            }
            oss << "}\n";
        }
        oss << "\t{ rank = same; " << s << "; }\n";
        oss << "\t{ rank = same; " << t << "; }\n";
        oss << "}\n";

        std::ofstream ofs(filename);
        ofs << oss.str();
    }
};

signed main() {
    int n, m, s, t;
    while (cin >> n >> m >> s >> t && n) {
        --s;
        --t;
        int g[300][300];
        const int INF = 1e9;
        rep(i, n) rep(j, n) g[i][j] = INF;
        rep(i, n) g[i][i] = 0;

        using E = tuple<int, int, int, int>;
        vec<E> es(m);
        rep(i, m) {
            int u, v, d, c;
            cin >> u >> v >> d >> c;
            --u;
            --v;
            es[i] = make_tuple(u, v, d, c);
            g[u][v] = d;
        }

        rep(k, n) rep(i, n) rep(j, n) {
            g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
        }

        dinic dn(n);

        rep(i, n) {
            dump(g[s][i]);
        }

        for (auto &e : es) {
            int u, v, d, c;
            tie(u, v, d, c) = e;
            if (g[s][u] + d == g[s][v]) {
                dn.add_edge(u, v, c);
                dump(u, v, c);
            }
        }

        dump(n, m, s, t);

        cout << dn.maximum_flow(s, t) << endl;
    }
}



Measured memory_bytes: 3680