#include "iostream"
#include "climits"
#include "list"
#include "queue"
#include "stack"
#include "set"
#include "functional"
#include "algorithm"
#include "string"
#include "map"
#include "unordered_map"
#include "unordered_set"
#include "iomanip"
#include "cmath"
#include "random"
#include "bitset"
#include "cstdio"

using namespace std;

const long long int MOD = 1000000007;

long long int N, M, K, H, W, L, R;

template< typename T >
struct FordFulkerson {
  struct edge {
    int to;
    T cap;
    int rev;
  };

  vector< vector< edge > > graph;
  vector< int > used;
  const T INF;
  int timestamp;

  FordFulkerson(int n) : INF(numeric_limits< T >::max()), timestamp(0) {
    graph.resize(n);
    used.assign(n, -1);
  }

  void add_edge(int from, int to, T cap) {
    graph[from].emplace_back((edge) {to, cap, (int) graph[to].size()});
    graph[to].emplace_back((edge) {from, 0, (int) graph[from].size() - 1});
  }

  T dfs(int idx, const int t, T flow) {
    if(idx == t) return flow;
    used[idx] = timestamp;
    for(auto &e : graph[idx]) {
      if(e.cap > 0 && used[e.to] != timestamp) {
        T d = dfs(e.to, t, min(flow, e.cap));
        if(d > 0) {
          e.cap -= d;
          graph[e.to][e.rev].cap += d;
          return d;
        }
      }
    }
    return 0;
  }

  T max_flow(int s, int t) {
    T flow = 0;
    for(T f; (f = dfs(s, t, INF)) > 0; timestamp++) {
      flow += f;
    }
    return flow;
  }
};


template< typename T >
struct edge {
  int src, to;
  T cost;

  edge(int to, T cost) : src(-1), to(to), cost(cost) {}

  edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}

  edge &operator=(const int &x) {
    to = x;
    return *this;
  }

  operator int() const { return to; }
};

template< typename T >
using Edges = vector< edge< T > >;
template< typename T >
using WeightedGraph = vector< Edges< T > >;
using UnWeightedGraph = vector< vector< int > >;
template< typename T >
using Matrix = vector< vector< T > >;

template< typename T >
vector< T > dijkstra(WeightedGraph< T > &g, int s) {
  const auto INF = numeric_limits< T >::max();
  vector< T > dist(g.size(), INF);

  using Pi = pair< T, int >;
  priority_queue< Pi, vector< Pi >, greater< Pi > > que;
  dist[s] = 0;
  que.emplace(dist[s], s);
  while(!que.empty()) {
    T cost;
    int idx;
    tie(cost, idx) = que.top();
    que.pop();
    if(dist[idx] < cost) continue;
    for(auto &e : g[idx]) {
      auto next_cost = cost + e.cost;
      if(dist[e.to] <= next_cost) continue;
      dist[e.to] = next_cost;
      que.emplace(dist[e.to], e.to);
    }
  }
  return dist;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);

  cin >> N >> M >> L >> R;
  int cc[1000][1000] = {{}};
  WeightedGraph< int > g(N + 1), rg(N + 1), beet(N + 1);
  for(int i = 0; i < M; i++) {
    int a, b, c, d;
    cin >> a >> b >> c >> d;
    g[a].emplace_back(b, c);
    rg[b].emplace_back(a, c);
    beet[a].emplace_back(b, d);
    cc[a][b] = c;
  }

  auto latte = dijkstra(g, L);
  auto malta = dijkstra(rg, R);


  FordFulkerson< int > d(N + 2);
  for(int i = 1; i <= N; i++) {
    for(auto &p : beet[i]) {
      if(latte[i] + malta[p.to] + cc[i][p.to] == latte[R]) {

        d.add_edge(i, p.to, p.cost);
      }
    }
  }
  cout << d.max_flow(L, R) << endl;
  return 0;
}


Measured memory_bytes: 7196