#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using dbl = double;
using pii = pair<int, int>;
using pl4 = pair<ll, ll>;
using tl6 = tuple<ll, ll, ll>;
using vi = vector<int>;
using vvi = vector<vi>;
using vs = vector<string>;
using vvs = vector<vs>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vvvll = vector<vvll>;
using vpii = vector<pii>;
using vvpii = vector<vpii>;
using vpl4 = vector<pl4>;
using vvpl4 = vector<vpl4>;
using vtl6 = vector<tl6>;
using vvtl6 = vector<vtl6>;
using vd = vector<dbl>;
using vvd = vector<vd>;
const ll MOD = 1e9 + 7;
const ll INF = 1LL << 62;
const double PI = acos(-1.0);
const vll DX = {0, -1, 0, 1, 0, -1, 1, 1, -1};
const vll DY = {0, 0, -1, 0, 1, -1, -1, 1, 1};
const string alp = "abcdefghijklmnopqrstuvwxyz";
const string ALP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
ll Bset(ll a, ll b, ll c) {
  if (c)
    a |= b;
  else
    a &= ~b;
  return a;
}
template <typename valtype>
class SumV {
 public:
  ll size;
  vector<valtype> vec;
  vector<valtype> sumv;
  SumV() {}
  SumV(const SumV &sumv) {}
  SumV(ll sizeget) {
    size = sizeget;
    Init();
  }
  SumV(vector<valtype> cpyvec) {
    size = cpyvec.size();
    Init();
    vec = cpyvec;
    if (size > 0) Calc();
  }
  void Init() {
    vec.assign(size, 0);
    sumv.assign(size + 1, 0);
  }
  void Calc() {
    sumv[0] = 0;
    for (ll(i) = (0); (i) <= (ll)((ll)(size)-1); (i)++) {
      sumv[i + 1] = sumv[i] + vec[i];
    }
  }
  void Copy(vector<valtype> cpyvec) {
    size = cpyvec.size();
    Init();
    vec = cpyvec;
    if (size > 0) Calc();
  }
  valtype sum(ll a, ll b) {
    if (a < 0 || a >= size || b < 0 || b >= size || a > b) return 0;
    return (sumv[b + 1] - sumv[a]);
  }
  void set(ll a, valtype x) { vec[a] = x; }
  valtype operator[](const ll &i) const { return vec[i]; }
};
template <typename valtype>
class SumV2 {
 public:
  vll size;
  vector<vector<valtype>> vec;
  vector<vector<valtype>> sumv;
  SumV2() {}
  SumV2(const SumV2 &sumv2) {}
  SumV2(ll sizeget0, ll sizeget1) {
    size.assign(2, 0);
    size[0] = sizeget0;
    size[1] = sizeget1;
    Init();
  }
  SumV2(vector<vector<valtype>> cpyvec) {
    size.assign(2, 0);
    size[0] = cpyvec.size();
    size[1] = cpyvec[0].size();
    Init();
    vec = cpyvec;
    Calc();
  }
  void Init() {
    vec.assign(size[0], vector<valtype>(size[1], 0));
    sumv.assign(size[0] + 1, vector<valtype>(size[1] + 1, 0));
  }
  void Calc() {
    sumv[0] = vector<valtype>(size[1] + 1, 0);
    for (ll(i) = (0); (i) <= (ll)((ll)(size[0]) - 1); (i)++) {
      sumv[i][0] = 0;
      for (ll(j) = (0); (j) <= (ll)((ll)(size[1]) - 1); (j)++) {
        sumv[i + 1][j + 1] = 0;
        sumv[i + 1][j + 1] = vec[i][j] - sum(i, i, j, j);
      }
    }
  }
  void Copy(vector<vector<valtype>> cpyvec) {
    size[0] = cpyvec.size();
    size[1] = cpyvec[0].size();
    Init();
    vec = cpyvec;
    Calc();
  }
  valtype sum(ll x1, ll x2, ll y1, ll y2) {
    if (x1 < 0 || x1 >= size[0] || x2 < 0 || x2 >= size[0] || y1 < 0 ||
        y1 >= size[1] || y2 < 0 || y2 >= size[1] || x1 > x2 || y1 > y2)
      return 0;
    return (sumv[x2 + 1][y2 + 1] - sumv[x2 + 1][y1] - sumv[x1][y2 + 1] +
            sumv[x1][y1]);
  }
  void set(ll a, ll b, valtype x) { vec[a][b] = x; }
};
class UFT {
 public:
  ll tsize;
  ll mode;
  vll par;
  vll rank;
  UFT() {}
  UFT(const UFT &uft) {}
  UFT(ll tsizeget, ll modeget = 0) {
    tsize = tsizeget;
    mode = modeget;
    par.assign(tsize, -1);
    if (!mode) rank.resize(tsize, 0);
  }
  ll root(ll x) { return par[x] < 0 ? x : par[x] = root(par[x]); }
  bool isRoot(ll x) { return x == root(x); }
  bool same(ll x, ll y) { return root(x) == root(y); }
  void merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if (x == y) return;
    if (mode) {
      par[x] += par[y];
      par[y] = x;
    } else {
      if (rank[x] < rank[y]) {
        par[y] += par[x];
        par[x] = y;
      } else {
        par[x] += par[y];
        par[y] = x;
        if (rank[x] == rank[y]) rank[x]++;
      }
    }
  }
  ll size(ll x) { return -par[root(x)]; }
};
class pUFT {
 public:
  ll tsize;
  ll now;
  vll par;
  vll rank;
  vll mtime;
  vvll sizepi;
  vvll sizepv;
  pUFT() {}
  pUFT(const pUFT &puft) {}
  pUFT(ll tsizeget) {
    tsize = tsizeget;
    now = 0;
    par.assign(tsize, -1);
    rank.assign(tsize, 0);
    mtime.assign(tsize, INF);
    sizepi.assign(tsize, {0});
    sizepv.assign(tsize, {1});
  }
  ll root(ll x, ll t) { return (mtime[x] > t) ? x : root(par[x], t); }
  bool same(ll x, ll y, ll t) { return root(x, t) == root(y, t); }
  ll merge(ll x, ll y) {
    now++;
    x = root(x, now);
    y = root(y, now);
    if (x != y) {
      if (rank[x] < rank[y]) {
        par[y] += par[x];
        sizepi[y].push_back(now);
        sizepv[y].push_back(-par[y]);
        par[x] = y;
        mtime[x] = now;
      } else {
        par[x] += par[y];
        sizepi[x].push_back(now);
        sizepv[x].push_back(-par[x]);
        par[y] = x;
        mtime[y] = now;
        if (rank[x] == rank[y]) rank[x]++;
      }
    }
    return now;
  }
  ll size(ll x, ll t) {
    x = root(x, t);
    return sizepv[x][(upper_bound((sizepi[x]).begin(), (sizepi[x]).end(), (t)) -
                      (sizepi[x]).begin()) -
                     1];
  }
};
template <typename valtype>
class SegT {
 public:
  ll size;
  vector<valtype> v;
  valtype initv;
  function<valtype(valtype x, valtype y)> calc;
  SegT() {}
  SegT(const SegT &segt) {}
  SegT(ll sizeget, ll modeget = 0) {
    sizeset(sizeget);
    modeset(modeget);
    init();
  }
  SegT(vector<valtype> cpyvec, ll modeget = 0) {
    sizeset(cpyvec.size());
    modeset(modeget);
    init();
    copy(cpyvec);
  }
  SegT(ll sizeget, valtype initvget,
       function<valtype(valtype x, valtype y)> calcget) {
    sizeset(sizeget);
    initv = initvget;
    calc = calcget;
    init();
  }
  SegT(vector<valtype> cpyvec, valtype initvget,
       function<valtype(valtype x, valtype y)> calcget) {
    sizeset(cpyvec.size());
    initv = initvget;
    calc = calcget;
    init();
    copy(cpyvec);
  }
  void sizeset(ll rsize) {
    size = ((llabs(rsize) == 0) ? (1) : ((ll)(log2(double(llabs(rsize)))) + 1));
    if (rsize == ((ll)(1) << (size - 1))) size--;
    return;
  }
  void modeset(ll mode) {
    switch (mode) {
      case 0:
        initv = 0;
        calc = [](valtype x, valtype y) { return x + y; };
        break;
      case 1:
        initv = INF;
        calc = [](valtype x, valtype y) { return ((x < y) ? (x) : (y)); };
        break;
      case 2:
        initv = -INF;
        calc = [](valtype x, valtype y) { return ((x > y) ? (x) : (y)); };
        break;
    }
    return;
  }
  void init() { v.assign(((ll)(1) << (size + 1)) - 1, initv); }
  void copy(vector<valtype> cpyvec) {
    for (ll(i) = (0); (i) <= (ll)((ll)(((cpyvec.size() < ((ll)(1) << (size)))
                                            ? (cpyvec.size())
                                            : (((ll)(1) << (size))))) -
                                  1);
         (i)++)
      set(i, cpyvec[i]);
  }
  ll i2v(ll i) const {
    if (i < 0 || i >= ((ll)(1) << (size))) return -1;
    return ((ll)(1) << (size)) + i - 1;
  }
  ll top(ll i) const {
    if (i == 0) return -1;
    return (i - 1) / 2;
  }
  pl4 bot(ll i) const {
    if (i + 1 >= ((ll)(1) << (size))) return make_pair(-1, -1);
    return make_pair(2 * i + 1, 2 * i + 2);
  }
  void set(ll i, valtype x) {
    i = i2v(i);
    v[i] = x;
    while (i > 0) {
      i = top(i);
      v[i] = calc(v[bot(i).first], v[bot(i).second]);
    }
    return;
  }
  void add(ll i, valtype x) {
    set(i, v[i2v(i)] + x);
    return;
  }
  valtype operator[](const ll &i) const { return v[i2v(i)]; }
  valtype que(ll a, ll b) {
    if (a == b) return v[i2v(a)];
    if (a > b) return initv;
    valtype ans = initv;
    ll ai = i2v(a);
    ll bi = i2v(b);
    for (ll i = (1); i <= (ll)(size + 1); i++) {
      if (a > b) break;
      if (a % ((ll)(1) << (i))) {
        ans = calc(ans, v[ai]);
        a += ((ll)(1) << (i - 1));
        ai = top(ai) + 1;
      } else {
        ai = top(ai);
      }
      if (a > b) break;
      if ((b + 1) % ((ll)(1) << (i))) {
        ans = calc(ans, v[bi]);
        b -= ((ll)(1) << (i - 1));
        bi = top(bi) - 1;
      } else {
        bi = top(bi);
      }
      if (a > b) break;
    }
    return ans;
  }
  valtype que(ll b) { return que(0, b); }
  valtype que() { return que(0, ((ll)(1) << (size)) - 1); }
};
template <typename lentype>
class Graph {
 public:
  ll size;
  ll mode;
  ll mapmode;
  lentype zlen;
  lentype ilen;
  vector<map<ll, ll>> v2n;
  vvll n2v;
  vector<vector<lentype>> Edge;
  vector<pair<lentype, ll>> Primresult;
  Graph() {}
  Graph(const Graph &graph) {}
  Graph(ll sizeget = 0, ll mapmodeget = 0, ll modeget = 0, lentype zlenget = 0,
        lentype ilenget = INF) {
    size = sizeget;
    mapmode = mapmodeget;
    mode = modeget;
    zlen = zlenget;
    ilen = ilenget;
    init();
  }
  void init() {
    Edge.resize(size);
    v2n.resize(size);
    n2v.resize(size);
    Primresult.assign(size, make_pair(ilen, -1));
  }
  lentype lenplus(lentype l, lentype r) { return l + r; }
  lentype lenequal(lentype l, lentype r) { return l == r; }
  lentype lenlcr(lentype l, lentype r) { return l < r; }
  ll addV(ll vs) {
    size += vs;
    init();
    return size;
  }
  void caddE(ll x, ll y, lentype c) {
    if (mapmode) v2n[x][y] = Edge[x].size();
    Edge[x].push_back(c);
    n2v[x].push_back(y);
  }
  void csetE(ll x, ll y, lentype c) {
    if (mapmode) Edge[x][v2n[x][y]] = c;
  }
  void cersE(ll x, ll y) {
    if (mapmode) {
      ll n = v2n[x][y];
      Edge[x][n] = ilen;
      n2v[x][n] = -1;
      v2n[x].erase(y);
    }
  }
  void addE(ll x, ll y, lentype c) {
    caddE(x, y, c);
    if (!mode) caddE(y, x, c);
  }
  void setE(ll x, ll y, lentype c) {
    csetE(x, y, c);
    if (!mode) csetE(y, x, c);
  }
  void ersE(ll x, ll y, lentype c) {
    cersE(x, y, c);
    if (!mode) cersE(y, x, c);
  }
  lentype getE(ll x, ll y) {
    if (mapmode) {
      if (v2n[x].count(y)) {
        return Edge[x][v2n[x][y]];
      }
    }
    return ilen;
  }
  ll getVsz(ll x) { return Edge[x].size(); }
  pair<lentype, ll> getV(ll x, ll n) {
    if (n >= getVsz(x)) return make_pair(ilen, -1);
    return make_pair(Edge[x][n], n2v[x][n]);
  }
  vector<pair<lentype, vll>> Dijk(ll x) {
    vector<pair<lentype, vll>> result(size);
    for (ll(i) = (0); (i) <= (ll)((ll)(size)-1); (i)++) {
      result[i].first = ilen;
      result[i].second = {-1};
    }
    vll stat(size, 0);
    pair<lentype, ll> now;
    pair<lentype, ll> nowlv;
    SegT<pair<lentype, ll>> Q(size, make_pair(ilen, -1),
                              [](pair<lentype, ll> l, pair<lentype, ll> r) {
                                if (l.second == -1) return r;
                                if (r.second == -1) return l;
                                return l.first < r.first ? l : r;
                              });
    Q.set(x, make_pair(zlen, x));
    result[x].first = zlen;
    result[x].second = {-1};
    while (Q.que(0, size - 1).second != -1) {
      now = Q.que(0, size - 1);
      Q.set(now.second, make_pair(ilen, -1));
      stat[now.second] = 1;
      for (ll(i) = (0); (i) <= (ll)((ll)(getVsz(now.second)) - 1); (i)++) {
        nowlv = getV(now.second, i);
        if (stat[nowlv.second]) continue;
        if (Q[nowlv.second].second == -1) {
          result[nowlv.second].first =
              lenplus(result[now.second].first, nowlv.first);
          result[nowlv.second].second = {now.second};
          Q.set(nowlv.second,
                make_pair(result[nowlv.second].first, nowlv.second));
        } else {
          if (lenlcr(lenplus(result[now.second].first, nowlv.first),
                     result[nowlv.second].first)) {
            result[nowlv.second].first =
                lenplus(result[now.second].first, nowlv.first);
            result[nowlv.second].second = {now.second};
            Q.set(nowlv.second,
                  make_pair(result[nowlv.second].first, nowlv.second));
          } else if (lenequal(lenplus(result[now.second].first, nowlv.first),
                              result[nowlv.second].first)) {
            result[nowlv.second].second.push_back(now.second);
          }
        }
      }
    }
    return result;
  }
  lentype Prim(ll x = 0) {
    lentype ans = zlen;
    vll stat(size, 0);
    pair<lentype, ll> now;
    pair<lentype, ll> nowlv;
    SegT<pair<lentype, ll>> Q(size, make_pair(ilen, -1),
                              [](pair<lentype, ll> l, pair<lentype, ll> r) {
                                if (l.second == -1) return r;
                                if (r.second == -1) return l;
                                return l.first < r.first ? l : r;
                              });
    Q.set(x, make_pair(zlen, x));
    Primresult[x] = make_pair(zlen, -1);
    while (Q.que(0, size - 1).second != -1) {
      now = Q.que(0, size - 1);
      Q.set(now.second, make_pair(ilen, -1));
      stat[now.second] = 1;
      ans = lenplus(ans, Primresult[now.second].first);
      for (ll(i) = (0); (i) <= (ll)((ll)(getVsz(now.second)) - 1); (i)++) {
        nowlv = getV(now.second, i);
        if (stat[nowlv.second]) continue;
        if (Q[nowlv.second].second == -1) {
          Primresult[nowlv.second] = make_pair(nowlv.first, now.second);
          Q.set(nowlv.second,
                make_pair(Primresult[nowlv.second].first, nowlv.second));
        } else {
          if (lenlcr(nowlv.first, Primresult[nowlv.second].first)) {
            Primresult[nowlv.second] = make_pair(nowlv.first, now.second);
            Q.set(nowlv.second,
                  make_pair(Primresult[nowlv.second].first, nowlv.second));
          }
        }
      }
    }
    return ans;
  }
};
ll gcd(ll a, ll b) {
  if (b == 0) return a;
  return gcd(b, a % b);
}
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }
pl4 Bezout(ll a, ll b) {
  if (b != 0) {
    pl4 xy;
    xy = Bezout(b, a % b);
    return make_pair(xy.second, xy.first - ((a / b) * xy.second));
  }
  if (b == 0) {
    return make_pair(1, 0);
  }
}
pl4 Bez(ll a, ll b, ll c) {
  pl4 xy;
  ll x, y, z, gc;
  xy = Bezout(a, b);
  gc = gcd(a, b);
  if (c % gc != 0) return make_pair(-1, -1);
  x = xy.first * (c / gc);
  y = xy.second * (c / gc);
  if (x < 0)
    z = ((((-x) % ((b / gc))) == 0) ? ((-x) / ((b / gc)))
                                    : (((-x) / ((b / gc))) + 1));
  if (x >= 0) z = -x / (b / gc);
  x += z * (b / gc);
  y -= z * (a / gc);
  return make_pair(x, y);
}
void salv(vll v) {
  cout << ("{");
  ;
  for (ll i = (0); i <= (ll)(v.size() - 1); i++) {
    cout << (v[i]);
    ;
    if (i != v.size() - 1) cout << (",");
    ;
  }
  cout << ("}") << endl;
}
ll DigS10(ll n) {
  ll ans = 0;
  while (1) {
    ans += n % 10;
    n /= 10;
    if (!n) break;
  }
  return ans;
}
ll isP(ll n) {
  if (n <= 1) return 0;
  for (ll i = (2); i <= (ll)((ll)sqrt(n) + 1); i++) {
    if (n % i == 0) return 0;
  }
  return 1;
}
vll FactM(1, 1);
vll FactMI(1, 1);
ll PowM(ll a, ll b) {
  ll ans = 1, x = (a % MOD);
  for (ll i = (0);
       i <=
       (ll)(((llabs(b) == 0) ? (1) : ((ll)(log2(double(llabs(b)))) + 1)) - 1);
       i++) {
    if ((((b) >> (i)) & 1) == 1) ans = (ans * x) % MOD;
    if (i != (((llabs(b) == 0) ? (1) : ((ll)(log2(double(llabs(b)))) + 1)) - 1))
      x = (x * x) % MOD;
  }
  return ans;
}
ll Mod(ll a) {
  a %= MOD;
  a = (a + MOD) % MOD;
  return a;
}
ll InvM(ll a) {
  a = Mod(a);
  a = PowM(a, MOD - 2);
  return a;
}
void CFactM(ll n) {
  if (FactM.size() <= n) {
    for (ll i = (FactM.size()); i <= (ll)(n); i++) {
      FactM.push_back((FactM[i - 1] * (i % MOD)) % MOD);
    }
  }
  return;
}
void CFactMI(ll n) {
  CFactM(n);
  if (FactMI.size() < (n + 1)) FactMI.resize(n + 1, -1);
  if (FactMI[n] == -1) FactMI[n] = PowM(FactM[n], MOD - 2);
  for (ll i = (n - 1); i >= (ll)(1); i--) {
    if (FactMI[i] != -1) break;
    FactMI[i] = ((FactMI[i + 1] * ((i + 1) % MOD)) % MOD);
  }
  return;
}
ll CombM(ll n, ll k) {
  if ((n < 0) || (k < 0)) return 0;
  if (n < k) return 0;
  if (n + 1 > FactMI.size()) CFactMI(n);
  return ((((FactMI[k] * FactMI[n - k]) % MOD) * FactM[n]) % MOD);
}
ll LIS(vll v, ll m = 0) {
  if (v.size() > 0) {
    ll ans = 0;
    vll dp(v.size(), INF);
    for (ll i = (0); i <= (ll)(v.size() - 1); i++) {
      dp[m ? (upper_bound((dp).begin(), (dp).end(), (v[i])) - (dp).begin())
           : (lower_bound((dp).begin(), (dp).end(), (v[i])) - (dp).begin())] =
          v[i];
    }
    for (ll i = (0); i <= (ll)(v.size() - 1); i++) {
      if (dp[i] == INF) break;
      ans++;
    }
    return ans;
  } else {
    return 0;
  }
}
void cmprs(vll &v) {
  if (v.size() == 0) return;
  vll vv(v);
  iota((vv).begin(), (vv).end(), (0));
  sort(vv.begin(), vv.end(), [&](ll v1, ll v2) { return v[v1] < v[v2]; });
  iota((v).begin(), (v).end(), (0));
  sort(v.begin(), v.end(), [&](ll v1, ll v2) { return vv[v1] < vv[v2]; });
  return;
}
ll BblCnt(vll v) {
  cmprs(v);
  SegT<ll> b(v.size(), 0);
  ll ans = 0;
  for (ll(i) = (0); (i) <= (ll)((ll)(v.size()) - 1); (i)++) {
    ans += (i - b.que(0, v[i]));
    b.add(v[i], 1);
  }
  return ans;
}
ll Bsrch(function<bool(ll x)> f, ll mi, ll ma) {
  ll ng = mi - 1, ok = ma, mid;
  while (ok - ng > 1) {
    mid = (ng + ok) / 2;
    (f(mid) ? ok : ng) = mid;
  }
  return ok;
}
signed main() {
  ll(V);
  cin >> (V);
  ;
  ll(E);
  cin >> (E);
  ;
  vvll len(V, vll(V, INF));
  vvll u(V, vll(V, 0));
  ll a, b, c;
  for (ll(i) = (0); (i) <= (ll)((ll)(E)-1); (i)++) {
    cin >> a >> b >> c;
    a--;
    b--;
    len[a][b] = c;
    len[b][a] = c;
    u[a][b] = 1;
    u[b][a] = 1;
  }
  for (ll(i) = (0); (i) <= (ll)((ll)(V)-1); (i)++) {
    len[i][i] = 0;
  }
  vvll ma(V, vll(V, 0));
  for (ll(i) = (0); (i) <= (ll)((ll)(V)-1); (i)++) {
    for (ll(j) = (0); (j) <= (ll)((ll)(V)-1); (j)++) {
      for (ll(k) = (0); (k) <= (ll)((ll)(V)-1); (k)++) {
        if (len[i][j] + len[j][k] < len[i][k]) {
          len[i][k] = len[i][j] + len[j][k];
          ma[i][k] = u[i][k];
          ma[k][i] = u[k][i];
        }
      }
    }
  }
  ll ans = 0;
  for (ll(i) = (0); (i) <= (ll)((ll)(V)-1); (i)++) {
    for (ll(j) = (0); (j) <= (ll)((ll)(V)-1); (j)++) {
      if (i <= j) continue;
      ans += ma[i][j];
    }
  }
  cout << (ans) << endl;
  ;
}
