#include <bits/stdc++.h>
using namespace std;
namespace iobuff {
const int LEN = 1000000;
char in[LEN + 5], out[LEN + 5];
char *pin = in, *pout = out, *ed = in, *eout = out + LEN;
inline char gc(void) {
  return pin == ed && (ed = (pin = in) + fread(in, 1, LEN, stdin), ed == in)
             ? EOF
             : *pin++;
}
template <typename T>
inline void read(T &x) {
  static int f;
  static char c;
  c = gc(), f = 1, x = 0;
  while (c < '0' || c > '9') f = (c == '-' ? -1 : 1), c = gc();
  while (c >= '0' && c <= '9') x = 10 * x + c - '0', c = gc();
  x *= f;
}
}  // namespace iobuff
using namespace iobuff;
const int N = 8e5 + 10, M = N << 1, T = 4e5 + 10;
const int inf = 0x3f3f3f3f;
struct node {
  int v, w, nxt;
} e[M];
int n, m, co, sum, cnt = 1, first[T], d[T], bel[T];
vector<int> cir[T];
inline void add(int u, int v, int w) {
  e[++cnt].v = v;
  e[cnt].w = w;
  e[cnt].nxt = first[u];
  first[u] = cnt;
}
bool vis[N], tp[T], pd[T];
int pos[N], pred[N], col[T], de[T];
inline void dfs(int u) {
  tp[u] = 1;
  vis[u] = 1;
  for (int i = first[u]; i; i = e[i].nxt) {
    if (bel[i >> 1] || i == (pos[u] ^ 1)) continue;
    int v = e[i].v;
    if (tp[v]) {
      ++sum;
      cir[sum].push_back(e[i].w);
      bel[i >> 1] = sum;
      int now = u;
      while (now != v) {
        cir[sum].push_back(e[pos[now]].w), bel[pos[now] >> 1] = sum;
        now = pred[now];
      }
    } else
      pos[v] = i, pred[v] = u, dfs(v);
  }
  tp[u] = 0;
}
int s, t;
namespace flow {
int tot, dis[N], cur[N];
struct edge {
  int v, f, nxt;
} e[M];
inline void add(int u, int v, int f) {
  e[++cnt] = (edge){v, f, first[u]};
  first[u] = cnt;
}
inline void Add(int u, int v, int f) {
  add(u, v, f);
  add(v, u, 0);
}
inline int bfs() {
  memset(dis + 1, -1, sizeof(int) * (tot));
  queue<int> q;
  dis[s] = 0;
  q.push(s);
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    for (int i = first[u]; i; i = e[i].nxt) {
      int v = e[i].v;
      if (e[i].f > 0 && dis[v] == -1) {
        dis[v] = dis[u] + 1;
        q.push(v);
        if (v == t) return 1;
      }
    }
  }
  return 0;
}
inline int dfs(int u, int f) {
  if (u == t || f == 0) return f;
  int used = 0;
  for (int &i = cur[u]; i; i = e[i].nxt) {
    if (e[i].f > 0 && dis[e[i].v] == dis[u] + 1) {
      int w = dfs(e[i].v, min(f, e[i].f));
      if (!w) continue;
      used += w;
      f -= w;
      e[i].f -= w;
      e[i ^ 1].f += w;
      if (!f) break;
    }
  }
  if (!used) dis[u] = -1;
  return used;
}
inline int dinic() {
  int f = 0;
  while (bfs()) {
    memcpy(cur, first, sizeof(int) * (tot + 1));
    f += dfs(s, inf);
  }
  return f;
}
}  // namespace flow
using flow::Add;
using flow::tot;
int main() {
  read(n);
  read(m);
  for (int i = 1, u, v, w; i <= m; ++i) {
    read(u);
    read(v);
    read(w);
    add(u, v, w);
    add(v, u, w);
    d[i] = w;
  }
  sort(d + 1, d + m + 1);
  co = unique(d + 1, d + m + 1) - d - 1;
  for (int i = 2; i <= cnt; i += 2)
    e[i].w = e[i + 1].w = lower_bound(d + 1, d + co + 1, e[i].w) - d;
  for (int i = 1; i <= n; ++i)
    if (!vis[i]) dfs(i);
  for (int i = 2; i <= cnt; i += 2)
    if (!bel[i >> 1]) pd[e[i].w] = true;
  vector<int> ve;
  for (int i = 1; i <= sum; ++i) {
    bool flag = false;
    for (int j : cir[i]) {
      col[j]++;
      if (col[j] != 1 || pd[j]) flag = true;
    }
    if (flag) {
      for (int j : cir[i]) pd[j] = true;
    } else {
      for (int j : cir[i]) de[j]++;
      ve.push_back(i);
    }
    for (int j : cir[i]) col[j]--;
  }
  for (int i = 1; i <= co; ++i) {
    if (!de[i]) continue;
    d[i] = ++tot;
  }
  int now = tot + 1;
  tot += ve.size();
  s = ++tot;
  t = ++tot;
  for (int i = 1; i <= co; ++i) {
    if (!de[i]) continue;
    if (pd[i])
      Add(s, d[i], de[i]);
    else if (de[i] > 1)
      Add(s, d[i], de[i] - 1);
  }
  for (int i = 0; i < ve.size(); ++i) {
    int u = ve[i];
    for (int j : cir[u]) Add(d[j], i + now, 1);
    Add(i + now, t, 1);
  }
  printf("%d\n", co - (ve.size() - flow::dinic()));
  return 0;
}
