#include <bits/stdc++.h>
using namespace std;
const long long MAX = 1e5 + 20;
vector<long long> adj[MAX];
struct DisjointSetUnion {
  long long parent[MAX];
  long long size[MAX];
  void make_set(long long v) {
    parent[v] = v;
    size[v] = 1;
  }
  long long find(long long v) {
    if (v == parent[v]) {
      return v;
    }
    return parent[v] = find(parent[v]);
  }
  void join(long long a, long long b) {
    a = find(a);
    b = find(b);
    if (a != b) {
      if (size[a] < size[b]) {
        swap(a, b);
      }
      parent[b] = a;
      size[a] += size[b];
    }
  }
} dsu;
map<pair<long long, long long>, vector<long long> > adj2;
void addEdge(long long u, long long v, long long c) {
  u--;
  v--;
  c--;
  adj[u].push_back(v);
  adj[v].push_back(u);
  adj2[{c, u}].push_back(v);
  adj2[{c, v}].push_back(u);
}
set<long long> neigh[MAX];
bool query(long long u, long long v) {
  u--;
  v--;
  if (dsu.find(u) == dsu.find(v)) {
    return true;
  }
  if (neigh[dsu.find(u)].find(v) != neigh[dsu.find(u)].end()) {
    return true;
  }
  return false;
}
void add(long long u, long long v, long long c) {
  u--;
  v--;
  c--;
  if ((long long)adj2[{c, u}].size() > 0) {
    long long a = dsu.find(adj2[{c, u}][0]);
    long long b = dsu.find(v);
    if (a != b) {
      dsu.join(adj2[{c, u}][0], v);
      long long c = dsu.find(v);
      if (c == a) {
        neigh[a].insert(neigh[b].begin(), neigh[b].end());
      } else {
        neigh[b].insert(neigh[a].begin(), neigh[a].end());
      }
    }
  }
  if ((long long)adj2[{c, v}].size() > 0) {
    long long a = dsu.find(adj2[{c, v}][0]);
    long long b = dsu.find(u);
    if (a != b) {
      dsu.join(adj2[{c, v}][0], u);
      long long c = dsu.find(u);
      if (c == a) {
        neigh[a].insert(neigh[b].begin(), neigh[b].end());
      } else {
        neigh[b].insert(neigh[a].begin(), neigh[a].end());
      }
    }
  }
  adj2[{c, u}].push_back(v);
  adj2[{c, v}].push_back(u);
  if (dsu.find(u) != dsu.find(v)) {
    neigh[dsu.find(u)].insert(v);
    neigh[dsu.find(v)].insert(u);
  }
}
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);
  long long n, m, c, q;
  cin >> n >> m >> c >> q;
  for (long long i = 0; i < (long long)n; i++) {
    dsu.make_set(i);
  }
  for (long long i = 0; i < (long long)m; i++) {
    long long u, v, c;
    cin >> u >> v >> c;
    addEdge(u, v, c);
  }
  for (pair<pair<long long, long long>, vector<long long> > p : adj2) {
    for (long long x : p.second) {
      if (x == p.second[0]) {
        continue;
      }
      dsu.join(p.second[0], x);
    }
  }
  for (long long i = 0; i < (long long)n; i++) {
    for (long long v : adj[i]) {
      neigh[dsu.find(i)].insert(v);
    }
  }
  for (long long i = 0; i < (long long)q; i++) {
    char x;
    cin >> x;
    long long u, v;
    cin >> u >> v;
    if (x == '?') {
      if (query(u, v)) {
        cout << "Yes" << '\n';
      } else {
        cout << "No" << '\n';
      }
    } else {
      long long c;
      cin >> c;
      add(u, v, c);
    }
  }
  return 0;
}
