Tree is a connected undirected graph that has no cycles. Edge cactus is a connected undirected graph without loops and parallel edges, such that each edge belongs to at most one cycle.

Vasya has an edge cactus, each edge of this graph has some color.

Vasya would like to remove the minimal number of edges in such way that his cactus turned to a tree. Vasya wants to make it in such a way that there were edges of as many different colors in the resulting tree, as possible. Help him to find how many different colors can the resulting tree have.

Input

The first line contains two integers: n, m (2 ≤ n ≤ 10 000) — the number of vertices and the number of edges in Vasya's graph, respectively.

The following m lines contain three integers each: u, v, c (1 ≤ u, v ≤ n, u ≠ v, 1 ≤ c ≤ m) — the numbers of vertices connected by the corresponding edge, and its color. It is guaranteed that the described graph is indeed an edge cactus.

Output

Output one integer: the maximal number of different colors that the resulting tree can have.

Examples

Input

4 4
1 2 4
2 3 1
3 4 2
4 2 3


Output

3


Input

7 9
1 2 1
2 3 4
3 1 5
1 4 5
4 5 2
5 1 6
1 6 4
6 7 6
7 1 3


Output

6
#include <bits/stdc++.h>
using namespace std;
template <typename Arg1>
void __f(const char* name, Arg1&& arg1) {
  cerr << name << " : " << arg1 << std::endl;
}
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args) {
  const char* comma = strchr(names + 1, ',');
  cerr.write(names, comma - names) << " : " << arg1 << " | ";
  __f(comma + 1, args...);
}
template <typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& a) {
  in >> a.first >> a.second;
  return in;
}
template <typename T1, typename T2>
ostream& operator<<(ostream& out, pair<T1, T2> a) {
  out << a.first << " " << a.second;
  return out;
}
template <typename T, typename T1>
T amax(T& a, T1 b) {
  if (b > a) a = b;
  return a;
}
template <typename T, typename T1>
T amin(T& a, T1 b) {
  if (b < a) a = b;
  return a;
}
const long long INF = 1e18;
const int32_t M = 1e9 + 7;
const int32_t MM = 998244353;
const long long N = 10004;
const long long MAXV = 5 * N;
struct Dinic {
  const static bool SCALING = true;
  long long lim = 1;
  const long long INF = numeric_limits<long long>::max();
  struct edge {
    long long to, rev;
    long long cap, flow;
  };
  long long s = 0, t = MAXV - 1;
  long long level[MAXV], ptr[MAXV];
  vector<edge> adj[MAXV];
  void addEdge(long long a, long long b, long long cap,
               bool isDirected = true) {
    adj[a].push_back({b, (long long)((adj[b]).size()), cap, 0});
    adj[b].push_back(
        {a, (long long)((adj[a]).size()) - 1, isDirected ? 0 : cap, 0});
  }
  bool bfs() {
    queue<long long> q({s});
    fill(level, level + MAXV, -1);
    level[s] = 0;
    while (!q.empty() && level[t] == -1) {
      long long v = q.front();
      q.pop();
      for (auto e : adj[v]) {
        if (level[e.to] == -1 && e.flow < e.cap &&
            (!SCALING || e.cap - e.flow >= lim)) {
          q.push(e.to);
          level[e.to] = level[v] + 1;
        }
      }
    }
    return level[t] != -1;
  }
  long long dfs(long long v, long long flow) {
    if (v == t || !flow) return flow;
    for (; ptr[v] < adj[v].size(); ptr[v]++) {
      edge& e = adj[v][ptr[v]];
      if (level[e.to] != level[v] + 1) continue;
      if (long long pushed = dfs(e.to, min(flow, e.cap - e.flow))) {
        e.flow += pushed;
        adj[e.to][e.rev].flow -= pushed;
        return pushed;
      }
    }
    return 0;
  }
  long long calc() {
    long long flow = 0;
    for (lim = SCALING ? (1 << 30) : 1; lim > 0; lim >>= 1) {
      while (bfs()) {
        fill(ptr, ptr + MAXV, 0);
        while (long long pushed = dfs(s, INF)) flow += pushed;
      }
    }
    return flow;
  }
} dog;
map<pair<long long, long long>, long long> edgecol;
vector<long long> adj[N];
long long par[N], dep[N];
vector<vector<long long>> cycles;
map<pair<long long, long long>, long long> edgesincycle;
void dfs(long long v, long long p = 0) {
  par[v] = p;
  dep[v] = dep[p] + 1;
  for (auto u : adj[v]) {
    if (u == p) continue;
    if (par[u] == -1)
      dfs(u, v);
    else if (dep[u] < dep[v]) {
      long long cur = v;
      vector<long long> cycle;
      while (cur != u) {
        cycle.push_back(edgecol[{cur, par[cur]}]);
        edgesincycle[{cur, par[cur]}] = 1;
        edgesincycle[{par[cur], cur}] = 1;
        cur = par[cur];
      }
      cycle.push_back(edgecol[{u, v}]);
      edgesincycle[{u, v}] = 1;
      edgesincycle[{v, u}] = 1;
      cycles.push_back(cycle);
    }
  }
}
void solve() {
  long long n, m;
  cin >> n >> m;
  memset(par, -1, sizeof par);
  for (long long i = 0; i < m; ++i) {
    long long u, v, c;
    cin >> u >> v >> c;
    edgecol[{u, v}] = c;
    edgecol[{v, u}] = c;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  long long ans = 0;
  dfs(1);
  long long source = 0, sink = MAXV - 1;
  long long cur = 1;
  for (auto z : cycles) {
    dog.addEdge(source, cur, (long long)((z).size()) - 1);
    for (auto x : z) {
      dog.addEdge(cur, 2 * N + x, 1);
    }
    cur++;
  }
  for (long long i = 0; i < 2 * n; ++i) {
    dog.addEdge(i + 1 + 2 * N, sink, 1);
  }
  for (long long i = 1; i <= n; ++i) {
    for (auto u : adj[i]) {
      if (!edgesincycle[{u, i}]) {
        dog.addEdge(cur, edgecol[{u, i}] + 2 * N, 1);
        dog.addEdge(source, cur, 1);
        edgesincycle[{u, i}] = 1;
        edgesincycle[{i, u}] = 1;
        cur++;
      }
    }
  }
  cout << dog.calc();
}
signed main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  long long t = 1;
  while (t--) solve();
  return 0;
}
