#include <bits/stdc++.h>
using namespace std;
template <typename VertexType = int32_t, typename WeightType = int32_t>
class Graph {
 public:
  Graph() : m_Graph(), m_NumEdges(0) {}
  ~Graph() {}
  void buildGraph(const vector<VertexType>& sources,
                  const vector<VertexType>& targets,
                  const vector<WeightType>& 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 VertexType& source, const VertexType& target,
               const WeightType& weight, bool bidirectional = true) {
    m_Graph[source].push_back(make_pair(target, weight));
    m_NumEdges++;
    if (bidirectional) {
      m_Graph[target].push_back(make_pair(source, weight));
      m_NumEdges++;
    }
  }
  size_t numVertices() const { return m_Graph.size(); }
  vector<VertexType> getVertices() const {
    if (m_Graph.empty()) {
      return vector<VertexType>();
    }
    vector<VertexType> vertices(m_Graph.size());
    size_t i = 0;
    for (auto iter = m_Graph.begin(); iter != m_Graph.end(); iter++, i++) {
      vertices[i] = iter->first;
    }
    return vertices;
  }
  size_t numEdges() const { return m_NumEdges; }
  const vector<pair<VertexType, WeightType>>& getEdges(
      const VertexType& vertex) const {
    auto iter = m_Graph.find(vertex);
    if (iter == m_Graph.end()) {
      static const vector<pair<VertexType, WeightType>> NullEdgeList;
      return NullEdgeList;
    }
    return iter->second;
  }

 private:
  using EdgeType = pair<VertexType, WeightType>;
  using EdgeList = vector<EdgeType>;
  map<VertexType, EdgeList> m_Graph;
  size_t m_NumEdges;
};
template <typename VertexType, typename WeightType>
void dijkstra(const Graph<VertexType, WeightType>& graph,
              const VertexType& from, const VertexType& to,
              set<pair<VertexType, VertexType>>& edgeSet,
              vector<VertexType>& result) {
  const size_t numVertex = graph.numVertices();
  vector<WeightType> weights(numVertex);
  vector<VertexType> pred(numVertex);
  for (size_t i = 0; i < numVertex; i++) {
    weights[i] = numeric_limits<WeightType>::max();
    pred[i] = numeric_limits<VertexType>::min();
  }
  weights[from] = 0;
  queue<VertexType> q;
  q.push(from);
  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;
      }
    }
  }
  result.clear();
  VertexType vertex = to;
  while (vertex != numeric_limits<VertexType>::min()) {
    result.push_back(vertex);
    vertex = pred[vertex];
  }
  reverse(result.begin(), result.end());
}
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);
    for (int i = 0; i < numEdge; i++) {
      cin >> sources[i] >> targets[i] >> weights[i];
      edgeSet.insert(
          make_pair(min(sources[i], targets[i]), max(sources[i], targets[i])));
    }
    graph.buildGraph(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], edgeSet, result);
      for (int k = 1; k < result.size(); k++) {
        int& v1 = result[k - 1];
        int& v2 = result[k];
        edgeSet.erase(make_pair(min(v1, v2), max(v1, v2)));
      }
    }
  }
  cout << edgeSet.size() << endl;
  return 0;
}
