#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, M = 31;
vector<int> vec[N << 2];
pair<int, int> qr[N];
map<pair<int, int>, int> mp;
namespace IO {
int read() {
  int ret = 0;
  char c = getchar();
  while (!isdigit(c)) c = getchar();
  while (isdigit(c)) ret = ret * 10 + (c ^ 48), c = getchar();
  return ret;
}
void write(int x) {
  if (x > 9) write(x / 10);
  write(x % 10 ^ 48);
}
void writeln(int x) {
  write(x);
  putchar('\n');
}
}  // namespace IO
using namespace IO;
namespace Linear_Basis {
int fc[M];
void initfc() {
  fc[0] = 1;
  for (int i = 1; i < M; ++i) fc[i] = fc[i - 1] << 1;
}
struct Basis {
  int c[22][M];
  void copy(int x, int y) { memcpy(c[x], c[y], sizeof(c[y])); }
  void insert(int dp, int x) {
    for (int i = M - 1; ~i; --i)
      if (x & fc[i]) {
        if (!c[dp][i]) {
          c[dp][i] = x;
          break;
        } else
          x ^= c[dp][i];
      }
  }
  int query(int dp, int x) {
    for (int i = M - 1; ~i; --i)
      if ((x ^ c[dp][i]) < x) x ^= c[dp][i];
    return x;
  }
} B;
}  // namespace Linear_Basis
using namespace Linear_Basis;
namespace Graph {
int top, ind, cnt;
int fa[N], siz[N], val[N], stk[N];
struct edge {
  int u, v, w, s, t;
  edge(int _u = 0, int _v = 0, int _w = 0, int _s = 0, int _t = 0)
      : u(_u), v(_v), w(_w), s(_s), t(_t) {}
} e[N << 1];
int findf(int x) {
  while (fa[x] ^ x) x = fa[x];
  return x;
}
int getval(int x) {
  int res = 0;
  while (fa[x] ^ x) res ^= val[x], x = fa[x];
  return res;
}
void undo(int goal) {
  while (top > goal) {
    int x = stk[top--];
    siz[fa[x]] -= siz[x];
    fa[x] = x;
    val[x] = 0;
  }
}
void add(int d, int dp) {
  for (auto i : vec[d]) {
    int x = e[i].u, y = e[i].v, fx = findf(x), fy = findf(y);
    if (fx ^ fy) {
      if (siz[fx] > siz[fy]) swap(fx, fy), swap(x, y);
      fa[fx] = fy;
      siz[fy] += siz[fx];
      val[fx] = getval(x) ^ getval(y) ^ e[i].w;
      stk[++top] = fx;
    } else {
      int w = getval(x) ^ getval(y) ^ e[i].w;
      B.insert(dp, w);
    }
  }
}
}  // namespace Graph
using namespace Graph;
namespace Seg {
struct Segment {
  void update(int x, int l, int r, int L, int R, int id) {
    if (L > R) return;
    if (L <= l && r <= R) {
      vec[x].push_back(id);
      return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid) update((x << 1), l, mid, L, R, id);
    if (R > mid) update((x << 1 | 1), mid + 1, r, L, R, id);
  }
  void solve(int x, int l, int r, int dp) {
    int tmp = top;
    add(x, dp);
    if (l == r) {
      int u = qr[l].first, v = qr[l].second, w = getval(u) ^ getval(v);
      printf("%d\n", B.query(dp, w));
      undo(tmp);
      return;
    }
    int mid = (l + r) >> 1;
    B.copy(dp + 1, dp);
    solve((x << 1), l, mid, dp + 1);
    B.copy(dp + 1, dp);
    solve((x << 1 | 1), mid + 1, r, dp + 1);
    undo(tmp);
  }
} tr;
}  // namespace Seg
using namespace Seg;
namespace DreamLolita {
int n, m, Q;
void solution() {
  initfc();
  n = read();
  m = read();
  for (int i = 1; i <= m; ++i) {
    int x = read(), y = read(), w = read();
    e[++cnt] = edge(x, y, w, 1, -1);
    mp[make_pair(x, y)] = cnt;
  }
  Q = read();
  for (int i = 1; i <= Q; ++i) {
    int op = read(), x = read(), y = read();
    if (op == 1) {
      int w = read();
      e[++cnt] = edge(x, y, w, ind + 1, -1);
      mp[make_pair(x, y)] = cnt;
    } else if (op == 2)
      e[mp[make_pair(x, y)]].t = ind, mp[make_pair(x, y)] = 0;
    else
      qr[++ind] = make_pair(x, y);
  }
  for (int i = 1; i <= cnt; ++i)
    tr.update(1, 1, ind, e[i].s, e[i].t == -1 ? ind : e[i].t, i);
  for (int i = 1; i <= n; ++i) fa[i] = i, siz[i] = 1;
  tr.solve(1, 1, ind, 0);
}
}  // namespace DreamLolita
int main() {
  DreamLolita::solution();
  return 0;
}
