#include <bits/stdc++.h>
#pragma GCC optimize("-O3")
using namespace std;
const long long mod = 998244353;
struct edge {
  int u, v, cap;
  edge() {}
  edge(int _u, int _v, int _cap) : u(_u), v(_v), cap(_cap) {}
  int get(int t) { return t ^ u ^ v; }
};
const int inf = 1e9;
struct edkarp {
  vector<edge> edges;
  vector<vector<int>> adj;
  int n;
  void init(int _n) {
    n = _n;
    adj.assign(n, vector<int>(0));
  }
  inline int comp(int i) { return i ^ 1; }
  inline int par(int i, int ed) { return edges[ed].get(i); }
  void add(int u, int v, int c) {
    adj[u].emplace_back(edges.size());
    edges.emplace_back(u, v, c);
    adj[v].emplace_back(edges.size());
    edges.emplace_back(u, v, 0);
  }
  int bfs(int st, int tar, vector<int> &p) {
    p.assign(n, -1);
    p[st] = -2;
    queue<pair<int, int>> q;
    q.push(make_pair(st, inf));
    while (!q.empty()) {
      int i = q.front().first, flow = q.front().second;
      q.pop();
      for (auto &x : adj[i]) {
        if (p[par(i, x)] == -1 && edges[x].cap) {
          p[par(i, x)] = x;
          int nf = min(flow, edges[x].cap);
          if (par(i, x) == tar) {
            return nf;
          }
          q.push(make_pair(par(i, x), nf));
        }
      }
    }
    return 0;
  }
  int maxflow(int st, int tar) {
    vector<int> p(n);
    int flow = 0;
    int nf;
    while (nf = bfs(st, tar, p)) {
      flow += nf;
      int cr = tar;
      while (cr != st) {
        edges[p[cr]].cap -= nf;
        edges[comp(p[cr])].cap += nf;
        cr = par(cr, p[cr]);
      }
    }
    return flow;
  }
};
struct edgeg {
  int u, v, c, lo;
  bool in_loop = false;
  edgeg() {}
  edgeg(int _u, int _v, int _c) : u(_u), v(_v), c(_c), lo(-1) {}
  int get(int t) { return t ^ u ^ v; }
};
const int N = 1e4 + 5;
int n, m, u, v, c, vis[N], p[N], vis1[N];
vector<int> adj[N];
vector<bool> avai;
vector<vector<int>> loops;
vector<edgeg> edges;
edkarp fo;
inline int geta(int x, int edge_num) { return edges[edge_num].get(x); }
void dfs(int i, int par) {
  vis[i] = 1;
  vis1[i] = 1;
  for (int x : adj[i]) {
    int u = geta(i, x);
    if (u == par) continue;
    if (vis[u]) {
      if (!vis1[u]) continue;
      edges[x].in_loop = true;
      vector<int> clrs;
      clrs.push_back(edges[x].c);
      u = i;
      while (u != geta(i, x)) {
        edges[p[u]].in_loop = true;
        clrs.push_back(edges[p[u]].c);
        u = geta(u, p[u]);
      }
      sort(clrs.begin(), clrs.end());
      auto it = unique(clrs.begin(), clrs.end());
      if (it != clrs.end()) {
        for (auto it1 = clrs.begin(); it1 != it; it1++) {
          avai[*it1] = 1;
        }
      } else {
        loops.push_back(clrs);
      }
    } else {
      p[u] = x;
      dfs(u, i);
    }
  }
  vis1[i] = 0;
}
void solve() {
  cin >> n >> m;
  avai.assign(m, 0);
  for (int i = 0; i < m; i++) {
    cin >> u >> v >> c;
    u--, v--, c--;
    adj[u].push_back(edges.size());
    adj[v].push_back(edges.size());
    edges.emplace_back(u, v, c);
  }
  fill(vis, vis + n, 0);
  fill(vis1, vis1 + n, 0);
  dfs(0, -1);
  int cr = 0;
  for (int i = 0; i < (int)edges.size(); i++) {
    if (!edges[i].in_loop) {
      avai[edges[i].c] = 1;
    }
  }
  fo.init((int)loops.size() + m + 2);
  for (int i = 0; i < (int)loops.size(); i++) {
    fo.add(0, i + 1, (int)loops[i].size() - 1);
    for (int c : loops[i]) {
      if (avai[c]) continue;
      fo.add(i + 1, (int)loops.size() + 1 + c, 1);
    }
  }
  int flow = 0;
  for (int i = 0; i < m; i++) {
    flow += avai[i];
  }
  for (int i = (int)loops.size() + 1; i <= (int)loops.size() + m; i++) {
    fo.add(i, (int)loops.size() + m + 1, 1);
  }
  flow += fo.maxflow(0, (int)loops.size() + m + 1);
  cout << flow << '\n';
}
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t = 1;
  while (t--) {
    solve();
  }
}
