#include <bits/stdc++.h>
#pragma GCC optimize("O3")
using namespace std;
using QWORD = uint64_t;
using SQWORD = int64_t;
using DWORD = uint32_t;
using SDWORD = int32_t;
using WORD = uint16_t;
using SWORD = int16_t;
using BYTE = uint8_t;
using SBYTE = int8_t;
using DOUBLE = double;
using FLOAT = float;
static inline DOUBLE MAX(DOUBLE a, DOUBLE b) { return a > b ? a : b; }
static inline QWORD MAX(QWORD a, QWORD b) { return a > b ? a : b; }
static inline DWORD MAX(DWORD a, DWORD b) { return a > b ? a : b; }
static inline SDWORD MAX(SDWORD a, SDWORD b) { return a > b ? a : b; }
static inline DOUBLE MIN(DOUBLE a, DOUBLE b) { return a < b ? a : b; }
static inline QWORD MIN(QWORD a, QWORD b) { return a < b ? a : b; }
static inline DWORD MIN(DWORD a, DWORD b) { return a < b ? a : b; }
static inline SDWORD MIN(SDWORD a, SDWORD b) { return a < b ? a : b; }
static inline void inputStringSpSeparated(char *pcStr) {
  char *pcCur = pcStr;
  for (;;) {
    char c = getchar();
    if (('\n' == c) || (EOF == c) || (' ' == c)) {
      break;
    }
    *pcCur = c;
    pcCur++;
  }
  *pcCur = '\0';
}
static inline void inputString(char *pcStr) {
  char *pcCur = pcStr;
  for (;;) {
    char c = getchar();
    if (('\n' == c) || (EOF == c)) {
      break;
    }
    *pcCur = c;
    pcCur++;
  }
  *pcCur = '\0';
}
static inline SQWORD inputSQWORD(void) {
  SQWORD sqNumber = 0;
  SQWORD sqMultiplier = 1;
  bool bRead = false;
  for (;;) {
    char c = getchar();
    if (!bRead) {
      if ('-' == c) {
        sqMultiplier = -1;
      }
    }
    if (('0' <= c) && (c <= '9')) {
      sqNumber *= 10LL;
      sqNumber += (SQWORD)(c - '0');
      bRead = true;
    } else {
      if (bRead) {
        return sqNumber * sqMultiplier;
      }
    }
  }
}
static inline SDWORD inputSDWORD(void) {
  SDWORD lNumber = 0;
  SDWORD lMultiplier = 1;
  bool bRead = false;
  for (;;) {
    char c = getchar();
    if (!bRead) {
      if ('-' == c) {
        lMultiplier = -1;
      }
    }
    if (('0' <= c) && (c <= '9')) {
      lNumber *= 10;
      lNumber += (c - '0');
      bRead = true;
    } else {
      if (bRead) {
        return lNumber * lMultiplier;
      }
    }
  }
}
static inline DOUBLE inputFP(void) {
  DOUBLE dInt = 0.0;
  DOUBLE dFrac = 0.0;
  DOUBLE dMultiplier = 1.0;
  DWORD dwFpCnt = 0;
  DOUBLE *pdCur = &dInt;
  bool bRead = false;
  for (;;) {
    char c = getchar();
    if (!bRead) {
      if ('-' == c) {
        dMultiplier = -1;
      }
    }
    if ('.' == c) {
      pdCur = &dFrac;
    } else if (('0' <= c) && (c <= '9')) {
      (*pdCur) *= 10;
      (*pdCur) += (DOUBLE)(c - '0');
      bRead = true;
      if (pdCur == &dFrac) {
        dwFpCnt++;
      }
    } else {
      if (bRead) {
        return dMultiplier *
               (dInt + dFrac / (pow((DOUBLE)10.0, (DOUBLE)dwFpCnt)));
      }
    }
  }
}
class MODINT {
  static SQWORD MOD;
  SQWORD m_x;

