#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=100005;
int n,m,p;
struct node{
int v,mul,add;
}tree[N<<2];
int a[N];
void pushdown(int i,int l,int r){
int mid=(l+r)>>1;
tree[i<<1].v=(tree[i<<1].v*tree[i].mul+tree[i].add*(mid-l+1))%p;
tree[i<<1|1].v=(tree[i<<1|1].v*tree[i].mul+tree[i].add*(r-mid))%p;
tree[i<<1].mul=(tree[i<<1].mul*tree[i].mul)%p;
tree[i<<1|1].mul=(tree[i<<1|1].mul*tree[i].mul)%p;
tree[i<<1].add=(tree[i<<1].add*tree[i].mul+tree[i].add)%p;
tree[i<<1|1].add=(tree[i<<1|1].add*tree[i].mul+tree[i].add)%p;
tree[i].mul=1;
tree[i].add=0;
}
void build(int i,int l,int r){
tree[i].mul=1;
tree[i].add=0;
if (l==r){tree[i].v=a[l];return;}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tree[i].v=(tree[i<<1].v+tree[i<<1|1].v)%p;
}
void update1(int i,int l,int r,int x,int y,int k){
if (x<=l&&y>=r){
tree[i].v=(tree[i].v*k)%p;
tree[i].mul=(tree[i].mul*k)%p;
tree[i].add=(tree[i].add*k)%p;
return;
}
pushdown(i,l,r);
int mid=(l+r)>>1;
if (x<=mid)update1(i<<1,l,mid,x,y,k);
if (y>mid)update1(i<<1|1,mid+1,r,x,y,k);
tree[i].v=(tree[i<<1].v+tree[i<<1|1].v)%p;
}
void update2(int i,int l,int r,int x,int y,int k){
if (x<=l&&y>=r){
tree[i].v=(tree[i].v+k*(r-l+1))%p;
tree[i].add=(tree[i].add+k)%p;
return;
}
pushdown(i,l,r);
int mid=(l+r)>>1;
if (x<=mid)update2(i<<1,l,mid,x,y,k);
if (y>mid)update2(i<<1|1,mid+1,r,x,y,k);
tree[i].v=(tree[i<<1].v+tree[i<<1|1].v)%p;
}
int query(int i,int l,int r,int x,int y){
if (x<=l&&y>=r)return tree[i].v;
pushdown(i,l,r);
int mid=(l+r)>>1,res=0;
if (x<=mid)res+=query(i<<1,l,mid,x,y);
if (y>mid)res+=query(i<<1|1,mid+1,r,x,y);
res%=p;
return res;
}
signed main(){
freopen("A.in","r",stdin);
scanf ("%lld %lld %lld",&n,&m,&p);
for (int i=1;i<=n;i++)scanf ("%lld",&a[i]);
while (m--){
int x,y,k,op;
scanf ("%lld",&op);
if (op==1ll){
scanf ("%lld %lld %lld",&x,&y,&k);
update1(1,1,n,x,y,k);
}
if (op==2ll){
scanf ("%lld %lld %lld",&x,&y,&k);
update2(1,1,n,x,y,k);
}
if (op==3ll){
scanf ("%lld %lld",&x,&y);
printf ("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}