#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,popcnt,abm,mmx,tune=native")
#pragma GCC optimize("fast-math")
using namespace std;
mt19937 rnd(time(nullptr));
const long long INF = 1e18, LOG = 20;
long long n, m, k, q;
vector<vector<pair<long long, long long> > > graph, tree;
vector<long long> dist, parent, depth, tin, tout;
vector<vector<pair<long long, long long> > > up;
long long timer = 0;
long long get_par(long long v) {
  if (parent[v] == v) return v;
  long long res = get_par(parent[v]);
  parent[v] = res;
  return res;
}
void dfs(long long v, long long par, long long weight) {
  timer++;
  tin[v] = timer;
  up[v][0].first = par;
  for (long long i = 1; i < LOG; i++)
    up[v][i].first = up[up[v][i - 1].first][i - 1].first;
  up[v][0].second = weight;
  for (long long i = 1; i < LOG; i++)
    up[v][i].second =
        max(up[up[v][i - 1].first][i - 1].second, up[v][i - 1].second);
  for (auto el : tree[v]) {
    long long to = el.first, w = el.second;
    if (to == par) continue;
    dfs(to, v, w);
  }
  timer++;
  tout[v] = timer;
}
bool check(long long a, long long b) {
  return (tin[a] <= tin[b] && tout[a] >= tout[b]);
}
long long lca(long long a, long long b) {
  if (check(a, b)) return 0;
  if (check(b, a)) return 0;
  long long res = 0;
  for (long long i = LOG - 1; i >= 0; i--) {
    if (!check(up[a][i].first, b)) {
      res = max(res, up[a][i].second);
      a = up[a][i].first;
    }
  }
  res = max(res, up[a][0].second);
  return res;
}
int32_t main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  cin >> n >> m >> k >> q;
  graph.resize(n);
  tree.resize(n);
  parent.resize(n);
  depth.resize(n);
  dist.resize(n, INF);
  up.resize(n);
  tin.resize(n);
  tout.resize(n);
  for (long long i = 0; i < n; i++) up[i].resize(LOG);
  for (long long i = 0; i < m; i++) {
    long long v, u, w;
    cin >> v >> u >> w;
    v--;
    u--;
    graph[v].push_back({u, w});
    graph[u].push_back({v, w});
  }
  for (long long i = 0; i < k; i++) dist[i] = 0;
  set<pair<long long, long long> > all;
  for (long long i = 0; i < n; i++) all.insert({dist[i], i});
  vector<long long> used(n);
  while (!all.empty()) {
    pair<long long, long long> kek = *all.begin();
    all.erase(kek);
    long long v = kek.second, d = kek.first;
    used[v] = 1;
    for (auto elem : graph[v]) {
      long long to = elem.first, w = elem.second;
      if (!used[to] && dist[to] > d + w) {
        all.erase({dist[to], to});
        dist[to] = d + w;
        all.insert({dist[to], to});
      }
    }
  }
  for (long long i = 0; i < n; i++) {
    for (long long j = 0; j < (long long)graph[i].size(); j++) {
      long long to = graph[i][j].first;
      graph[i][j].second = dist[i] + dist[to] + graph[i][j].second;
    }
  }
  vector<vector<long long> > all_edges;
  for (long long i = 0; i < n; i++) {
    for (long long j = 0; j < (long long)graph[i].size(); j++) {
      long long to = graph[i][j].first;
      if (i < to) all_edges.push_back({graph[i][j].second, i, to});
    }
  }
  sort(all_edges.begin(), all_edges.end());
  for (long long i = 0; i < n; i++) parent[i] = i;
  for (auto el : all_edges) {
    long long w = el[0], a = el[1], b = el[2];
    long long c = get_par(a);
    long long d = get_par(b);
    if (c == d) continue;
    tree[a].push_back({b, w});
    tree[b].push_back({a, w});
    parent[c] = d;
  }
  dfs(0, 0, 0);
  while (q--) {
    long long a, b;
    cin >> a >> b;
    a--;
    b--;
    cout << max(lca(a, b), lca(b, a)) << "\n";
  }
  return 0;
}
