// HDU5634 Rikka with Phi 线段树
// 思路:操作1的时候,判断一下当前区间是不是每个数都相等,在每个数相等的区间上操作。相当于lazy,不必更新到底。 #include <bits/stdc++.h>
using namespace std;
#define clc(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N = ;
const int MOD = 1e9+;
#define LL long long
double const pi = acos(-);
void fre() {
freopen("in.txt","r",stdin);
}
// inline int r() {
// int x=0,f=1;char ch=getchar();
// while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();}
// while(ch>='0'&&ch<='9') { x=x*10+ch-'0';ch=getchar();}return x*f;
// }
const int MAXM=1e7+;
LL euler[];
int a[N];
void Geteuler()
{
memset(euler, , sizeof(euler));
euler[] = ;
for(LL i = ; i < MAXM; i++) if(!euler[i])
for(LL j = i; j < MAXM; j += i){
if(!euler[j]) euler[j] = j;
euler[j] = euler[j] / i * (i-);
}
} struct tree{
int l,r;
LL sum,lazy;
}t[N<<]; void pushup(int rt){
t[rt].sum=t[rt<<].sum+t[rt<<|].sum;
if(t[rt<<].lazy==t[rt<<|].lazy) t[rt].lazy=t[rt<<].lazy;
else t[rt].lazy=;
} void pushdown(int rt){
if(t[rt].lazy){
t[rt<<].lazy=t[rt<<|].lazy=t[rt].lazy;
t[rt<<].sum=(t[rt<<].r-t[rt<<].l+)*t[rt<<].lazy;
t[rt<<|].sum=(t[rt<<|].r-t[rt<<|].l+)*t[rt<<|].lazy;
t[rt].lazy=;
}
}
void build(int rt,int x,int y){
t[rt].l=x;
t[rt].r=y;
if(x==y){
t[rt].sum=a[x];
t[rt].lazy=a[x];
return;
}
int mid=(x+y)>>;
build(rt<<,x,mid);
build(rt<<|,mid+,y);
pushup(rt);
} void update1(int rt,int x,int y){
if(t[rt].lazy&&t[rt].l==x&&t[rt].r==y){
t[rt].sum=(t[rt].r-t[rt].l+)*euler[t[rt].lazy];
t[rt].lazy=euler[t[rt].lazy];
return;
}
pushdown(rt);
int mid=(t[rt].l+t[rt].r)>>;
if(y<=mid) update1(rt<<,x,y);
else if(x>mid) update1(rt<<|,x,y);
else {
update1(rt<<,x,mid);
update1(rt<<|,mid+,y);
}
pushup(rt);
} void update2(int rt,int x,int y,int z){
if(t[rt].l==x&&t[rt].r==y){
t[rt].lazy=z;
t[rt].sum=1LL*z*(t[rt].r-t[rt].l+);
return;
}
pushdown(rt);
int mid=(t[rt].l+t[rt].r)>>;
if(y<=mid) update2(rt<<,x,y,z);
else if(x>mid) update2(rt<<|,x,y,z);
else {
update2(rt<<,x,mid,z);
update2(rt<<|,mid+,y,z);
}
pushup(rt);
} LL query(int rt,int x,int y){
if(t[rt].l==x&&t[rt].r==y){
return t[rt].sum;
}
pushdown(rt);
int mid=(t[rt].l+t[rt].r)>>;
if(y<=mid) return query(rt<<,x,y);
else if(x>mid) return query(rt<<|,x,y);
else{
return query(rt<<,x,mid)+query(rt<<|,mid+,y);
}
}
int main(){
// fre();
Geteuler();
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) scanf("%d",&a[i]);
build(,,n);
while(m--){
int op,x,y,z;
scanf("%d%d%d",&op,&x,&y);
if(op==) update1(,x,y);
else if(op==) {
scanf("%d",&z);
update2(,x,y,z);
}
else {
// cout<<"sdf"<<endl;
LL ans=query(,x,y);
printf("%I64d\n",ans);
}
}
}
return ;
}