package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
const INF int64 = 2e18
func multiply(a, b int64) int64 {
if a == 0 || b == 0 {
return 0
}
if a > INF/b {
return INF
}
return a * b
}
type Edge struct {
to, id int
weight int64
}
var (
adj [][]Edge
head, pos []int
depth, parent []int
heavy []int
edgeToNode []int
initialWeight []int64
arr []int64
st []int64
timer int
n int
)
func dfs1(v, p, d int) int {
depth[v] = d
parent[v] = p
size := 1
maxSub := 0
for _, e := range adj[v] {
if e.to != p {
edgeToNode[e.id] = e.to
initialWeight[e.to] = e.weight
sub := dfs1(e.to, v, d+1)
size += sub
if sub > maxSub {
maxSub = sub
heavy[v] = e.to
}
}
}
return size
}
func dfs2(v, p, h int) {
head[v] = h
timer++
pos[v] = timer
arr[timer] = initialWeight[v]
if heavy[v] != 0 {
dfs2(heavy[v], v, h)
}
for _, e := range adj[v] {
if e.to != p && e.to != heavy[v] {
dfs2(e.to, v, e.to)
}
}
}
func build(node, l, r int) {
if l == r {
st[node] = arr[l]
return
}
mid := (l + r) / 2
build(2*node, l, mid)
build(2*node+1, mid+1, r)
st[node] = multiply(st[2*node], st[2*node+1])
}
func update(node, l, r, idx int, val int64) {
if l == r {
st[node] = val
return
}
mid := (l + r) / 2
if idx <= mid {
update(2*node, l, mid, idx, val)
} else {
update(2*node+1, mid+1, r, idx, val)
}
st[node] = multiply(st[2*node], st[2*node+1])
}
func query(node, l, r, ql, qr int) int64 {
if ql <= l && r <= qr {
return st[node]
}
mid := (l + r) / 2
res := int64(1)
if ql <= mid {
res = multiply(res, query(2*node, l, mid, ql, qr))
}
if qr > mid {
res = multiply(res, query(2*node+1, mid+1, r, ql, qr))
}
return res
}
func queryPath(u, v int) int64 {
res := int64(1)
for head[u] != head[v] {
if depth[head[u]] < depth[head[v]] {
u, v = v, u
}
res = multiply(res, query(1, 1, n, pos[head[u]], pos[u]))
u = parent[head[u]]
}
if depth[u] > depth[v] {
u, v = v, u
}
if pos[u]+1 <= pos[v] {
res = multiply(res, query(1, 1, n, pos[u]+1, pos[v]))
}
return res
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
scanner.Buffer(make([]byte, 1024*1024), 64*1024*1024)
nextInt := func() int {
scanner.Scan()
res, _ := strconv.Atoi(scanner.Text())
return res
}
nextInt64 := func() int64 {
scanner.Scan()
res, _ := strconv.ParseInt(scanner.Text(), 10, 64)
return res
}
n = nextInt()
m := nextInt()
adj = make([][]Edge, n+1)
head = make([]int, n+1)
pos = make([]int, n+1)
depth = make([]int, n+1)
parent = make([]int, n+1)
heavy = make([]int, n+1)
edgeToNode = make([]int, n)
initialWeight = make([]int64, n+1)
arr = make([]int64, n+1)
st = make([]int64, 4*n+1)
for i := 1; i < n; i++ {
u, v := nextInt(), nextInt()
w := nextInt64()
adj[u] = append(adj[u], Edge{v, i, w})
adj[v] = append(adj[v], Edge{u, i, w})
}
initialWeight[1] = 1
dfs1(1, 0, 0)
dfs2(1, 0, 1)
build(1, 1, n)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
for i := 0; i < m; i++ {
typ := nextInt()
if typ == 1 {
a, b := nextInt(), nextInt()
y := nextInt64()
prod := queryPath(a, b)
fmt.Fprintln(out, y/prod)
} else {
p := nextInt()
c := nextInt64()
update(1, 1, n, pos[edgeToNode[p]], c)
}
}
}