use std::collections::HashMap;
use std::io::{self, BufRead, BufWriter, Write};

fn solve<R, W>(reader: R, mut writer: W)
where
    R: BufRead,
    W: Write,
{
    let mut lines = reader.lines();
    let a_line = lines.next().unwrap().unwrap();
    let mut splited = a_line.trim().split_whitespace();
    let n: usize = splited.next().unwrap().parse().unwrap();
    let m: usize = splited.next().unwrap().parse().unwrap();
    let l: i32 = splited.next().unwrap().parse().unwrap();

    let mut distance: HashMap<usize, HashMap<usize, i32>> = HashMap::new();
    for _ in 1..m + 1 {
        let a_line = lines.next().unwrap().unwrap();
        let mut splited = a_line.trim().split_whitespace();
        let a: usize = splited.next().unwrap().parse().unwrap();
        let b: usize = splited.next().unwrap().parse().unwrap();
        let c: i32 = splited.next().unwrap().parse().unwrap();
        if c > l {
            continue;
        }

        {
            let entry = distance.entry(a).or_insert(HashMap::new());
            entry.insert(b, c);
        }
        {
            let entry = distance.entry(b).or_insert(HashMap::new());
            entry.insert(a, c);
        }
    }

    // make tables for all nodes. use from 1 to n (don't use 0)
    let mut results: Vec<Vec<(i32, i32, usize)>> = vec![vec![(0, 0, 0)]; n + 1];
    for source in 1..n + 1 {
        results[source] = dijkstra(&distance, n, l, source);
    }

    let _ = lines.next();
    let query = lines.map(|s| {
        let unwraped = s.unwrap();
        let mut splited = unwraped.trim().split_whitespace();
        let s: usize = splited.next().unwrap().parse().unwrap();
        let t: usize = splited.next().unwrap().parse().unwrap();
        (s, t)
    });
    for (s, t) in query {
        let table = &results[s];
        let (_, count, _) = table[t];
        writeln!(&mut writer, "{}", if count >= MAX { -1 } else { count }).unwrap();
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
struct QueueItem {
    gas_left: i32,
    count: i32,
    node: usize,
}

impl QueueItem {
    fn new(gas_left: i32, count: i32, node: usize) -> Self {
        QueueItem {
            gas_left: gas_left,
            count: count,
            node: node,
        }
    }
}

use std::cmp::Ordering;
impl PartialOrd for QueueItem {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        // lower count is better
        match other.count.cmp(&self.count) {
            Ordering::Equal => Some(self.gas_left.cmp(&other.gas_left)),
            x => Some(x),
        }
    }
}

impl Ord for QueueItem {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

static MAX: i32 = 1000;
fn dijkstra(
    distance: &HashMap<usize, HashMap<usize, i32>>,
    n: usize,
    l: i32,
    source: usize,
) -> Vec<(i32, i32, usize)> {
    let mut queue: Vec<QueueItem> = vec![QueueItem::new(0, MAX, 0); n + 1];
    let mut done: Vec<bool> = vec![false; n + 1];
    // use from 1 to n (Don't use 0). gas_left, count, previous_node
    let mut table: Vec<(i32, i32, usize)> = vec![(0, MAX, 0); n + 1];
    queue[source] = QueueItem::new(l, 0, source);
    table[source] = (l, 0, 0); // previous_node = 0 means NULL

    loop {
        let mut item = QueueItem::new(0, MAX, 0);
        for i in 1..queue.len() {
            if !done[i] && queue[i] > item {
                item = queue[i].clone();
            }
        }
        if item.count >= MAX {
            break;
        }
        done[item.node] = true;

        let neighbors = match distance.get(&item.node) {
            Some(n) => n,
            None => continue,
        };

        for &neighbor in neighbors.keys() {
            if let Some(&d) = neighbors.get(&neighbor) {
                let (new_gas_left, new_count) = if item.gas_left >= d {
                    (item.gas_left - d, item.count)
                } else if l >= d {
                    (l - d, item.count + 1)
                } else {
                    unreachable!();
                };
                let (gas_left, count, _) = table[neighbor];
                if new_count < count || new_count == count && new_gas_left > gas_left {
                    table[neighbor] = (new_gas_left, new_count, item.node);
                    queue[neighbor] = QueueItem::new(new_gas_left, new_count, neighbor);
                }
            }
        }
    }

    table
}

fn main() {
    let stdio = io::stdin();
    let input = stdio.lock();

    let stdout = io::stdout();
    let output = BufWriter::new(stdout.lock());

    solve(input, output);
}

#[cfg(test)]
mod tests {
    use super::*;

    const TESTS: &'static [(&'static [u8], &'static [u8])] = &[
        (
            br"3 2 5
1 2 3
2 3 3
2
3 2
1 3",
            br"0
1
",
        ),
        (
            br"4 0 1
1
2 1",
            br"-1
",
        ),
        (
            br"5 4 4
1 2 2
2 3 2
3 4 3
4 5 2
20
2 1
3 1
4 1
5 1
1 2
3 2
4 2
5 2
1 3
2 3
4 3
5 3
1 4
2 4
3 4
5 4
1 5
2 5
3 5
4 5",
            br"0
0
1
2
0
0
1
2
0
0
0
1
1
1
0
0
2
2
1
0
",
        ),
    ];

    #[test]
    fn test_e() {
        for &(input, expected) in TESTS {
            let mut output = Vec::new();
            solve(input, &mut output);
            let expected_str = String::from_utf8(expected.to_vec()).unwrap();
            let output_str = String::from_utf8(output).unwrap();
            assert_eq!(expected_str, output_str);
        }
    }
}


Measured memory_bytes: 8572