#include <bits/stdc++.h>
using namespace std;

using VI = vector<int>;
using VVI = vector<VI>;
using PII = pair<int, int>;
using LL = long long;
using VL = vector<LL>;
using VVL = vector<VL>;
using PLL = pair<LL, LL>;
using VS = vector<string>;

#define ALL(a)  begin((a)),end((a))
#define RALL(a) (a).rbegin(), (a).rend()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define SZ(a) int((a).size())
#define SORT(c) sort(ALL((c)))
#define RSORT(c) sort(RALL((c)))
#define UNIQ(c) (c).erase(unique(ALL((c))), end((c)))

#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n)  FOR(i,0,n)

#define FF first
#define SS second

#define DUMP(x) cout<<#x<<":"<<(x)<<endl
template<class S, class T>
istream& operator>>(istream& is, pair<S,T>& p){
  return is >> p.FF >> p.SS;
}
template<class T>
istream& operator>>(istream& is, vector<T>& xs){
  for(auto& x: xs)
	is >> x;
  return is;
}
template<class S, class T>
ostream& operator<<(ostream& os, const pair<S,T>& p){
  return os << p.FF << " " << p.SS;
}
template<class T>
ostream& operator<<(ostream& os, const vector<T>& xs){
  for(unsigned int i=0;i<xs.size();++i)
	os << (i?" ":"") << xs[i];
  return os;
}
template<class T>
void maxi(T& x, T y){
  if(x < y) x = y;
}
template<class T>
void mini(T& x, T y){
  if(x > y) x = y;
}


const double EPS = 1e-10;
const double PI  = acos(-1.0);
const LL MOD = 1e9+7;
const int INF = 1e9;

struct Edge{
  int to, cost, dd;

  Edge(int t, int c = 0, int dd = 0): to(t), cost(c), dd(dd)
  {}
};
using Graph = vector<vector<Edge>>;
void Dijkstra(const Graph& G, vector<int>& d, int s){
  using Type = pair<int,int>;
  priority_queue<Type, vector<Type>, greater<Type> > pq;
  fill(d.begin(), d.end(), INF);
  d[s] = 0;
  pq.push(MP(0,s));

  while(!pq.empty()){
	Type pii = pq.top();
	pq.pop();
	int v = pii.second;
	if(d[v] < pii.first) continue;
	for(const auto& e: G[v]){
	  if(d[e.to] > d[v] + e.cost){
		d[e.to] = d[v] + e.cost;
		pq.push(MP(d[e.to], e.to));
	  }
	}
  }
}


struct EdgeF{
  int to, cap, rev;
  EdgeF(int to_=0, int cap_ = 0, int rev_ = 0)
	:to(to_), cap(cap_), rev(rev_){}
};
using GraphF = vector<vector<EdgeF>>;
void add_edge(GraphF& G, int from, int to, int cap){
  G[from].emplace_back(to, cap, G[to].size());
  G[to].emplace_back(from, 0, G[from].size()-1);
}
int max_flow(GraphF& G, int s, int t, int maximum = INF){
  const int V = G.size();
  vector<bool> used(V);
 
  function<int(int,int,int)> dfs = [&](int v, int t, int f){
    if(v == t) return f;
    used[v] = true;
    for(auto& e: G[v]){
      if(!used[e.to] && e.cap > 0){
        int d = dfs(e.to, t, min(f, e.cap));
        if(d > 0){
          e.cap -= d;
          G[e.to][e.rev].cap += d;
          return d;
        }
      }
    }
    return 0;
  };
 
  int flow = 0;
  while(true){
    fill(used.begin(), used.end(), false);
    int f = dfs(s, t, maximum);
    if(f == 0) return flow;
    flow += f;
	maximum -= f;
	if(maximum == 0)
	  return flow;
  }
}

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

  int N, M, S, T;
  cin >> N >> M >> S >> T;
  --S;
  --T;

  Graph G(N);
  REP(i,M){
	int u, v, d, c;
	cin >> u >> v >> d >> c;
	--u;
	--v;
	G[u].PB(Edge(v, d, c));
  }

  VI dist(N);
  Dijkstra(G, dist, S);

  GraphF GF(N);
  REP(i,N)
	for(auto& e: G[i]){
	  if(dist[i] + e.cost == dist[e.to]){
		add_edge(GF, i, e.to, e.dd);
	  }
	}

  cout << max_flow(GF, S, T) << endl;

  return 0;
}



Measured memory_bytes: 3252