#include <bits/stdc++.h>
using namespace std;
const int INF = (int)2e9;
const long long INFL = (long long)9e18;
const int MAXINT = ((~0) ^ (1 << 31));
const long long MAXLL = ((~0) ^ ((long long)1 << 63));
template <class T>
inline T pow2(T a) {
  return a * a;
}
template <class T>
inline bool mineq(T& a, T b) {
  return (a > b) ? (a = b, true) : false;
}
template <class T>
inline bool maxeq(T& a, T b) {
  return (a < b) ? (a = b, true) : false;
}
template <class flow, const int maxn = 5100, const int maxm = 30100,
          flow INFF = INFL / 10>
struct Max_flow {
  int pr[2 * maxm], head[maxn], to[2 * maxm], it[maxn], newid;
  flow cap[2 * maxm];
  Max_flow() {
    newid = 0;
    memset(head, -1, sizeof(head));
  }
  inline void add_edge(int v, int u, flow vu, flow uv = 0) {
    if (newid >= 2 * maxm) exit(0);
    to[newid] = u, cap[newid] = vu, pr[newid] = head[v], head[v] = newid++;
    to[newid] = v, cap[newid] = uv, pr[newid] = head[u], head[u] = newid++;
  }
  int dist[maxn], q[maxn], h, t;
  inline int bfs(int src, int sink) {
    h = t = 0;
    memset(dist, -1, sizeof(dist));
    q[h++] = src;
    dist[src] = 0;
    while (h != t) {
      int v = q[t++];
      for (int e = head[v]; e != -1; e = pr[e]) {
        if (cap[e] > 0 && dist[to[e]] == -1) {
          dist[to[e]] = dist[v] + 1;
          q[h++] = to[e];
        }
      }
    }
    return dist[sink] != -1;
  }
  flow dfs(int v, int sink, flow lim = INFF) {
    flow tmp = lim;
    if (v == sink) return lim;
    for (int& e = it[v]; e != -1; e = pr[e]) {
      int u = to[e];
      if (dist[u] == dist[v] + 1 && cap[e]) {
        flow f = dfs(u, sink, min(lim, cap[e]));
        cap[e] -= f;
        cap[e ^ 1] += f;
        lim -= f;
        if (lim == 0) break;
      }
    }
    return tmp - lim;
  }
  flow max_flow(int src, int sink) {
    flow ans = 0;
    while (bfs(src, sink)) {
      memcpy(it, head, sizeof(head));
      ans += dfs(src, sink);
    }
    return ans;
  }
};
Max_flow<int, 50100, 100100, INF / 2> mf;
const int maxn = 2e4 + 100;
int n, m;
int pr[maxn], cl[maxn];
int visited[maxn];
vector<pair<int, int> > adj[maxn];
vector<vector<int> > cmp;
void dfs(int v, int par, int col) {
  visited[v] = 1;
  pr[v] = par;
  cl[v] = col;
  for (auto id : adj[v]) {
    int u = id.first;
    int c = id.second;
    if (u == par) continue;
    if (visited[u] == 1) {
      cmp.push_back(vector<int>());
      vector<int>& now = cmp.back();
      int id = v;
      now.push_back(c);
      do {
        now.push_back(cl[id]);
        id = pr[id];
      } while (id != u);
    } else if (!visited[u])
      dfs(u, v, c);
  }
  visited[v] = 2;
}
set<int> colors;
int cycle_id[maxn], color_id[maxn];
int deg[maxn];
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cin >> n >> m;
  for (int i = 0; i < (int)(m); i++) {
    int a, b, c;
    cin >> a >> b >> c;
    colors.insert(c);
    deg[c]++;
    adj[a].push_back(make_pair(b, c));
    adj[b].push_back(make_pair(a, c));
  }
  dfs(1, -1, -1);
  int src = 0, sink = 1, newid = 2;
  for (int i = 0; i < (int)(cmp.size()); i++) {
    cycle_id[i] = newid++;
    mf.add_edge(cycle_id[i], sink, 1);
  }
  for (auto it : colors) {
    color_id[it] = newid++;
    mf.add_edge(src, color_id[it], deg[it] - 1);
  }
  for (int i = 0; i < (int)(cmp.size()); i++) {
    for (int j = 0; j < (int)(cmp[i].size()); j++) {
      mf.add_edge(color_id[cmp[i][j]], cycle_id[i], 1);
    }
  }
  cout << colors.size() - (cmp.size() - mf.max_flow(src, sink)) << endl;
}
