#include <bits/stdc++.h>
using namespace std;
template <typename T1, typename T2>
inline T1 max(T1 a, T2 b) {
  return a < b ? b : a;
}
template <typename T1, typename T2>
inline T1 min(T1 a, T2 b) {
  return a < b ? a : b;
}
namespace ae86 {
const int bufl = 1 << 15;
char buf[bufl], *s = buf, *t = buf;
inline int fetch() {
  if (s == t) {
    t = (s = buf) + fread(buf, 1, bufl, stdin);
    if (s == t) return EOF;
  }
  return *s++;
}
inline int ty() {
  int a = 0, b = 1, c = fetch();
  while (!isdigit(c)) b ^= c == '-', c = fetch();
  while (isdigit(c)) a = a * 10 + c - 48, c = fetch();
  return b ? a : -a;
}
}  // namespace ae86
using ae86::ty;
const int _ = 200007, lglg = 30;
struct edges {
  int a, b, val;
  edges(int _a = 0, int _b = 0, int _val = 0) { a = _a, b = _b, val = _val; }
};
struct linegay {
  int val[lglg + 1];
  linegay() { memset(val, 0, sizeof(val)); }
  inline void push(int x) {
    for (int i = lglg; i >= 0; i--) {
      if (!(((x) >> (i)) & 1)) continue;
      if (!(((val[i]) >> (i)) & 1)) {
        val[i] = x;
        return;
      }
      x ^= val[i];
    }
  }
  inline int finder(int x) {
    for (int i = lglg; i >= 0; i--)
      if ((x ^ val[i]) < x) x ^= val[i];
    return x;
  }
};
int bfa[_], bva[_], bsz[_];
inline int findbfa(int x) { return bfa[x] == x ? x : findbfa(bfa[x]); }
inline int findbva(int x) { return bfa[x] == x ? 0 : bva[x] ^ findbva(bfa[x]); }
int qryx[_], qryy[_];
struct dsgt {
  static const int _t = _ << 2;
  vector<edges> t[_t];
  inline void push(int x, int l, int r, int tl, int tr, edges dlt) {
    if (l > r || tl > tr) return;
    if (tl <= l && r <= tr) {
      t[x].emplace_back(dlt);
      return;
    }
    if (tl <= ((l + r) >> 1)) push((x << 1), l, ((l + r) >> 1), tl, tr, dlt);
    if (tr > ((l + r) >> 1))
      push((x << 1 | 1), ((l + r) >> 1) + 1, r, tl, tr, dlt);
  }
  struct cookies {
    int aa, bb;
    cookies(int _aa = 0, int _bb = 0) { aa = _aa, bb = _bb; }
  };
  void dfs(int x, int l, int r, linegay gay) {
    vector<cookies> cxk;
    for (auto i : t[x]) {
      int a = i.a, b = i.b, val = i.val, aa = findbfa(a), bb = findbfa(b);
      val ^= findbva(a) ^ findbva(b);
      if (aa == bb) {
        gay.push(val);
        continue;
      }
      if (bsz[aa] < bsz[bb]) swap(aa, bb);
      bfa[bb] = aa, bva[bb] = val, bsz[aa] += bsz[bb], cxk.emplace_back(aa, bb);
    }
    if (l == r)
      printf("%d\n", gay.finder(findbva(qryx[l]) ^ findbva(qryy[l])));
    else
      dfs((x << 1), l, ((l + r) >> 1), gay),
          dfs((x << 1 | 1), ((l + r) >> 1) + 1, r, gay);
    while (!cxk.empty()) {
      auto i = cxk.back();
      cxk.pop_back();
      int aa = i.aa, bb = i.bb;
      bfa[bb] = bb, bva[bb] = 0, bsz[aa] -= bsz[bb];
    }
  }
} t;
struct edgesbuff {
  edges a;
  int l, r;
  edgesbuff(edges _a = edges(), int _l = 0, int _r = 0) {
    a = _a, l = _l, r = _r;
  }
};
vector<edgesbuff> temp;
int n, m, qn, qcnt = 0;
unordered_map<int, int> val[_], las[_];
int main() {
  n = ty(), m = ty();
  for (int i = 1, a, b, c; i <= m; i++)
    a = ty(), b = ty(), c = ty(), val[a][b] = c, las[a][b] = 1;
  qn = ty();
  for (int i = 1; i <= qn; i++) {
    int op = ty();
    if (op == 1) {
      int a = ty(), b = ty(), c = ty();
      val[a][b] = c, las[a][b] = qcnt + 1;
    } else if (op == 2) {
      int a = ty(), b = ty();
      temp.emplace_back(edges(a, b, val[a][b]), las[a][b], qcnt);
      val[a].erase(b), las[a].erase(b);
    } else if (op == 3)
      qcnt++, qryx[qcnt] = ty(), qryy[qcnt] = ty();
  }
  for (int i = 1; i <= n; val[i].clear(), las[i].clear(), i++)
    for (auto j : val[i])
      temp.emplace_back(edges(i, j.first, j.second), las[i][j.first], qcnt);
  for (auto i : temp) t.push(1, 1, qcnt, i.l, i.r, i.a);
  for (int i = 1; i <= n; i++) bfa[i] = i, bva[i] = 0, bsz[i] = 1;
  t.dfs(1, 1, qcnt, linegay());
  return 0;
}
