#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#define LL long long
#define rint register int
#define RG register
using namespace std;
namespace FastIO
{
const int _SIZE = (1 << 21) + 1;
char ibuf[_SIZE],obuf[_SIZE];
char *iS,*iT;
char c;
char qu[55];
char *oS=obuf,*oT=oS+_SIZE-1;
bool _sign=false;
int qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, _SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
// print the remaining part
inline void flush()
{
fwrite(obuf,1,oS-obuf,stdout);
oS=obuf;
return;
}
// putchar
inline void putc(const char &x)
{
*oS++=x;
if(oS==oT)
flush();
return;
}
inline char getc()
{
return gc();
}
// input a signed integer
template <class T>
inline void read(T &x)
{
x=0;
_sign=false;
for (c=gc();c<'0'||c>'9';c=gc())
if (c=='-')
_sign=true;
for (;c>='0'&&c<='9';c=gc())
x=(x<<1)+(x<<3)+(c&15);
x=(_sign) ? (~x+1) : x;
return;
}
// print a signed integer
template <class T>
inline void print(T x) {
if (!x) {
putc('0');
return;
}
if (x<0)
putc('-'),x=~x+1;
while(x) qu[++qr]=x%10+'0',x/=10;
while(qr) putc(qu[qr--]);
return;
}
// no need to call flush at the end manually!
struct Flusher_
{
~Flusher_() { flush(); }
}io_flusher_;
} // namespace io
using FastIO::read;
using FastIO::print;
using FastIO::putc;
using FastIO::getc;
//=======================================
const int N=1e5+5;
namespace LCT
{
struct Node *nul;
Node *merge(Node *x,Node *y);
struct Node
{
Node *lc,*rc;
Node *fa;
int val;
int pri;
int sum;
bool rev;
Node(Node *L,Node *R,int V) : lc(L),rc(R),val(V) { rev=false,pri=rand(),fa=nul; }
Node() { lc=rc=nul; }
inline void pushup()
{
sum=val;
if(lc!=nul)
sum^=lc->sum;
if(rc!=nul)
sum^=rc->sum;
return;
}
inline bool ntroot() { return (fa==nul) ? false : (fa->lc==this||fa->rc==this); }
inline void save()
{
Node *now=this;
for(;now->ntroot();now->pushup());
now->pushup();
}
inline void Addrev()
{
rev^=1;
swap(lc,rc);
}
inline void pushdown()
{
if(rev==false)
return;
lc->Addrev();
rc->Addrev();
rev=false;
return;
}
inline void preview() { if(ntroot()) fa->preview(); pushdown(); }
inline Node* root()
{
if(this==nul) return nul;
Node *now=this;
while(now->ntroot())
now=now->fa;
return now;
}
inline Node* split(Node *&x,Node *&y)
{
preview();
Node *now=this;
x=now; y=now->rc;
x->rc=nul;
x->pushup();
for(;now->fa!=nul;now=now->fa) {
if(now==now->fa->lc) {
now->fa->lc=y;
y->fa=now->fa;
y=now->fa;
}
else if(now==now->fa->lc) {
now->fa->rc=x;
x->fa=now->fa;
x=now->fa;
}
else break;
now->fa->pushup();
}
if(fa!=nul)
now->fa->pushup();
if(x!=nul) x->fa=nul;
if(y!=nul) y->fa=nul;
return now;
}
inline Node* access()
{
Node *now=this,*res,*root;
Node *x,*y;
for(;now!=nul;now=res->fa=root) {
root=now->split(x,y);
now->save();
if(y!=nul)
y->fa=now;
res=merge(x,res);
}
return res;
}
inline void make_root() { access()->Addrev(); }
}*t[N];
Node *merge(Node *x,Node *y)
{
if(x==nul||y==nul) return (x==nul) ? y : x;
if(x->pri>y->pri) {
x->rc=merge(x->rc,y);
x->rc->fa=x;
x->pushup();
return x;
}
else {
y->lc=merge(x,y->lc);
y->lc->fa=y;
y->pushup();
return y;
}
}
inline Node* findroot(Node *now)
{
now=now->access();
while(now->lc!=nul) {
now->pushdown();
now=now->lc;
}
return now;
}
inline void link(Node *x,Node *y)
{
x->make_root();
Node *r=x->root();
y->access();
if(y->root()==r)
return;
r->fa=y;
return;
}
inline Node* pull(Node *x,Node *y)
{
x->make_root();
return y->access();
}
inline void cut(Node *x,Node *y)
{
x->make_root();
if(findroot(y)==x&&y->fa==x&&y->lc==nul) {
x->rc=y->fa=nul;
x->pushup();
}
return;
}
}
using namespace LCT;
int n,m;
int main()
{
// freopen("1.in","r",stdin);
srand((unsigned)time(0));
rint i,j;
int x,y,z,opt;
read(n); read(m);
for(i=1;i<=n;i++) {
read(x);
t[i]=new Node(nul,nul,x);
t[i]->pushup();
}
while(m--) {
read(opt); read(x); read(y);
if(opt==0) print(pull(t[x],t[y])->sum);
else if(opt==1) link(t[x],t[y]);
else if(opt==2) cut(t[x],t[y]);
else if(opt==3) t[x]->val=y,t[x]->save();
}
return 0;
}