#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
using namespace std;
template <class T>
using V = vector<T>;
using Graph = vector<vector<int>>;
using P = pair<long long, long long>;
template <class T>
inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
template <class T>
inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
const long long INF = 1000000000000000000;
const long long max_V = 1000;
struct edge {
  long long to, cost;
};
long long N;
long long M;
long long cst[max_V][max_V];
vector<edge> G[max_V];
void dijkstra_restore(long long s, V<long long> &d, V<long long> &pr,
                      V<bool> &used) {
  d[s] = 0;
  while (true) {
    long long v = -1;
    for (int u = 0; u < N; u++) {
      if (!used[u] && (v == -1 || d[u] < d[v])) v = u;
    }
    if (v == -1) break;
    used[v] = 1;
    for (int u = 0; u < N; u++) {
      if (d[u] > d[v] + cst[v][u]) {
        d[u] = d[v] + cst[v][u];
        pr[u] = v;
      }
    }
  }
}
vector<long long> get_path(long long t, V<long long> pr) {
  vector<long long> path;
  for (; t != -1; t = pr[t]) path.push_back(t);
  reverse((path).begin(), (path).end());
  return path;
}
signed main() {
  cin >> N >> M;
  set<long long> st;
  map<P, long long> mp;
  for (int i = 0; i < M; i++) {
    long long a, b, c;
    cin >> a >> b >> c;
    a--;
    b--;
    cst[a][b] = cst[b][a] = c;
    mp[P(a, b)] = mp[P(b, a)] = i;
  }
  for (int i = 0; i < N - 1; i++) {
    for (int j = i + 1; j < N; j++) {
      V<long long> dist(N, INF), pr(N, -1);
      V<bool> used(N, 0);
      dijkstra_restore(i, dist, pr, used);
      V<long long> path = get_path(j, pr);
      long long m = path.size();
      for (int k = 0; k < m - 1; k++) {
        long long v = mp[P(path[i], path[i + 1])];
        st.insert(v);
      }
    }
  }
  cout << M - st.size() << endl;
}
