/* Dwukryterialny wybor drogi */
/* Rozwiazanie wzorcowe       */
/* Autor: Tomek Czajka        */

#include <iostream>
#include <fstream>
#include <algorithm>
#include <vector>
using namespace std;

const int INF = 1000000000;
const int MAXK = 100;

template<class T>
int size(const T&x) { return x.size(); }

int mod(int a,int b) { // dziala tez dla ujemnych a
  a%=b;
  if(a<0) a+=b;
  return a;
}

template<class T>
class Przewijana { // tablica 2D pamietajaca tylko sy ostatnich wierszy
  T *p;
  int sx,sy;
  public:
  Przewijana(int sx,int sy):sx(sx),sy(sy) {
    p=new T[sx*sy];
  }
  ~Przewijana() { delete[] p; }
  T &operator()(int x,int y) {
    return p[x+mod(y,sy)*sx];
  }
};

struct Krawedz {
  int a,b,k,c;
};

int n,start,meta;
vector<Krawedz> niezerowe;
vector<vector<Krawedz> > zerowe; // zerowe[i].a==i

void wczytaj() {
  ifstream f("bic.in");
  int m;
  f >> n >> m >> start >> meta;
  --start; --meta;
  zerowe.assign(n,vector<Krawedz>());
  for(int i=0;i<m;++i) {
    Krawedz k;
    f >> k.a >> k.b >> k.k >> k.c;
    --k.a; --k.b;
    if(k.k!=0) niezerowe.push_back(k);
      else zerowe[k.a].push_back(k);
    swap(k.a,k.b);
    if(k.k!=0) niezerowe.push_back(k);
      else zerowe[k.a].push_back(k);
  }
}

void wypisz(int wynik) {
  ofstream f("bic.out");
  f << wynik << "\n";
}

void popraw(Przewijana<int> &czas,int k) {
  // poprawia po krawedziach niezerowych
  for(int i=0;i<size(niezerowe);++i) {
    Krawedz kr=niezerowe[i];
    czas(kr.b,k)=min(czas(kr.b,k),czas(kr.a,k-kr.k)+kr.c);
  }
}

namespace DIJKSTRA {
  
  vector<int> dist;
  vector<int> heap;
  vector<int> inHeap;

  int rozmiar() { return size(heap)-1; }

  void sw(int a,int b) {
    swap(heap[a],heap[b]);
    inHeap[heap[a]]=a;
    inHeap[heap[b]]=b;
  }
  
  void upheap(int p) {
    while(p>1 && dist[heap[p>>1]]>dist[heap[p]]) {
      sw(p,p>>1);
      p>>=1;
    }
  }
  
  void downheap(int p) {
    int s=rozmiar();
    for(;;) {
      p<<=1;
      if(p+1<=s && dist[heap[p+1]]<dist[heap[p]]) ++p;
      if(p>s || dist[heap[p>>1]]<=dist[heap[p]]) break;
      sw(p,p>>1);
    }
  }

  int extractMin() {
    int res=heap[1];
    sw(1,rozmiar());
    inHeap[res]=-1;
    heap.pop_back();
    if(!heap.empty()) downheap(1);
    return res;
  }

  void dijkstra(Przewijana<int> &czas,int k) {
    dist.resize(n);
    for(int i=0;i<n;++i) dist[i]=czas(i,k);
    heap.resize(n+1);
    inHeap.resize(n);
    for(int i=0;i<n;++i) {
      heap[i+1]=i;
      inHeap[i]=i+1;
    }
    for(int i=n;i>=1;--i) downheap(i);
    while(rozmiar()>0) {
      int x=extractMin();
      for(int i=0;i<size(zerowe[x]);++i) {
        Krawedz kr=zerowe[x][i];
        dist[kr.b]=min(dist[kr.b],dist[x]+kr.c);
        if(inHeap[kr.b]!=-1) upheap(inHeap[kr.b]);
      }
    }
    for(int i=0;i<n;++i) czas(i,k)=dist[i];
  }
  
} using DIJKSTRA::dijkstra;

int oblicz() {
  Przewijana<int> czas(n,MAXK+1);
  for(int k=-MAXK;k<=0;++k)
    for(int x=0;x<n;++x)
      czas(x,k)=INF;
  czas(start,0)=0;
  dijkstra(czas,0);
  int wynik=0;
  int najszybciej=czas(meta,0);
  if(najszybciej<INF) ++wynik;
  for(int k=1;k<=n*MAXK;++k) {
    for(int x=0;x<n;++x) czas(x,k)=INF;
    popraw(czas,k);
    dijkstra(czas,k);
    if(czas(meta,k)<najszybciej) {
      najszybciej=czas(meta,k);
      ++wynik;
    }
  }
  return wynik;
}

int main() {
  wczytaj();
  wypisz(oblicz());
}
