#include <bits/stdc++.h>
using namespace std;
void Fastio() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
}
const int INF = INT_MAX;
int n, m;
vector<pair<int, int> > adj[10005];
vector<pair<int, int> > E;
int vis[10005], h[10005];
pair<int, int> p[10005];
int Color[15005];
bool In_tree[15005], Done[15005], ok[15005];
void dfs(int u) {
  vis[u] = 1;
  for (auto T : adj[u]) {
    int v = T.first, id = T.second;
    if (!vis[v]) {
      In_tree[id] = 1;
      h[v] = h[u] + 1;
      p[v] = {u, id};
      dfs(v);
    }
  }
}
vector<int> Cyc[10005];
int cyc_cnt;
struct MF {
  struct Edge {
    int u, v, f;
  };
  vector<Edge> E;
  vector<int> adj[25050];
  int S, T, Nodes;
  int dis[25050], nxt[25050];
  void add_edge(int u, int v, int cap) {
    E.push_back({u, v, cap});
    E.push_back({v, u, 0});
    adj[u].push_back(E.size() - 2);
    adj[v].push_back(E.size() - 1);
  }
  bool bfs() {
    queue<int> q;
    for (int i = 0; i < Nodes; i++) dis[i] = INF;
    dis[S] = 0;
    q.push(S);
    while (!q.empty()) {
      int u = q.front();
      q.pop();
      for (auto id : adj[u]) {
        int v = E[id].v, f = E[id].f;
        if (f and dis[v] > dis[u] + 1) {
          dis[v] = dis[u] + 1;
          q.push(v);
        }
      }
    }
    return (dis[T] < INF);
  }
  int flow_dfs(int u, int flow) {
    if (u == T or !flow) return flow;
    for (int &i = nxt[u]; i < (int)adj[u].size(); i++) {
      int id = adj[u][i];
      int v = E[id].v, f = E[id].f;
      if (dis[v] == dis[u] + 1) {
        int new_flow = flow_dfs(v, min(f, flow));
        if (new_flow) {
          E[id].f -= new_flow;
          E[id ^ 1].f += new_flow;
          return new_flow;
        }
      }
    }
    return 0;
  }
  int maxflow() {
    int ret = 0;
    while (bfs()) {
      for (int i = 0; i <= Nodes; i++) nxt[i] = 0;
      while (int flow = flow_dfs(S, INF)) ret += flow;
    }
    return ret;
  }
} Max_flow;
signed main() {
  Fastio();
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v >> Color[i];
    E.push_back({u, v});
    adj[u].push_back({v, i});
    adj[v].push_back({u, i});
  }
  p[1] = {0, 0};
  dfs(1);
  for (int i = 1; i <= n; i++) {
    vis[i] = 0;
  }
  for (int i = 1; i <= m; i++) {
    if (In_tree[i]) continue;
    cyc_cnt++;
    Cyc[cyc_cnt].push_back(i);
    Done[i] = 1;
    pair<int, int> T = E[i - 1];
    int u = T.first, v = T.second;
    if (h[u] < h[v]) swap(u, v);
    while (h[u] > h[v]) {
      Cyc[cyc_cnt].push_back(p[u].second);
      Done[p[u].second] = 1;
      u = p[u].first;
    }
  }
  Max_flow.Nodes = cyc_cnt + m + 2;
  Max_flow.S = 0;
  Max_flow.T = cyc_cnt + m + 1;
  for (int i = 1; i <= cyc_cnt; i++) {
    Max_flow.add_edge(0, i, (int)Cyc[i].size() - 1);
    vector<int> available;
    for (auto id : Cyc[i]) {
      if (!ok[Color[id]]) {
        available.push_back(Color[id]);
      }
      ok[Color[id]] = 1;
    }
    for (auto c : available) {
      Max_flow.add_edge(i, cyc_cnt + c, 1);
      ok[c] = 0;
    }
  }
  for (int i = 1; i <= m; i++) {
    if (!Done[i]) {
      Max_flow.add_edge(0, cyc_cnt + Color[i], 1);
    }
    Max_flow.add_edge(cyc_cnt + i, cyc_cnt + m + 1, 1);
  }
  cout << Max_flow.maxflow();
}
