#include <bits/stdc++.h>
class DIJKSTRA {
 private:
  long long N;
  std::vector<long long> from;
  std::vector<long long> cost;
  std::vector<std::unordered_map<long long, long long>> direction;
  long long start;
  bool directed;

 public:
  DIJKSTRA(long long N_, bool dir = false) : N(N_), directed(dir) {
    from.assign(N, std::numeric_limits<long long>::max());
    cost.assign(N, std::numeric_limits<long long>::max());
    direction.resize(N);
  }
  void push(long long i, long long j, long long x) {
    direction[i][j] = x;
    if (!directed) direction[j][i] = x;
  }
  void Reset(long long i) {
    from.assign(N, std::numeric_limits<long long>::max());
    cost.assign(N, std::numeric_limits<long long>::max());
    SetStart(i);
  }
  void SetStart(long long i) {
    start = i;
    cost[i] = 0;
    from[i] = i;
  }
  void Go() {
    std::priority_queue<std::pair<long long, long long>> pq;
    pq.push({0, start});
    while (!pq.empty()) {
      auto cur = pq.top();
      pq.pop();
      if (cost[cur.second] < (cur.first * -1)) continue;
      for (auto itr = direction[cur.second].begin();
           itr != direction[cur.second].end(); ++itr) {
        if (cost[itr->first] > cost[cur.second] + itr->second) {
          cost[itr->first] = cost[cur.second] + itr->second;
          from[itr->first] = cur.second;
          pq.push({(cost[itr->first] * -1), itr->first});
        }
      }
    }
    return;
  }
  long long get_cost(long long i) { return cost[i]; }
  std::vector<long long> get_path(long long i) {
    std::vector<long long> rtn;
    rtn.push_back(i);
    long long cur = i;
    while (from[cur] != cur) {
      rtn.push_back(from[cur]);
      cur = from[cur];
    }
    return rtn;
  }
};
int main(void) {
  int N, M;
  std::cin >> N >> M;
  DIJKSTRA d(N);
  std::vector<std::vector<long long>> memo(N);
  for (int i = 0; i < N; i++) memo[i].assign(N, 0);
  for (int i = 0; i < M; i++) {
    int a, b, c;
    std::cin >> a >> b >> c;
    d.push(a - 1, b - 1, c);
    memo[a - 1][b - 1] = 1;
  }
  int ans = 0;
  for (int i = 0; i < N; i++) {
    d.Reset(i);
    d.Go();
    for (int j = i + 1; j < N; j++) {
      auto v = d.get_path(j);
      for (int k = (v.size() - 1); k > 0; k--) {
        if (memo[v[k]][v[k - 1]] == 1) {
          memo[v[k]][v[k - 1]] = 0;
          ans++;
        }
      }
    }
  }
  std::cout << M - ans << std::endl;
  return 0;
}
