#include <bits/stdc++.h>
using namespace std;
long long power(long long a, long long b, long long modi) {
  long long res = 1;
  while (b) {
    if (b % 2) {
      res = (res * a) % modi;
    }
    b /= 2;
    a = (a * a) % modi;
  }
  return res;
}
long long dirx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
long long diry[] = {-1, 0, 1, -1, 1, -1, 0, 1};
long long dx[] = {-1, 1, 0, 0};
long long dy[] = {0, 0, -1, 1};
struct Dinic {
  struct Edge {
    int to, rev;
    long long c, oc;
    long long flow() { return max(oc - c, 0LL); }
  };
  vector<int> lvl, ptr, q;
  vector<vector<Edge>> adj;
  Dinic(int n) : lvl(n), ptr(n), q(n), adj(n) {}
  void addEdge(int a, int b, long long c, int rcap = 0) {
    adj[a].push_back({b, (int)(adj[b]).size(), c, c});
    adj[b].push_back({a, (int)(adj[a]).size() - 1, rcap, rcap});
  }
  long long dfs(int v, int t, long long f) {
    if (v == t || !f) return f;
    for (int& i = ptr[v]; i < (int)(adj[v]).size(); i++) {
      Edge& e = adj[v][i];
      if (lvl[e.to] == lvl[v] + 1)
        if (long long p = dfs(e.to, t, min(f, e.c))) {
          e.c -= p, adj[e.to][e.rev].c += p;
          return p;
        }
    }
    return 0;
  }
  long long calc(int s, int t) {
    long long flow = 0;
    q[0] = s;
    for (int L = 0; L < 31; L++) do {
        lvl = ptr = vector<int>((int)(q).size());
        int qi = 0, qe = lvl[s] = 1;
        while (qi < qe && !lvl[t]) {
          int v = q[qi++];
          for (Edge e : adj[v])
            if (!lvl[e.to] && e.c >> (30 - L))
              q[qe++] = e.to, lvl[e.to] = lvl[v] + 1;
        }
        while (long long p = dfs(s, t, LLONG_MAX)) flow += p;
      } while (lvl[t]);
    return flow;
  }
  bool leftOfMinCut(int a) { return lvl[a] != 0; }
};
long long color[2000005];
set<long long> all_color;
vector<long long> num, st;
vector<pair<long long, long long>> adj[10005];
vector<vector<long long>> comp;
int Time;
int ans = 0;
int dfs(int at, int par) {
  long long me = num[at] = ++Time, e, y, top = me;
  for (auto pa : adj[at]) {
    if (pa.second != par) {
      tie(y, e) = pa;
      if (num[y]) {
        top = min(top, num[y]);
        if (num[y] < me) st.push_back(e);
      } else {
        long long si = (int)(st).size();
        long long up = dfs(y, e);
        top = min(top, up);
        if (up == me) {
          st.push_back(e);
          vector<long long> temp(st.begin() + si, st.end());
          comp.push_back(temp);
          st.resize(si);
        } else if (up < me)
          st.push_back(e);
        else {
          if (all_color.count(color[e]) != 0) {
            all_color.erase(color[e]);
            ans++;
          }
        }
      }
    }
  }
  return top;
}
void bicomps(int n) {
  num.assign(n, 0);
  for (int i = 0; i < n; i++)
    if (!num[i]) dfs(i, -1);
}
int main() {
  int n, m;
  cin >> n >> m;
  int eid = 0;
  for (int i = 0; i < m; i++) {
    int u, v, c;
    cin >> u >> v >> c;
    u--;
    v--;
    all_color.insert(c);
    color[eid] = c;
    adj[u].push_back({v, eid});
    adj[v].push_back({u, eid});
    eid++;
  }
  bicomps(n);
  int left = comp.size();
  int right = all_color.size();
  int total = 1 + left + right + 1;
  int source = 0;
  int sink = total - 1;
  Dinic d(total);
  map<long long, long long> idx;
  int tmp = 0;
  for (auto x : all_color) {
    idx[x] = tmp;
    d.addEdge(1 + left + tmp, sink, 1);
    tmp++;
  }
  tmp = 0;
  for (auto x : comp) {
    if (x.empty()) continue;
    d.addEdge(source, 1 + tmp, x.size() - 1);
    for (auto y : x) {
      int cur_color = color[y];
      if (all_color.count(cur_color) != 0) {
        d.addEdge(1 + tmp, 1 + left + idx[cur_color], 1);
      }
    }
    tmp++;
  }
  cout << ans + d.calc(source, sink) << endl;
  return 0;
}
