/*
 * D.cpp	ABC051 D Candidates of No Shortest Paths
 *
 *  Created on: 2017/06/26
 *      Author: LitMc
 */
//#define MYDEBUG
#include <bits/stdc++.h>
#ifdef MYDEBUG
#define dbp(x) cout<<#x<<": "<<x<<endl
#define dbp2(x,y) cout<<#x<<","<<#y<<": "<<x<<","<<y<<endl
#define dbp3(x,y,z) cout<<#x<<","<<#y<<","<<#z<<": "<<x<<","<<y<<","<<z<<endl
#define dbp4(w,x,y,z) cout<<#w<<","<<#x<<","<<#y<<","<<#z<<": "<<w<<","<<x<<","<<y<<","<<z<<endl
#define ifcin(x) std::ifstream cin(x)
#else
#define dbp(x)
#define dbp2(x,y)
#define dbp3(x,y,z)
#define dbp4(w,x,y,z)
#define ifcin(x)
#endif
#define ll long long
#define ull unsigned long long
#define all(x) x.begin(), x.end()
#define rep(i, from, to) for(int i=from; i<to; ++i)
#define REP(i, from, to) for(int i=from; i<=to; ++i)
#define EPS = 1e-14;
using std::vector;
using std::cout;
using std::cin;
using std::endl;
using std::max;
using std::min;
using std::swap;
using std::string;
using std::fill;
using std::pair;
using std::sort;
using std::reverse;
using std::pair;
using std::greater;
using std::priority_queue;
using std::ostream;
using std::unique;

template<typename T>
ostream& operator<<(ostream& out, const vector<vector<T> >& v) {
	for (size_t i = 0; i < v.size(); ++i) {
		out << v[i] << endl;
	}
	return out;
}

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& v) {
	out << "[";
	size_t last = v.size() - 1;
	for (size_t i = 0; i < v.size(); ++i) {
		out << v[i];
		if (i != last) {
			out << ",";
		}
	}
	out << "]";
	return out;
}

struct edge {
	int from, to, cost;
	edge() {
		*this = edge(-1, -1, -1);
	}
	edge(int from_, int to_, int cost_) {
		from = from_;
		to = to_;
		cost = cost_;
	}
	bool operator<(const edge& e) const {
		return cost < e.cost;
	}
	bool operator==(const edge& e) const {
		return (from == e.from && to == e.to && cost == e.cost)
				|| (from == e.to && to == e.from && cost == e.cost);
	}
};

static const int INF = INT_MAX;
typedef struct edge edge;
static const int MAX_N = 101;
int N, M;
int d[MAX_N][MAX_N];
vector<int> path[MAX_N][MAX_N];
vector<int> ans;

void pushAll(vector<int>& v1, vector<int>& v2) {
	rep(i,0,(int)v2.size())
	{
		v1.push_back(v2[i]);
	}
}

void warshallFloyd() {
	REP(k,1,N)
	{
		REP(s,1,N)
		{
			if (s == k || d[s][k] == INF) {
				continue;
			}
			REP(t,1,N)
			{
				if (t == k || s == t || d[k][t] == INF) {
					continue;
				}
				int sk = d[s][k], kt = d[k][t], st = d[s][t];
				if (st == sk + kt) {
					pushAll(path[s][t], path[s][k]);
					pushAll(path[s][t], path[k][t]);
				} else if (st > sk + kt) {
					d[s][t] = sk + kt;
					path[s][t] = vector<int>();
					pushAll(path[s][t], path[s][k]);
					pushAll(path[s][t], path[k][t]);
				}
			}
		}
	}
}

void solve() {
	cin >> N >> M;
	rep(s,0,MAX_N)
	{
		rep(t,0,MAX_N)
		{
			d[s][t] = INF;
		}
	}
	REP(i,1,M)
	{
		int a, b, c;
		cin >> a >> b >> c;
		d[a][b] = d[b][a] = c;
		path[a][b].push_back(i);
		path[b][a].push_back(i);
	}
	warshallFloyd();
	REP(u,1,N)
	{
		REP(v,1,N)
		{
			if (u != v) {
				dbp4(u, v, d[u][v], path[u][v]); //print debug
				pushAll(ans, path[u][v]);
			}
		}
	}
	sort(all(ans));
	ans.erase(unique(all(ans)), ans.end());
	cout << M - ans.size() << endl;
}

int main() {
	solve();
}



Measured memory_bytes: 4728