#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int,int> pii;

typedef int _loop_int;
#define REP(i,n) for(_loop_int i=0;i<(_loop_int)(n);++i)
#define FOR(i,a,b) for(_loop_int i=(_loop_int)(a);i<(_loop_int)(b);++i)
#define FORR(i,a,b) for(_loop_int i=(_loop_int)(b)-1;i>=(_loop_int)(a);--i)

#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define DEBUG2(x,y) cout<<#x<<": "<<x<<" "<<#y<<": "<<y<<endl
#define DEBUG_VEC(v) cout<<#v<<":";REP(i,v.size())cout<<" "<<v[i];cout<<endl
#define DEBUG_ARR(v,n) cout<<#v<<":";REP(i,n)cout<<" "<<v[i];cout<<endl
#define ALL(a) (a).begin(),(a).end()

const ll MOD = 1000000007ll;
#define FIX(a) ((a)%MOD+MOD)%MOD

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; }
};

vector<int> p(105);

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 >
// void warshall_floyd(Matrix< T > &g, T INF) {
//   for(int k = 0; k < g.size(); k++) {
//     for(int i = 0; i < g.size(); i++) {
//       for(int j = 0; j < g.size(); j++) {
//         if(g[i][k] == INF || g[k][j] == INF) continue;
//         g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
//       }
//     }
//   }
// }



template< typename T >
vector< T > dijkstra(WeightedGraph< T > &g, int s) {
  const auto INF = numeric_limits< T >::max();
  vector< T > dist(g.size(), INF);
  fill(ALL(p), -1);
  p[s] = 0;

  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);
      p[e.to] = idx;
    }
  }
  return dist;
}

template <typename ... Args>
string format(const string& fmt, Args ... args )
{
    size_t len = snprintf( nullptr, 0, fmt.c_str(), args ... );
    vector<char> buf(len + 1);
    snprintf(&buf[0], len + 1, fmt.c_str(), args ... );
    return string(&buf[0], &buf[0] + len);
}

string getidx(int a, int b){
    return format("%02d%02d",a,b);
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int n,m;
    cin>>n>>m;
    WeightedGraph<int> g(n+1);
    map<string, int> usepath;
    REP(i,m){
        int a,b,c;
        cin>>a>>b>>c;
        
        g[a].emplace_back(b,c);
        g[b].emplace_back(a,c);
        if(a>b) swap(a,b);
        usepath[getidx(a,b)] = 0;
    }

    
    FOR(i,1,n+1){
        auto dist = dijkstra(g,i);
        //DEBUG_VEC(dist);
        FOR(j,1,n+1){
            //DEBUG(j);
            //DEBUG_ARR(p,n+1);
            int a = j, b = p[j];
            if(p[j] <= 0) continue;
            if(a>b) swap(a,b);
            usepath[getidx(a,b)]++;
        }
    }

    ll ans = 0;
    for(auto q: usepath){
        //DEBUG2(q.first,q.second);
        if(q.second==0) ans++;
    }
    cout << ans << endl;
    return 0;
}

Measured memory_bytes: 384