#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 200


struct Edge{
	Edge(){
		from = to = dist = larger_cost = 0;
	}

	Edge(int arg_from,int arg_to,int arg_dist,int arg_larger_cost){
		from = arg_from;
		to = arg_to;
		dist = arg_dist;
		larger_cost = arg_larger_cost;
	}
	int from,to,dist,larger_cost;
};

struct Info{
	Info(int arg_node_id,int arg_sum_cost){
		node_id = arg_node_id;
		sum_cost = arg_sum_cost;
	}
	bool operator<(const struct Info &arg) const{
		return sum_cost > arg.sum_cost;
	}
	int node_id,sum_cost;
};

struct Edge2{
	Edge2(int arg_to,int arg_capacity,int arg_rev_index){
		to = arg_to;
		capacity = arg_capacity;
		rev_index = arg_rev_index;
	}
	int to,capacity,rev_index;
};

int V;

vector<Edge2> Graph[NUM];
int dist[NUM];
int cheked_index[NUM];

int N,M,start,goal;
int min_dist[NUM],rev_min_dist[NUM];
Edge edge[2000];
vector<Edge> G[NUM],rev_G[NUM];

void calc_from_start(){

	for(int i = 0; i < N; i++)min_dist[i] = BIG_NUM;
	min_dist[start] = 0;

	priority_queue<Info> Q;
	Q.push(Info(start,0));

	int next_node,next_cost;

	while(!Q.empty()){

		if(Q.top().node_id == goal){
			Q.pop();
		}else if(Q.top().sum_cost > min_dist[Q.top().node_id]){
			Q.pop();
		}else{

			for(int i = 0; i < G[Q.top().node_id].size(); i++){
				next_node = G[Q.top().node_id][i].to;
				next_cost = Q.top().sum_cost+G[Q.top().node_id][i].dist;

				if(min_dist[next_node] > next_cost){
					min_dist[next_node] = next_cost;
					Q.push(Info(next_node,next_cost));
				}
			}
			Q.pop();
		}
	}
}

void calc_from_goal(){

	for(int i = 0; i < N; i++)rev_min_dist[i] = BIG_NUM;
	rev_min_dist[goal] = 0;

	priority_queue<Info> Q;
	Q.push(Info(goal,0));

	int next_node,next_cost;

	while(!Q.empty()){

		if(Q.top().node_id == start){
			Q.pop();
		}else if(Q.top().sum_cost > rev_min_dist[Q.top().node_id]){
			Q.pop();
		}else{

			for(int i = 0; i < rev_G[Q.top().node_id].size(); i++){
				next_node = rev_G[Q.top().node_id][i].to;
				next_cost = Q.top().sum_cost+rev_G[Q.top().node_id][i].dist;

				if(rev_min_dist[next_node] > next_cost){
					rev_min_dist[next_node] = next_cost;
					Q.push(Info(next_node,next_cost));
				}
			}
			Q.pop();
		}
	}
}


void add_edge(int from,int to,int capacity){
	Graph[from].push_back(Edge2(to,capacity,Graph[to].size()));
	Graph[to].push_back(Edge2(from,0,Graph[from].size()-1));
}

void bfs(int source){
	for(int i = 0; i < V; i++)dist[i] = -1;
	queue<int> Q;
	dist[source] = 0;
	Q.push(source);

	while(!Q.empty()){
		int node_id = Q.front();
		Q.pop();
		for(int i = 0; i < Graph[node_id].size(); i++){
			Edge2 &e = Graph[node_id][i];
			if(e.capacity > 0 && dist[e.to] < 0){
				dist[e.to] = dist[node_id]+1;
				Q.push(e.to);
			}
		}
	}
}

int dfs(int node_id,int sink,int flow){
	if(node_id == sink)return flow;

	for(int &i = cheked_index[node_id]; i < Graph[node_id].size(); i++){
		Edge2 &e = Graph[node_id][i];
		if(e.capacity > 0 && dist[node_id] < dist[e.to]){
			int tmp_flow = dfs(e.to,sink,min(flow,e.capacity));
			if(tmp_flow > 0){
				e.capacity -= tmp_flow;
				Graph[e.to][e.rev_index].capacity += tmp_flow;
				return tmp_flow;
			}
		}
	}
	return 0;
}


int max_flow(int source,int sink){
	int flow = 0,add;
	while(true){
		bfs(source);
		if(dist[sink] < 0)break;
		for(int i = 0; i < V; i++)cheked_index[i] = 0;
		while((add = dfs(source,sink,BIG_NUM)) > 0){
			flow += add;
		}
	}
	return flow;
}


int main(){

	scanf("%d %d %d %d",&N,&M,&start,&goal);
	start--;
	goal--;

	for(int i = 0; i < M; i++){
		scanf("%d %d %d %d",&edge[i].from,&edge[i].to,&edge[i].dist,&edge[i].larger_cost);
		edge[i].from--;
		edge[i].to--;
		G[edge[i].from].push_back(edge[i]);
		rev_G[edge[i].to].push_back(Edge(edge[i].to,edge[i].from,edge[i].dist,edge[i].larger_cost));
	}

	calc_from_start();
	calc_from_goal();

	int shortest_cost = min_dist[goal];

	for(int i = 0; i < M; i++){
		if(min_dist[edge[i].from]+edge[i].dist+rev_min_dist[edge[i].to] > shortest_cost){
			//Do nothing
		}else{
			add_edge(edge[i].from,edge[i].to,edge[i].larger_cost);
		}
	}

	V = N;

	printf("%d\n",max_flow(start,goal));

	return 0;
}



Measured memory_bytes: 3308