#include "bits/stdc++.h"
using namespace std;
#define FOR(i,j,k) for(int (i)=(j);(i)<(int)(k);++(i))
#define rep(i,j) FOR(i,0,j)
#define each(x,y) for(auto &(x):(y))
#define mp make_pair
#define mt make_tuple
#define all(x) (x).begin(),(x).end()
#define debug(x) cout<<#x<<": "<<(x)<<endl
#define smax(x,y) (x)=max((x),(y))
#define smin(x,y) (x)=min((x),(y))
#define MEM(x,y) memset((x),(y),sizeof (x))
#define sz(x) (int)(x).size()
#define pb push_back
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;

typedef int Weight;
struct Edge{
    int from, to;
    Weight wei;
    Edge(int from_, int to_, Weight wei_):from(from_),to(to_),wei(wei_){}
};
typedef vector<Edge> Edges;
// typedef vector<Edges> Graph;
// const Weight INFW = INT_MAX;
const Weight INFW = numeric_limits<Weight>::max();

/*
グラフライブラリの基本となるクラス
vector<vector<Edge>> を継承しているので今までのライブラリも使える。
*/
struct Graph : public vector<Edges>{
    Graph(){ }
    Graph(int V): vector<Edges>(V){ }
    /*
    有向辺を追加する
    */
    void addEdge(int from, int to, Weight wei=1){
        (*this)[from].push_back(Edge(from, to, wei));
    }
    /*
    無向辺を追加する
    */
    void addUEdge(int u, int v, Weight wei=1){
        (*this)[u].push_back(Edge(u, v, wei));
        (*this)[v].push_back(Edge(v, u, wei));
    }
};

bool operator<(const Edge &a, const Edge &b){
    return a.wei<b.wei;
}
/*
ダイクストラで使うのはこっち
*/
bool operator>(const Edge &a, const Edge &b){
    return a.wei>b.wei;
}

vector<Weight> dijkstra(const Graph &G, int src){
    typedef pair<Weight, int> pwi;
    priority_queue<pwi, vector<pwi>, greater<pwi>> pq;
    pq.push(mp(0, src));
    int V = (int)G.size();
    vector<Weight> res(V, -1);
    while(pq.size()){
        auto p = pq.top(); pq.pop();
        Weight d = p.first;
        int v = p.second;
        if(res[v] > -1)continue;
        res[v] = d;
        for(const auto &edge : G[v]){
            int to = edge.to;
            Weight wei = edge.wei;
            pq.push(make_pair(d + wei, to));
        }
    }
    return res;
}

const int MA = 22000;
int N, M, A[MA], B[MA], C[MA], used[101][101], visited[100];
Graph G;
vi dist;

void init() {
    MEM(used, 0);
    G = Graph(N);
    rep(i, M) {
        --A[i];
        --B[i];
        G.addUEdge(A[i], B[i], C[i]);
    }
}

void rest(int goal) {
    MEM(visited, 0);

    queue<int> Q;
    Q.push(goal);

    while(sz(Q)) {
        int cur = Q.front(); Q.pop();
        if(visited[cur]++)continue;
        each(edge, G[cur]) {
            if(dist[edge.to] + edge.wei == dist[cur]) {
                used[edge.to][cur] = 1;
                used[cur][edge.to] = 1;
                if(!visited[edge.to]) {
                    Q.push(edge.to);
                }
            }
        }
    }
}

void solve() {
    rep(i, N) {
        dist = dijkstra(G, i);

        rep(goal, N) {
            rest(goal);
        }
    }

    int ans = 0;
    rep(i, M) {
        if(!used[A[i]][B[i]]) {
            ans++;
        }
    }
    cout << ans << endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> N >> M) {
        rep(i, M)cin >> A[i] >> B[i] >> C[i];
        init();
        solve();
    }
}


Measured memory_bytes: 512