#include <bits/stdc++.h>
#pragma warning(disable : 4996)
#pragma comment(linker, "/STACK:336777216")
using namespace std;
inline long long gcd(long long a, long long b) {
  if (a == 0) return b;
  return gcd(b % a, a);
}
inline long long power(long long a, long long n, long long m) {
  if (n == 0) return 1;
  long long p = power(a, n / 2, m);
  p = (p * p) % m;
  if (n % 2)
    return (p * a) % m;
  else
    return p;
}
const long long MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const long long LL_INF = 0x3f3f3f3f3f3f3f3f;
int n, m;
vector<pair<int, long long> > adj[200001];
int up[200001][30];
long long mx[200001][30];
int tin[200001];
int tout[200001];
int Time = 0;
struct subset {
  int parent;
  int rank;
};
int find(subset subsets[], int i) {
  if (subsets[i].parent == i) return i;
  subsets[i].parent = find(subsets, subsets[i].parent);
  return subsets[i].parent;
}
void Union(subset subsets[], int x, int y) {
  if (subsets[x].rank > subsets[y].rank) {
    subsets[y].parent = x;
  } else if (subsets[y].rank > subsets[x].rank) {
    subsets[x].parent = y;
  } else {
    subsets[y].parent = x;
    subsets[x].rank++;
  }
}
void dfs(int u, int parent, long long w) {
  tin[u] = (++Time);
  up[u][0] = parent;
  mx[u][0] = w;
  for (int j = 1; j < 30; j++) {
    up[u][j] = up[up[u][j - 1]][j - 1];
    mx[u][j] = max(mx[u][j], mx[u][j - 1]);
    mx[u][j] = max(mx[u][j], mx[up[u][j - 1]][j - 1]);
  }
  int v;
  long long w1;
  for (int i = 0; i < adj[u].size(); i++) {
    v = adj[u][i].first;
    w1 = adj[u][i].second;
    if (v != parent) {
      dfs(v, u, w1);
    }
  }
  tout[u] = (++Time);
}
bool is_ancestor(int u, int v) {
  if (tin[u] <= tin[v] && tout[u] >= tout[v]) return 1;
  return 0;
}
long long findmax(int u, int v) {
  long long ans = 0;
  int u1 = u;
  for (int j = 29; j >= 0; j--) {
    if (!is_ancestor(up[u][j], v)) {
      ans = max(ans, mx[u][j]);
      u = up[u][j];
    }
  }
  ans = max(ans, mx[u][0]);
  u = up[u][0];
  if (u != v) {
    for (int j = 29; j >= 0; j--) {
      if (!is_ancestor(up[v][j], u1)) {
        ans = max(ans, mx[v][j]);
        v = up[v][j];
      }
    }
    ans = max(ans, mx[v][0]);
  }
  return ans;
}
int main() {
  std::ios::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  ;
  cin >> n >> m;
  pair<long long, pair<int, int> > edge[m];
  long long w;
  int u, v, x, y;
  for (int i = 0; i < m; i++) {
    cin >> edge[i].second.first >> edge[i].second.second;
    cin >> edge[i].first;
  }
  sort(edge, edge + m);
  subset subsets[n + 1];
  bool can[m + 1];
  memset(can, 0, sizeof(can));
  vector<pair<pair<int, int>, long long> > edges;
  for (int i = 0; i <= n; i++) {
    subsets[i].parent = i;
    subsets[i].rank = 0;
  }
  int j;
  int ans = 0;
  for (int i = 0; i < m;) {
    j = i;
    while (j < m && (edge[j].first == edge[i].first)) j++;
    for (int k = i; k < j; k++) {
      x = find(subsets, edge[k].second.first);
      y = find(subsets, edge[k].second.second);
      if (x != y) can[k] = 1;
    }
    for (int k = i; k < j; k++) {
      x = find(subsets, edge[k].second.first);
      y = find(subsets, edge[k].second.second);
      if (x == y && can[k]) ans++;
      if (x != y) {
        Union(subsets, x, y);
      }
    }
    i = j;
  }
  cout << ans << endl;
  return 0;
}
