#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
inline int read(){
int ret=0,f=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-f;
for(;c>='0'&&c<='9';c=getchar()) ret=ret*10+c-'0';
return ret*f;
}
inline long long _read(){
long long ret=0,f=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-f;
for(;c>='0'&&c<='9';c=getchar()) ret=ret*10+c-'0';
return ret*f;
}
#define ll long long
int p;
const int maxn=1e5+10;
ll a[maxn];
struct trees{
ll v,sum,add;
}tree[maxn*4];
void build(int root,int l,int r){
tree[root].sum=1;
tree[root].add=0;
if(l==r){
tree[root].v=a[1];
} else {
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
tree[root].v=tree[root<<1].v+tree[root<<1|1].v;
}
tree[root].v%=p;
return;
}
void pushdown(int root,int l,int r){
int mid=(l+r)>>1;
tree[root<<1].v=(tree[root<<1].v*tree[root].sum+tree[root].add*(mid-l+1))%p;
tree[root<<1|1].v=(tree[root<<1|1].v*tree[root].sum+tree[root].add*(r-mid))%p;
tree[root<<1].sum=(tree[root<<1].sum*tree[root].sum)%p;
tree[root<<1|1].sum=(tree[root<<1|1].sum*tree[root].sum)%p;
tree[root<<1].add=(tree[root<<1].add*tree[root].sum+tree[root].add)%p;
tree[root<<1|1].add=(tree[root<<1|1].add*tree[root].sum+tree[root].add)%p;
tree[root].sum=1;
tree[root].add=0;
return ;
}
void update1(int root,int x,int y,int l,int r,ll k){
if(r<x || y<l){
return;
}
if(l<=x && y<=r){
tree[root].v=(tree[root].v*k)%p;
tree[root].sum=(tree[root].sum*k)%p;
tree[root].add=(tree[root].add*k)%p;
return ;
}
pushdown(root,x,y);
int mid=(x+y)>>1;
update1(root<<1,x,mid,l,r,k);
update1(root<<1|1,mid+1,y,l,r,k);
tree[root].v=(tree[root<<1].v+tree[root<<1|1].v)%p;
return ;
}
void update2(int root,int x,int y,int l,int r,long long k){
if(r<x || y<l){
return ;
}
if(l<=x && y<=r){
tree[root].add=(tree[root].add+k)%p;
tree[root].v=(tree[root].v+k*(y-x+1))%p;
return ;
}
pushdown(root,x,y);
int mid=(x+y)>>1;
update1(root<<1,x,mid,l,r,k);
update1(root<<1|1,mid+1,y,l,r,k);
tree[root].v=(tree[root<<1].v+tree[root<<1|1].v)%p;
return ;
}
long long query(int root,int x,int y,int l,int r){
if(r<x || y<l){
return 0;
}
if(l<=x&&y<=r){
return tree[root].v;
}
pushdown(root,x,y);
int mid=(x+y)>>1;
return (query(root<<1,x,mid,l,r)+query(root<<1|1,mid+1,y,l,r))%p;
}
signed main(void){
int n,m;
n=read();m=read();p=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
build(1,1,n);
while(m--){
int oc,x,y;
ll k;
oc=read();
x=read();y=read();
if(oc==1){
k=_read();
update1(1,1,n,x,y,k);
} else if(oc==2){
k=_read();
update2(1,1,n,x,y,k);
} else {
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}