#include <bits/stdc++.h>
using namespace std;
const int BUFFER_SIZE = 1 << 20;
char rb[BUFFER_SIZE], *rp = rb, *rt = rb;
inline char read_char() {
  return rp == rt ? (rt = rb + fread(rb, 1, BUFFER_SIZE, stdin), rp = rb, *rp++)
                  : *rp++;
}
inline int read_int() {
  int x = 0;
  char ch = read_char(), flag = 0;
  while (ch != '-' && (ch < '0' || ch > '9')) {
    ch = read_char();
  }
  if (ch == '-') {
    flag = 1;
    ch = read_char();
  }
  for (x = 0; ch >= '0' && ch <= '9'; ch = read_char()) {
    x = x * 10 + (ch - '0');
  }
  return flag ? -x : x;
}
const int MAX_N = 200000 + 5;
const int MAX_SEG = 1 << 19 | 5;
const int MAX_B = 32 + 5;
int N, M, Q, T;
struct Edge {
  int x, y, z, tl, tr;
} e[MAX_N << 1];
struct Query {
  int x, y, ans;
} q[MAX_N];
map<pair<int, int>, int> em;
vector<int> seg[MAX_SEG];
struct UnionSet {
  int fath[MAX_N], dis[MAX_N], size[MAX_N];
  int b[MAX_B], B, bt[MAX_B];
  int s[MAX_N + MAX_B], top;
  void init() {
    for (int i = 1; i <= N; i++) {
      fath[i] = i;
      size[i] = 1;
    }
  }
  void get_fath(int &x, int &d) {
    while (x != fath[x]) {
      d ^= dis[x];
      x = fath[x];
    }
  }
  void add_edge(int x, int y, int z) {
    int fx = x, dx = 0, fy = y, dy = 0;
    get_fath(fx, dx);
    get_fath(fy, dy);
    if (fx != fy) {
      swap(fx, size[fx] > size[fy] ? fy : fx);
      fath[fx] = fy;
      dis[fx] = z ^ dx ^ dy;
      size[fy] += size[fx];
      s[top++] = fx;
    } else {
      z = z ^ dx ^ dy;
      for (int i = 1; i <= B && z; i++) {
        if (z > (z ^ b[i])) {
          z ^= b[i];
        }
      }
      if (z) {
        b[++B] = z;
        s[top++] = -1;
      }
    }
  }
  void back_to(int t) {
    while (top > t) {
      int x = s[--top];
      if (x == -1) {
        B--;
      } else {
        size[fath[x]] -= size[x];
        fath[x] = x;
        dis[x] = 0;
      }
    }
  }
  int query(int x, int y) {
    int fx = x, dx = 0, fy = y, dy = 0;
    get_fath(fx, dx);
    get_fath(fy, dy);
    int z = dx ^ dy;
    for (int i = 1; i <= B; i++) {
      if (z > (z ^ b[i])) {
        z ^= b[i];
      }
    }
    return z;
  }
} U;
void segment_insert(int i, int l, int r, int k) {
  if (e[k].tl <= l && r <= e[k].tr) {
    seg[i].push_back(k);
    return;
  }
  int mid = l + r >> 1;
  if (e[k].tl <= mid) {
    segment_insert(i << 1, l, mid, k);
  }
  if (mid + 1 <= e[k].tr) {
    segment_insert(i << 1 | 1, mid + 1, r, k);
  }
}
void segment_solve(int i, int l, int r) {
  int top = U.top;
  for (int k : seg[i]) {
    U.add_edge(e[k].x, e[k].y, e[k].z);
  }
  if (l == r) {
    q[l].ans = U.query(q[l].x, q[l].y);
  } else {
    int mid = l + r >> 1;
    segment_solve(i << 1, l, mid);
    segment_solve(i << 1 | 1, mid + 1, r);
  }
  U.back_to(top);
}
int main() {
  N = read_int();
  M = read_int();
  for (int i = 1; i <= M; i++) {
    e[i].x = read_int();
    e[i].y = read_int();
    assert(e[i].x < e[i].y);
    e[i].z = read_int();
    e[i].tl = 1;
    e[i].tr = -1;
    em[make_pair(e[i].x, e[i].y)] = i;
  }
  Q = read_int();
  for (int i = 1; i <= Q; i++) {
    int op = read_int();
    int x = read_int();
    int y = read_int();
    assert(x < y);
    if (op == 1) {
      e[++M].x = x;
      e[M].y = y;
      e[M].z = read_int();
      e[M].tl = T + 1;
      e[M].tr = -1;
      em[make_pair(x, y)] = M;
    } else if (op == 2) {
      int j = em[make_pair(x, y)];
      e[j].tr = T;
    } else {
      q[++T].x = x;
      q[T].y = y;
    }
  }
  for (int i = 1; i <= M; i++) {
    e[i].tr = e[i].tr >= 0 ? e[i].tr : T;
    if (e[i].tl <= e[i].tr) {
      segment_insert(1, 1, T, i);
    }
  }
  U.init();
  segment_solve(1, 1, T);
  for (int i = 1; i <= T; i++) {
    printf("%d\n", q[i].ans);
  }
  return 0;
}
