#include<iostream>
using namespace std;
const int N = 1e5 + 10;
long long a[N], n, m, p;
struct node {
long long val;
long long mul = 1;
long long add;
}b[N * 4];
void downmul(long long node, long long L, long long R, long long x, long long y, long long z);
void downadd(long long node, long long L, long long R, long long x, long long y, long long z);
void downmul(long long node, long long L, long long R, long long x, long long y, long long z)
{
long long mid = L + R >> 1;
long long left_node = node << 1;
long long right_node = node << 1 | 1;
if (x > R || y < L) return;
else if (L == R) { b[node].val = b[node].val * z % p; return; }
else if (x <= L && y >= R) {
if (b[node].add) downadd(left_node, L, mid, L, R, b[node].add), downadd(right_node, mid + 1, R, L, R, b[node].add), b[node].add = 0;
b[node].mul = b[node].mul * z % p;
b[node].val = b[node].val * z % p;
return;
}
if (b[node].add) downadd(left_node, L, mid, L, R, b[node].add), downadd(right_node, mid + 1, R, L, R, b[node].add), b[node].add = 0;
else if (b[node].mul != 1) downmul(left_node, L, mid, L, R, b[node].mul), downmul(right_node, mid + 1, R, L, R, b[node].mul), b[node].mul = 1;
downmul(left_node, L, mid, x, y, z);
downmul(right_node, mid + 1, R, x, y, z);
b[node].val = (b[left_node].val + b[right_node].val) % p;
return;
}
void downadd(long long node, long long L, long long R, long long x, long long y, long long z)
{
long long mid = L + R >> 1;
long long left_node = node << 1;
long long right_node = node << 1 | 1;
if (x > R || y < L) return;
else if (L == R) { b[node].val = (b[node].val + z) % p; return; }
else if (x <= L && y >= R) {
if (b[node].mul != 1)
downmul(left_node, L, mid, L, R, b[node].mul), downmul(right_node, mid + 1, R, L, R, b[node].mul), b[node].mul = 1;
b[node].add = b[node].add + z;
b[node].val = (b[node].val + (R - L + 1) * z) % p;
return;
}
if (b[node].add) downadd(left_node, L, mid, L, R, b[node].add), downadd(right_node, mid + 1, R, L, R, b[node].add), b[node].add = 0;
else if (b[node].mul != 1) {
downmul(left_node, L, mid, L, R, b[node].mul);
downmul(right_node, mid + 1, R, L, R, b[node].mul), b[node].mul = 1;
}
downadd(left_node, L, mid, x, y, z);
downadd(right_node, mid + 1, R, x, y, z);
b[node].val = (b[left_node].val + b[right_node].val) % p;
return;
}
long long qur(long long node, long long L, long long R, long long x, long long y)
{
if (x > R || y < L) return 0;
else if (L == R) return b[node].val;
else if (x <= L && y >= R) return b[node].val;
long long mid = L + R >> 1;
long long left_node = node << 1;
long long right_node = node << 1 | 1;
if (b[node].add) downadd(left_node, L, mid, L, R, b[node].add), downadd(right_node, mid + 1, R, L, R, b[node].add), b[node].add = 0;
else if (b[node].mul != 1)
downmul(left_node, L, mid, L, R, b[node].mul), downmul(right_node, mid + 1, R, L, R, b[node].mul), b[node].mul = 1;
long long left_sum = qur(left_node, L, mid, x, y) % p;
long long right_sum = qur(right_node, mid + 1, R, x, y) % p;
return (left_sum + right_sum) % p;
}
void buit_tree(long long node, long long L, long long R)
{
if (L == R) {
b[node].val = a[L];
return;
}
long long mid = L + R >> 1;
long long left_node = node << 1;
long long right_node = node << 1 | 1;
buit_tree(left_node, L, mid);
buit_tree(right_node, mid + 1, R);
b[node].val = b[left_node].val + b[right_node].val;
return;
}
int main()
{
cin >> n >> m >> p;
for (long long i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
buit_tree(1, 1, n);
for (long long i = 1; i <= m; i++) {
long long op, x, y, z;
scanf("%lld", &op);
if (op == 1) {
scanf("%lld", &x);
scanf("%lld", &y);
scanf("%lld", &z);
downmul(1, 1, n, x, y, z);
}
else if (op == 2) {
scanf("%lld", &x);
scanf("%lld", &y);
scanf("%lld", &z);
downadd(1, 1, n, x, y, z);
}
else if (op == 3) {
scanf("%lld", &x);
scanf("%lld", &y);
long long tem = qur(1, 1, n, x, y);
printf("%lld\n", tem);
}
}
return 0;
}