#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using p = pair<ll, ll>;
constexpr ll MAX_N = 100;
bool unused_edge[MAX_N][MAX_N];
ll cost[MAX_N][MAX_N];
vector<ll> G[MAX_N];
ll N, M;
struct token {
  ll pos;
  ll cost;
  vector<ll> history;
  token() {}
  token(ll pos, ll cost) : pos(pos), cost(cost) { history.push_back(pos); }
};
struct tokencomp {
  bool operator()(const token& l, const token& r) { return l.cost > r.cost; }
};
void dijkstra(ll from, ll to) {
  vector<token> least(N);
  for (ll i = 0; i < N; i++) {
    least[i] = token(i, 1e18);
  }
  priority_queue<token, vector<token>, tokencomp> que;
  que.push(least[from] = token(from, 0));
  while (!que.empty()) {
    auto tok = que.top();
    que.pop();
    for (auto g : G[tok.pos]) {
      auto t = tok;
      ll newcost = t.cost + cost[t.pos][g];
      if (newcost < least[g].cost) {
        t.pos = g;
        t.cost = newcost;
        t.history.push_back(g);
        least[g] = t;
        if (g != to) {
          que.push(t);
        }
      }
    }
  }
  auto route = least[to].history;
  for (ll i = 1; i < ll(route.size()); i++) {
    ll l = route[i - 1], r = route[i];
    unused_edge[l][r] = unused_edge[r][l] = false;
  }
}
int main(void) {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> N >> M;
  for (ll i = 0; i < M; i++) {
    ll a, b, c;
    cin >> a >> b >> c;
    a--;
    b--;
    G[a].push_back(b);
    G[b].push_back(a);
    cost[a][b] = cost[b][a] = c;
    unused_edge[a][b] = unused_edge[b][a] = true;
  }
  for (ll i = 0; i < N; i++) {
    for (ll j = 0; j < N; j++) {
      if (i != j) {
        dijkstra(i, j);
      }
    }
  }
  ll ans = 0;
  for (ll i = 0; i < N; i++) {
    for (ll j = i + 1; j < N; j++) {
      if (unused_edge[i][j]) {
        ans++;
      }
    }
  }
  cout << ans << endl;
  return 0;
}
