#include <bits/stdc++.h>
using namespace std;
int read() {
  int x = 0;
  char c = getchar();
  int f = 1;
  while (c < '0' || c > '9') {
    if (c == '-') f = -f;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = (x << 3) + (x << 1) + c - '0';
    c = getchar();
  }
  return x * f;
}
void write(int x) {
  if (x < 0) {
    x = -x;
    putchar('-');
  }
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
struct Line_Base {
  int b[31];
  void Insert(int w) {
    for (register int i = 29; i >= 0; --i) {
      if (!(w >> i)) continue;
      if (!b[i]) {
        b[i] = w;
        break;
      }
      w ^= b[i];
    }
  }
  int query(int w) {
    for (register int i = 29; i >= 0; --i) {
      if (!((w >> i) & 1)) continue;
      w ^= b[i];
    }
    return w;
  }
} Lb[20];
struct T {
  int fa, w, size;
};
struct Dsu {
  T t[200005];
  int findSet(int x, int &w) {
    while (x != t[x].fa) w ^= t[x].w, x = t[x].fa;
    return x;
  }
  void Merge(int x, int y, int w, int id) {
    if (x == y) return Lb[id].Insert(w);
    if (t[x].size > t[y].size) swap(x, y);
    t[x].fa = y, t[x].w = w, t[y].size += t[x].size;
  }
  int query(int x, int y, int id) {
    int w = 0;
    findSet(x, w), findSet(y, w);
    return Lb[id].query(w);
  }
} dsu;
struct Node {
  int x, y;
};
vector<Node> Que[200005];
struct Edge {
  int x, y, l, r, w;
} e[200005 << 1];
class Segment {
 private:
  T stt[200005 << 2];
  int top, tag[20], st[200005 << 2];
  vector<Edge> tree[200005 << 2];

 public:
  void update(int k, int l, int r, Edge e) {
    if (l > e.r || e.l > r) return;
    if (e.l <= l && r <= e.r) return tree[k].push_back(e);
    int mid = (l + r) >> 1;
    update(k << 1, l, mid, e);
    update(k << 1 | 1, mid + 1, r, e);
  }
  void Insert(int k, int d) {
    Lb[d] = Lb[d - 1], tag[d] = top;
    for (register int i = 0; i < tree[k].size(); ++i) {
      int x = tree[k][i].x, y = tree[k][i].y, wx = 0, wy = 0;
      x = dsu.findSet(x, wx), y = dsu.findSet(y, wy);
      st[++top] = x, stt[top] = dsu.t[x];
      st[++top] = y, stt[top] = dsu.t[y];
      dsu.Merge(x, y, wx ^ wy ^ tree[k][i].w, d);
    }
  }
  void Delete(int d) {
    while (top > tag[d]) dsu.t[st[top]] = stt[top--];
  }
  void Divide(int k, int l, int r, int d) {
    Insert(k, d);
    if (l == r) {
      for (register int i = 0; i < Que[l].size(); ++i)
        write(dsu.query(Que[l][i].x, Que[l][i].y, d)), putchar('\n');
      return Delete(d);
    }
    int mid = (l + r) >> 1;
    Divide(k << 1, l, mid, d + 1);
    Divide(k << 1 | 1, mid + 1, r, d + 1);
    Delete(d);
  }
} Tree;
map<int, int> mp[200005];
signed main() {
  int n = read(), m = read();
  for (register int i = 1; i <= n; ++i) dsu.t[i] = T{i, 0, 1};
  for (register int i = 1; i <= m; ++i) {
    int u = read(), v = read();
    e[mp[u][v] = mp[v][u] = i] = Edge{u, v, 1, 0, read()};
  }
  int q = read();
  int ecnt = m, cnt = 1;
  for (register int i = 1; i <= q; ++i) {
    int type = read(), x = read(), y = read();
    if (type == 1)
      e[mp[x][y] = mp[y][x] = ++ecnt] = Edge{x, y, ++cnt, 0, read()};
    else if (type == 2)
      e[mp[x][y]].r = cnt++;
    else
      Que[cnt].push_back(Node{x, y});
  }
  for (register int i = 1; i <= ecnt; ++i)
    e[i].r = (e[i].r == 0 ? cnt : e[i].r), Tree.update(1, 1, cnt, e[i]);
  Tree.Divide(1, 1, cnt, 1);
  return 0;
}
