#include <bits/stdc++.h>
using namespace std;
struct Set {
  int f[100005];
  set<int> g[100005];
  void init(int n) {
    for (int i = 1; i <= n; i++) f[i] = i;
  }
  int get_parent(int i) {
    if (f[i] == i) return f[i];
    f[i] = get_parent(f[i]);
    return f[i];
  }
  bool in_set(int i, int j) { return get_parent(i) == get_parent(j); }
  void combine_e(int i, int j) {
    if (in_set(i, j)) return;
    f[f[j]] = f[i];
  }
  void combine(int i, int j) {
    if (in_set(i, j)) return;
    if (g[f[i]].size() < g[f[j]].size()) swap(i, j);
    set<int>::iterator it = g[f[j]].begin();
    for (; it != g[f[j]].end(); it++) {
      g[f[i]].insert(*it);
    }
    g[f[j]].clear();
    f[f[j]] = f[i];
  }
} s;
struct adj {
  int size;
  int head[100005];
  int next[100005 << 2];
  int node[100005 << 2];
  int edge[100005 << 2];
  void add_edge(int x, int y, int c) {
    ++size;
    next[size] = head[x];
    node[size] = y;
    edge[size] = c;
    head[x] = size;
  }
} a;
int cl[100005];
unordered_map<long long, int> h;
inline long long unique_value(long long x, long long z) {
  return x * 100005 + z;
}
void connect_r(int n) {
  s.init(n);
  for (int u = 1; u <= n; u++) {
    for (int i = a.head[u]; i; i = a.next[i]) {
      if (!cl[a.edge[i]]) {
        cl[a.edge[i]] = a.node[i];
        h.insert(make_pair(unique_value(u, a.edge[i]), a.node[i]));
      } else
        s.combine_e(cl[a.edge[i]], a.node[i]);
    }
    for (int i = a.head[u]; i; i = a.next[i]) cl[a.edge[i]] = 0;
  }
  for (int u = 1; u <= n; u++) {
    int p = s.get_parent(u);
    for (int i = a.head[u]; i; i = a.next[i]) {
      s.g[p].insert(a.node[i]);
    }
  }
}
void update(int x, int y, int z) {
  unordered_map<long long, int>::iterator i;
  long long p = unique_value(x, z);
  i = h.find(p);
  if (i == h.end())
    h.insert(make_pair(p, y));
  else
    s.combine(y, i->second);
  p = unique_value(y, z);
  i = h.find(p);
  if (i == h.end())
    h.insert(make_pair(p, x));
  else
    s.combine(x, i->second);
  a.add_edge(x, y, z);
  a.add_edge(y, x, z);
  s.g[s.get_parent(x)].insert(y);
  s.g[s.get_parent(y)].insert(x);
}
bool enquiry(int x, int y) {
  if (s.in_set(x, y)) return true;
  int p = s.get_parent(x);
  if (s.g[p].find(y) != s.g[p].end()) return true;
  return false;
}
int n, m, c, q;
int main() {
  scanf("%d%d%d%d", &n, &m, &c, &q);
  for (int i = 0; i < m; i++) {
    int x, y, z;
    scanf("%d%d%d", &x, &y, &z);
    a.add_edge(x, y, z);
    a.add_edge(y, x, z);
  }
  connect_r(n);
  char o;
  for (int i = 0; i < q; i++) {
    int x, y;
    cin >> o;
    scanf("%d%d", &x, &y);
    if (o == '?') {
      if (enquiry(x, y))
        printf("Yes\n");
      else
        printf("No\n");
    } else {
      int z;
      scanf("%d", &z);
      update(x, y, z);
    }
  }
  return 0;
}
