#include <bits/stdc++.h>
using namespace std;
template <class T>
inline void upmax(T &a, T b) {
  if (a < b) a = b;
}
template <class T>
inline void upmin(T &a, T b) {
  if (a > b) a = b;
}
const int maxn = 40007;
const int maxm = 80007;
const int mod = 1000000007;
const int inf = 0x7fffffff;
const double eps = 1e-7;
typedef int arr[maxn];
typedef int adj[maxm];
inline int fcmp(double a, double b) {
  if (fabs(a - b) <= eps) return 0;
  if (a < b - eps) return -1;
  return 1;
}
inline int add(int a, int b) { return ((long long)a + b) % mod; }
inline int mul(int a, int b) { return ((long long)a * b) % mod; }
inline int dec(int a, int b) { return add(a, mod - b % mod); }
inline int Pow(int a, int b) {
  int t = 1;
  while (b) {
    if (b & 1) t = mul(t, a);
    a = mul(a, a), b >>= 1;
  }
  return t;
}
template <typename Type>
inline Type RD() {
  char c = getchar();
  Type x = 0, flag = 1;
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-')
    flag = -1;
  else
    x = c - '0';
  while (isdigit(c = getchar())) x = x * 10 + c - '0';
  return x * flag;
}
inline char rdch() {
  char c = getchar();
  while (!isalpha(c)) c = getchar();
  return c;
}
arr head;
adj to, nxt, val;
int ett = 1;
inline void ins(int u, int v, int w) {
  to[++ett] = v, nxt[ett] = head[u], val[ett] = w, head[u] = ett;
}
int n, m;
void input() {
  n = RD<int>(), m = RD<int>();
  for (int i = 1, _ = m; i <= _; i++) {
    int u = RD<int>(), v = RD<int>(), w = RD<int>();
    ins(u, v, w), ins(v, u, w);
  }
}
int S, T;
namespace dinic {
arr head, cur, dis;
adj to, nxt, cap, flow;
int ett = 1;
queue<int> Q;
inline void ins(int u, int v, int c) {
  to[++ett] = v, nxt[ett] = head[u], cap[ett] = c, head[u] = ett;
  to[++ett] = u, nxt[ett] = head[v], cap[ett] = 0, head[v] = ett;
}
bool bfs() {
  for (int i = 1, _ = T; i <= _; i++) dis[i] = 0;
  dis[S] = 1, Q.push(S);
  while (!Q.empty()) {
    int u = Q.front();
    Q.pop();
    for (int i = head[u]; i; i = nxt[i])
      if (cap[i] > flow[i]) {
        int v = to[i];
        if (!dis[v]) dis[v] = dis[u] + 1, Q.push(v);
      }
  }
  return dis[T] > 0;
}
int dfs(int u, int a) {
  if (u == T || !a) return a;
  int ret = 0, f;
  for (int &i = cur[u]; i; i = nxt[i]) {
    int v = to[i];
    if (dis[v] == dis[u] + 1 && (f = dfs(v, min(a, cap[i] - flow[i])))) {
      flow[i] += f, flow[i ^ 1] -= f, a -= f, ret += f;
      if (!a) break;
    }
  }
  return ret;
}
inline int max_flow() {
  int ret = 0;
  while (bfs()) {
    for (int i = 1, _ = T; i <= _; i++) cur[i] = head[i];
    ret += dfs(S, inf);
  }
  return ret;
}
}  // namespace dinic
arr pos, low, cnt;
int tot, dfs_clock;
void dfs(int u, int p) {
  low[u] = pos[u] = ++dfs_clock;
  static arr S;
  static int top = 0;
  for (int i = head[u]; i; i = nxt[i]) {
    int v = to[i];
    if (!pos[v]) {
      S[top++] = i, dfs(v, i), upmin(low[u], low[v]);
      if (low[v] >= pos[u]) {
        ++tot;
        for (;;) {
          int e = S[--top];
          cnt[tot]++;
          dinic::ins(tot + m, val[e], 1);
          if (to[e] == v && to[e ^ 1] == u) break;
        }
      }
    } else if (pos[v] < pos[u] && (i ^ p ^ 1))
      S[top++] = i, upmin(low[u], pos[v]);
  }
}
int ans;
void solve() {
  for (int i = 1, _ = n; i <= _; i++)
    if (!pos[i]) dfs(i, 0);
  S = m + tot + 1, T = m + tot + 2;
  for (int i = 1, _ = m; i <= _; i++) dinic::ins(i, T, 1);
  for (int i = 1, _ = tot; i <= _; i++)
    dinic::ins(S, i + m, max(cnt[i] - 1, 1));
  ans += dinic::max_flow();
  printf("%d\n", ans);
}
int main() {
  input();
  solve();
  return 0;
}
