#include <bits/stdc++.h>
template <class T, class U>
std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& p) {
  os << "(" << p.first << ", " << p.second << ")";
  return os;
}
using namespace std;
template <class T>
using V = vector<T>;
V<V<set<int>>> edges;
V<V<int>> dist;
int n, m;
const int INF = INT_MAX;
int edge_no(int u, int v) {
  if (u > v) swap(u, v);
  return u * n + v;
}
int get_dist(int u, int v) {
  if (u > v) swap(u, v);
  return dist[u][v];
}
void set_dist(int u, int v, int d) {
  if (u > v) swap(u, v);
  dist[u][v] = d;
}
set<int>* get_edges(int u, int v) {
  if (u > v) swap(u, v);
  return &edges[u][v];
}
int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> n >> m;
  dist = V<V<int>>(n, V<int>(n, INF));
  edges = V<V<set<int>>>(n, V<set<int>>(n, set<int>()));
  for (int i = 0; i < m; ++i) {
    int u, v, w;
    cin >> u >> v >> w;
    u--;
    v--;
    if (u > v) swap(u, v);
    dist[u][v] = w;
    edges[u][v].insert(edge_no(u, v));
  }
  for (int k = 0; k < n; ++k) {
    for (int i = 0; i < n; ++i) {
      for (int j = 0; j < n; ++j) {
        if (i == j || i == k || j == k) continue;
        int u = i, v = j;
        if (u > v) swap(u, v);
        int orig = get_dist(u, v);
        if (get_dist(u, k) == INF) continue;
        if (get_dist(k, v) == INF) continue;
        int mod = get_dist(u, k) + get_dist(k, v);
        if (orig >= mod) {
          if (orig > mod) {
            edges[u][v].clear();
          }
          auto s1 = get_edges(u, k);
          auto s2 = get_edges(k, v);
          edges[u][v].insert(s1->begin(), s1->end());
          edges[u][v].insert(s2->begin(), s2->end());
        }
      }
    }
  }
  auto all_edges = set<int>();
  for (int i = 0; i < n; ++i)
    for (int j = i + 1; j < n; ++j) {
      all_edges.insert(edges[i][j].begin(), edges[i][j].end());
    }
  cout << m - all_edges.size() << endl;
}
