#include <bits/stdc++.h>
const int INF = 1e9;
const int MOD = 1e9+7;
const long long LINF = 1e18;
#define dump(x)  cout << 'x' << ' = ' << (x) << ` `;
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n) for(int i=0;i<(n);++i)
#define REPR(i,n) for(int i=n;i>=0;i--)
#define FOREACH(x,a) for(auto& (x) : (a) )
typedef long long ll;
using namespace std;

typedef struct DP {
    int dist;
    int from;
    bool sure;
} DP;

//自作構造体にしたいけど比較演算子の定義がわからないので保留
typedef pair<int, int> P; 

int main(int argc, char const *argv[]) {
    int n, m;
    cin >> n >> m;

    int G[n][n];
    bool E[n][n];
    REP(i,n) REP(j,n) G[i][j] = INF;
    REP(i,n) REP(j,n) E[i][j] = false;

    REP(i,m) {
        int a, b, c;
        cin >> a >> b >> c;
        G[a-1][b-1] = G[b-1][a-1] = c;
    }
    DP dp(n);
    for (int I = 0; I < n; ++I) {

        
        REP(i,n) {
            dp[i].dist = INF;
            dp[i].from = INF;
            dp[i].sure = false;
        }
        priority_queue<P, vector<P>, greater<P>> pq;
        P p = P(0, I);
        pq.push(p);
        dp[I].dist = 0;
        dp[I].from = I;
        
        while(!pq.empty()){
            P now = pq.top();
            int v = now.second;
          if(dp[v].sure)continue;
            dp[v].sure = true;
            pq.pop();
            REP(i,n) {
                if(G[v][i] != INF && dp[i].sure == false) {
                    //dp[i].dist = min(dp[v].dist+G[v][i], dp[i].dist);←本当はこうしたいが、fromの更新をするためにはどうせifがいる
                    if (dp[v].dist+G[v][i] <= dp[i].dist) {
                        dp[i].dist = dp[v].dist+G[v][i];
                        dp[i].from = v;
                     P temp = P(dp[i].dist, i);
                    pq.push(temp);
                    }
            
                    
                }
            }
        }
        /*cout << "------------------------dist------------------------" << std::endl;
        REP(i,n) cout << i+1 << " = " << dp[i].dist << std::endl;
        cout << "------------------------from------------------------" << std::endl;
        REP(i,n) if (i != dp[i].from) cout << dp[i].from+1 <<  " -> " << i+1 << std::endl;*/
        REP(i,n) if (i != dp[i].from) E[dp[i].from][i] = E[i][dp[i].from] = true;
    }
    int c = 0;
    //cout << "------------------------egde------------------------" << std::endl;
    REP(i,n) {
        REP(j,n) {
            //cout << E[i][j] << " ";
            if(E[i][j]) c++;
        }
        //cout << "" << std::endl;
    }
    int ans = m-(c/2);
    cout << ans << std::endl;
    return 0;
    /*cout << "------------------------dist------------------------" << std::endl;
    REP(i,n) cout << i << " = " << dp[i].dist << std::endl;
    cout << "------------------------from------------------------" << std::endl;
    REP(i,n) cout << i << " = " << dp[i].from << std::endl; */
}
