Problem:
Score : 700 points

Problem Statement

We have a graph with N vertices and M edges, and there are two people on the graph: Takahashi and Aoki.

The i-th edge connects Vertex U_i and Vertex V_i.
The time it takes to traverse this edge is D_i minutes, regardless of direction and who traverses the edge (Takahashi or Aoki).

Takahashi departs Vertex S and Aoki departs Vertex T at the same time. Takahashi travels to Vertex T and Aoki travels to Vertex S, both in the shortest time possible.
Find the number of the pairs of ways for Takahashi and Aoki to choose their shortest paths such that they never meet (at a vertex or on an edge) during the travel, modulo 10^9 + 7.

Constraints

1 \leq N \leq 100 000

1 \leq M \leq 200 000

1 \leq S, T \leq N

S \neq T

1 \leq U_i, V_i \leq N (1 \leq i \leq M)

1 \leq D_i \leq 10^9 (1 \leq i \leq M)

If i \neq j, then (U_i, V_i) \neq (U_j, V_j) and (U_i, V_i) \neq (V_j, U_j).

U_i \neq V_i (1 \leq i \leq M)

D_i are integers.

The given graph is connected.

Input

Input is given from Standard Input in the following format:

N M
S T
U_1 V_1 D_1
U_2 V_2 D_2
:
U_M V_M D_M

Output

Print the answer.

Sample Input 1

4 4
1 3
1 2 1
2 3 1
3 4 1
4 1 1

Sample Output 1

2

There are two ways to choose shortest paths that satisfies the condition:

Takahashi chooses the path 1 \rightarrow 2 \rightarrow 3, and Aoki chooses the path 3 \rightarrow 4 \rightarrow 1.

Takahashi chooses the path 1 \rightarrow 4 \rightarrow 3, and Aoki chooses the path 3 \rightarrow 2 \rightarrow 1.

Sample Input 2

3 3
1 3
1 2 1
2 3 1
3 1 2

Sample Output 2

2

Sample Input 3

3 3
1 3
1 2 1
2 3 1
3 1 2

Sample Output 3

2

Sample Input 4

8 13
4 2
7 3 9
6 2 3
1 6 4
7 6 9
3 8 9
1 2 2
2 8 12
8 6 9
2 5 5
4 2 18
5 3 7
5 1 515371567
4 8 6

Sample Output 4

6

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Accepted)

import java.io.*;
import java.util.*;

public class Main {
    static MyScanner sc;
    private static PrintWriter out;
    static long M2 = 1_000_000_000L + 7;

    public static void main(String[] s) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();


//        stringBuilder.append("1000000 10000 ");
//        for (int l = 0; l < 10000; l++) {
//            stringBuilder.append((l + 1) + "   " + (l + 2) + " 100000 ");
//        }

//        stringBuilder.append("4 5\n" +
//                "1 2\n" +
//                " 1 2 18\n" +
//                "                1 3 3\n" +
//                "                3 4 9\n" +
//                "                1 4 12\n" +
//                "                2 4 6");

//        stringBuilder.append("8 13\n" +
//                "4 2\n" +
//                "7 3 9\n" +
//                "6 2 3\n" +
//                "1 6 4\n" +
//                "7 6 9\n" +
//                "3 8 9\n" +
//                "1 2 2\n" +
//                "2 8 12\n" +
//                "8 6 9\n" +
//                "2 5 5\n" +
//                "4 2 18\n" +
//                "5 3 7\n" +
//                "5 1 515371567\n" +
//                "4 8 6");

        if (stringBuilder.length() == 0) {
            sc = new MyScanner(System.in);
        } else {
            sc = new MyScanner(new BufferedReader(new StringReader(stringBuilder.toString())));
        }


