#include <bits/stdc++.h>
using namespace std;
int p[123456];
set<int> has[123456];
vector<pair<int, int> > v[123456];
int getRoot(int a) {
  if (a == p[a]) return a;
  return p[a] = getRoot(p[a]);
}
void mrge(int a, int b) {
  int A = getRoot(a);
  int B = getRoot(b);
  if (A == B) return;
  if ((int)has[A].size() < (int)has[B].size()) {
    swap(A, B);
  }
  for (auto it : has[B]) {
    has[A].insert(it);
  }
  p[B] = A;
}
map<pair<int, int>, set<int> > M;
int main() {
  int n, m, c, q;
  scanf("%d%d%d%d", &n, &m, &c, &q);
  for (auto(i) = (0); (i) < (n + 1); (i)++) {
    p[i] = i;
    has[i].insert(i);
  }
  for (auto(i) = (0); (i) < (m); (i)++) {
    int x, y, z;
    scanf("%d %d %d ", &x, &y, &z);
    M[{x, z}].insert(y);
    M[{y, z}].insert(x);
    has[x].insert(y);
    has[y].insert(x);
  }
  for (auto itt : M) {
    auto it = itt.second;
    int f = *(it.begin());
    for (auto z : it) {
      mrge(z, f);
    }
  }
  for (auto(i) = (0); (i) < (q); (i)++) {
    char t;
    scanf(" %c ", &t);
    if (t == '?') {
      int a, b;
      scanf("%d %d\n", &a, &b);
      int A = getRoot(a);
      int B = getRoot(b);
      if (A == B || has[A].find(b) != has[A].end()) {
        printf("Yes\n");
      } else {
        printf("No\n");
      }
    } else {
      int x, y, z;
      scanf("%d %d %d ", &x, &y, &z);
      M[{x, z}].insert(y);
      M[{y, z}].insert(x);
      int X = getRoot(x);
      int Y = getRoot(y);
      has[X].insert(y);
      has[Y].insert(x);
      mrge(*(M[{x, z}].begin()), y);
      mrge(*(M[{x, z}].rbegin()), y);
      mrge(*(M[{y, z}].begin()), x);
      mrge(*(M[{y, z}].rbegin()), x);
    }
  }
  return 0;
}
