#include <bits/stdc++.h>
using namespace std;
using P = pair<int, int>;
struct edge {
  int to;
  int cost;
};
vector<vector<edge>> node;
map<string, bool> isUsed;
long long d[105];
int n, m;
string make_key(int n1, int n2) {
  string key = to_string(n1) + to_string(n2);
  sort(key.begin(), key.end());
  return key;
}
void dijkstra(int s, int g) {
  priority_queue<P, vector<P>, greater<P>> pq;
  fill(d, d + n, 1e18);
  pq.push(P{0, s});
  d[s] = 0;
  vector<vector<P>> path(n);
  path[s].push_back(make_pair(-1, -1));
  while (!pq.empty()) {
    auto p = pq.top();
    pq.pop();
    int v = p.second;
    for (int i = 0; i < (node[v].size()); i++) {
      edge e = node[v][i];
      if (d[e.to] >= d[v] + e.cost) {
        d[e.to] = d[v] + e.cost;
        pq.push(P{d[v] + e.cost, e.to});
        path[e.to].push_back(make_pair(v, d[e.to]));
      }
    }
  }
  queue<P> q;
  int now = g;
  int next;
  for (int i = 0; i < (path[now].size()); i++) {
    next = path[now][i].first;
    if (path[now][i].second == d[now]) {
      q.push(make_pair(now, next));
    }
  }
  while (!q.empty()) {
    auto pos = q.front();
    q.pop();
    if (pos.second < 0) {
      continue;
    }
    isUsed[make_key(pos.first, pos.second)] = true;
    now = pos.second;
    for (int i = 0; i < (path[now].size()); i++) {
      next = path[now][i].first;
      if (path[now][i].second == d[now]) {
        q.push(make_pair(now, next));
      }
    }
  }
}
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> n >> m;
  node.resize(n);
  for (int i = 0; i < (m); i++) {
    int a, b, c;
    cin >> a >> b >> c;
    a--, b--;
    node[a].push_back(edge{b, c});
    node[b].push_back(edge{a, c});
    isUsed[make_key(a, b)] = false;
  }
  for (int i = 0; i < (n); i++) {
    for (int j = 0; j < (n); j++) {
      if (i == j) continue;
      dijkstra(i, j);
    }
  }
  int ans = 0;
  for (auto m : isUsed) {
    ans += !m.second;
  }
  cout << ans << endl;
  return 0;
}
