//#include "stdafx.h"
#include <iostream>
#include <set>
#include <queue>
#include <vector>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <string>
#include <cstring>
#include <climits>
#include <sstream>
#include <iomanip>
#include <map>
#include <stack>
#include <tuple>
#include <numeric>
#include <assert.h>
#include <functional>
#include <unordered_map>

using namespace std;

/*-----------------------------------------------------------------------------
　定義
 -------------------------------------------------------------------------------*/
#define REP(i, n)				for (int (i) = 0 ; (i) < (int)(n) ; ++(i))
#define REPN(i, m, n)			for (int (i) = m ; (i) < (int)(n) ; ++(i))
#define INF						2e9
#define MOD						(1000 * 1000 * 1000 + 7)
#define Ceil(x, n)				(((((x))+((n)-1))/n))		/* Nの倍数に切り上げ割り算 */
#define CeilN(x, n)				(((((x))+((n)-1))/n)*n)		/* Nの倍数に切り上げ */
#define FloorN(x, n)			((x)-(x)%(n))				/* Nの倍数に切り下げ */
#define IsOdd(x)				(((x)&0x01UL) == 0x01UL)			
#define IsEven(x)				(!IsOdd((x)))						
#define	BitSetV(Val,Bit)		((Val) |= (Bit))			
#define	BitTstV(Val,Bit)		((Val) & (Bit))				
#define ArrayLength(x)			(sizeof( x ) / sizeof( x[ 0 ]))
#define	MAX_QWORD				((QWORD)0xFFFFFFFFFFFFFFFF)
#define M_PI					3.14159265358979323846
typedef long long				ll;
typedef unsigned long long int	QWORD;
typedef pair<ll, ll>			P;

/*-----------------------------------------------------------------------------
　処理
 -------------------------------------------------------------------------------*/
// 枝定義
struct Edge
{
	int	to;
	ll	dist;
	Edge(){}
	Edge(int to, ll dist): to(to), dist(dist){}
};

// グラフ定義(二次元配列)
template <class E>
using Graph = vector<vector<E>>;
 
// ダイクストラ
template<class D, D infVal>
class Dijkstra 
{
public:
	// res[i] = sからiまでの最短距離
	vector<D> res;

	// 復元パス
//	vector<int> pathList;

private:
	vector<int> pathRev;
    
public:
	template<class E>
	Dijkstra(const Graph<E> &G, int startFrom)
	{
		int V = (int)G.size();
		res = vector<D>(V, infVal);
		pathRev.resize(V, -1);

		using P = pair<D, int>;
		priority_queue<P, vector<P>, greater<P>> que;
		que.push(P(0, startFrom));
		res[startFrom] = 0;

		while (!que.empty()) {
			P p = que.top(); que.pop();
			int egdeFrom = p.second;

			// 違う最短距離がキューにあるのでスキップ
			if (res[egdeFrom] < p.first) {
				continue;
			}

			// 最短距離を更新
			for (E edge : G[egdeFrom]) {
				D nextDist = p.first + edge.dist;
				if (nextDist < res[edge.to]) {
					res[edge.to] = nextDist;
					pathRev[edge.to] = egdeFrom;
					que.push(P(nextDist, edge.to));
				}
			}
		}
	}

	// 経路を取得する
	vector<int> getPath(int dstPos)
	{
		vector<int> pathList;
		
		while (dstPos != -1) {
			pathList.push_back(dstPos);
			dstPos = pathRev[dstPos];
		}
		reverse(pathList.begin(), pathList.end());
		return pathList;
	}
};


int main()
{
	int N, M;
	cin >> N >> M;
	Graph<Edge>	G(N);
	REP(i, M) {
		int a, b, c;
		cin >> a >> b >> c;
		a--, b--;
		G[a].emplace_back(Edge(b, c));
		G[b].emplace_back(Edge(a, c));
	}

	map<P, int> edgeMap;
//	vector<bool> usedEdgeList(M, false);
	for (int i = 0; i < N; i++) {
		// iから全頂点への最短経路
		Dijkstra<ll, (ll)2e18> res(G, i);

		// iからjへの経路
		for (int j = 0; j < N; j++) {
			if (i != j) {
				vector<int> pathList = res.getPath(j);
				int fromG = pathList[0];
				for (int k = 1; k < (int)pathList.size(); k++) {
					int toG = pathList[k];
					edgeMap[{fromG, toG}] = 1;
					edgeMap[{toG, fromG}] = 1;
					fromG = toG;
				}
			}
		}
	}

/*	int ans = 0;
	for (auto usedOne : usedEdgeList) {
		if (!usedOne) {
			ans++;
		}
	}*/
	cout << (2 * M - edgeMap.size()) / 2 << endl;
	return 0;
}


Measured memory_bytes: 384