#include <bits/stdc++.h>
using namespace std;
template <class T1, class T2>
inline bool minimize(T1& a, T2 b) {
  return b < a && (a = b, 1);
}
template <class T1, class T2>
inline bool maximize(T1& a, T2 b) {
  return a < b && (a = b, 1);
}
template <class T, class V>
istream& operator>>(istream& ist, pair<T, V>& p) {
  return ist >> p.first >> p.second;
}
template <class T>
ostream& operator<<(ostream& ost, pair<T, T>& p) {
  return ost << p.first << ", " << p.second;
}
template <class T>
istream& operator>>(istream& ist, vector<T>& vs) {
  for (auto& e : vs) ist >> e;
  return ist;
}
int const inf = 1 << 29;
template <class T>
using PQ_G = priority_queue<T, vector<T>, greater<T>>;
int main() {
  int N, M;
  cin >> N >> M;
  vector<vector<pair<int, int>>> g(N);
  for (int i = 0; i < (int)M; i++) {
    int a, b, c;
    cin >> a >> b >> c;
    g[a - 1].emplace_back(b - 1, c);
    g[b - 1].emplace_back(a - 1, c);
  }
  set<pair<int, int>> st;
  for (int i = 0; i < (int)N; i++) {
    vector<int> dist(N, inf);
    dist[i] = i;
    PQ_G<tuple<int, int, int>> pq;
    pq.emplace(0, i, -1);
    while (!pq.empty()) {
      int cost, pos, prev;
      tie(cost, pos, prev) = pq.top();
      pq.pop();
      if (dist[pos] > cost) {
        continue;
      }
      if (prev >= 0) {
        int a = prev, b = pos;
        if (a > b) swap(a, b);
        st.emplace(a, b);
      }
      for (auto e : g[pos]) {
        int to, ecost;
        tie(to, ecost) = e;
        if (minimize(dist[to], cost + ecost)) {
          pq.emplace(dist[to], to, pos);
        }
      }
    }
  }
  cout << M - (int)st.size() << "\n";
}
