import std.algorithm;
import std.bigint;
import std.bitmanip;
import std.concurrency;
import std.container;
import std.conv;
import std.functional;
import std.format;
import std.math;
import std.meta;
import std.numeric;
import std.random;
import std.range;
import std.stdio;
import std.string;
import std.traits;
import std.typecons;


auto solve(long N, long M, long L, long[] A, long[] B, long[] C, long Q, long[] s, long[] t) {
    auto g = AdjacencyListGraph(N + 1, A ~ B, B ~ A, C ~ C);
    
    auto adj = new long[][](N + 1, N + 1);
    foreach (row; adj) row.fill(long.max);

    void dig(long s, long c, long fuel) {
        if (fuel >= 0 && adj[s][c] >= long.max) 
            adj[s][c] = adj[c][s] = (s == c ? 0 : 1);
        else
            return;

        foreach (e; g.edgesFrom(c))
            dig(s, e.to, fuel - e.cost);
    }

    foreach (i; 1 .. N + 1) dig(i, i, L);

    foreach (k; 1 .. N + 1)
        foreach (i; 1 .. N + 1)
            foreach (j; 1 .. N + 1)
                if (adj[i][k] != long.max && adj[k][j] != long.max)
                    adj[i][j] = min(adj[i][j], adj[i][k] + adj[k][j]);
    
    foreach (si, ti; zip(s, t)) {
        if (adj[si][ti] == long.max)
            writeln(-1);
        else
            writeln(adj[si][ti] - 1);
    }
}


void main() {
    auto input = stdin.byLine.map!split.joiner;

    long N;
    N = input.front.to!long;
    input.popFront;

    long M;
    M = input.front.to!long;
    input.popFront;

    long L;
    L = input.front.to!long;
    input.popFront;

    long[] A = new long[](cast(size_t) (M));
    long[] B = new long[](cast(size_t) (M));
    long[] C = new long[](cast(size_t) (M));
    foreach (i; 0 .. cast(size_t) (M)) {
        A[i] = input.front.to!long;
        input.popFront;
        B[i] = input.front.to!long;
        input.popFront;
        C[i] = input.front.to!long;
        input.popFront;
    }

    long Q;
    Q = input.front.to!long;
    input.popFront;

    long[] s = new long[](cast(size_t) (Q));
    long[] t = new long[](cast(size_t) (Q));
    foreach (i; 0 .. cast(size_t) (Q)) {
        s[i] = input.front.to!long;
        input.popFront;
        t[i] = input.front.to!long;
        input.popFront;
    }

    static if (is(ReturnType!solve == void))
        solve(N, M, L, A, B, C, Q, s, t);
    else
        solve(N, M, L, A, B, C, Q, s, t).writeln;
}


struct AdjacencyListGraph {
public:
    alias EdgeIDList = SList!long;
    alias AdjacencyList = EdgeIDList[];
    
    struct Edge {
        long from;
        long to;
        long cost;
    }

private:
    AdjacencyList adj;
    long nodesSize_;
    Edge[] edges_;
    
public:
    this(long N, long[] from, long[] to, long[] cost = []) {
        const hasCosts = !cost.empty;
        assert(from.length == to.length);
        assert(!hasCosts || cost.length == from.length);
        
        adj = new AdjacencyList(N);
        nodesSize_ = N;
        edges_ = new Edge[](from.length);

        foreach (ref dests; adj)
            dests = make!EdgeIDList;
        foreach (i; 0 .. from.length) {
            edges_[i] = Edge(from[i], to[i], hasCosts ? cost[i] : 0);
            adj[from[i]].insert(i);
        }
    }

    auto destsFrom(long from) {
        return adj[from][].map!(i => edges[i].to);
    }

    auto edgesFrom(long from) {
        return adj[from][].map!(i => edges[i]);
    }

    @property Edge[] edges() {
        return edges_;
    }

    @property long nodesSize() {
        return nodesSize_;
    }
}
