#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:256000000")
using namespace std;
const long double PI = acosl((long double)-1.0);
const long long LINF = (long long)4e18;
int test;
const int MAXN = 20005;
struct edge {
  int v1, v2, color;
  inline int get_to(int v) { return v == v1 ? v2 : v1; }
};
int n, m;
edge e[MAXN];
vector<vector<int> > g, eg;
vector<vector<int> > cycles;
int used[MAXN];
int ccount;
int colors[MAXN];
bool del[MAXN];
int ptr[MAXN];
pair<int, int> dfs_init(int v, int p) {
  used[v] = 1;
  pair<int, int> res = pair<int, int>(0, 0);
  for (int i = 0; i < (int)g[v].size(); i++) {
    int num = g[v][i];
    int to = e[num].get_to(v);
    if (to == p) {
      continue;
    }
    if (used[to]) {
      if (used[to] == 1) {
        res = pair<int, int>(to, ++ccount);
        cycles[ccount].push_back(num);
      }
    } else {
      pair<int, int> r = dfs_init(to, v);
      if (r.second) {
        cycles[r.second].push_back(num);
      }
      if (r.first && r.first != v) {
        res = r;
      }
    }
  }
  used[v] = 2;
  return res;
}
void init() {
  scanf("%d%d", &n, &m);
  g.resize(n + 1);
  for (int i = 1; i <= m; i++) {
    scanf("%d%d%d", &e[i].v1, &e[i].v2, &e[i].color);
    g[e[i].v1].push_back(i);
    g[e[i].v2].push_back(i);
  }
}
bool edges_cmp(int num1, int num2) { return e[num1].color < e[num2].color; }
inline void make_edge(int v1, int v2) {
  eg[v1].push_back(v2);
  eg[v2].push_back(v1);
}
void get_init_colors() {
  for (int i = 1; i <= m; i++) {
    colors[e[i].color]++;
  }
  for (int i = 1; i <= ccount; i++) {
    sort(cycles[i].begin(), cycles[i].end(), edges_cmp);
    del[cycles[i][0]] = true;
    colors[e[cycles[i][0]].color]--;
  }
}
void get_graph() {
  eg.resize(m + 1);
  for (int i = 1; i <= ccount; i++) {
    for (int j = 1; j < (int)cycles[i].size(); j++) {
      make_edge(cycles[i][j - 1], cycles[i][j]);
    }
  }
  for (int j = 1; j <= m; j++) {
    int last = 0;
    for (int i = 1; i <= ccount; i++) {
      while (ptr[i] < (int)cycles[i].size() && e[cycles[i][ptr[i]]].color < j) {
        ptr[i]++;
      }
      if (ptr[i] < (int)cycles[i].size() && e[cycles[i][ptr[i]]].color == j) {
        if (last) {
          make_edge(last, cycles[i][ptr[i]]);
        }
        last = cycles[i][ptr[i]];
      }
    }
  }
}
bool dfs(int v, int p) {
  used[v] = 1;
  if (colors[e[v].color]) {
    return true;
  }
  for (int i = 0; i < (int)eg[v].size(); i++) {
    int to = eg[v][i];
    if (used[to]) {
      continue;
    }
    if (e[v].color != e[to].color) {
      del[v] = false;
      colors[e[v].color]++;
      del[to] = true;
      colors[e[to].color]--;
    }
    if (dfs(to, v)) {
      return true;
    }
  }
  if (p && e[v].color != e[p].color) {
    del[v] = false;
    colors[e[v].color]++;
    del[p] = true;
    colors[e[p].color]--;
  }
  return false;
}
void edit_colors() {
  for (int i = 1; i <= ccount; i++) {
    for (int j = 0; j < (int)cycles[i].size(); j++) {
      int num = cycles[i][j];
      if (del[num]) {
        if (!colors[e[num].color]) {
          memset(used, 0, sizeof(used));
          dfs(num, 0);
        }
        break;
      }
    }
  }
}
int calc_ans() {
  int res = 0;
  for (int i = 1; i <= m; i++) {
    if (colors[i]) {
      res++;
    }
  }
  return res;
}
void solve() {
  init();
  cycles.resize(m);
  dfs_init(1, 0);
  get_init_colors();
  get_graph();
  edit_colors();
  printf("%d\n", calc_ans());
}
void test_output() {}
int main() {
  srand(333);
  double start = clock();
  int tests = 1;
  for (test = 1; test <= tests; test++) {
    solve();
  }
  fprintf(stderr, "%.5lf\n", (clock() - start) / CLOCKS_PER_SEC);
  return 0;
}
