#include <bits/stdc++.h>
using namespace std;
struct edge {
  int to, cost;
};
vector<vector<edge>> graph;
const int INF = 10000000;
vector<int> dist;
vector<int> prever;
int idx[100][100];
void dijkstra(int n, int s) {
  dist = vector<int>(n, INF);
  prever = vector<int>(n, -1);
  dist[s] = 0;
  priority_queue<pair<int, int>, vector<pair<int, int>>,
                 greater<pair<int, int>>>
      que;
  que.push(pair<int, int>(0, s));
  while (!que.empty()) {
    pair<int, int> 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(pair<int, int>(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 = vector<vector<edge>>(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;
    idx[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[idx[ans[k]][ans[k + 1]]] = true;
      }
    }
  }
  int result = 0;
  for (int i = 0; i < M; i++) {
    if (!used[i]) {
      result++;
    }
  }
  cout << result << endl;
  return 0;
}
