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 <class NumT = long long>
struct Flow {
  template <class Num = long long>
  struct Edge {
    long long a, b;
    Num w;
    Edge(long long _a, long long _b) : a(_a), b(_b), w(1) {}
    Edge(long long _a, long long _b, Num _w) : a(_a), b(_b), w(_w) {}
  };
  long long size, source, sink;
  vector<vector<NumT> > adj;
  vector<Edge<NumT> > e;
  vector<bool> visited;
  vector<long long> dist;
  vector<long long> used;
  Flow(long long sz) : size(sz) {
    adj = vector<vector<NumT> >(sz);
    source = sz - 2, sink = sz - 1;
  }
  void setup(const long long s, const long long t) {
    source = s;
    sink = t;
  }
  void add_edge(const long long u, const long long v, const NumT weight) {
    adj[u].push_back(e.size());
    e.push_back(Edge<NumT>(u, v, weight));
    adj[v].push_back(e.size());
    e.push_back(Edge<NumT>(v, u, 0));
  }
  bool layered_bfs() {
    dist.assign(size, -1);
    dist[source] = 0;
    vector<long long> q;
    q.push_back(source);
    for (long long i = 0; i < q.size(); i++) {
      if (dist[sink] != -1) break;
      long long u = q[i];
      for (long long x : adj[u]) {
        if (dist[e[x].b] == -1 && e[x].w > 0) {
          dist[e[x].b] = dist[u] + 1;
          q.push_back(e[x].b);
        }
      }
    }
    return dist[sink] != -1;
  }
  NumT augmenting(const long long u, const NumT bottle) {
    if (!bottle) return 0;
    if (u == sink) return bottle;
    for (long long& i = used[u]; i < adj[u].size(); i++) {
      long long x = adj[u][i];
      if (dist[e[x].b] != dist[u] + 1) continue;
      NumT cf = augmenting(e[x].b, min(bottle, e[x].w));
      e[x].w -= cf;
      e[x ^ 1].w += cf;
      if (cf) return cf;
    }
    return 0;
  }
  NumT blocking_flow() {
    if (!layered_bfs()) return 0;
    used.assign(size, 0);
    NumT aug, flow = 0;
    while ((aug = augmenting(source, numeric_limits<NumT>::max()))) flow += aug;
    return flow;
  }
  NumT max_flow_dinic() {
    NumT aug, flow = 0;
    while ((aug = blocking_flow())) flow += aug;
    return flow;
  }
};
const long long N = 1e6 + 10;
long long r[N];
long long find(long long x) { return x == r[x] ? x : r[x] = find(r[x]); }
long long merge(long long a, long long b) {
  a = find(a);
  b = find(b);
  if (a == b) return 0;
  r[b] = a;
  return 1;
}
struct Edge {
  long long u, v, c;
  long long other(long long x) const { return x == u ? v : u; }
};
vector<Edge> adj[N];
long long L[N], P[N], pc[N], cnt[N], vis[N];
void dfs(long long u, long long p) {
  P[u] = p;
  for (Edge& e : adj[u]) {
    long long v = e.other(u);
    if (v != p) {
      L[v] = L[u] + 1;
      pc[v] = e.c;
      dfs(v, u);
    }
  }
}
int32_t main() {
  long long n = ({
    long long x;
    scanf("%lld", &x);
    x;
  });
  long long m = ({
    long long x;
    scanf("%lld", &x);
    x;
  });
  iota(r, r + n, 0);
  vector<Edge> br;
  set<long long> brid;
  long long ans = 0;
  for (long long i = 0; i < m; i++) {
    long long u = ({
                    long long x;
                    scanf("%lld", &x);
                    x;
                  }) -
                  1;
    long long v = ({
                    long long x;
                    scanf("%lld", &x);
                    x;
                  }) -
                  1;
    long long c = ({
                    long long x;
                    scanf("%lld", &x);
                    x;
                  }) -
                  1;
    ans += cnt[c] == 0;
    cnt[c]++;
    if (merge(u, v))
      adj[u].push_back({u, v, c}), adj[v].push_back({u, v, c});
    else
      br.push_back({u, v, c});
  }
  L[0] = 1;
  dfs(0, -1);
  Flow<long long> f(m + ((long long)(br).size()) + 5);
  long long idx = 0;
  for (Edge e : br) {
    long long u = e.u, v = e.v;
    set<long long> dis;
    dis.insert(e.c);
    long long counter = 0;
    while (u != v) {
      if (L[u] < L[v]) swap(u, v);
      vis[u] = 1;
      dis.insert(pc[u]);
      u = P[u];
      counter++;
    }
    for (long long x : dis) f.add_edge(x, m + idx, 1);
    f.add_edge(m + idx, f.sink, counter);
    idx++;
  }
  for (long long i = 1; i < n; i++)
    if (!vis[i]) brid.insert(pc[i]);
  for (long long i = 0; i < m; i++)
    if (!brid.count(i)) f.add_edge(f.source, i, 1);
  cout << ((long long)(brid).size()) + f.max_flow_dinic() << endl;
}
