#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>
#include <cstring>
#define int long long
#define enter() putchar(10)
#define debug(c,que) cerr<<#c<<" = "<<c<<que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i=(st);i<=(ed);i++)cout<<arr[i]<<w;
#define speed_up() cin.tie(0),cout.tie(0)
#define endl "\n"
#define Input_Int(n,a) for(register int i=1;i<=n;i++)scanf("%d",a+i);
#define Input_Long(n,a) for(register long long i=1;i<=n;i++)scanf("%lld",a+i);
#define mst(a,k) memset(a,k,sizeof(a))
namespace Newstd
{
inline int read()
{
int x=0,k=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-')
{
k=-1;
}
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*k;
}
inline void write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9)
{
write(x/10);
}
putchar(x%10+'0');
}
}
using namespace Newstd;
using namespace std;
const int ma=1e5+5;
int a[ma];
int sum[ma<<2],add[ma<<2],mul[ma<<2];
int n,m,mod;
namespace Segment_Tree
{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
inline void pushup(int p)
{
sum[p]=(sum[ls(p)]+sum[rs(p)])%mod;
}
inline void build(int p,int l,int r)
{
mul[p]=1;
if(l==r)
{
sum[p]=a[l]%mod;
return;
}
int mid=(l+r)>>1;
Segment_Tree::build(ls(p),l,mid);
Segment_Tree::build(rs(p),mid+1,r);
Segment_Tree::pushup(p);
}
inline void pushdown(int p,int l,int r)
{
int mid=(l+r)>>1;
sum[ls(p)]=(sum[ls(p)]*mul[p]+add[p]*(mid-l+1))%mod;
sum[rs(p)]=(sum[rs(p)]*mul[p]+add[p]*(r-mid))%mod;
mul[ls(p)]=(mul[ls(p)]*mul[p])%mod;
mul[rs(p)]=(mul[rs(p)]*mul[p])%mod;
add[ls(p)]=(add[ls(p)]*mul[p]+add[p])%mod;
add[rs(p)]=(add[rs(p)]*mul[p]+add[p])%mod;
add[p]=0,mul[0]=1;
}
inline void updata(int nl,int nr,int l,int r,int p,int k,int opt)
{
if(nl<=l && r<=nr)
{
if(opt==1)
{
add[p]=(add[p]*k)%mod;
mul[p]=(mul[p]*k)%mod;
sum[p]=(sum[p]*k)%mod;
}
else
{
add[p]=(add[p]+k)%mod;
sum[p]=(sum[p]+k*(r-l+1))%mod;
}
return;
}
Segment_Tree::pushdown(p,l,r);
int mid=(l+r)>>1;
if(nl<=mid)
{
Segment_Tree::updata(nl,nr,l,mid,ls(p),k,opt);
}
if(nr>mid)
{
Segment_Tree::updata(nl,nr,mid+1,r,rs(p),k,opt);
}
Segment_Tree::pushup(p);
}
inline int query(int nl,int nr,int l,int r,int p)
{
if(nl<=l && r<=nr)
{
return sum[p];
}
Segment_Tree::pushdown(p,l,r);
int mid=(l+r)>>1,res=0;
if(nl<=mid)
{
res=(res+Segment_Tree::query(nl,nr,l,mid,ls(p)))%mod;
}
if(nr>mid)
{
res=(res+Segment_Tree::query(nl,nr,mid+1,r,rs(p)))%mod;
}
return res;
}
#undef ls
#undef rs
}
#undef int
int main(void)
{
#define int long long
n=read(),m=read(),mod=read();
Input_Long(n,a);
Segment_Tree::build(1,1,n);
while(m--)
{
int opt=read(),x=read(),y=read();
if(opt==1)
{
int k=read();
Segment_Tree::updata(x,y,1,n,1,k,opt);
}
else if(opt==2)
{
int k=read();
Segment_Tree::updata(x,y,1,n,1,k,opt);
}
else
{
printf("%lld\n",Segment_Tree::query(x,y,1,n,1));
}
}
return 0;
}