/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author champon
 */

#include <iostream>
#include <fstream>

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i, n) for(ll i=0; i<n; i++)
#define repr(i, s, e) for(ll i=s; i>=e; i--)
#define reps(i, s, e) for(ll i=s; i<=e; i++)
#define inf 1e18
#define all(v) v.begin(),v.end()
#define ceill(a, b) (a+b-1)/b
#define ok cout << "ok" << endl;
#define sp << " " <<
void ans(bool b){ cout << (b ? "Yes" : "No") << endl; }
void ans2(bool b){ cout << (b ? "YES" : "NO") << endl; }
template<typename T> inline bool chmax(T &a, T b){ if(a<b) a=b; return a<b; }
template<typename T> inline bool chmin(T &a, T b){ if(b<a) a=b; return b<a; }
template<typename A, size_t N, typename T>
void Fill(A (&array)[N], const T &val){
    fill((T*)array, (T*)(array+N), val);
}
template<typename T> void vdeb(T v){
    cout << "#vector set debug" << endl;
    for(auto vv : v) cout << vv << " ";
    cout << endl;
}
template<typename T1, typename T2> void adeb(T1 arr[], T2 n){
    cout << "#adebug" << endl;
    for(int i=0; i<=n; i++) cout << arr[i] << " ";
    cout << endl;
}
template<typename T1> void mdeb(T1 mp){
    cout << "#map pair debug" << endl;
    for(auto const& m : mp) cout << m.first sp m.second << endl;
}
int dx[] = {1, 0, -1, 0, 1, -1, 1, -1};
int dy[] = {0, 1, 0, -1, 1, -1, -1, 1};


template<typename T>
class WarshallFloyd {
public:
    int n;
    vector<vector<T>> g;
    WarshallFloyd(int n, T mx):n(n){
        g.assign(n+1, vector<T>(n+1, mx));
        reps(i, 0, n){
            g[i][i] = 0;
        }
    }

    void add(int from, int to, ll cost){
        g[from][to] = cost;
    }

    void build(){
        reps(k, 1, n){
            reps(i, 1, n){
                reps(j, 1, n){
                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
                }
            }
        }
    }

    T result(int from, int to){
        return g[from][to];
    }
};


class DCandidatesOfNoShortestPaths {
public:
	void solve(std::istream& in, std::ostream& out) {
	    struct edge{
	        int to, cost, id;
	        edge(int to, int cost, int id):to(to),cost(cost),id(id){}
	    };
        int n, m, a, b, c;
        in >> n >> m;
        WarshallFloyd<ll> wf(n, inf);
        vector<edge> g[1010];
        rep(i, m){
            in >> a >> b >> c;
            wf.add(a, b, c);
            wf.add(b, a, c);
            g[a].push_back(edge(b, c, i));
            g[b].push_back(edge(a, c, i));
        }

        wf.build();

        vector<int> used;
        vector<int> visit(n+1, 0);
        int dist;
        function<void(int, ll, vector<int>)> dfs = [&](int u, ll sum, vector<int> edges){
            if(u == dist){
                used.insert(used.end(), all(edges));
            }
            visit[u] = 1;
            for(auto const& e : g[u]){
                if(visit[e.to]) continue;
                if(sum + e.cost > wf.result(u, e.to)) continue;
                edges.push_back(e.id);
                dfs(e.to, sum + e.cost, edges);
                edges.pop_back();
            }
        };
        reps(i, 1, n){
            reps(j, 1, n){
                if(i == j) continue;
                visit.assign(n+1, 0);
                dist = j;
                dfs(i, 0, vector<int>());
            }
        }

        set<int> res;
        for(auto const& e : used){
            res.insert(e);
        }

        out << m-res.size() << endl;
    }
};


int main() {
	DCandidatesOfNoShortestPaths solver;
	std::istream& in(std::cin);
	std::ostream& out(std::cout);
	solver.solve(in, out);
	return 0;
}


Measured memory_bytes: 384