#include <bits/stdc++.h>
using namespace std;
struct edge {
  int to, cost;
  edge(int i, int j) {
    to = i;
    cost = j;
  }
};
vector<edge> es[100];
int used[100][100];
void dij(int s) {
  int d[100];
  for (int i = 0; i < (int)100; ++i) d[i] = INT_MAX;
  priority_queue<tuple<int, int, int>, std::vector<tuple<int, int, int>>,
                 std::greater<tuple<int, int, int>>>
      pq;
  pq.push(make_tuple(0, s, s));
  while (!pq.empty()) {
    int v, cost, from;
    tie(cost, v, from) = pq.top();
    pq.pop();
    if (d[v] == INT_MAX) {
      d[v] = cost;
      used[v][from] = 1;
      used[from][v] = 1;
      for (auto e : es[v]) {
        pq.push(make_tuple(cost + e.cost, e.to, v));
      }
    }
  }
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.precision(10);
  cout << fixed;
  int N, M;
  cin >> N >> M;
  vector<int> a(M), b(M), c(M);
  for (int i = 0; i < (int)M; ++i) {
    cin >> a[i] >> b[i] >> c[i];
    a[i]--;
    b[i]--;
    es[a[i]].push_back(edge(b[i], c[i]));
    es[b[i]].push_back(edge(a[i], c[i]));
  }
  for (int i = 0; i < (int)N; ++i) {
    dij(i);
  }
  int ans = M;
  for (int i = 0; i < (int)M; ++i) {
    if (used[a[i]][b[i]]) ans--;
  }
  cout << ans << endl;
  return 0;
}
