#include<bits/stdc++.h>
using namespace std;
struct tree{
    int l,r;
    long long sum,add;
}tr[1200000];
long long a[1200000],n,m;
void pushup(int u){
    tr[u].sum=tr[u*2].sum^tr[u*2+1].sum;
}
void build(int u,int l,int r){
    tr[u].l=l,tr[u].r=r;
    if(l==r){
        tr[u].sum=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(u*2,l,mid);
    build(u*2+1,mid+1,r); 
    pushup(u);
}
void spread(int u){
    if(tr[u].add!=0){
    	if((tr[u*2].r-tr[u*2].l+1)%2==1)tr[u*2].sum^=tr[u].add;
    	if((tr[u*2+1].r-tr[u*2+1].l+1)%2==1)tr[u*2+1].sum^=tr[u].add;
        tr[u*2].add^=tr[u].add;
        tr[u*2+1].add^=tr[u].add;
        tr[u].add=0;
    }
}
void change(int u,int l,int r,int d){
    if(l<=tr[u].l&&r>=tr[u].r){
    	if((tr[u].r-tr[u].l+1)%2==1)tr[u].sum^=d;
        tr[u].add^=d;
        return;
    }
    spread(u);
    int mid=(tr[u].l+tr[u].r)/2;
    if(l<=mid)change(u*2,l,r,d);
    if(r>mid)change(u*2+1,l,r,d);
    pushup(u);
}
long long ask(int u,int l,int r){
    if(l<=tr[u].l&&r>=tr[u].r)return tr[u].sum;
    spread(u);
    int mid=(tr[u].l+tr[u].r)/2;
    long long ans=0;
    if(l<=mid)ans^=ask(u*2,l,r);
    if(r>mid)ans^=ask(u*2+1,l,r);
    return ans;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    int as,b,c,d;
    for(int i=1;i<=m;i++){
        cin>>as;
        if(as==2){
            cin>>b>>c;
            cout<<ask(1,b,c)<<endl;
        }
        if(as==1){
            cin>>b>>d;
            change(1,b,b,d);
            continue;
        }

    }
    return 0;
}
#include<bits/stdc++.h>
using namespace std;
#define ff              first
#define ss              second
#define pb              push_back
#define mp              make_pair
#define ll 				long long
#define pii             pair<ll,ll>
#define vi              vector<ll>
#define mii               map<ll,ll>
#define pqi             priority_queue<ll>			//max pq
#define pqd             priority_queue<ll,vi,greater<ll> >
#define setbits(x)      __builtin_popcountll(x)
#define zrobits(x)      __builtin_ctzll(x)
#define MOD             1000000007
#define inf             1e18
#define ps(x,y)         fixed<<setprecision(y)<<x
#define pw(b,p)         pow(b,p) + 0.1
#define f(i,k,n)		for(ll i=k;i<n;i++)
#define fd(i,start,end)	for(ll i=start;i>=end;i--)

ll power(ll a, ll b, ll mod) {ll res = 1; a %= mod; assert(b >= 0); for (; b; b >>= 1) {if (b & 1)res = res * a % mod; a = a * a % mod;} return res;}
ll power(ll a, ll b) {ll res = 1; assert(b >= 0); for (; b; b >>= 1) {if (b & 1)res = res * a ; a = a * a;} return res;}
ll min( ll a,  ll b) { return (a < b) ? a : b;}
ll max(ll a, ll b) {return (a > b) ? a : b;}
ll gcd (ll a, ll b) {if (a == 0) return  b; return gcd(b % a, a);}



void bwayne()
{
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
	freopen("output2.txt", "w", stdout);
#endif
}
// ll n;
// cin >> n;
// vi a(n);
// f(i, 0, n) {
// 	cin >> a[i];
// }

// vector<vi> a(n,vector<vi>(m,0));
// f(i,0,n){
// 	f(j,0,m){
// 		cin>>a[i][j];
// 	}
// }

int getXOR(int BITree[], int index)
{
	int ans = 0;
	index += 1;

	// Traverse ancestors
	// of BITree[index]
	while (index > 0) {

		// XOR current element
		// of BIT to ans
		ans ^= BITree[index];

		// Update index to that
		// of the parent node in
		// getXor() view by
		// subtracting LSB(Least
		// Significant Bit)
		index -= index & (-index);
	}
	return ans;
}

// Updates the Binary Index Tree by
// replacing all ancestores of index
// by their respective XOR with val
void updateBIT(int BITree[], int n,
               int index, int val)
{
	index = index + 1;

	// Traverse all ancestors
	// and XOR with 'val'.
	while (index <= n) {

		// XOR 'val' to current
		// node of BIT
		BITree[index] ^= val;

		// Update index to that
		// of the parent node in
		// updateBit() view by
		// adding LSB(Least
		// Significant Bit)
		index += index & (-index);
	}
}

// Constructs and returns a Binary
// Indexed Tree for the given array
int* constructBITree(vi &arr, int n)
{
	// Create and initialize
	// the Binary Indexed Tree
	int* BITree = new int[n + 1];
	for (int i = 1; i <= n; i++)
		BITree[i] = 0;

	// Store the actual values in
	// BITree[] using update()
	for (int i = 0; i < n; i++)
		updateBIT(BITree, n, i, arr[i]);

	return BITree;
}

int rangeXor(int BITree[], int l, int r)
{
	return getXOR(BITree, r)
	       ^ getXOR(BITree, l - 1);
}




void solve() {
	ll q, n;
	cin >> n >> q;
	vi a(n);
	f(i, 0, n) {
		cin >> a[i];
	}

	int* BITree = constructBITree(a, n);
	f(i, 0, q) {
		ll t, x, y;
		cin >> t >> x >> y;
		if (t == 1) {
			int idx = x - 1;
			int val = y;
			a[idx] ^= val;

			// Update the values of all
			// ancestors of idx
			updateBIT(BITree, n, idx, val);
		}

		if (t == 2) {
			cout << rangeXor(BITree, x - 1, y - 1) << endl;
		}
	}


}
int main()
{
	bwayne();// remember
	ll t = 1;
	// cin >> t;

	for (ll tt = 1; tt <= t; tt++)
	{
		// cout << "Case #" << tt << ": ";
		solve();
	}

	return 0;
}