#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
#define maxn 100001
int n,m;
ll mod;
ll a[maxn],tree[maxn<<2],add[maxn<<2],mul[maxn<<2];
ll ls(ll p){
return p<<1;
}
ll rs(ll p){
return p<<1|1;
}
void push_up(ll p){
tree[p]=(tree[ls(p)]+tree[rs(p)]) %mod;
}
void build(ll l,ll r,ll p){
add[p]=0;
mul[p]=1;
if(l==r) {tree[p]=a[l];return;}
ll mid=(l+r)>>1;
build(l,mid,ls(p));
build(mid+1,r,rs(p));
push_up(p);
}
void f(ll l,ll r,ll p,ll kadd,ll kmul){
tree[p]=tree[p]*kmul %mod + (r-l+1)*kadd %mod;
mul[p]=(mul[p]*kmul)%mod;
add[p]=(add[p]*kmul+kadd)%mod;
}
void push_down(ll l,ll r,ll p){
ll mid=(l+r)>>1;
f(l,mid,ls(p),add[p],mul[p]);
f(mid+1,r,rs(p),add[p],mul[p]);
mul[p]=1;
add[p]=0;
return;
}
void update_add(ll nl,ll nr,ll l,ll r,ll p,ll k){
if(l>=nl && r<=nr){
tree[p]+=k*(r-l+1) %mod;
add[p]+=k %mod;
return;
}
push_down(l,r,p);
ll mid=(l+r)>>1;
if(mid>=nl) update_add(nl,nr,l,mid,ls(p),k);
if(mid<nr) update_add(nl,nr,mid+1,r,rs(p),k);
push_up(p);
}
void update_mul(ll nl,ll nr,ll l,ll r,ll p,ll k){
if(l>=nl && r<=nr){
tree[p]=tree[p]*k %mod;
mul[p]=mul[p]*k%mod;
add[p]=add[p]*k%mod;
return;
}
push_down(l,r,p);
ll mid=(l+r)>>1;
if(mid>=nl) update_mul(nl,nr,l,mid,ls(p),k);
if(mid<nr) update_mul(nl,nr,mid+1,r,rs(p),k);
push_up(p);
}
ll query(ll qx,ll qy,ll l,ll r,ll p){
ll res=0;
if(l>=qx && r<=qy) return tree[p] %mod;
push_down(l,r,p);
ll mid=(l+r)>>1;
if(mid>=qx) res+=query(qx,qy,l,mid,ls(p)) %mod;
if(mid<qy) res+=query(qx,qy,mid+1,r,rs(p)) %mod;
return res%mod;
}
int main(){
cin>>n>>m>>mod;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,n,1);
while(m--){
int pd;
cin>>pd;
ll x,y,k;
if(pd==1){
cin>>x>>y>>k;
update_mul(x,y,1,n,1,k);
}
else if(pd==2){
cin>>x>>y>>k;
update_add(x,y,1,n,1,k);
}
else if(pd==3){
cin>>x>>y;
cout<<query(x,y,1,n,1)<<endl;
}
}
return 0;
}