#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct tree{
int l,r;
ll v,add,mu;
#define l(x) e[x].l
#define r(x) e[x].r
#define v(x) e[x].v
#define add(x) e[x].add
#define mu(x) e[x].mu
}e[100005*4];
ll a[100005];
int n,m,mod;
int ls(int x){return x<<1;}
int rs(int x){return x<<1|1;}
void down(int x){
int mid=(l(x)+r(x))/2;
v(ls(x))=(v(ls(x))*mu(x)+add(x)*(mid-l(x)+1))%mod;
v(rs(x))=(v(rs(x))*mu(x)+add(x)*(r(x)-mid))%mod;
mu(ls(x))=(mu(ls(x))*mu(x))%mod;
mu(rs(x))=(mu(rs(x))*mu(x))%mod;
add(ls(x))=(add(ls(x))*mu(x)+add(x))%mod;
add(rs(x))=(add(rs(x))*mu(x)+add(x))%mod;
mu(x)=1;
add(x)=0;
return;
}
void update_add(int x,int l,int r,ll num){
if(l<=l(x)&&r>=r(x)){
v(x)=(v(x)+num*(r-l+1))%mod;
add(x)=(add(x)+num)%mod;
return;
}
down(x);
int mid=(l(x)+r(x))/2;
if(l<=mid)update_add(ls(x),l,r,num);
if(r>mid)update_add(rs(x),l,r,num);
v(x)=(v(ls(x))+v(rs(x)))%mod;
}
void update_mu(int x,int l,int r,ll num){
if(l<=l(x)&&r>=r(x)){
v(x)=(v(x)*num)%mod;
mu(x)=(mu(x)*num)%mod;
add(x)=(add(x)*num)%mod;
return;
}
down(x);
int mid=(l(x)+r(x))/2;
if(l<=mid)update_mu(ls(x),l,r,num);
if(r>mid)update_mu(rs(x),l,r,num);
v(x)=(v(ls(x))+v(rs(x)))%mod;
}
ll query(int x,int l,int r){
if(l<=l(x)&&r>=r(x))return v(x);
down(x);
int mid=(l(x)+r(x))/2;
ll ans=0;
if(l<=mid)ans+=query(ls(x),l,r);
if(r>mid)ans+=query(rs(x),l,r);
return ans%mod;
}
void build(int x,int l,int r){
l(x)=l,r(x)=r;
mu(x)=1,add(x)=0;
if(l==r){v(x)=a[l];return;}
int mid=(l+r)/2;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
v(x)=(v(ls(x))+v(rs(x)))%mod;
}
int main(){
scanf("%d%d%d",&n,&m,&mod);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n);
while(m--){
int chk;
scanf("%d", &chk);
int x, y;
long long k;
if(chk==1){
scanf("%d%d%lld", &x, &y, &k);
update_mu(1,x,y,k);
}
else if(chk==2){
scanf("%d%d%lld", &x, &y, &k);
update_add(1,x,y,k);
}
else{
scanf("%d%d", &x, &y);
printf("%lld\n", query(1, x, y));
}
}
}