#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,m;
int p[N];
struct edge{
int a,b;
int w;
}e[N];
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int find(int x)
{
if(p[x]!=x)
p[x]=find(p[x]);
return p[x];
}
void Unit(int x,int y)
{
int px=find(x);
int py=find(y);
if(px!=py)
p[px]=py;
}
int main()
{
while(cin>>n>>m)
{
for(int i=1;i<=n;i++)
p[i]=i;
for(int i=0;i<m;i++)
cin>>e[i].a>>e[i].b>>e[i].w;
sort(e,e+m,cmp);
int ans=0;
for(int i=0;i<m;)
{
int num=0;
int j=i;
//找到边权相同的尾部坐标
while(e[i].w==e[j].w)
j++;
//从i到j都是边权最小且相同的边
for(int k=i;k<j;k++)
{
int pa=find(e[k].a);
int pb=find(e[k].b);
//统计可以选择的合法边的数量
if(pa!=pb)
num++;
}
for(int k=i;k<j;k++)
{
int pa=find(e[k].a);
int pb=find(e[k].b);
//从合法边中减去非冲突边(即可以被选入到同一个方案里,不互相冲突的边)
//比如 有两条边是相同的
//如果选择第一条之后,fx=fy
//遍历到第二条时,num--就不能进行
if(pa!=pb)
Unit(pa,pb),num--;
}
i=j;
//冲突边 = 合法边 - 非冲突边
ans+=num;
}
cout<<ans<<endl;
}
}