#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline void build(ll l, ll r, ll rt);
inline void push_up(ll rt);
inline void f1(ll l, ll r, ll rt, ll k);
inline void f2(ll l, ll r, ll rt, ll k);
inline void push_down(ll l, ll r, ll rt, ll mod);
inline void update(ll L, ll R, ll l, ll r, ll rt, ll k, ll mod);
inline ll query(ll L, ll R, ll l, ll r, ll rt);
ll a[100001];
ll n, m, p;
struct node {
ll val;
ll tag1;
ll tag2;
}tree[100001<<2];
int main() {
scanf("%lld %lld %lld", &n, &m, &p);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, n, 1);
while (m--)
{
ll mod,x,y,k;
scanf("%lld", &mod);
switch (mod) {
case 1: {
scanf("%lld %lld %lld", &x, &y, &k);
update(x, y, 1, n, 1, k, mod);
break;
}
case 2: {
scanf("%lld %lld %lld", &x, &y, &k);
update(x, y, 1, n, 1, k, mod);
break;
}
case 3: {
scanf("%lld %lld", &x, &y);
ll ans;
ans = query(x, y, 1, n, 1);
printf("%lld\n", ans%p);
break;
}
}
}
}
inline void build(ll l, ll r, ll rt)
{
tree[rt].tag1 = 0;
tree[rt].tag2 = 1;
if (l == r)
{
tree[rt].val = a[l];
return;
}
ll mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
push_up(rt);
}
inline void push_up(ll rt)
{
tree[rt].val = tree[rt << 1].val + tree[rt << 1 | 1].val;
}
inline void f1(ll l, ll r, ll rt, ll k)
{
if (tree[rt].tag2 != 1)
push_down(l, r, rt, 1);
tree[rt].val += (r - l + 1) * k;
tree[rt].tag1 += k;
}
inline void f2(ll l, ll r, ll rt, ll k)
{
if (tree[rt].tag1 != 0)
push_down(l, r, rt, 2);
tree[rt].val *= k;
tree[rt].tag2 *= k;
}
inline void push_down(ll l, ll r, ll rt, ll mod)
{
ll mid = (l + r) >> 1;
if (mod == 2)
{
f1(l, mid, rt << 1, tree[rt].tag1);
f1(mid + 1, r, rt << 1 | 1, tree[rt].tag1);
tree[rt].tag1 = 0;
}
else if (mod == 1)
{
f2(l, mid, rt << 1, tree[rt].tag2);
f2(mid + 1, r, rt << 1 | 1, tree[rt].tag2);
tree[rt].tag2 = 1;
}
}
inline void update(ll L, ll R, ll l, ll r, ll rt, ll k, ll mod)
{
switch (mod)
{
case 1: {
if (L <= l && R >= r)
{
tree[rt].val *= k;
tree[rt].tag2 *= k;
return;
}
break;
}
case 2: {
if (L <= l && R >= r)
{
tree[rt].val += k * (r - l + 1);
tree[rt].tag1 += k;
return;
}
break;
}
}
ll mid = (l + r) >> 1;
push_down(l, r, rt, mod);
if (mid >= L)update(L, R, l, mid, rt << 1, k, mod);
if (mid + 1 <= R)update(L, R, mid + 1, r, rt << 1 | 1, k, mod);
push_up(rt);
}
inline ll query(ll L, ll R, ll l, ll r, ll rt)
{
ll res = 0;
if (L <= l && r <= R) {
return tree[rt].val;
}
ll mid = (l + r) >> 1;
ll mod;
if (tree[rt].tag1 == 0)
mod = 1;
else if (tree[rt].tag2 == 1)
mod = 2;
push_down(l, r, rt, mod);
if (L <= mid)res += query(L, R, l, mid, rt << 1);
if (R >= mid + 1)res += query(L, R, mid + 1, r, rt << 1 | 1);
return res;
}