#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <cmath>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ccomplex>
#include <cfenv>
#include <cinttypes>
#include <cstdbool>
#include <cstdint>
#include <ctgmath>
#include <cwchar>
#include <cwctype>
#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#include <string>
#include <array>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <forward_list>
#include <future>
#include <initializer_list>
#include <mutex>
#include <random>
#include <ratio>
#include <regex>
#include <system_error>
#include <thread>
#include <tuple>
#include <typeindex>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
using namespace std;
struct edge { int to, cost; };
typedef vector<vector<edge>> AdjList;
AdjList graph;
typedef pair<int, int> P;
const int INF = 10000000;
vector<int> dist;
vector<int> prever;
int index[100][100];
void dijkstra(int n, int s) {
	dist = vector<int>(n, INF);
	prever = vector<int>(n, -1);
	dist[s] = 0;
	priority_queue<P, vector<P>, greater<P>> que;
	que.push(P(0, s));
	while (!que.empty()) {
		P p = que.top();
		que.pop();
		int v = p.second;
		if (dist[v] < p.first) {
			continue;
		}
		for (int i = 0; i < graph[v].size(); i++) {
			edge e = graph[v][i];
			if (dist[e.to] > dist[v] + e.cost) {
				dist[e.to] = dist[v] + e.cost;
				prever[e.to] = v;
				que.push(P(dist[e.to], e.to));
			}
		}
	}
}
vector<int> get_path(int t) {
	vector<int> path;
	for (; t != -1; t = prever[t]) {
		path.push_back(t);
	}
	reverse(path.begin(), path.end());
	return path;
}
int main(void) {
	int N, M;
	cin >> N >> M;
	graph = AdjList(N);
	bool used[1000];
	for (int i = 0; i < M; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		a--; b--;
		graph[a].push_back({ b, c });
		graph[b].push_back({ a, c });
		used[i] = false;
		index[a][b] = i;
	}
	for (int i = 0; i < N; i++) {
		dijkstra(N, i);
		for (int j = 0; j < N; j++) {
			vector<int> ans = get_path(j);
			for (int k = 0; k < ans.size() - 1; k++) {
				used[index[ans[k]][ans[k + 1]]] = true;
				
			}
		}
	}
	int result = M;
	for (int i = 0; i < M; i++) {
		if (used[i]) {
			result--;
		}
	}
	cout << result << endl;
	return 0;
}