#include <bits/stdc++.h>
using namespace std;
int n, m, low[10000], idx[10000], dfs = 1;
vector<vector<pair<int, int> > > g;
int match[50000], vis[10000], vs, cap[10000];
vector<vector<int> > G;
map<pair<int, int>, int> br;
set<int> has;
void DFS(int u, int p) {
  low[u] = idx[u] = dfs++;
  for (int i = 0; i < g[u].size(); ++i)
    if (idx[g[u][i].first] == 0) {
      DFS(g[u][i].first, u);
      low[u] = min(low[u], low[g[u][i].first]);
      if (low[g[u][i].first] > idx[u]) {
        br[make_pair(min(g[u][i].first, u), max(g[u][i].first, u))] = true;
        has.insert(g[u][i].second);
      }
    } else if (g[u][i].first != p)
      low[u] = min(low[u], idx[g[u][i].first]);
}
bool findPath(int u) {
  vis[u] = vs;
  for (int i = 0; i < G[u].size(); ++i) {
    int v = G[u][i];
    if (match[v] == -1 || vis[match[v]] != vs && findPath(match[v])) {
      match[v] = u;
      return true;
    }
  }
  return false;
}
vector<vector<int> > cyc;
vector<int> S;
map<pair<int, int>, bool> block;
void build(int u, int p) {
  if (vis[u] == vs) {
    cyc.push_back(vector<int>());
    int i = S.size() - 1;
    while (true) {
      int z = S[i--];
      cyc.back().push_back(z);
      if (z == u) break;
    }
    for (int i = 0; i < cyc.back().size(); ++i)
      block[make_pair(
          min(cyc.back()[i], cyc.back()[(i + 1) % cyc.back().size()]),
          max(cyc.back()[i], cyc.back()[(i + 1) % cyc.back().size()]))] = true;
    return;
  }
  vis[u] = vs;
  S.push_back(u);
  int ret = -1;
  for (int i = 0; i < g[u].size(); ++i)
    if (g[u][i].first != p) {
      int a = u;
      int b = g[u][i].first;
      if (a > b) swap(a, b);
      if (block[make_pair(a, b)]) continue;
      build(g[u][i].first, u);
    }
  S.pop_back();
}
int main() {
  scanf("%d%d", &n, &m);
  g.resize(n);
  for (int i = 0, a, b, c; i < m; ++i) {
    scanf("%d%d%d", &a, &b, &c);
    --a;
    --b;
    --c;
    g[a].push_back(make_pair(b, c));
    swap(a, b);
    g[a].push_back(make_pair(b, c));
  }
  DFS(0, -1);
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < g[i].size(); ++j) {
      int u = i;
      int v = g[i][j].first;
      if (u > v) swap(u, v);
      if (br[make_pair(u, v)]) {
        g[i][j] = g[i].back();
        g[i].pop_back();
        --j;
      }
    }
  ++vs;
  for (int i = 0; i < n; ++i)
    if (g[i].size() > 0 && vis[i] != vs) {
      build(i, -1);
      assert(S.empty());
    }
  for (int i = 0; i < cyc.size(); ++i) {
    set<int> clr;
    int u = cyc[i][0];
    for (int j = 1; j <= cyc[i].size(); ++j) {
      int nxt = cyc[i][j % cyc[i].size()];
      int found = 0;
      for (int i = 0; i < g[u].size(); ++i)
        if (g[u][i].first == nxt) {
          clr.insert(g[u][i].second);
          found = 1;
          break;
        }
      assert(found == 1);
      u = nxt;
    }
    G.push_back(vector<int>());
    cap[G.size() - 1] = cyc[i].size() - 1;
    for (auto x : clr)
      if (has.find(x) == has.end()) G[G.size() - 1].push_back(x);
  }
  memset(match, -1, sizeof(match));
  int res = has.size();
  for (int i = 0; i < G.size(); ++i) {
    ++vs;
    while (cap[i]-- && findPath(i)) {
      ++vs;
      ++res;
    }
  }
  printf("%d\n", res);
  return 0;
}
