#include <bits/stdc++.h>
using namespace std;
vector<vector<int> > cykle;
vector<pair<int, int> > stos;
using namespace std;
struct krawedz;
const int MAX = 1e9;
struct krawedz {
  int a, b, przeplyw, max_przeplyw;
  krawedz(int a, int b, int max_przeplyw) {
    this->a = a;
    this->b = b;
    this->przeplyw = 0;
    this->max_przeplyw = max_przeplyw;
  }
  int dokad(int skad) { return a == skad ? b : a; }
  int war_przeplywu(int skad) {
    if (a == skad) return przeplyw;
    if (b == skad) return -przeplyw;
    return 0;
  }
  void zmien_przeplyw(int skad, int ile) {
    if (a == skad)
      przeplyw += ile;
    else
      przeplyw -= ile;
  }
  bool jest_w_sieci(int skad) {
    return (skad == a && przeplyw < max_przeplyw) ||
           (skad == b && przeplyw > 0);
  }
};
void dodaj_krawedz(vector<krawedz>& krawedzie,
                   vector<vector<int> >& siec_rezydualna, int skad, int dokad,
                   int waga, int num) {
  krawedzie.push_back(krawedz(skad, dokad, waga));
  siec_rezydualna[skad].push_back(num);
  siec_rezydualna[dokad].push_back(num);
}
vector<vector<int> > bfs_graf_warstwowy(vector<vector<int> >& graf,
                                        vector<krawedz>& krawedzie, int s) {
  vector<vector<int> > warstwowy(graf.size(), vector<int>());
  queue<int> q;
  q.push(s);
  vector<int> odleglosc(graf.size(), -1);
  odleglosc[s] = 0;
  while (!q.empty()) {
    int a = q.front();
    q.pop();
    for (int i = 0; i < graf[a].size(); ++i)
      if (krawedzie[graf[a][i]].jest_w_sieci(a)) {
        int koniec = krawedzie[graf[a][i]].dokad(a);
        if (odleglosc[koniec] < 0) {
          q.push(koniec);
          odleglosc[koniec] = odleglosc[a] + 1;
        }
        if (odleglosc[koniec] == odleglosc[a] + 1)
          warstwowy[a].push_back(graf[a][i]);
      }
  }
  return warstwowy;
}
bool dfs_sciezka(int v, vector<vector<int> >& warstwowy,
                 vector<krawedz>& krawedzie, vector<int>& nast_krawedz,
                 vector<int>& poprz_krawedz, int t) {
  for (int i = nast_krawedz[v]; i < warstwowy[v].size(); ++i) {
    int koniec = krawedzie[warstwowy[v][i]].dokad(v);
    if (poprz_krawedz[koniec] < 0 &&
        krawedzie[warstwowy[v][i]].jest_w_sieci(v)) {
      poprz_krawedz[koniec] = warstwowy[v][nast_krawedz[v]];
      if (dfs_sciezka(koniec, warstwowy, krawedzie, nast_krawedz, poprz_krawedz,
                      t))
        return true;
    }
    if ((!krawedzie[warstwowy[v][i]].jest_w_sieci(v) ||
         nast_krawedz[koniec] >= warstwowy[koniec].size()) &&
        i == nast_krawedz[v])
      nast_krawedz[v]++;
  }
  if (v == t)
    return true;
  else
    return false;
}
void dinic(vector<vector<int> >& graf, vector<krawedz>& krawedzie, int s,
           int t) {
  while (true) {
    vector<vector<int> > warstwowy = bfs_graf_warstwowy(graf, krawedzie, s);
    bool niezerowy_przeplyw = false;
    vector<int> nastepna_krawedz(graf.size(), 0);
    while (true) {
      vector<int> poprzednia_krawedz(graf.size(), -1);
      if (!dfs_sciezka(s, warstwowy, krawedzie, nastepna_krawedz,
                       poprzednia_krawedz, t))
        break;
      niezerowy_przeplyw = true;
      int x = t;
      int min_sciezka = MAX;
      while (poprzednia_krawedz[x] >= 0) {
        int k = poprzednia_krawedz[x];
        int skad = krawedzie[k].dokad(x);
        if (krawedzie[k].war_przeplywu(skad) < 0)
          min_sciezka = min(min_sciezka, -krawedzie[k].war_przeplywu(skad));
        else
          min_sciezka = min(min_sciezka, krawedzie[k].max_przeplyw -
                                             krawedzie[k].war_przeplywu(skad));
        x = skad;
      }
      x = t;
      while (poprzednia_krawedz[x] >= 0) {
        int k = poprzednia_krawedz[x];
        int skad = krawedzie[k].dokad(x);
        krawedzie[k].zmien_przeplyw(skad, min_sciezka);
        x = skad;
      }
    }
    if (!niezerowy_przeplyw) break;
  }
}
void dfs_cykle_kaktusa(int v, vector<vector<pair<int, int> > >& graf,
                       vector<bool>& odwiedzone,
                       vector<bool>& odwiedzone_krawedzie) {
  odwiedzone[v] = true;
  for (int i = 0; i < graf[v].size(); ++i)
    if (!odwiedzone[graf[v][i].first]) {
      stos.push_back(make_pair(v, graf[v][i].second));
      odwiedzone_krawedzie[graf[v][i].second] = true;
      dfs_cykle_kaktusa(graf[v][i].first, graf, odwiedzone,
                        odwiedzone_krawedzie);
      stos.pop_back();
    } else if (!odwiedzone_krawedzie[graf[v][i].second]) {
      odwiedzone_krawedzie[graf[v][i].second] = true;
      vector<int> cykl;
      cykl.push_back(graf[v][i].second);
      int j = stos.size() - 1;
      while (stos[j].first != graf[v][i].first) {
        cykl.push_back(stos[j].second);
        j--;
      }
      cykl.push_back(stos[j].second);
      cykle.push_back(cykl);
    }
}
int main() {
  int n, m, i, j, a, b;
  scanf("%d%d", &n, &m);
  vector<bool> odwiedzone(n + 1, false), odwiedzone_krawedzie(m + 1, false);
  vector<int> kolor(m + 1, 0);
  vector<vector<pair<int, int> > > graf(n + 1, vector<pair<int, int> >());
  for (i = 0; i < m; ++i) {
    scanf("%d%d%d", &a, &b, &kolor[i]);
    graf[a].push_back(make_pair(b, i));
    graf[b].push_back(make_pair(a, i));
  }
  for (i = 1; i <= n; ++i)
    if (!odwiedzone[i])
      dfs_cykle_kaktusa(i, graf, odwiedzone, odwiedzone_krawedzie);
  vector<bool> krawedzie_na_cyklu(m + 1, false);
  for (i = 0; i < cykle.size(); ++i)
    for (j = 0; j < cykle[i].size(); ++j)
      krawedzie_na_cyklu[cykle[i][j]] = true;
  vector<bool> kolor_poza_cyklami(m + 1, false);
  for (i = 0; i < m; ++i)
    if (!krawedzie_na_cyklu[i]) kolor_poza_cyklami[kolor[i]] = true;
  int liczba_kolorow_poza_cyklami = 0;
  for (i = 1; i <= m; ++i)
    if (kolor_poza_cyklami[i]) liczba_kolorow_poza_cyklami++;
  vector<vector<int> > dwudzielny(cykle.size() + m + 2, vector<int>());
  vector<krawedz> dwudzielny_krawedzie;
  int num = 0;
  for (i = 1; i <= cykle.size(); ++i) {
    dodaj_krawedz(dwudzielny_krawedzie, dwudzielny, 0, i,
                  cykle[i - 1].size() - 1, num);
    num++;
  }
  for (i = 0; i < cykle.size(); ++i)
    for (j = 0; j < cykle[i].size(); ++j)
      if (!kolor_poza_cyklami[kolor[cykle[i][j]]]) {
        dodaj_krawedz(dwudzielny_krawedzie, dwudzielny, i + 1,
                      cykle.size() + kolor[cykle[i][j]], 1, num);
        num++;
      }
  for (i = 1; i <= m; ++i)
    if (!kolor_poza_cyklami[i]) {
      dodaj_krawedz(dwudzielny_krawedzie, dwudzielny, i + cykle.size(),
                    dwudzielny.size() - 1, 1, num);
      num++;
    }
  dinic(dwudzielny, dwudzielny_krawedzie, 0, dwudzielny.size() - 1);
  int max_przeplyw = 0;
  for (i = 0; i < dwudzielny_krawedzie.size(); ++i)
    max_przeplyw += dwudzielny_krawedzie[i].war_przeplywu(0);
  printf("%d", liczba_kolorow_poza_cyklami + max_przeplyw);
  return 0;
}
