package main
import (
"fmt"
"io"
"os"
"sort"
)
type Edge struct {
u, v, w int
}
type DSU struct {
p []int
sz []int
}
func NewDSU(n int) *DSU {
p := make([]int, n)
sz := make([]int, n)
for i := 0; i < n; i++ {
p[i] = i
sz[i] = 1
}
return &DSU{p: p, sz: sz}
}
func (d *DSU) Find(x int) int {
r := x
for d.p[r] != r {
r = d.p[r]
}
for d.p[x] != x {
px := d.p[x]
d.p[x] = r
x = px
}
return r
}
func (d *DSU) Union(a, b int) bool {
a = d.Find(a)
b = d.Find(b)
if a == b {
return false
}
if d.sz[a] < d.sz[b] {
a, b = b, a
}
d.p[b] = a
d.sz[a] += d.sz[b]
return true
}
var data []byte
var ptr int
func nextInt() int {
for ptr < len(data) && (data[ptr] < '0' || data[ptr] > '9') {
ptr++
}
v := 0
for ptr < len(data) && data[ptr] >= '0' && data[ptr] <= '9' {
v = v*10 + int(data[ptr]-'0')
ptr++
}
return v
}
func main() {
data, _ = io.ReadAll(os.Stdin)
n := nextInt()
m := nextInt()
edges := make([]Edge, m)
for i := 0; i < m; i++ {
u := nextInt() - 1
v := nextInt() - 1
w := nextInt()
edges[i] = Edge{u: u, v: v, w: w}
}
sort.Slice(edges, func(i, j int) bool {
return edges[i].w < edges[j].w
})
mainDSU := NewDSU(n)
var ans int64
for l := 0; l < m; {
r := l + 1
for r < m && edges[r].w == edges[l].w {
r++
}
mp := make(map[int]int, 2*(r-l))
as := make([]int, 0, r-l)
bs := make([]int, 0, r-l)
for i := l; i < r; i++ {
a := mainDSU.Find(edges[i].u)
b := mainDSU.Find(edges[i].v)
if a == b {
continue
}
ia, ok := mp[a]
if !ok {
ia = len(mp)
mp[a] = ia
}
ib, ok := mp[b]
if !ok {
ib = len(mp)
mp[b] = ib
}
as = append(as, ia)
bs = append(bs, ib)
}
tmpDSU := NewDSU(len(mp))
for i := 0; i < len(as); i++ {
if !tmpDSU.Union(as[i], bs[i]) {
ans++
}
}
for i := l; i < r; i++ {
mainDSU.Union(edges[i].u, edges[i].v)
}
l = r
}
fmt.Print(ans)
}