package main
import (
"bufio"
"os"
)
type Edge struct {
u, v, w, id int
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
data, _ := os.ReadFile("/dev/stdin")
var idx int
readInt := func() int {
for idx < len(data) && (data[idx] < '0' || data[idx] > '9') {
idx++
}
if idx >= len(data) {
return 0
}
res := 0
for idx < len(data) && data[idx] >= '0' && data[idx] <= '9' {
res = res*10 + int(data[idx]-'0')
idx++
}
return res
}
n := readInt()
if n == 0 {
return
}
m := readInt()
edges := make([]Edge, m)
for i := 0; i < m; i++ {
edges[i] = Edge{
u: readInt(),
v: readInt(),
w: readInt(),
id: i + 1,
}
}
// Radix sort or just simple sort equivalent.
// Since weights are up to 10^9, we'll use a fast custom sort for edges.
// To avoid heavy imports, we implement a quicksort.
var sortEdges func(l, r int)
sortEdges = func(l, r int) {
if l >= r {
return
}
pivot := edges[l+(r-l)/2].w
i, j := l, r
for i <= j {
for edges[i].w < pivot {
i++
}
for edges[j].w > pivot {
j--
}
if i <= j {
edges[i], edges[j] = edges[j], edges[i]
i++
j--
}
}
sortEdges(l, j)
sortEdges(i, r)
}
sortEdges(0, m-1)
parent := make([]int, n+1)
for i := 1; i <= n; i++ {
parent[i] = i
}
var find func(i int) int
find = func(i int) int {
root := i
for root != parent[root] {
root = parent[root]
}
curr := i
for curr != root {
nxt := parent[curr]
parent[curr] = root
curr = nxt
}
return root
}
adj := make([][]Edge, n+1)
nonTreeEdges := make([]Edge, 0, m-n+1)
for _, e := range edges {
pu := find(e.u)
pv := find(e.v)
if pu != pv {
parent[pu] = pv
adj[e.u] = append(adj[e.u], Edge{u: e.u, v: e.v, w: e.w, id: e.id})
adj[e.v] = append(adj[e.v], Edge{u: e.v, v: e.u, w: e.w, id: e.id})
} else {
nonTreeEdges = append(nonTreeEdges, e)
}
}
depth := make([]int, n+1)
up := make([][20]int, n+1)
mx := make([][20]int, n+1)
edgeIdToParent := make([]int, n+1)
q := make([]int, 0, n)
visited := make([]bool, n+1)
q = append(q, 1)
visited[1] = true
depth[1] = 0
for len(q) > 0 {
u := q[0]
q = q[1:]
for _, e := range adj[u] {
v := e.v
if !visited[v] {
visited[v] = true
depth[v] = depth[u] + 1
up[v][0] = u
mx[v][0] = e.w
edgeIdToParent[v] = e.id
q = append(q, v)
}
}
}
for j := 1; j < 20; j++ {
for i := 1; i <= n; i++ {
p := up[i][j-1]
if p != 0 {
up[i][j] = up[p][j-1]
mx[i][j] = max(mx[i][j-1], mx[p][j-1])
}
}
}
getLcaAndMax := func(u, v int) (int, int) {
res := 0
if depth[u] < depth[v] {
u, v = v, u
}
diff := depth[u] - depth[v]
for j := 0; j < 20; j++ {
if (diff & (1 << j)) != 0 {
res = max(res, mx[u][j])
u = up[u][j]
}
}
if u == v {
return u, res
}
for j := 19; j >= 0; j-- {
if up[u][j] != up[v][j] {
res = max(res, max(mx[u][j], mx[v][j]))
u = up[u][j]
v = up[v][j]
}
}
res = max(res, max(mx[u][0], mx[v][0]))
return up[u][0], res
}
dsuParent := make([]int, n+1)
for i := 1; i <= n; i++ {
dsuParent[i] = i
}
var dsuFind func(i int) int
dsuFind = func(i int) int {
root := i
for root != dsuParent[root] {
root = dsuParent[root]
}
curr := i
for curr != root {
nxt := dsuParent[curr]
dsuParent[curr] = root
curr = nxt
}
return root
}
ansTree := make([]int, n+1)
for i := 1; i <= n; i++ {
ansTree[i] = 1000000000
}
ans := make([]int, m+1)
for _, e := range nonTreeEdges {
lca, maxW := getLcaAndMax(e.u, e.v)
ans[e.id] = maxW
curr := dsuFind(e.u)
for depth[curr] > depth[lca] {
ansTree[curr] = e.w
dsuParent[curr] = dsuFind(up[curr][0])
curr = dsuParent[curr]
}
curr = dsuFind(e.v)
for depth[curr] > depth[lca] {
ansTree[curr] = e.w
dsuParent[curr] = dsuFind(up[curr][0])
curr = dsuParent[curr]
}
}
for i := 2; i <= n; i++ {
ans[edgeIdToParent[i]] = ansTree[i]
}
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
writeInt := func(val int) {
if val == 0 {
out.WriteByte('0')
out.WriteByte('\n')
return
}
var buf [20]byte
i := 19
for val > 0 {
buf[i] = byte(val%10 + '0')
val /= 10
i--
}
out.Write(buf[i+1:])
out.WriteByte('\n')
}
for i := 1; i <= m; i++ {
writeInt(ans[i])
}
}