#include <bits/stdc++.h>
using namespace std;
const int INF = 1 << 29;
struct Edge {
  int to;
  long long cost;
};
using Graph = vector<vector<Edge>>;
set<pair<int, int>> isUsed;
void dijkstra(const Graph &G, int s) {
  priority_queue<pair<long long, int>, vector<pair<long long, int>>,
                 greater<pair<long long, int>>>
      q;
  vector<long long> d((int)G.size(), INF);
  vector<vector<int>> path((int)G.size());
  q.push({0, s});
  d[s] = 0;
  while (!q.empty()) {
    auto cur = q.top();
    q.pop();
    long long cur_dist = cur.second;
    int v = cur.first;
    if (cur_dist > d[v]) continue;
    for (auto e : G[v]) {
      if (d[e.to] > d[v] + e.cost) {
        d[e.to] = d[v] + e.cost;
        q.push({d[v] + e.cost, e.to});
        path[e.to].clear();
        path[e.to].push_back(v);
      } else if (d[e.to] == d[v] + e.cost) {
        path[e.to].push_back(v);
      }
    }
  }
  for (int v = 0; v < ((int)G.size()); v++) {
    for (auto u : path[v]) {
      isUsed.insert(make_pair(min(u, v), max(u, v)));
    }
  }
}
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  int n, m;
  cin >> n >> m;
  Graph G(n);
  for (int i = 0; i < (m); i++) {
    int a, b, c;
    cin >> a >> b >> c;
    a--, b--;
    G[a].push_back(Edge{b, (long long)c});
    G[b].push_back(Edge{a, (long long)c});
  }
  for (int i = 0; i < (n); i++) {
    dijkstra(G, i);
  }
  cout << m - (int)isUsed.size() << endl;
  return 0;
}
