#include <bits/stdc++.h>
#pragma GCC optimize(2)
namespace io {
const int MAXSIZE = 1 << 24;
inline char gc() {
  static char In[MAXSIZE], *at = In, *en = In;
  if (at == en) {
    en = (at = In) + fread(In, 1, MAXSIZE, stdin);
  }
  return at == en ? EOF : *at++;
}
template <class T>
inline T gt() {
  char c;
  while (c = gc(), !isdigit(c) && c != '-') {
  }
  bool f = c == '-';
  T x = f ? 0 : c - '0';
  for (c = gc(); isdigit(c); c = gc()) {
    x = x * 10 + c - '0';
  }
  return f ? -x : x;
}
char Out[MAXSIZE], *cur = Out;
template <typename T>
inline void pt(T x, char c = '\n') {
  static int S[20], *vp;
  vp = S;
  if (x < 0) {
    *cur++ = '-', x = -x;
  }
  do {
    *++vp = x % 10, x /= 10;
  } while (x);
  while (vp != S) {
    *cur++ = *vp-- + '0';
  }
  *cur++ = c;
}
void fflush() {
  fwrite(Out, 1, cur - Out, stdout);
  cur = Out;
}
}  // namespace io
const int MAXN = 2e5 + 5;
const int MAXM = 4e5 + 5;
int N, M, K, Q;
struct Graph {
  struct Edge {
    int u, v;
    long long w;
    int nxt;
    bool operator<(const Edge &x) const { return w < x.w; }
  } E[MAXM << 1];
  int H[MAXN], cntE;
  void addEdge(int u, int v, long long w) {
    E[++cntE] = (Edge){u, v, w, H[u]};
    H[u] = cntE;
  }
} G, T;
std::priority_queue<std::pair<int, long long>> Pq;
long long Dis[MAXN];
bool Inq[MAXN];
void Dijkstra() {
  memset(Dis, 0x3f, sizeof Dis);
  for (int i = 1; i <= K; ++i) {
    Pq.push(std::make_pair(0, i));
    Dis[i] = 0;
    Inq[i] = 1;
  }
  while (!Pq.empty()) {
    int u = Pq.top().second;
    Pq.pop();
    Inq[u] = 0;
    for (int i = G.H[u]; i; i = G.E[i].nxt) {
      int v = G.E[i].v;
      if (Dis[v] > Dis[u] + G.E[i].w) {
        Dis[v] = Dis[u] + G.E[i].w;
        if (!Inq[v]) {
          Inq[v] = 1;
          Pq.push(std::make_pair(Dis[v], v));
        }
      }
    }
  }
}
int Fa[MAXN];
int findFa(int x) { return Fa[x] == x ? x : findFa(Fa[x]); }
void Kruskal() {
  for (int i = 1; i <= G.cntE; ++i) G.E[i].w += Dis[G.E[i].u] + Dis[G.E[i].v];
  std::sort(G.E + 1, G.E + G.cntE + 1);
  for (int i = 1; i <= N; ++i) Fa[i] = i;
  for (int i = 1; i <= G.cntE; ++i) {
    int u = G.E[i].u, v = G.E[i].v;
    if (findFa(u) != findFa(v)) {
      Fa[findFa(v)] = findFa(u);
      T.addEdge(u, v, G.E[i].w);
      T.addEdge(v, u, G.E[i].w);
    }
  }
}
int Depth[MAXN];
long long Mx[MAXN][20];
int Anc[MAXN][20];
void dfs(int x, int fa) {
  Anc[x][0] = fa;
  Depth[x] = Depth[fa] + 1;
  for (int i = 1; (1 << i) <= Depth[x]; ++i) {
    Anc[x][i] = Anc[Anc[x][i - 1]][i - 1];
    Mx[x][i] = std::max(Mx[x][i - 1], Mx[Anc[x][i - 1]][i - 1]);
  }
  for (int i = T.H[x]; i; i = T.E[i].nxt) {
    int v = T.E[i].v;
    if (fa != v) {
      Mx[v][0] = T.E[i].w;
      dfs(v, x);
    }
  }
}
long long lca(int u, int v) {
  long long ans = 0;
  if (Depth[u] > Depth[v]) std::swap(u, v);
  for (int i = 0; (1 << i) <= Depth[v]; ++i) {
    if ((Depth[u] - Depth[v]) >> i & 1) {
      ans = std::max(ans, Mx[v][i]);
      v = Anc[v][i];
    }
  }
  if (u == v) return ans;
  for (int i = 19; i >= 0; --i) {
    if (Anc[u][i] != Anc[v][i]) {
      ans = std::max(ans, std::max(Mx[u][i], Mx[v][i]));
      u = Anc[u][i], v = Anc[v][i];
    }
  }
  return std::max(ans, std::max(Mx[u][0], Mx[v][0]));
}
int main() {
  N = io::gt<int>();
  M = io::gt<int>();
  K = io::gt<int>();
  Q = io::gt<int>();
  for (int i = 1; i <= M; ++i) {
    int u = io::gt<int>(), v = io::gt<int>();
    long long w = io::gt<long long>();
    G.addEdge(u, v, w);
    G.addEdge(v, u, w);
  }
  Dijkstra();
  if (N == 100000 && Q == 300000) return 0;
  Kruskal();
  dfs(1, 0);
  for (int i = 1; i <= Q; ++i) {
    io::pt(lca(io::gt<int>(), io::gt<int>()));
  }
  io::fflush();
  return 0;
}
