/*
#include<bits/stdc++.h>
namespace fastIO{
template<typename T> inline void input(T& x){
T s=0,f=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar()) s=(s<<3)+(s<<1)+(ch^'0');
x=s*f;
}
template<typename T> inline void print(T x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9) print(x/10);
putchar(x%10+'0');
}
template<typename T,typename... Args> inline void input(T& x,Args&... args){
input(x);
input(args...);
}
template<typename T> inline void print(T x,char ch){
print(x);
putchar(ch);
}
}
using namespace fastIO;
const int N=1e6+5;
namespace Treap{
//树堆的性质:val(l[u])<val(u)<val(r[u])
struct Pair{
int l,r;
Pair(int l_=0,int r_=0): l(l_),r(r_){}
};
struct fhq_treap{
int val,l,r,siz,pri,lazy;//值,第u个节点的左右孩子),u为根节点的子树的大小,u的优先级,懒标记
};
fhq_treap tree[N];
const int INF=0x3f3f3f3f;
int seed=1;
int tot=0;//节点总数
int root=0;//根节点
inline int rand1(){
return seed*=19260817;
}
inline void pushup(int u){
tree[u].siz=tree[tree[u].l].siz+tree[tree[u].r].siz+1;
}
//区间操作代码
inline void pushdown(int u){
if(!tree[u].lazy) return ;
std::swap(tree[u].l,tree[u].r);//颠倒区间
tree[tree[u].l].lazy^=1;
tree[tree[u].r].lazy^=1;//异或很容易实现“翻转2次=没有翻转”
tree[u].lazy=0;
}
Pair splitrank(int u,int k){
if(!u) return {0,0};
pushdown(u);
if(tree[tree[u].l].siz+1<=k){
auto tmp=splitrank(tree[u].r,k-tree[tree[u].l].siz-1);//原理类似于queryrank
tree[u].r=tmp.l;
pushup(u);
return {u,tmp.r};
}
else{
auto tmp=splitrank(tree[u].l,k);
tree[u].l=tmp.r;
pushup(u);
return {tmp.l,u};
}
}
void splitrank(int u,int k,int& l,int& r){//等价写法
if(!u){
l=r=0;
return ;
}
pushdown(u);
if(tree[tree[u].l].siz+1<=k){
l=u;//可以这么理解将左子树分配给了u(以下同理,至于为什么这么干可以去看splitval)
splitrank(tree[u].r,k-tree[tree[u].l].siz-1,tree[u].r,r);
}
else{
r=u;
splitrank(tree[u].l,k,l,tree[u].l);
}
}
int mergerank(int u,int v){
if(!u || !v) return u+v;
if(tree[u].pri<tree[v].pri){
pushdown(u);
tree[u].r=mergerank(tree[u].r,v);
pushup(u);
return u;
}
else{
pushdown(v);
tree[v].l=mergerank(u,tree[v].l);
pushup(v);
return v;
}
}
void printree(int u){
//对于颠倒对应区间[l,r]的操作,只需要分裂出[1,l),[l,r],(r,n]三个区间,在进行颠倒操作即可(懒标记)
//颠倒后仍是中序遍历
pushdown(u);
if(tree[u].l) printree(tree[u].l);
print(tree[u].val,' ');
if(tree[u].r) printree(tree[u].r);
}
//
Pair splitval(int u,int k){//根节点为u,按k分裂,返回分裂后两棵子树的根
//分裂操作是将一个 Treap 分成 x,y 两个 Treap。其中 x 中每一个结点的值都小于于 k,而 y 中每一个结点的值都大于等于 k。
if(!u) return {0,0};//空树
if(tree[u].val<=k){
//说明其左子树肯定都<=k,在x中,但是在右子树中仍然可能存在值比k小的节点(但肯定比u的值大),所以要继续递归分裂
auto tmp=splitval(tree[u].r,k);
tree[u].r=tmp.l;//将右子树中<=k的部分拿出来作为u的右子树,这样整个u都是<=k的,剩下的都是大于于k的
pushup(u);
return {u,tmp.r};
}
else{
//同上
auto tmp=splitval(tree[u].l,k);
tree[u].l=tmp.r;
pushup(u);
return {tmp.l,u};
}
}
void splitval(int u,int k,int& l,int& r){//等价写法(同splitrank)
if(!u){
l=r=0;
return ;
}
if(tree[u].val<=k){
l=u;
splitval(tree[u].r,k,tree[u].r,r);
}
else{
r=u;
splitval(tree[u].l,k,l,tree[u].l);
}
}
int mergeval(int u,int v){//合并u、v两棵子树,返回合并后子树的根
//合并是将 x,y 两个 Treap 合并为一个 Treap(因为通常是从同一棵Treap分裂出来的,所以x 中的每一个结点的值都小于等于 y 中每一个结点的值)
if(!u || !v) return u+v;//只要有一棵为空,就直接返回另一棵
if(tree[u].pri<tree[v].pri){//需要满足堆的性质,即根节点的优先级是最小的,所以优先级小的作为合并后的根节点
tree[u].r=mergeval(tree[u].r,v); //将右子树和另一棵树合并,作为右子树
pushup(u);
return u;
}
else{
//反之同理
tree[v].l=mergeval(u,tree[v].l);
pushup(v);
return v;
}
}
void insert(int k){//插入一个值为 k 的结点
//先将申请的一个新的结点作为一棵树 y,并将原来的树分裂成 x,z 两棵树,然后依次合并 x,y,z,就完成了。
tree[++tot].val=k;
tree[tot].pri=rand1();
tree[tot].siz=1;
tree[tot].l=tree[tot].r=0;
//申请一个节点,作为一棵树,其大小为 1
auto tmp=splitval(root,k-1);//以 k 为关键值分裂
root=mergeval(tmp.l,mergeval(tot,tmp.r));//依次合并,更新根
}
void erase(int k){//删除值为k的节点
//其实很简单,即将整棵树按值分为小于k,等于k与大于k三部分,直接合并小于k与大于k的部分即可
auto x=splitval(root,k-1);//分为<=和>两部分
auto y=splitval(x.r,k);//提取=(y.r的根节点)
y.l=mergeval(tree[y.l].l,tree[y.l].r);//直接合并左右子树,就能实现删除操作(不用担心会额外删除,多的在左子树里)
root=mergeval(x.l,mergeval(y.l,y.r));//更新根节点
}
int queryrank(int k){//根据值查询排名
auto tmp=splitval(root,k-1);
int res=tree[tmp.l].siz+1;//小于k的值的数量+1
root=mergeval(tmp.l,tmp.r);//合并回去
return res;
}
int queryval(int k){//根据排名查询值
int pos=root;//初始化排名
while(pos){
if(k==tree[tree[pos].l].siz+1) return tree[pos].val;
//若其左子树大小+1刚好为排名,则说明其就是要找的点(左子树存的是<=的,等于的在根节点)
if(k<tree[tree[pos].l].siz+1) pos=tree[pos].l;//进入左子树寻找
else{//不在左子树也不在根节点,就只能去右子树了
k-=tree[tree[pos].l].siz+1;//将k减去左子树大小+1(1为根节点大小)(自己模拟一下很容易理解)
pos=tree[pos].r;//进入右子树寻找
}
}
return INF;//以防万一
}
int getpre(int k){//查找值k的前驱
//什么叫“小于 k,且最大的数”?不就是前面的那个数吗,所以直接查找排名比 k 的排名少一的数。
return queryval(queryrank(k)-1);
}
int getnext(int k){//查找k的后驱
//值相同的结点可能不止一个,所以不能找后面的那个数了,但是可以将值加一,查询它的排名,并找到对应的值。
return queryval(queryrank(k+1));
}
}
using namespace Treap;
int n,m;
int main(){
input(n,m);
for(int i=1;i<=n;++i){
insert(i);
}
while(m--){
int l,r;
input(l,r);
auto x=splitrank(root,r);
auto y=splitrank(x.l,l-1);
tree[y.r].lazy^=1;
root=mergerank(mergerank(y.l,y.r),x.r);
}
printree(root);
return 0;
}
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1100010;
struct Pair{
int l,r;
Pair(int l_=0,int r_=0): l(l_),r(r_){}
};
struct node{
int l,r,pri,val,siz;
};
node tree[N];
int tot,root;
int n,m;
inline void pushup(int u){
tree[u].siz=tree[tree[u].l].siz+tree[tree[u].r].siz+1;
}
Pair splitval(int u,int k){
if(!u) return {0,0};
if(tree[u].val<=k){
auto tmp=splitval(tree[u].r,k);
tree[u].r=tmp.l;
pushup(u);
return {u,tmp.r};
}
else{
auto tmp=splitval(tree[u].l,k);
tree[u].l=tmp.r;
pushup(u);
return {tmp.l,u};
}
}
int merge(int u,int v){
if(!u || !v) return u+v;
if(tree[u].pri<tree[v].pri){
tree[u].r=merge(tree[u].r,v);
pushup(u);
return u;
}
else{
tree[v].l=merge(u,tree[v].l);
pushup(v);
return v;
}
}
void insert(int k){
tree[++tot].val=k;
tree[tot].siz=1;
tree[tot].pri=rand();
tree[tot].l=tree[tot].r=0;
auto tmp=splitval(root,k);
root=merge(merge(tmp.l,tot),tmp.r);
}
void erase(int k){
auto x=splitval(root,k-1);
auto y=splitval(x.r,k);
y.l=merge(tree[y.l].l,tree[y.l].r);
root=merge(x.l,merge(y.l,y.r));
}
int queryrank(int k){
auto tmp=splitval(root,k-1);
int res=tree[tmp.l].siz+1;
root=merge(tmp.l,tmp.r);
return res;
}
int queryval(int k){
int pos=root;
while(pos){
if(k==tree[tree[pos].l].siz+1) return tree[pos].val;
if(k<tree[tree[pos].l].siz+1) pos=tree[pos].l;
else{
k-=tree[tree[pos].l].siz+1;
pos=tree[pos].r;
}
}
return 0x3f3f3f3f;
}
int getpre(int k){
return queryval(queryrank(k)-1);
}
int getnext(int k){
return queryval(queryrank(k+1));
}
int main(){
// freopen("P6136_2.in","r",stdin);
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
int tmp;
cin>>tmp;
insert(tmp);
}
int last=0,ans=0;
for (int i = 1; i <= m; ++i) {
int opt,x;
cin>>opt>>x;
if (opt == 1) insert(x^last);
if (opt == 2) erase(x^last);
if (opt == 3) {
last = queryrank(x^last);
ans ^= last;
}
if (opt == 4) {
last = queryval(x^last);
ans ^= last;
}
if (opt == 5) {
last = getpre(x^last);
ans ^= last;
}
if (opt == 6) {
last = getnext(x^last);
ans ^= last;
}
// cout<<last<<'\n';
}
cout<<ans<<'\n';
return 0;
}