#include <bits/stdc++.h>
using Pair = std::pair<int, int>;
struct Node {
  std::vector<Pair> edges;
};
int minCost[100][100];
bool hasUsed[100][100];
int main() {
  int N, M;
  std::cin >> N >> M;
  std::memset(minCost, -1, sizeof(minCost));
  for (int i = 0; i < N; ++i) minCost[i][i] = 0;
  std::vector<Node> nodes(N);
  for (int i = 0; i < M; ++i) {
    int a, b, c;
    std::cin >> a >> b >> c;
    --a, --b;
    nodes[a].edges.push_back({b, c});
    nodes[b].edges.push_back({a, c});
  }
  int useCount = 0;
  for (int s = 0; s < N; ++s) {
    for (int g = 0; g < N; ++g) {
      if (minCost[s][g] != -1) continue;
      enum { EdgeTo, Cost, Prev };
      using Tuple = std::tuple<int, int, int>;
      std::priority_queue<Tuple, std::vector<Tuple>, std::greater<Tuple>> que;
      que.push({s, 0, -1});
      while (!que.empty()) {
        const auto top = que.top();
        que.pop();
        const int edgeTo = std::get<EdgeTo>(top);
        const int cost = std::get<Cost>(top);
        const int prev = std::get<Prev>(top);
        if (minCost[s][edgeTo] != -1) continue;
        if (prev != -1) {
          if (!hasUsed[prev][edgeTo]) ++useCount;
          hasUsed[prev][edgeTo] = hasUsed[edgeTo][prev] = true;
        }
        minCost[s][edgeTo] = minCost[edgeTo][s] = cost;
        if (edgeTo == g) break;
        for (auto&& node : nodes[edgeTo].edges)
          que.push({node.first, cost + node.second, edgeTo});
      }
    }
  }
  std::cout << M - useCount - 2 << std::endl;
}
