#include <iostream>
using std::cin;
using std::cout;
using std::endl;
class SegmentTreeR
{
#define MAXN 100100
public:
using ull = unsigned long long;
int arr[MAXN];
int sum[MAXN << 2];
int lazyAdd[MAXN << 2];
int lazyMul[MAXN << 2];
int p;
void update(int k);
void build(int k, int l, int r);
void pushDown(int k, int l, int r);
void changeAdd(int L, int R, int value, int k, int l, int r);
void changeMul(int L, int R, int value, int k, int l, int r);
ull search(int L, int R, int k, int l, int r);
};
void SegmentTreeR::update(int k)
{
sum[k] = sum[k << 1] + sum[k << 1 | 1];
sum[k] %= p;
}
void SegmentTreeR::build(int k, int l, int r)
{
lazyAdd[k] = 0;
lazyMul[k] = 1;
if (l == r)
{
sum[k] = arr[l];
sum[k] %= p;
return;
}
int m = (l + r) >> 1;
build(k << 1, l, m);
build(k << 1 | 1, m + 1, r);
update(k);
}
void SegmentTreeR::pushDown(int k, int l, int r)
{
int m = (l + r) >> 1;
int lnum = m - l + 1;
int rnum = r - m;
int ll = k << 1, rr = k << 1 | 1;
sum[ll] *= lazyMul[k], sum[rr] *= lazyMul[k];
lazyMul[ll] *= lazyMul[k], lazyMul[rr] *= lazyMul[k];
lazyAdd[ll] *= lazyMul[k], lazyAdd[rr] *= lazyMul[k];
sum[ll] += lazyAdd[k] * lnum, sum[rr] += lazyAdd[k] * rnum;
lazyAdd[ll] += lazyAdd[k], lazyAdd[rr] += lazyAdd[k];
sum[ll] %= p;
sum[rr] %= p;
lazyAdd[ll] %= p;
lazyAdd[rr] %= p;
lazyMul[ll] %= p;
lazyMul[rr] %= p;
lazyAdd[k] = 0;
lazyMul[k] = 1;
}
void SegmentTreeR::changeAdd(int L, int R, int value, int k, int l, int r)
{
if (L <= l && R >= r)
{
sum[k] += (r - l + 1) * value;
lazyAdd[k] += value;
sum[k] %= p;
lazyAdd[k] %= p;
return;
}
int m = (l + r) >> 1;
pushDown(k, l, r);
if (L <= m) changeAdd(L, R, value, k << 1, l, m);
if (R > m) changeAdd(L, R, value, k << 1 | 1, m + 1, r);
update(k);
}
void SegmentTreeR::changeMul(int L, int R, int value, int k, int l, int r)
{
if (L <= l && R >= r)
{
lazyAdd[k] *= value;
sum[k] *= value;
lazyMul[k] *= value;
lazyAdd[k] %= p;
lazyMul[k] %= p;
sum[k] %= p;
return;
}
int m = (l + r) >> 1;
pushDown(k, l, r);
if (L <= m) changeMul(L, R, value, k << 1, l, m);
if (R > m) changeMul(L, R, value, k << 1 | 1, m + 1, r);
update(k);
}
SegmentTreeR::ull SegmentTreeR::search(int L, int R, int k, int l, int r)
{
if (L <= l && R >= r)
{
return sum[k] % p;
}
int m = (l + r) >> 1;
pushDown(k, l, r);
ull ans = 0;
if (L <= m) ans += search(L, R, k << 1, l, m);
if (R > m) ans += search(L, R, k << 1 | 1, m + 1, r);
return ans % p;
}
SegmentTreeR S;
int main()
{
int n, m, p;
cin >> n >> m >> p;
S.p = p;
for (int i = 1; i <= n; i++)
{
int x;
cin >> x;
S.arr[i] = x % p;
}
S.build(1, 1, n);
for (int i = 0; i < m; i++)
{
int op, x, y, k;
cin >> op;
switch (op)
{
case 1:
cin >> x >> y >> k;
S.changeMul(x, y, k, 1, 1, n);
break;
case 2:
cin >> x >> y >> k;
S.changeAdd(x, y, k, 1, 1, n);
break;
case 3:
cin >> x >> y;
cout << S.search(x, y, 1, 1, n) << endl;
break;
}
}
return 0;
}