extern crate core;

use std::fmt;
use std::cmp::{max, min, Ordering};
use std::collections::{BinaryHeap, BTreeMap, VecDeque};
use std::fmt::{Display, Formatter, Error};
use std::ops::Range;
macro_rules! read_line{
    () => {{
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        line
    }};
    (delimiter: ' ') => {
        read_line!().split_whitespace().map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (delimiter: $p:expr) => {
        read_line!().split($p).map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (' ') => {
        read_line!(delimiter: ' ')
    };
    ($delimiter:expr) => {
        read_line!(delimiter: $delimiter)
    };
    (' '; $ty:ty) => {
        read_line!().split_whitespace().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
    ($delimiter:expr; $ty:ty) => {
        read_line!($delimiter).into_iter().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
}
macro_rules! let_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}

struct ValueWithKey<K, V> {
    key: K, value: V,
}
impl <K, V> Ord for ValueWithKey<K, V> where K: Ord {
    fn cmp(&self, other: &Self) -> Ordering {
        self.key.cmp(&other.key)
    }
}
impl <K, V> PartialOrd for ValueWithKey<K, V> where K: PartialOrd {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.key.partial_cmp(&other.key)
    }
}
impl <K, V> PartialEq for ValueWithKey<K, V> where K: PartialEq {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key
    }
}
impl <K, V> Eq for ValueWithKey<K, V> where K: Eq {}

fn lower_bound<T>(array: &Vec<T>, target: &T) -> usize where T: Ord {
    let mut left = 0;
    let mut right = array.len();
    while left < right {
        let mid = (left + right) / 2;
        if array[mid] < *target {
            left = mid + 1;
        }else {
            right = mid;
        }
    }
    right
}
fn upper_bound<T>(array: &Vec<T>, target: &T) -> usize where T: Ord {
    let mut left = 0;
    let mut right = array.len();
    while left < right {
        let mid = (left + right) / 2;
        if array[mid] <= *target {
            left = mid + 1;
        }else {
            right = mid;
        }
    }
    right
}
fn main(){
    let_all!(n: usize, m: usize, s: usize, t: usize);
    let mut nodes = vec![Vec::<usize>::new(); n];
    for _ in 0 .. m {
        let_all!(x: usize, y: usize);
        nodes[x - 1].push(y - 1);
        nodes[y - 1].push(x - 1);
    }
    let mut queue = VecDeque::<usize>::new();
    let mut min_length_from_start = vec![-1; n];
    min_length_from_start[s - 1] = 0;
    queue.push_back(s -1);
    while let Some(c) = queue.pop_front() {
        for &n in &nodes[c] {
            if min_length_from_start[n] == -1 {
                min_length_from_start[n] = min_length_from_start[c] + 1;
                queue.push_back(n);
            }
        }
    }
    let mut min_length_from_goal = vec![-1; n];
    min_length_from_goal[t - 1] = 0;
    queue.push_back(t - 1);
    while let Some(c) = queue.pop_front() {
        for &n in &nodes[c] {
            if min_length_from_goal[n] == -1 {
                min_length_from_goal[n] = min_length_from_goal[c] + 1;
                queue.push_back(n);
            }
        }
    }
    min_length_from_goal.sort();
    let target_length = min_length_from_start[t - 1] - 1;
    let mut result = 0;
    for &from_start in &min_length_from_start {
        if target_length > from_start && from_start >= 0 {
            result += upper_bound(&min_length_from_goal, &(target_length - from_start - 1)) - lower_bound(&min_length_from_goal, &(target_length - from_start - 1));
        }
    }
    println!("{}", result);
}




Measured memory_bytes: 15272