#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100005;
int read(){
int x=0,f=1;
char c=getchar();
while(!isdigit(c)) {
if(c=='-')
f=-1;
c=getchar();
}
while(isdigit(c))
x=x*10+c-'0',c=getchar();
return x*f;
}
vector<int> zaa[N*4];
int n,m;
int siz[N],fa[N],dep[N],son[N],id[N],top[N],a[N];
int sum[N];
int cnt=0;
struct chain{
void dfs1(int now,int father){
fa[now]=father;
siz[now]=1;
dep[now]=dep[father]+1;
son[now]=0;
for(int i=0;i<zaa[now].size();i++){
int v=zaa[now][i];
if(v==father) continue;
dfs1(v,now);
siz[now]+=siz[v];
if(siz[v]>siz[son[now]]){
son[now]=v;
}
}
}
void dfs2(int now,int tp){
id[now]=++cnt;
top[now]=tp;
if(!son[now])
return;
dfs2(son[now],tp);
for(int i=0;i<zaa[now].size();i++){
int v=zaa[now][i];
if(v==fa[now]||v==son[now])
continue;
dfs2(v,v);
}
}
int LCA(int x,int y)
{
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]])
x^=y^=x^=y;
x=fa[top[x]];
}
if(dep[x]>dep[y])
x^=y^=x^=y;
return x;
}
}Chain;
//---------------------------------------------树剖
struct XXX{
int ans,l,r,tag,sans;
};
XXX tree[N<<3];
struct ttree{
#define ls(k) ((k)<<1)
#define rs(k) ((k)<<1|1)
XXX merge(XXX x,XXX y){
XXX res;
res.tag=0;
res.ans=x.ans+y.ans;
res.sans=x.sans+y.sans;
if(x.r==y.l)
++res.ans;
res.l=x.l;
res.r=y.r;
return res;
}
void push_up(int k){
tree[k]=merge(tree[ls(k)],tree[rs(k)]);
}
void push_down(int k){
if(tree[k].tag)
{
tree[ls(k)].ans=tree[ls(k)].sans-1;
tree[rs(k)].ans=tree[rs(k)].sans-1;
tree[ls(k)].l=tree[ls(k)].r=tree[rs(k)].l=tree[rs(k)].r=tree[ls(k)].tag=tree[rs(k)].tag=tree[k].tag;
tree[k].tag=0;
}
}
void build(int k,int l,int r){
tree[k].sans=r-l+1;
tree[k].ans=0;
tree[k].tag=0;
if(l==r){
tree[k].l=tree[k].r=l;
return;
}
int mid=(l+r)>>1;
build(ls(k),l,mid);
build(rs(k),mid+1,r);
push_up(k);
}
void update(int l,int r,int L,int R,int k,int p){
if(l>=L&&r<=R){
tree[k].ans=tree[k].sans-1;
tree[k].l=tree[k].r=tree[k].tag=p;
return;
}
push_down(k);
int mid=(l+r)>>1;
if(L<=mid)
update(l,mid,L,R,ls(k),p);
if(R>mid)
update(mid+1,r,L,R,rs(k),p);
push_up(k);
}
XXX query(int l,int r,int L,int R,int k){
if(l>=L&&r<=R)
return tree[k];
push_down(k);
int mid=(l+r)>>1;
if(R<=mid)
return query(l,mid,L,R,ls(k));
if(L>mid)
return query(mid+1,r,L,R,rs(k));
return merge(query(l,mid,L,R,ls(k)),query(mid+1,r,L,R,rs(k)));
}
void _swap(XXX& a,XXX& b){
XXX p=a;
a=b;
b=p;
}
void Treechange(int x,int y,int p){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])
x^=y^=x^=y;
update(1,n,id[top[x]],id[x],1,p);
x=fa[top[x]];
}
if(dep[x]>dep[y])
x^=y^=x^=y;
update(1,n,id[x],id[y],1,p);
return;
}
int LCA(int x,int y){
while(top[x]^top[y])
{
if(dep[top[x]]<dep[top[y]])
x^=y^=x^=y;
x=fa[top[x]];
}
if(dep[x]>dep[y])
x^=y^=x^=y;
return x;
}
int Treequery(int x,int y){
XXX res11,res22;
int lca=LCA(x,y);
while(top[x]^top[lca]){
res11=merge(query(1,n,id[top[x]],id[x],1),res11);
x=fa[top[x]];
}
res11=merge(query(1,n,id[lca],id[x],1),res11);
while(top[y]^top[lca]){
res22=merge(query(1,n,id[top[y]],id[y],1),res22);
y=fa[top[y]];
}
res22=merge(query(1,n,id[lca],id[y],1),res22);
return res11.ans+res22.ans;
}
}Tree;
//-------------------------------------线段树
void init();
signed main() {
int TTTTTTT;
TTTTTTT=read();
while(TTTTTTT--){
n=read(),m=read();
init();
for(int i=1;i<n;i++) {
int xxx,yyy;
xxx=read(),yyy=read();
zaa[yyy].push_back(xxx);
zaa[xxx].push_back(yyy);
}
Chain.dfs1(1,0);
Chain.dfs2(1,1);
Tree.build(1,1,n);
int colorl=0;
for(int i=1;i<=m;i++){
int op,x,y;
op=read(),x=read(),y=read();
if(op==1)
Tree.Treechange(x,y,++colorl);
else
printf("%d\n",Tree.Treequery(x,y));
}
}
return 0;
}
void init(){
cnt=0;
for(int i=0;i<N*4;i++){
zaa[i].clear();
}
for(int i=0;i<N<<3;i++){
tree[i].ans=0;
tree[i].l=0;
tree[i].r=0;
tree[i].sans=0;
tree[i].tag=0;
}
return;
}