#include <bits/stdc++.h>
using namespace std;
struct ADJ {
 public:
  vector<vector<int>> adja;
  int adjS;
  ADJ(int N) {
    adjS = N;
    adja.resize(adjS, vector<int>(adjS, INT_MAX));
  }
  void uninsert(int start, int to, int cost) {
    adja.at(start).at(to) = cost;
    adja.at(to).at(start) = cost;
  }
  void showadj() {
    for (auto i = 0; i < adjS; i++) {
      for (auto j = 0; j < adjS; j++) cout << adja.at(i).at(j) << ", ";
      cout << endl;
    }
  }
};
struct edge {
  int to, cost;
};
struct graph {
  long attsize;
  vector<vector<edge>> gra;
  vector<int> dist;
  vector<int> route;
  graph(long N) {
    attsize = N;
    gra.resize(attsize);
    dist.resize(attsize);
    route.resize(attsize);
    for (auto& itr : dist) itr = INT_MAX;
  }
  void insert(int start, int to, int cost) {
    edge ed;
    ed.to = to;
    ed.cost = cost;
    gra.at(start).push_back(ed);
    ed.to = start;
    gra.at(to).push_back(ed);
  }
  void dijkstra_route(int start) {
    for (auto& itr : dist) itr = INT_MAX;
    dist.at(start) = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>,
                   greater<pair<int, int>>>
        que;
    que.push(pair<int, int>(start, start));
    for (int i = 0; i < route.size(); i++) route.at(i) = i;
    while (!que.empty()) {
      pair<int, int> p = que.top();
      que.pop();
      if (dist.at(p.second) < p.first) continue;
      for (auto itr : gra.at(p.second)) {
        if (dist.at(itr.to) > dist.at(p.second) + itr.cost) {
          cout << p.second << ", " << itr.to << endl;
          route.at(itr.to) = p.second;
          dist.at(itr.to) = dist.at(p.second) + itr.cost;
          que.push(pair<int, int>(dist.at(itr.to), itr.to));
        }
      }
    }
  }
  void dijkstra_route2(int start) {
    for (auto& itr : dist) itr = INT_MAX >> 2;
    dist.at(start) = 0;
    for (int i = 0; i < route.size(); i++) route.at(i) = i;
    vector<int> visit(attsize, -1);
    visit.at(start) = 1;
    while (1) {
      bool donef = true;
      int cost_min = (INT_MAX >> 2);
      int fi, fj;
      for (auto i = 0; i < attsize; i++) {
        if (visit.at(i) != 1) continue;
        for (auto j = 0; j < gra.at(i).size(); j++) {
          if (visit.at(gra.at(i).at(j).to) == -1) {
            if ((dist.at(i) + gra.at(i).at(j).cost) < cost_min) {
              cost_min = (dist.at(i) + gra.at(i).at(j).cost);
              fi = i;
              fj = gra.at(i).at(j).to;
            }
            dist.at(gra.at(i).at(j).to) = (dist.at(i) + gra.at(i).at(j).cost);
          }
        }
      }
      if (cost_min != (INT_MAX >> 2)) {
        visit.at(fj) = 1;
        dist.at(fj) = cost_min;
        route.at(fj) = fi;
      }
      for (int i = 0; i < visit.size(); i++)
        if (visit.at(i) == -1) donef = false;
      if (donef) break;
    }
  }
  void showroute_run(int x, vector<int>& att) {
    if (route.at(x) == x) return;
    showroute_run(route.at(x), att);
    att.push_back(route.at(x));
    return;
  }
  void showroute(int x, vector<int>& att) {
    att.clear();
    showroute_run(x, att);
    att.push_back(x);
    return;
  }
};
int main() {
  int N, M, ina, inb, inc;
  cin >> N >> M;
  graph g(N);
  ADJ adj(N);
  long count = 0;
  vector<int> route_att;
  for (int i = 0; i < M; i++) {
    cin >> ina >> inb >> inc;
    g.insert(ina - 1, inb - 1, inc);
    adj.uninsert(ina - 1, inb - 1, inc);
  }
  for (int i = 0; i < N; i++) {
    g.dijkstra_route2(i);
    for (int j = 0; j < N; j++) {
      if (i == j) continue;
      g.showroute(j, route_att);
      for (int k = 0; k < route_att.size() - 1; k++)
        adj.uninsert(k, k + 1, INT_MAX);
    }
  }
  for (int i = 0; i < N; i++) {
    for (int j = i + 1; j < N; j++)
      if (adj.adja.at(i).at(j) != INT_MAX) count++;
  }
  cout << count << endl;
}
