#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <queue>
#include <map>
#include <set>
#define prt printf
#define ll long long
#define spc putchar(' ')
#define ent putchar('\n')
#define pr_ prt("---")
#define prx prt("***")
#define prtn (putchar('N') , putchar('o'))
#define prty (putchar('Y') , putchar('e') , putchar('s'))
inline ll read() {
bool f = 1 ; ll k = 0;
char c = getchar() ;
while(c < '0' || c > '9') {
f = c == '-' ? 0 : 1 ;
c = getchar() ;
}
while(c >= '0' && c <= '9') {
k = (k << 3) + (k << 1) + (c ^ 48);
c = getchar() ;
}
return (f == 1 ? k : -k);
}
void output(ll now) {
if(now < 0){
putchar('-');
output(- now);
}
else{
if(now > 9) output(now / 10);
putchar((now % 10) ^ 48);
}
}
inline ll max(ll a , ll b) { return a > b ? a : b ;}
inline ll min(ll a , ll b) { return a < b ? a : b ;}
#define ls k << 1
#define rs k << 1 | 1
const int maxn = 1e5 + 1 ;
struct SegTree {
int l , r ;
ll mul = 1 , add ;
ll val ;
}tree[maxn << 2] ;
int n , Q , mod ;
int a[maxn] ;
void push_up(int k) { tree[k].val = (tree[ls].val + tree[rs].val) % mod ;}
void push_down(int k) {
if(tree[k].mul <= 1 && ! tree[k].add) return ;
tree[ls].val *= tree[k].mul ;
tree[rs].val *= tree[k].mul ;
tree[ls].mul *= tree[k].mul ;
tree[ls].mul %= mod ;
tree[rs].mul *= tree[k].mul ;
tree[rs].mul %= mod ;
tree[ls].add *= tree[k].mul ;
tree[rs].add *= tree[k].mul ;
tree[k].mul = 1 ;
tree[ls].val += (tree[ls].r - tree[ls].l + 1) * tree[k].add ;
tree[ls].val %= mod ;
tree[rs].val += (tree[rs].r - tree[rs].l + 1) * tree[k].add ;
tree[rs].val %= mod ;
tree[ls].add += tree[k].add ;
tree[ls].add %= mod ;
tree[rs].add += tree[k].add ;
tree[rs].add %= mod ;
tree[k].add = 0 ;
}
void build(int k , int l , int r) {
tree[k].l = l , tree[k].r = r ;
if(l == r) return tree[k].val = a[l] , (void) 0 ;
int mid = (l + r) >> 1 ;
build(ls , l , mid) , build(rs , mid + 1 , r) ;
push_up(k) ;
}
void Change_mul(int k , int l , int r , int mul) {
if(tree[k].l > r || tree[k].r < l) return ;
if(l <= tree[k].l && tree[k].r <= r) {
tree[k].val *= mul ;
tree[k].mul *= mul ;
tree[k].add *= mul ;
tree[k].val %= mod ;
tree[k].mul %= mod ;
tree[k].add %= mod ;
return ;
}
push_down(k) ;
Change_mul(ls , l , r , mul) , Change_mul(rs , l , r , mul) ;
push_up(k) ;
}
void Change_add(int k , int l , int r , int add) {
if(tree[k].l > r || tree[k].r < l) return ;
if(l <= tree[k].l && tree[k].r <= r) {
tree[k].val += (tree[k].r - tree[k].l + 1) * add ;
tree[k].add += add ;
tree[k].val %= mod ;
tree[k].add %= mod ;
return ;
}
push_down(k) ;
Change_add(ls , l , r , add) , Change_add(rs , l , r , add) ;
push_up(k) ;
}
ll Ask(int k , int l , int r) {
if(tree[k].l > r || tree[k].r < l) return 0 ;
if(l <= tree[k].l && tree[k].r <= r) return tree[k].val % mod ;
push_down(k) ;
return (Ask(ls , l , r) + Ask(rs , l , r)) % mod ;
}
signed main() {
n = read() , Q = read() , mod = read() ;
for(int i=1 ; i<=n ; i++) a[i] = read() ;
build(1 , 1 , n) ;
while(Q --) {
int opt = read() ;
if(opt == 1) {
int l = read() , r = read() , mul = read() ;
Change_mul(1 , l , r , mul) ;
}
else if(opt == 2) {
int l = read() , r= read() , add = read() ;
Change_add(1 , l , r , add) ;
}
else {
int l = read() , r = read() ;
output(Ask(1 , l , r) % mod) , ent ;
}
}
return 0 ;
}