#include <bits/stdc++.h>
using namespace std;
struct DSU {
  int parts;
  vector<int> rt, sz;
  DSU(int n) {
    rt.resize(n + 1);
    sz.resize(n + 1);
    iota(rt.begin(), rt.end(), 0);
    fill(sz.begin(), sz.end(), 1);
    parts = n;
  }
  int root(int k) {
    while (k != rt[k]) k = rt[k] = rt[rt[k]];
    return k;
  }
  void merge(int u, int v) {
    int r1 = root(u), r2 = root(v);
    if (r1 == r2) return;
    parts--;
    if (sz[r1] > sz[r2]) swap(r1, r2);
    sz[r2] += sz[r1];
    sz[r1] = 0;
    rt[r1] = rt[r2];
  }
};
struct Edge {
  int u, v;
  int64_t w;
  Edge(int u, int v, int64_t w) {
    this->u = u;
    this->v = v;
    this->w = w;
  }
  bool operator<(Edge const& e) { return w < e.w; }
};
int kruskal(map<int64_t, vector<Edge>>& mve, int n) {
  int ans = 0;
  DSU dsu(n);
  for (auto [we, ve] : mve) {
    int count = 0;
    for (auto [u, v, w] : ve)
      if (dsu.root(u) != dsu.root(v)) count++;
    for (auto [u, v, w] : ve)
      if (dsu.root(u) != dsu.root(v)) dsu.merge(u, v), count--;
    ans += count;
  }
  return ans;
}
int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  ;
  int n, i, u, v, w, m;
  map<int64_t, vector<Edge>> mve;
  cin >> n >> m;
  for (i = 0; i < m; ++i) {
    cin >> u >> v >> w;
    mve[w].push_back({u, v, w});
  }
  cout << kruskal(mve, n);
  return 0;
}
