#include <bits/stdc++.h>
using namespace std;
long long MOD = 1000000007;
struct DSU {
  vector<int> rep;
  vector<set<int>> edges;
  void clear() { iota((rep).begin(), (rep).end(), 0); }
  DSU(int n = 0) : rep(n + 1), edges(n + 1) { clear(); }
  int find(int a) { return rep[a] == a ? a : rep[a] = find(rep[a]); }
  void merge(int a, int b) {
    if (a == 0 or b == 0) return;
    if (edges[a = find(a)].size() < edges[b = find(b)].size()) swap(a, b);
    if (a == b) return;
    for (auto x : edges[b]) edges[a].insert(x);
    rep[b] = a;
  }
  void add_edge(int a, int b) {
    edges[find(a)].insert(b), edges[find(b)].insert(a);
  }
};
const long long LIM = 1e5 + 5;
map<long long, long long> mmap[LIM];
void solve() {
  long long n, m, c, q;
  cin >> n >> m >> c >> q;
  DSU D = DSU(n);
  for (long long i = 0; i < m; i++) {
    long long x, y, z;
    cin >> x >> y >> z;
    D.merge(mmap[x][z], y);
    D.merge(mmap[y][z], x);
    mmap[x][z] = y;
    mmap[y][z] = x;
    D.add_edge(x, y);
  }
  for (long long i = 0; i < q; i++) {
    char ch;
    cin >> ch;
    if (ch == '?') {
      long long x, y;
      cin >> x >> y;
      long long rx = D.find(x);
      long long ry = D.find(y);
      bool bo = (rx == ry || D.edges[rx].find(y) != D.edges[rx].end());
      if (bo)
        cout << "Yes"
             << "\n";
      else
        cout << "No"
             << "\n";
    } else {
      long long x, y, z;
      cin >> x >> y >> z;
      D.merge(mmap[x][z], y);
      D.merge(mmap[y][z], x);
      mmap[x][z] = y;
      mmap[y][z] = x;
      D.add_edge(x, y);
    }
  }
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  long long t = 1;
  for (long long i = 0; i < t; i++) solve();
  return 0;
}
