#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using ll = long long;
using ull = unsigned long long;
struct S {
  int from;
  int to;
  int cost;
  bool operator<(const S& rval) const {
    if (cost > rval.cost) return true;
    if (cost < rval.cost) return false;
    if (from > rval.from) return true;
    if (from < rval.from) return false;
    return to > rval.from;
  }
};
int main(int argc, char* argv[]) {
  std::cin.tie(0);
  std::ios::sync_with_stdio(false);
  int n, m;
  cin >> n >> m;
  vector<vector<int> > adj(n, vector<int>(n, numeric_limits<int>::max()));
  vector<vector<bool> > used(n, vector<bool>(n, false));
  for (int i = 0; i < m; ++i) {
    int a, b, c;
    cin >> a >> b >> c;
    --a;
    --b;
    adj[a][b] = c;
    adj[b][a] = c;
  }
  for (int i = 0; i < n; ++i) {
    set<int> visit_flag;
    visit_flag.insert(i);
    priority_queue<S> queue;
    for (int j = 0; j < n; ++j) {
      if (adj[i][j] != numeric_limits<int>::max()) {
        queue.push(S{i, j, 0 + adj[i][j]});
      }
    }
    while (!queue.empty()) {
      auto s = queue.top();
      queue.pop();
      if (visit_flag.find(s.to) != visit_flag.end()) continue;
      used[s.from][s.to] = true;
      visit_flag.insert(s.to);
      for (int j = 0; j < n; ++j) {
        if (visit_flag.find(j) != visit_flag.end()) continue;
        if (adj[i][j] == numeric_limits<int>::max()) continue;
        queue.push(S{s.to, j, s.cost + adj[s.to][j]});
      }
    }
  }
  int ans = m;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < i; ++j) {
      if (used[i][j]) {
        --ans;
      }
    }
  }
  cout << ans << endl;
  return EXIT_SUCCESS;
}
