#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline T fromString(const string &s) {
  T res;
  istringstream iss(s);
  iss >> res;
  return res;
};
template <typename T>
inline string toString(const T &a) {
  ostringstream oss;
  oss << a;
  return oss.str();
};
template <typename T = int>
inline void dump(vector<T> vs, bool ent = false) {
  for (int i = 0; i < (int)(vs.size()); i++)
    cout << vs[i] << (i + 1 == vs.size() ? '\n' : ' ');
  if (ent) cout << endl;
}
template <typename T = int>
inline void dump(vector<vector<T>> vs, bool ent = false) {
  for (int i = 0; i < (int)(vs.size()); i++) dump<T>(vs[i]);
  if (ent) cout << endl;
}
const int INF = 0x3f3f3f3f;
const long long INFL = 0x3f3f3f3f3f3f3f3fLL;
const double DINF = 0x3f3f3f3f;
const int DX[] = {1, 0, -1, 0};
const int DY[] = {0, -1, 0, 1};
const double EPS = 1e-12;
template <typename T = int>
inline T in() {
  T x;
  cin >> x;
  return x;
}
template <typename T = int>
inline vector<T> in(int n) {
  vector<T> xs(n);
  for (int i = 0; i < (int)(n); i++) cin >> xs[i];
  return xs;
}
template <typename T = int>
inline vector<vector<T>> in(int n, int m) {
  vector<vector<T>> xs(n, vector<T>(m));
  for (int i = 0; i < (int)(n); i++)
    for (int j = 0; j < (int)(m); j++) cin >> xs[i][j];
  return xs;
}
vector<int> dijkstra(vector<vector<pair<int, int>>> &es, int start_v) {
  typedef pair<int, int> State;
  int N = es.size();
  vector<int> dists(N, INF);
  priority_queue<State, vector<State>, greater<State>> que;
  dists[start_v] = 0;
  que.emplace(0, start_v);
  while (que.size()) {
    int cur_cost = que.top().first;
    int cur_v = que.top().second;
    que.pop();
    if (dists[cur_v] < cur_cost) continue;
    for (auto &e : es[cur_v]) {
      int next_cost = cur_cost + e.second;
      int next_v = e.first;
      if (dists[next_v] > next_cost) {
        dists[next_v] = next_cost;
        que.emplace(next_cost, next_v);
      }
    }
  }
  return dists;
}
vector<int> get_path(vector<int> &prevs, int t) {
  vector<int> path;
  for (; t != -1; t = prevs[t]) path.push_back(t);
  reverse(path.begin(), path.end());
  return path;
}
int main(void) {
  int V, E;
  cin >> V >> E;
  vector<tuple<int, int, int>> es;
  for (int i = 0; i < (int)(E); i++) {
    int a, b, c;
    cin >> a >> b >> c;
    --a;
    --b;
    es.emplace_back(a, b, c);
  }
  vector<vector<int>> dists(V, vector<int>(V, INF));
  for (auto &e : es) {
    int from, to, cost;
    tie(from, to, cost) = e;
    dists[from][to] = cost;
    dists[to][from] = cost;
  }
  for (int k = 0; k < (int)(V); k++)
    for (int i = 0; i < (int)(V); i++)
      for (int j = 0; j < (int)(V); j++) {
        if (dists[i][j] > dists[i][k] + dists[k][j]) {
          dists[i][j] = dists[i][k] + dists[k][j];
        }
      }
  vector<bool> use_es(E, true);
  for (int ei = 0; ei < (int)(E); ei++) {
    vector<vector<pair<int, int>>> es2(V);
    for (int i = 0; i < (int)(E); i++)
      if (use_es[i] && i != ei) {
        int a, b, cost;
        tie(a, b, cost) = es[i];
        es2[a].emplace_back(b, cost);
        es2[b].emplace_back(a, cost);
      }
    int a, b, cost;
    tie(a, b, cost) = es[ei];
    vector<int> dists1 = dijkstra(es2, a);
    bool use_e = false;
    for (int i = 0; i < (int)(V); i++)
      if (i != a) {
        if (dists1[i] > dists[a][i]) {
          use_e = true;
          break;
        }
      }
    if (!use_e) {
      vector<int> dists2 = dijkstra(es2, b);
      for (int i = 0; i < (int)(V); i++)
        if (i != b) {
          if (dists2[i] > dists[b][i]) {
            use_e = true;
            break;
          }
        }
    }
    use_es[ei] = use_e;
  }
  cout << count((use_es).begin(), (use_es).end(), false) << endl;
  return 0;
}
