//パラメータ値格納用配列
var N = 0;
var M = 0;
var A = [];
var B = [];
var C = [];

function Main(input){
  input = input.split("\n");
  inputStr = input[0].split(" ");
  N = Number.parseInt(inputStr[0]);
  M = Number.parseInt(inputStr[1]);
  //頂点と辺の情報を格納
  for(var i=1 ; i<=M ; i++){
    inputStr = input[i].split(" ");
    A.push(Number.parseInt(inputStr[0]-1));
    B.push(Number.parseInt(inputStr[1]-1));
    C.push(Number.parseInt(inputStr[2]));
  }
  //メモ用配列の定義と初期化()
  var dp = [];
  for (var i=0; i<N ; i++){
	dp[i] = [];	//2次元配列の次元追加
	for (var j=0 ; j<N ; j++){
      if (i!=j)
        dp[i].push(Number.MAX_VALUE);
      else
        dp[i].push(0);
    }
  }
  //メモ(dp)に対して現在分かっている最短経路を記録する。
  //メモは各ポイント間の2次元マップ。パラメタはA→Bだが、メモはB→Aも記録。
  for (var i=0; i < M; i++){
    if (dp[A[i]][B[i]] > C[i]){
        dp[A[i]][B[i]] = C[i];
    }
    if (dp[B[i]][A[i]] > C[i]){
        dp[B[i]][A[i]] = C[i];
    }
  }
  
  //比較対象を取っておく(値渡しが上手く行かないのでループで回して格納)
  var cp = [];
  for(var i=0 ; i<N ; i++ ){
    cp[i] = [];
    cp[i] = dp[i].slice();
  }
  
  //ダイクストラ法本体処理
  //始点・終点が明示されていない。Kは全ノードを回る為のループ
  for ( var k=0; k<N; k++){
    //以下の2つがダイクストラ法。双方ノード数N回ループ
    for ( var i=0; i<N; i++){
      for ( var j=0; j<N; j++){
        //ある経路(dp[i][j])より、別経路を通る方が早く着く場合は、その値で上書きする。
        if (dp[i][j] > dp[i][k] + dp[k][j]){
          dp[i][j] = dp[i][k] + dp[k][j];
        }
      }
    }
  }

  //console.log(cp);
  //console.log(dp);
  
  //集計処理(ここがダイクストラ法本体だった)
  var ans=M;
  for ( var i=0; i<M ; i++){
    var shortest = false;
    for (var j=0; j < N; j++){
      if (dp[j][A[i]] + C[i] == dp[j][B[i]]){
        shortest = true;
      }
    }
    if (shortest == true){ ans-- };
  }
  console.log(ans);
}
Main(require("fs").readFileSync("/dev/stdin", "utf8"));