 public:
  MODINT(SQWORD val) { m_x = (val % MOD + MOD) % MOD; };
  MODINT() { m_x = 0; }
  static void Init(SQWORD sqMod) { MOD = sqMod; }
  MODINT &operator+=(const MODINT a) {
    m_x = (m_x + a.m_x) % MOD;
    return *this;
  };
  MODINT &operator-=(const MODINT a) {
    m_x = (m_x - a.m_x + MOD) % MOD;
    return *this;
  };
  MODINT &operator*=(const MODINT a) {
    m_x = (m_x * a.m_x) % MOD;
    return *this;
  };
  MODINT pow(SQWORD t) const {
    if (!t) return 1;
    MODINT a = pow(t >> 1);
    a *= a;
    if (t & 1) a *= *this;
    return a;
  }
  MODINT operator+(const MODINT a) const {
    MODINT res(*this);
    return (res += a);
  }
  MODINT operator-(const MODINT a) const {
    MODINT res(*this);
    return (res -= a);
  }
  MODINT operator*(const MODINT a) const {
    MODINT res(*this);
    return (res *= a);
  }
  MODINT operator/(const MODINT a) const {
    MODINT res(*this);
    return (res /= a);
  }
  MODINT inv() const { return pow(MOD - 2); }
  MODINT &operator/=(const MODINT a) { return (*this) *= a.inv(); }
  MODINT &operator+=(const SQWORD a) {
    *this += MODINT(a);
    return *this;
  };
  MODINT &operator-=(const SQWORD a) {
    *this -= MODINT(a);
    return *this;
  };
  MODINT &operator*=(const SQWORD a) {
    *this *= MODINT(a);
    return *this;
  };
  MODINT &operator/=(const SQWORD a) {
    *this /= MODINT(a);
    return *this;
  };
  SQWORD getVal() { return m_x; };
};
SQWORD MODINT::MOD = (1000000007);
struct EDGE {
  SQWORD sqTo;
  SQWORD sqD;
  SQWORD sqIdx;
  EDGE(SQWORD t, SQWORD d, SQWORD i) : sqTo(t), sqD(d), sqIdx(i){};
};
static bool dfs(const vector<vector<EDGE>> &vvE, vector<bool> &vbIsUsed,
                vector<SQWORD> &vsqPos, SQWORD sqCur) {
  for (auto e : vvE[sqCur]) {
    if (!vbIsUsed[e.sqIdx]) {
      vbIsUsed[e.sqIdx] = true;
      SQWORD sqNextPos = vsqPos[sqCur] + e.sqD;
      if ((100100100100100100) == vsqPos[e.sqTo]) {
        vsqPos[e.sqTo] = sqNextPos;
        if (!dfs(vvE, vbIsUsed, vsqPos, e.sqTo)) {
          return false;
        }
      } else {
        if (vsqPos[e.sqTo] != sqNextPos) {
          return false;
        }
      }
    }
  }
  return true;
}
struct EDGE_ST {
  SQWORD sqTo;
  SQWORD sqCost;
  EDGE_ST(SQWORD to, SQWORD cost) {
    sqTo = to;
    sqCost = cost;
  };
};
class WarshallFloyd {
  SQWORD sqNumVertice;
  vector<vector<SQWORD>> *pvDist;
  void solve(void) {
    for (SQWORD sqK = 1; sqK <= sqNumVertice; sqK++) {
      for (SQWORD sqI = 1; sqI <= sqNumVertice; sqI++) {
        for (SQWORD sqJ = 1; sqJ <= sqNumVertice; sqJ++) {
          (*pvDist)[sqI][sqJ] = min((*pvDist)[sqI][sqJ],
                                    (*pvDist)[sqI][sqK] + (*pvDist)[sqK][sqJ]);
        }
      }
    }
  }

 public:
  WarshallFloyd(SQWORD sqV, const vector<vector<EDGE_ST>> vvE)
      : sqNumVertice(sqV) {
    pvDist = new vector<vector<SQWORD>>(
        sqNumVertice + 1,
        vector<SQWORD>(sqNumVertice + 1, (100100100100100100)));
    for (SWORD sqI = 1; sqI <= sqV; sqI++) {
      for (SQWORD sqJ = 1; sqJ <= sqNumVertice; sqJ++) {
        (*pvDist)[sqI][sqJ] = (100100100100100100);
      }
    }
    for (SQWORD sqI = 1; sqI <= sqNumVertice; sqI++) {
      for (auto v : vvE[sqI]) {
        (*pvDist)[sqI][v.sqTo] = v.sqCost;
      }
    }
    for (SQWORD sqI = 1; sqI <= sqNumVertice; sqI++) {
      (*pvDist)[sqI][sqI] = 0;
    }
    solve();
  };
  SQWORD GetDist(SQWORD sqF, SQWORD sqT) { return (*pvDist)[sqF][sqT]; }
};
struct EDGE_DEF_ST {
  SQWORD sqA;
  SQWORD sqB;
  SQWORD sqC;
};
int main(void) {
  SQWORD sqN = inputSQWORD();
  SQWORD sqM = inputSQWORD();
  vector<vector<EDGE_ST>> vvE(sqN + 1);
  vector<EDGE_DEF_ST> vEd;
  for (SQWORD sqIdx = 0; sqIdx < sqM; sqIdx++) {
    SQWORD sqA = inputSQWORD();
    SQWORD sqB = inputSQWORD();
    SQWORD sqC = inputSQWORD();
    vvE[sqA].emplace_back(sqB, sqC);
    vvE[sqB].emplace_back(sqA, sqC);
    vEd.push_back(EDGE_DEF_ST{sqA, sqB, sqC});
  }
  WarshallFloyd wf(sqN, vvE);
  SQWORD sqAns = 0;
  for (auto ed : vEd) {
    bool bOnShortestPath = false;
    for (SQWORD sqStart = 1; sqStart <= sqN; sqStart++) {
      if (wf.GetDist(sqStart, ed.sqA) + ed.sqC > wf.GetDist(sqStart, ed.sqB)) {
        bOnShortestPath = true;
      }
      if (wf.GetDist(sqStart, ed.sqB) + ed.sqC > wf.GetDist(sqStart, ed.sqA)) {
        bOnShortestPath = true;
      }
    }
    if (!bOnShortestPath) {
      sqAns++;
    }
  }
  printf("%lld\n", sqAns);
  return 0;
}
