#include <bits/stdc++.h>
using namespace std;
int n, m, c, p, root[100005];
set<int> Q[100005];
set<pair<int, int> > P[100005];
int getroot(int u) {
  if (!root[u]) return u;
  return (root[u] = getroot(root[u]));
}
int main() {
  cin >> n >> m >> c >> p;
  for (int i = 1; i <= m; i++) {
    int u, v, c;
    cin >> u >> v >> c;
    int x = getroot(u);
    int y = getroot(v);
    Q[x].insert(v);
    Q[y].insert(u);
    set<pair<int, int> >::iterator k = P[u].lower_bound({c, 0});
    if (k != P[u].end() && (*k).first == c) {
      int t = getroot((*k).second);
      if (t != y) {
        if (Q[t].size() <= Q[y].size()) {
          root[t] = y;
          set<int>::iterator a = Q[t].begin();
          while (a != Q[t].end()) {
            Q[y].insert(*a);
            ++a;
          }
        } else {
          root[y] = t;
          set<int>::iterator a = Q[y].begin();
          while (a != Q[y].end()) {
            Q[t].insert(*a);
            ++a;
          }
        }
      }
    } else
      P[u].insert({c, v});
    k = P[v].lower_bound({c, 0});
    if (k != P[v].end() && (*k).first == c) {
      int t = getroot((*k).second);
      if (t != x) {
        int t = getroot((*k).second);
        if (t != x) {
          if (Q[t].size() <= Q[x].size()) {
            root[t] = x;
            set<int>::iterator a = Q[t].begin();
            while (a != Q[t].end()) {
              Q[x].insert(*a);
              ++a;
            }
          } else {
            root[x] = t;
            set<int>::iterator a = Q[x].begin();
            while (a != Q[x].end()) {
              Q[t].insert(*a);
              ++a;
            }
          }
        }
      }
    } else
      P[v].insert({c, u});
  }
  while (p--) {
    char k;
    cin >> k;
    if (k == '+') {
      int u, v, c;
      cin >> u >> v >> c;
      int x = getroot(u);
      int y = getroot(v);
      Q[x].insert(v);
      Q[y].insert(u);
      set<pair<int, int> >::iterator k = P[u].lower_bound({c, 0});
      if (k != P[u].end() && (*k).first == c) {
        int t = getroot((*k).second);
        if (t != y) {
          if (Q[t].size() <= Q[y].size()) {
            root[t] = y;
            set<int>::iterator a = Q[t].begin();
            while (a != Q[t].end()) {
              Q[y].insert(*a);
              ++a;
            }
          } else {
            root[y] = t;
            set<int>::iterator a = Q[y].begin();
            while (a != Q[y].end()) {
              Q[t].insert(*a);
              ++a;
            }
          }
        }
      } else
        P[u].insert({c, v});
      k = P[v].lower_bound({c, 0});
      if (k != P[v].end() && (*k).first == c) {
        int t = getroot((*k).second);
        if (t != x) {
          if (Q[t].size() <= Q[x].size()) {
            root[t] = x;
            set<int>::iterator a = Q[t].begin();
            while (a != Q[t].end()) {
              Q[x].insert(*a);
              ++a;
            }
          } else {
            root[x] = t;
            set<int>::iterator a = Q[x].begin();
            while (a != Q[x].end()) {
              Q[t].insert(*a);
              ++a;
            }
          }
        }
      } else
        P[v].insert({c, u});
    } else {
      int u, v;
      cin >> u >> v;
      int x = getroot(u);
      int y = getroot(v);
      if (x == y)
        cout << "Yes" << endl;
      else if (Q[x].find(v) != Q[x].end())
        cout << "Yes" << endl;
      else
        cout << "No" << endl;
    }
  }
}
