#include <bits/stdc++.h>
using namespace std;
template <typename VertexType, typename WeightType>
class Graph {
 public:
  using vertex_type = VertexType;
  using weight_type = WeightType;
  using edge_type = std::pair<vertex_type, weight_type>;
  using edge_list_type = std::vector<edge_type>;

 public:
  Graph() : graph() {}
  ~Graph() {}
  void addEdges(const std::vector<vertex_type>& sources,
                const std::vector<vertex_type>& targets,
                const std::vector<weight_type>& weights,
                bool bidirectional = true) {
    if (sources.size() != targets.size() || sources.size() != weights.size()) {
      throw std::runtime_error("");
    }
    for (size_t i = 0; i < sources.size(); i++) {
      addEdge(sources[i], targets[i], weights[i], bidirectional);
    }
  }
  void addEdge(const vertex_type& source, const vertex_type& target,
               const weight_type& weight, bool bidirectional = true) {
    graph[source].push_back(std::make_pair(target, weight));
    if (bidirectional) {
      addEdge(target, source, weight, false);
    }
  }
  void removeEdge(const vertex_type& source, const vertex_type& target,
                  bool bidirectional = true) {
    auto iter = graph.find(source);
    if (iter != graph.end()) {
      graph.erase(iter);
    }
    if (bidirectional) {
      removeEdge(target, source, false);
    }
  }
  size_t numVertices() const { return graph.size(); }
  std::vector<vertex_type> getVertices() const {
    std::vector<vertex_type> vertices(graph.size());
    size_t i = 0;
    for (auto iter = graph.cbegin(); iter != graph.cend(); iter++, i++) {
      vertices[i] = iter->first;
    }
    return vertices;
  }
  const edge_list_type& getEdges(const vertex_type& vertex) const {
    auto iter = graph.find(vertex);
    if (iter == graph.end()) {
      static const edge_list_type NullEdgeList;
      return NullEdgeList;
    }
    return iter->second;
  }

 public:
  Graph& operator=(Graph&& other) {
    graph = std::move(other.graph);
    return *this;
  }
  Graph& operator=(const Graph& other) {
    for (auto iter = other.graph.cbegin(); iter != other.graph.cend(); iter++) {
      graph[iter->first] = iter->second;
    }
    return *this;
  }

 private:
  std::map<vertex_type, edge_list_type> graph;
};
template <typename VertexType, typename WeightType>
std::vector<VertexType> dijkstra(const Graph<VertexType, WeightType>& graph,
                                 const VertexType& source,
                                 const VertexType& target) {
  const vector<VertexType> vertices = graph.getVertices();
  const size_t numVertices = graph.numVertices();
  std::map<VertexType, WeightType> weights;
  std::map<VertexType, VertexType> pred;
  for (size_t i = 0; i < vertices.size(); i++) {
    VertexType vertex = vertices[i];
    weights[vertex] = std::numeric_limits<WeightType>::max();
    pred[vertex] = std::numeric_limits<VertexType>::max();
  }
  weights[source] = 0;
  std::queue<VertexType> q;
  q.push(source);
  while (!q.empty()) {
    VertexType u = q.front();
    q.pop();
    const auto& edges = graph.getEdges(u);
    for (auto iter = edges.cbegin(); iter != edges.cend(); iter++) {
      WeightType newWeight = weights[u] + iter->second;
      if (newWeight < weights[iter->first]) {
        q.push(iter->first);
        weights[iter->first] = newWeight;
        pred[iter->first] = u;
      }
    }
  }
  std::vector<VertexType> result;
  VertexType vertex = target;
  while (vertex != std::numeric_limits<VertexType>::max()) {
    result.push_back(vertex);
    vertex = pred[vertex];
  }
  std::reverse(result.begin(), result.end());
  return result;
}
int main() {
  int numVertex, numEdge;
  cin >> numVertex >> numEdge;
  Graph<int, int> graph;
  set<pair<int, int>> edgeSet;
  {
    vector<int> sources(numEdge);
    vector<int> targets(numEdge);
    vector<int> weights(numEdge);
    int source, target, weight;
    for (int i = 0; i < numEdge; i++) {
      cin >> source >> target >> weight;
      graph.addEdge(source, target, weight);
      if (source > target) swap(source, target);
      edgeSet.insert(make_pair(source, target));
    }
    graph.addEdges(sources, targets, weights);
  }
  auto vertices = graph.getVertices();
  for (int i = 0; i < vertices.size(); i++) {
    for (int j = i + 1; j < vertices.size(); j++) {
      vector<int> result = dijkstra(graph, vertices[i], vertices[j]);
      for (int k = 1; k < result.size(); k++) {
        int v1 = result[k - 1];
        int v2 = result[k];
        if (v1 > v2) swap(v1, v2);
        edgeSet.erase(make_pair(v1, v2));
      }
    }
  }
  cout << edgeSet.size() << endl;
  return 0;
}