        out = new PrintWriter(new OutputStreamWriter(System.out));
        initData();
        solve();
        out.flush();
    }


    private static void initData() {

    }


    private static void solve() throws IOException {

        int n = sc.nextInt();
        int m = sc.nextInt();
        int s = sc.nextInt() - 1;
        int t = sc.nextInt() - 1;

        HashMap<Integer, Integer>[] ne = new HashMap[n];
        Arrays.setAll(ne, l -> new HashMap<>());
        for (int i = 0; i < m; i++) {
            int l1 = sc.nextInt() - 1;
            int l2 = sc.nextInt() - 1;
            int d = sc.nextInt();
            ne[l1].put(l2, d);
            ne[l2].put(l1, d);
        }
        long[][] l1 = dks(s, ne);
        long[][] l2 = dks(t, ne);


        long totalP = l1[1][t];
        totalP = (totalP * (totalP - 1)) % M2;

        long dist = l1[0][t];
        for (int r = 0; r < n; r++) {
            if (l1[0][r] + l2[0][r] != dist || l1[0][r] * 2 > dist) continue;

            if (l1[0][r] * 2 == dist) {
                // mid vertex
                long r0 = (l1[1][r] * l2[1][r]) % M2;
                r0 = (r0 * (r0 - 1)) % M2;
                totalP = (totalP - r0 + M2) % M2;
                continue;
            }

            long toThis = l1[0][r];
            for (Map.Entry<Integer, Integer> edge : ne[r].entrySet()) {
                if (dist != toThis + edge.getValue() + l2[0][edge.getKey()] || (toThis + edge.getValue()) * 2 <= dist)
                    continue;

                // mid edge
                long r0 = (l1[1][r] * l2[1][edge.getKey()]) % M2;
                r0 = (r0 * (r0 - 1)) % M2;
                totalP = (totalP - r0 + M2) % M2;

            }
        }

        out.println(totalP % M2);
    }

    /*
                2 4 18

                2 6 3
                6 8 9

                2 8 12
                4 8 6
                */
    private static long[][] dks(int start, HashMap<Integer, Integer>[] neig) {
        int n = neig.length;
        Heap heap = new Heap(n + 1, true);
        boolean[] visited = new boolean[n];
        visited[start] = true;

        heap.add(start, 0);
        long[] length = new long[n];
        long[] ct = new long[n];
        ct[start] = 1;
        while (heap.size() > 0) {
            int topIndex = heap.topIndex();
            long val = heap.removeTop();
            visited[topIndex] = true;
            length[topIndex] = val;

            for (Map.Entry<Integer, Integer> entry : neig[topIndex].entrySet()) {
                if (visited[entry.getKey()]) continue;
                if (heap.exists(entry.getKey())) {
                    if (heap.get(entry.getKey()) > val + entry.getValue()) {
                        heap.update(entry.getKey(), val + entry.getValue());
                        ct[entry.getKey()] = ct[topIndex];
                    } else if (heap.get(entry.getKey()) == val + entry.getValue()) {
                        ct[entry.getKey()] += ct[topIndex];
                        ct[entry.getKey()] %= M2;
                    }
                } else {
                    heap.add(entry.getKey(), entry.getValue() + val);
                    ct[entry.getKey()] = ct[topIndex];
                }
            }

        }

        return new long[][]{length, ct};
    }


    private static final class Heap {
        long[] vals;
        int[] indexesForw;
        boolean[] indexesExist;
        int[] indexesBack;
        int size;
        boolean min;

        public Heap(int n, boolean min) {
            vals = new long[n + 1];
            indexesForw = new int[n + 1];
            indexesBack = new int[n + 1];
            indexesExist = new boolean[n + 1];
            this.min = min;
        }

        public long get(int index) {
            return vals[indexesForw[index]];
        }

        public void add(int index, long p) {
            size++;
            vals[size] = p;
            indexesForw[index] = size;
            indexesBack[size] = index;
            indexesExist[index] = true;
            up(size);
        }

        public int size() {
            return size;
        }

        public long top() {
            return vals[1];
        }

        public int topIndex() {
            return indexesBack[1];
        }

        public long removeTop() {
            long val = vals[1];
            swap(1, size);
            indexesExist[indexesBack[size]] = false;
            size--;
            down(1);
            return val;
        }

        void down(int v) {
            int l = v * 2;
            int r = v * 2 + 1;
            if (l > size) {
                return;
            }
            if (r > size && leftUpIndex(l, v)) {
                swap(l, v);
            }
            if (size > l) {
                if (leftUpIndex(r, l)) {
                    if (leftUpIndex(r, v)) {
                        swap(r, v);
                        down(r);
                    }

                } else {
                    if (leftUpIndex(l, v)) {
                        swap(l, v);
                        down(l);
                    }
                }
            }

        }

        void swap(int i1, int i2) {
            long prev = vals[i1];
            vals[i1] = vals[i2];
            vals[i2] = prev;

            int prev1 = indexesBack[i1];
            indexesBack[i1] = indexesBack[i2];
            indexesBack[i2] = prev1;

            indexesForw[indexesBack[i1]] = i1;
            indexesForw[indexesBack[i2]] = i2;

        }

        void up(int p) {
            if (p == 1) {
                return;
            }
            if (leftUpIndex(p, p / 2)) {
                swap(p, p / 2);
                up(p / 2);
            }
        }

        boolean leftUpIndex(int a, int b) {
            return leftUp(vals[a], vals[b]);
        }

        boolean leftUp(long a, long b) {
            return min ? a < b : a > b;
        }


        public void update(int index, long value) {
            int valll = indexesForw[index];
            long oldVal = vals[valll];
            vals[valll] = value;
            if (leftUp(oldVal, value)) {
                down(valll);
            } else {
                up(valll);
            }

        }

        public boolean exists(int index) {
            return indexesExist[index];
        }
    }


    static boolean bpm(boolean[][] bpGraph, int u, boolean seen[],
                       int matchR[]) {
        // Try every job one by one
        for (int v = 0; v < bpGraph.length; v++) {
            // If applicant u is interested in job v and v
            // is not visited
            if (bpGraph[u][v] && !seen[v]) {
                seen[v] = true; // Mark v as visited

                // If job 'v' is not assigned to an applicant OR
                // previously assigned applicant for job v (which
                // is matchR[v]) has an alternate job available.
                // Since v is marked as visited in the above line,
                // matchR[v] in the following recursive call will
                // not get job 'v' again
                if (matchR[v] < 0 || bpm(bpGraph, matchR[v],
                        seen, matchR)) {
                    matchR[v] = u;
                    return true;
                }
            }
        }
        return false;
    }

    // Returns maximum number of matching from M to N
    static int[] maxBPM(boolean[][] bpGraph) {
        // An array to keep track of the applicants assigned to
        // jobs. The value of matchR[i] is the applicant number
        // assigned to job i, the value -1 indicates nobody is
        // assigned.
        int matchR[] = new int[bpGraph.length];

        // Initially all jobs are available
        for (int i = 0; i < bpGraph.length; ++i)
            matchR[i] = -1;

        int result = 0; // Count of jobs assigned to applicants
        for (int u = 0; u < bpGraph.length; u++) {
            // Mark all jobs as not seen for next applicant.
            boolean seen[] = new boolean[bpGraph.length];
            for (int i = 0; i < bpGraph.length; ++i)
                seen[i] = false;

            // Find if the applicant 'u' can get a job
            if (bpm(bpGraph, u, seen, matchR))
                result++;
        }
        return matchR;
    }

    private static final int[][] mm(int[][] v1, int[][] v2) {
        int[][] res = new int[v1.length][v1.length];
        for (int s = 0; s < v1.length; s++)
            for (int s1 = 0; s1 < v1.length; s1++) {
                for (int k = 0; k < v1.length; k++) {
                    res[s1][s] += v1[k][s] * v2[s1][k];
                }
            }
        return res;
    }


    private static int ccw(int[] i1, int[] i2) {
        return ccw(i1, i2, new int[]{0, 0});
    }

    private static int ccw(int[] s1, int[] s2, int[] right) {
        long xa = s1[0] - right[0];
        long xb = s1[1] - right[1];
        long ya = s2[0] - right[0];
        long yb = s2[1] - right[1];
        return Long.compare(xb * ya, xa * yb);
    }


    private static void solveT() throws IOException {
        int t = sc.nextInt();
        while (t-- > 0) {
            solve();
        }
    }

    private static long gcd(long l, long l1) {
        if (l > l1) return gcd(l1, l);
        if (l == 0) return l1;
        return gcd(l1 % l, l);
    }

    private static long pow(long a, long b, long m) {
        if (b == 0) return 1;
        if (b == 1) return a;
        long pp = pow(a, b / 2, m);
        pp *= pp;
        pp %= m;
        return (pp * (b % 2 == 0 ? 1 : a)) % m;
    }


    static class MyScanner {
        BufferedReader br;
        StringTokenizer st;

        MyScanner(BufferedReader br) {
            this.br = br;
        }

        public MyScanner(InputStream in) {
            this(new BufferedReader(new InputStreamReader(in)));
        }

        void findToken() {
            while (st == null || !st.hasMoreTokens()) {
                try {
                    st = new StringTokenizer(br.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        String next() {
            findToken();
            return st.nextToken();
        }

        Integer[] nab(int n) {
            Integer[] k = new Integer[n];
            for (int i = 0; i < n; i++) {
                k[i] = sc.fi();
            }
            return k;
        }

        int[] na(int n) {
            int[] k = new int[n];
            for (int i = 0; i < n; i++) {
                k[i] = sc.fi();
            }
            return k;
        }

        long[] nl(int n) {
            long[] k = new long[n];
            for (int i = 0; i < n; i++) {
                k[i] = sc.nextLong();
            }
            return k;
        }

        int nextInt() {
            return Integer.parseInt(next());
        }

        int fi() {
            String t = next();
            int cur = 0;
            boolean n = t.charAt(0) == '-';
            for (int a = n ? 1 : 0; a < t.length(); a++) {
                cur = cur * 10 + t.charAt(a) - '0';
            }
            return n ? -cur : cur;
        }

        long nextLong() {
            return Long.parseLong(next());
        }

        double nextDouble() {
            return Double.parseDouble(next());
        }
    }


}