Greg has a pad. The pad's screen is an n × m rectangle, each cell can be either black or white. We'll consider the pad rows to be numbered with integers from 1 to n from top to bottom. Similarly, the pad's columns are numbered with integers from 1 to m from left to right.

Greg thinks that the pad's screen displays a cave if the following conditions hold:

  * There is a segment [l, r] (1 ≤ l ≤ r ≤ n), such that each of the rows l, l + 1, ..., r has exactly two black cells and all other rows have only white cells. 
  * There is a row number t (l ≤ t ≤ r), such that for all pairs of rows with numbers i and j (l ≤ i ≤ j ≤ t) the set of columns between the black cells in row i (with the columns where is these black cells) is the subset of the set of columns between the black cells in row j (with the columns where is these black cells). Similarly, for all pairs of rows with numbers i and j (t ≤ i ≤ j ≤ r) the set of columns between the black cells in row j (with the columns where is these black cells) is the subset of the set of columns between the black cells in row i (with the columns where is these black cells). 



Greg wondered, how many ways there are to paint a cave on his pad. Two ways can be considered distinct if there is a cell that has distinct colors on the two pictures.

Help Greg.

Input

The first line contains two integers n, m — the pad's screen size (1 ≤ n, m ≤ 2000).

Output

In the single line print the remainder after dividing the answer to the problem by 1000000007 (109 + 7).

Examples

Input

1 1


Output

0


Input

4 4


Output

485


Input

3 5


Output

451
import static java.util.Arrays.deepToString;

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

public class D {
	final static int MOD = 1000000007;

	static void solve() {
		int n = nextInt(), m = nextInt();
		int[][] d1 = new int[n + 1][m + 1];
		int[][] d2 = new int[n + 1][m + 1];
		for (int len = 2; len <= m; len++) {
			d1[1][len] = 1;
			d2[1][len] = 1;
		}
		int[] sums = new int[m + 1];
		for (int h = 2; h <= n; h++) {
			for (int len = 2; len <= m; len++) {
				sums[len] = (sums[len - 1] + d1[h - 1][len]) % MOD;
			}
			int val = 0;
			for (int len = 1; len <= m; len++) {
				val = (val + sums[len]) % MOD;
				d1[h][len] = val;
			}
		}
		for (int len = 1; len <= m; len++) {
			for (int h = 2; h <= n; h++) {
				d1[h][len] = (d1[h][len] + d1[h - 1][len]) % MOD;
			}
		}
		int res = 0;
		for (int t = 1; t <= n; t++) {
			for (int len = 1; len <= m; len++) {
				int cur = (int) ((long) (d1[t][len] - d1[t - 1][len] + MOD) * d1[n - t + 1][len] % MOD);
				cur = (int) ((long) cur * (m - len + 1) % MOD);
				res = (res + cur) % MOD;
			}
		}
		writer.println(res);
	}

	public static void main(String[] args) throws Exception {
		reader = new BufferedReader(new InputStreamReader(System.in));
		writer = new PrintWriter(System.out);

		setTime();
		solve();
		printTime();
		printMemory();

		writer.close();
	}

	static BufferedReader reader;
	static PrintWriter writer;
	static StringTokenizer tok = new StringTokenizer("");
	static long systemTime;

	static void debug(Object... o) {
		System.err.println(deepToString(o));
	}

	static void setTime() {
		systemTime = System.currentTimeMillis();
	}

	static void printTime() {
		System.err.println("Time consumed: " + (System.currentTimeMillis() - systemTime));
	}

	static void printMemory() {
		System.err.println("Memory consumed: "
				+ (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1000 + "kb");
	}

	static String next() {
		while (!tok.hasMoreTokens()) {
			String w = null;
			try {
				w = reader.readLine();
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (w == null)
				return null;
			tok = new StringTokenizer(w);
		}
		return tok.nextToken();
	}

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

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

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

	static BigInteger nextBigInteger() {
		return new BigInteger(next());
	}
}