#include <bits/stdc++.h>
using namespace std;
constexpr long long INF = 1e18;
constexpr long long MOD = 1e9 + 7;
constexpr double PI = 3.14159265358979323846;
constexpr int dx[] = {0, 0, 1, -1};
constexpr int dy[] = {1, -1, 0, 0};
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  struct edge {
    int to;
    int cost;
  };
  struct edge_q {
    int cost;
    int from;
    int to;
    int f;
    int t;
    bool operator<(const edge_q &another) const { return cost > another.cost; };
  };
  int n, m, a[1001], b[1001], c[1001];
  bool used[110][110] = {false}, usedE[110][110] = {false};
  vector<edge> g[110];
  priority_queue<edge_q> q;
  cin >> n >> m;
  for (int i = (0); i < (m); i++) {
    cin >> a[i] >> b[i] >> c[i];
    g[a[i]].push_back({b[i], c[i]});
    g[b[i]].push_back({a[i], c[i]});
    q.push(edge_q{c[i], a[i], b[i], a[i], b[i]});
  }
  int cnt = n * (n - 1) / 2;
  int res = m;
  while (cnt) {
    edge_q e = q.top();
    q.pop();
    if (used[e.from][e.to] || used[e.to][e.from]) continue;
    used[e.from][e.to] = true;
    cnt--;
    if (!usedE[e.f][e.t] && !usedE[e.t][e.f]) {
      res--;
      usedE[e.f][e.t] = true;
    }
    for (int i = 0; i < g[e.from].size(); i++) {
      if (used[e.to][g[e.from][i].to] || used[g[e.from][i].to][e.to]) continue;
      q.push({e.cost + g[e.from][i].cost, e.to, g[e.from][i].to, e.from,
              g[e.from][i].to});
    }
    for (int i = 0; i < g[e.to].size(); i++) {
      if (used[e.from][g[e.to][i].to] || used[g[e.to][i].to][e.from]) continue;
      q.push({e.cost + g[e.to][i].cost, e.from, g[e.to][i].to, e.to,
              g[e.to][i].to});
    }
  }
  cout << res << endl;
  return 0;
}
