#include <bits/stdc++.h>
using namespace std;
const int INF =
    sizeof(int) == sizeof(long long) ? 0x3f3f3f3f3f3f3f3fLL : 0x3f3f3f3f;
const int MOD = (int)(1e9 + 7);
template <class T>
bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
template <class T>
bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return true;
  }
  return false;
}
using Weight = int;
class Edge {
 public:
  int s;
  int d;
  Weight w;
  Edge(int s = 0, int d = 0, Weight w = INF) : s(s), d(d), w(w){};
  bool operator<(const Edge &e) const { return w < e.w; }
};
using Edges = vector<Edge>;
using Graph = vector<Edges>;
using Array = vector<Weight>;
using Matrix = vector<Array>;
enum { WHITE, GRAY, BLACK };
vector<int> dijkstra(const Graph &g, int s, vector<Weight> &dist) {
  using State = tuple<Weight, int, int>;
  priority_queue<State, vector<State>, greater<State>> PQ;
  int n = g.size();
  vector<int> color(n, WHITE);
  vector<int> prev(n, -1);
  dist.assign(n, INF);
  dist[s] = 0;
  PQ.emplace(0, s, -1);
  color[s] = GRAY;
  while (PQ.size()) {
    Weight d;
    int v, u;
    tie(d, v, u) = PQ.top();
    PQ.pop();
    if (dist[v] < d) continue;
    color[v] = BLACK;
    prev[v] = u;
    for (auto &e : g[v]) {
      if (color[e.d] == BLACK) continue;
      if (dist[e.d] > dist[v] + e.w) {
        dist[e.d] = dist[v] + e.w;
        PQ.emplace(dist[e.d], e.d, v);
        color[e.d] = GRAY;
      }
    }
  }
  return prev;
}
pair<vector<int>, bool> bellman_ford(const Graph &g, int s,
                                     vector<Weight> &dist) {
  int n = g.size();
  vector<int> prev(n, -1);
  Edges es;
  for (int i = 0; i < n; i++)
    for (auto &e : g[i]) es.emplace_back(e);
  dist.assign(n, INF);
  dist[s] = 0;
  bool negative_cycle = false;
  for (int i = 0;; i++) {
    bool update = false;
    for (auto &e : es) {
      if (dist[e.s] != INF && dist[e.d] > dist[e.s] + e.w) {
        dist[e.d] = dist[e.s] + e.w;
        prev[e.d] = e.s;
        update = true;
      }
    }
    if (!update) break;
    if (i > n) {
      negative_cycle = true;
      break;
    }
  }
  return make_pair(prev, !negative_cycle);
}
vector<int> get_path(int s, int g, vector<int> prev) {
  vector<int> path;
  path.emplace_back(g);
  for (int i = g; i != s;) {
    if (i == -1) return vector<int>();
    path.emplace_back(i = prev[i]);
  }
  reverse(path.begin(), path.end());
  return path;
}
vector<int> get_path(int s, int g, vector<vector<int>> next) {
  vector<int> path;
  for (int i = s; i != g; i = next[i][g]) path.emplace_back(i);
  path.emplace_back(g);
  return path;
}
pair<vector<vector<int>>, bool> warshall_floyd(const Graph &g, Matrix &dist) {
  int n = g.size();
  dist.assign(n, Array(n, INF));
  for (int i = 0; i < n; i++) dist[i][i] = 0;
  for (int i = 0; i < n; i++)
    for (auto e : g[i]) dist[e.s][e.d] = min(dist[e.s][e.d], e.w);
  vector<vector<int>> next(n, vector<int>(n));
  for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) next[i][j] = j;
  for (int k = 0; k < n; k++)
    for (int i = 0; i < n; i++)
      for (int j = 0; j < n; j++) {
        if (dist[i][k] == INF || dist[k][j] == INF) continue;
        if (dist[i][j] > dist[i][k] + dist[k][j]) {
          dist[i][j] = dist[i][k] + dist[k][j];
          next[i][j] = next[i][k];
        } else if (k != i && dist[i][k] + dist[k][j] == dist[i][j])
          next[i][j] = min(next[i][j], next[i][k]);
      }
  bool negative_cycle = false;
  for (int i = 0; i < n; i++)
    if (dist[i][i] != 0) negative_cycle = true;
  return make_pair(next, !negative_cycle);
}
vector<int> topological_sort(const Graph &g) {
  int n = g.size(), k = 0;
  vector<int> ord(n), indeg(n);
  for (auto &es : g)
    for (auto &e : es) indeg[e.d]++;
  queue<int> Q;
  for (int i = 0; i < n; i++)
    if (indeg[i] == 0) Q.push(i);
  while (!Q.empty()) {
    int v = Q.front();
    Q.pop();
    ord[k++] = v;
    for (auto &e : g[v])
      if (--indeg[e.d] == 0) Q.push(e.d);
  }
  return *max_element(indeg.begin(), indeg.end()) == 0 ? ord : vector<int>();
}
Edge diameter(const Graph &g, int s = 0) {
  vector<Weight> dist;
  dijkstra(g, s, dist);
  int n = g.size(), u = -1, v = -1;
  for (int i = 0; i < n; i++)
    if (dist[i] != INF && (u == -1 || dist[i] > dist[u])) u = i;
  dijkstra(g, u, dist);
  for (int i = 0; i < n; i++)
    if (dist[i] != INF && (v == -1 || dist[i] > dist[v])) v = i;
  Weight d = dist[v];
  if (u > v) swap(u, v);
  return Edge(u, v, d);
}
signed main() {
  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].emplace_back(a, b, c);
    G[b].emplace_back(b, a, c);
  }
  Matrix dist;
  auto ret = warshall_floyd(G, dist);
  auto next = ret.first;
  set<pair<int, int>> st;
  for (int i = (0); i < (N); i++)
    for (int j = (i + 1); j < (N); j++) {
      auto path = get_path(i, j, next);
      for (int k = (1); k < (path.size()); k++) {
        int s = path[k - 1], d = path[k];
        if (s > d) swap(s, d);
        st.insert(make_pair(s, d));
      }
    }
  cout << M - st.size() << endl;
  return 0;
}
