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.

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

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

import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author AlexFetisov
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskD_EgorAndCaves solver = new TaskD_EgorAndCaves();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskD_EgorAndCaves {
    public static final int MOD = (int)1e9 + 7;

    public void solve(int testNumber, InputReader in, PrintWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();

        if (m < 2) {
            out.println(0);
            return;
        }
        long[][] dp = new long[n+1][m+1];
        long[][] lastRow = new long[n+1][m+1];

        for (int width = 2; width <= m; ++width) {
            dp[0][width] = 1;
        }
        long[] sum = new long[m+1];
        long[] sum2 = new long[m+1];
        for (int row = 1; row < n; ++row) {
            sum[0] = sum[1] = 0;
            sum2[0] = sum2[1] = 0;
            for (int i = 2; i <= m; ++i) {
                sum[i] = sum[i-1] + dp[row-1][i];
                if (sum[i] >= MOD) sum[i] -= MOD;
                sum2[i] = sum2[i-1] + (dp[row-1][i] * i) % MOD;
                if (sum2[i] >= MOD) sum2[i] -= MOD;
            }
            for (int width = 2; width <= m; ++width) {
                dp[row][width] = 1;
                lastRow[row][width] = 0;
                dp[row][width] += ((width + 1) * sum[width]) % MOD;
                if (dp[row][width] >= MOD) {
                    dp[row][width] -= MOD;
                }
                dp[row][width] -= sum2[width];
                if (dp[row][width] < 0) {
                    dp[row][width] += MOD;
                }
                lastRow[row][width] += ((width + 1) * sum[width-1]) % MOD;
                if (lastRow[row][width] >= MOD) {
                    lastRow[row][width] -= MOD;
                }
                lastRow[row][width] -= sum2[width-1];
                if (lastRow[row][width] < 0) {
                    lastRow[row][width] += MOD;
                }
            }
        }

        long res = 0;
        for (int t = 0; t < n; ++t) {
            for (int width = 2; width <= m; ++width) {
                res += ((dp[t][width]) % MOD) * (m - width + 1) % MOD;
                if (res >= MOD) {
                    res -= MOD;
                }
                res += ((dp[t][width] * lastRow[n - t - 1][width]) % MOD) * (m - width + 1) % MOD;
                if (res >= MOD) {
                    res -= MOD;
                }
            }
        }
        out.println(res);
    }
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer stt;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
    }

    public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            return null;
        }
    }

    public String nextString() {
        while (stt == null || !stt.hasMoreTokens()) {
            stt = new StringTokenizer(nextLine());
        }
        return stt.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextString());
    }

}

//package round179;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.InputMismatchException;

public class D {
	InputStream is;
	PrintWriter out;
	String INPUT = "";
	int mod = 1000000007;
	int[][] fif = enumFIF(7000, mod);

	void solve()
	{
		int n = ni(), m = ni();

		long ret = 0;
		for(int w = 1;w < m;w++){
			long[] num = new long[n+1];
			long[] cum = new long[n+1];
			for(int L = 1;L <= n;L++){
				num[L] = C(w-1+2*(L-1), 2*(L-1));
				cum[L] = cum[L-1] + num[L];
				if(cum[L] >= mod)cum[L]-=mod;
			}

			long lsum = 0;
			for(int L = 1;L <= n;L++){
				lsum += num[L]*cum[n-L+1]%mod*(m-w)%mod;
			}
			ret += lsum%mod;
		}
		out.println(ret%mod);
	}

	long C(int n, int r)
	{
		if(n < 0 || r < 0 || n < r)return 0;
		return (long)fif[0][n]*fif[1][r]%mod*fif[1][n-r]%mod;
	}

	public static int[][] enumFIF(int n, int mod)
	{
		int[] f = new int[n+1];
		int[] invf = new int[n+1];
		invf[1] = 1;
		for(int i = 2;i <= n;i++){
			invf[i] = (int)(invf[mod%i]*(long)(mod-mod/i)%mod);
		}

		f[0] = invf[0] = 1;
		for(int i = 1;i <= n;i++){
			f[i] = (int)((long)f[i-1] * i % mod);
			invf[i] = (int)((long)invf[i-1] * invf[i] % mod);
		}
		return new int[][]{f, invf};
	}

	void run() throws Exception
	{
		is = oj ? System.in : new ByteArrayInputStream(INPUT.getBytes());
		out = new PrintWriter(System.out);

		long s = System.currentTimeMillis();
		solve();
		out.flush();
		tr(System.currentTimeMillis()-s+"ms");
	}

	public static void main(String[] args) throws Exception { new D().run(); }

	private byte[] inbuf = new byte[1024];
	private int lenbuf = 0, ptrbuf = 0;

	private int readByte()
	{
		if(lenbuf == -1)throw new InputMismatchException();
		if(ptrbuf >= lenbuf){
			ptrbuf = 0;
			try { lenbuf = is.read(inbuf); } catch (IOException e) { throw new InputMismatchException(); }
			if(lenbuf <= 0)return -1;
		}
		return inbuf[ptrbuf++];
	}

	private boolean isSpaceChar(int c) { return !(c >= 33 && c <= 126); }
	private int skip() { int b; while((b = readByte()) != -1 && isSpaceChar(b)); return b; }

	private double nd() { return Double.parseDouble(ns()); }
	private char nc() { return (char)skip(); }

	private String ns()
	{
		int b = skip();
		StringBuilder sb = new StringBuilder();
		while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
			sb.appendCodePoint(b);
			b = readByte();
		}
		return sb.toString();
	}

	private char[] ns(int n)
	{
		char[] buf = new char[n];
		int b = skip(), p = 0;
		while(p < n && !(isSpaceChar(b))){
			buf[p++] = (char)b;
			b = readByte();
		}
		return n == p ? buf : Arrays.copyOf(buf, p);
	}

	private char[][] nm(int n, int m)
	{
		char[][] map = new char[n][];
		for(int i = 0;i < n;i++)map[i] = ns(m);
		return map;
	}

	private int[] na(int n)
	{
		int[] a = new int[n];
		for(int i = 0;i < n;i++)a[i] = ni();
		return a;
	}

	private int ni()
	{
		int num = 0, b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private long nl()
	{
		long num = 0;
		int b;
		boolean minus = false;
		while((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'));
		if(b == '-'){
			minus = true;
			b = readByte();
		}

		while(true){
			if(b >= '0' && b <= '9'){
				num = num * 10 + (b - '0');
			}else{
				return minus ? -num : num;
			}
			b = readByte();
		}
	}

	private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
	private void tr(Object... o) { if(!oj)System.out.println(Arrays.deepToString(o)); }
}

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.StringTokenizer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Parser in = new Parser(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }
}

class TaskD {
    int mod = 1000000007;
    long[][] sum1d = new long[3000][3000];
    long[][] sum2d = new long[3000][3000];

    public void solve(int testNumber, Parser in, OutputWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();

        for (int rows = 0; rows <= n; rows++) {
            for (int i = 2; i <= m; i++) {
                sum1d[rows][i] = (rows==0) ? 1 : sum2d[rows-1][i]+1;
                if (i != 0) sum1d[rows][i] = (sum1d[rows][i] + sum1d[rows][i-1]) % mod;

                sum2d[rows][i] = sum1d[rows][i];
                if (i != 0) sum2d[rows][i] = (sum2d[rows][i] + sum2d[rows][i-1]) % mod;
            }
        }

        long tot = 0;
        for (int i = 0; i < n; i++) {
            for (int mid = 2; mid <= m; mid++) {
                // res=(res+(((((pd[w][t][1]-pd[w][t-1][1]+M)%M)*pd[w][n-t+1][1])%M)*(m-w+1))%M)%M;
                long top = (i==0) ? 1 : (sum2d[i-1][mid-1]+sum1d[i-1][mid-1]+1);
                long bot = (n-i-1==0) ? 1 : sum2d[n-i-2][mid]+1;

                bot = (bot * top) % mod;
                bot = (bot * (m-mid+1)) % mod;
                tot = (tot + bot) % mod;
            }
        }

        out.println(tot);
    }
}

class Parser
{
    private BufferedReader din;
    private StringTokenizer tokenizer;

    public Parser(InputStream in)
    {
        din = new BufferedReader(new InputStreamReader(in));
        tokenizer = null;
    }

    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(din.readLine());
            } catch (Exception e) {
                throw new UnknownError();
            }
        }
        return tokenizer.nextToken();
    }

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

    }

class OutputWriter extends PrintWriter {
    public OutputWriter(Writer out) {
        super(out);
    }

    public OutputWriter(OutputStream out) {
        super(out);
    }

    }

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());
	}
}

import static java.lang.System.in;
import static java.lang.System.out;

import java.util.Scanner;

public class D295 {
	static long MAX = 1000000007;
	public static Scanner sc=new Scanner(in);
	public void run(){
		// TODO Auto-generated method stub
		int n=sc.nextInt(), m = sc.nextInt();
		m--;

		long [][] dp= new long[2010][2010];
		for (int i=1; i<=n; i++){
			long sub = 0, sum =0;
			for (int j=1; j<=m; j++){
				sub+=dp[i-1][j];
				sum+=sub;
				dp[i][j]= (sum + 1)%MAX;
			}
		}
		long ans=0;
		for (int i=1; i<=n; i++){
			for (int j=1; j<=m; j++){
				ans = ans+ ((dp[i][j]*dp[n-i+1][j])%MAX)*(m-j+1);
			}
			if (i<n){
				for (int j=1; j<=m; j++)
					ans = ans - ((dp[i][j]*dp[n-i][j])%MAX)*(m-j+1);
			}
			ans%=MAX;
		}
		ln((ans+MAX)%MAX);
	}

	public static void main(String[] args) {
		new D295().run();
	}
	public static void ln(Object obj){
		out.println(obj);
	}

}

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

public class CF {
    FastScanner in;
    PrintWriter out;

    long mod = (long) 1e9 + 7;

    void solve() {
//        for (int i = 1; i <= 10; i++) {
//            for (int j = 1; j <= 10; j++) {
//                System.err.print(sol2(i, j) - sol3(i, j) + " ");
//            }
//            System.err.println();
//        }
         out.println(sol2(in.nextInt(), in.nextInt()));
    }

    long sol2(int n, int m) {
        long res = 0;
        long[][] dp01 = new long[m + 1][n + 1];
        long[][] dp01Sum = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp01[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                dp01Sum[w][h] = (dp01Sum[w - 1][h] + dp01[w][h - 1]) % mod;
                dp01[w][h] = (dp01[w - 1][h] + dp01Sum[w][h]) % mod;
            }
        long[][] dp1 = new long[m + 1][n + 1];
        long[][] dp1Sum = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp1[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                dp1Sum[w][h] = (dp1Sum[w - 1][h] + dp01[w - 1][h - 1]) % mod;
                dp1[w][h] = (dp1[w - 1][h] + dp1Sum[w][h] + dp01[w - 1][h - 1]) % mod;
//                 for (int w1 = 2; w1 < w; w1++) {
//                 dp1[w][h] = (dp1[w][h] + (dp01[w1][h - 1] * (w - w1 + 1))
//                 % mod)
//                 % mod;
//                 }
            }
        long[][] dpSum01 = new long[m + 1][n + 1];
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++)
                dpSum01[w][h] = (dpSum01[w][h - 1] + dp01[w][h]) % mod;
        long[][] dpSum1 = new long[m + 1][n + 1];
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++)
                dpSum1[w][h] = (dpSum1[w][h - 1] + dp1[w][h]) % mod;
        for (int h1 = 1; h1 <= n; h1++)
            for (int w1 = 1; w1 <= m; w1++) {
                long curRes = (dpSum01[w1][h1] * dpSum1[w1][n - h1 + 1]) % mod;
                curRes = (curRes * (m - w1 + 1)) % mod;
                res = (res + curRes) % mod;
            }
        return res;
    }

    long sol3(int n, int m) {
        long res = 0;
        long[][] dp01 = new long[m + 1][n + 1];
        long[][] dp01Sum = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp01[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                dp01Sum[w][h] = (dp01Sum[w - 1][h] + dp01[w][h - 1]) % mod;
                dp01[w][h] = (dp01[w - 1][h] + dp01Sum[w][h]) % mod;
            }
        long[][] dp1 = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp1[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                for (int w1 = 2; w1 < w; w1++) {
                    dp1[w][h] = (dp1[w][h] + (dp01[w1][h - 1] * (w - w1 + 1))
                            % mod)
                            % mod;
                }
            }
        long[][] dp3 = new long[m + 1][n + 1];
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++) {
                for (int h1 = 1; h1 <= h; h1++) {
                    dp3[w][h] = (dp3[w][h] + (dp01[w][h1] * dp1[w][h - h1 + 1])
                            % mod)
                            % mod;

                }
                // dp3[w][h] = dp3[w][h - 1] + dp01[w][h] * dp1[w][h - h + 1];
            }
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++) {
                long curRes = (dp3[w][h] * (n - h + 1)) % mod;
                curRes = (curRes * (m - w + 1)) % mod;
                res = (res + curRes) % mod;
            }
        return res;
    }

    int stupid(int n, int m) {
        int total = n * m;
        int sum = 0;
        boolean[][] field = new boolean[n][m];
        for (int st = 0; st < (1 << total); st++) {
            int it = 0;
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    if (((1 << it++) & st) != 0)
                        field[i][j] = true;
                    else
                        field[i][j] = false;
            int l = -1, r = -1;
            boolean ok = true;
            int[] first = new int[n];
            int[] last = new int[n];
            Arrays.fill(first, -1);
            Arrays.fill(last, -1);
            for (int i = 0; i < n; i++) {
                int cnt = 0;
                for (int j = 0; j < m; j++)
                    if (field[i][j]) {
                        if (cnt == 0)
                            first[i] = j;
                        last[i] = j;
                        cnt++;
                    }
                if (cnt != 2 && cnt != 0)
                    ok = false;
                if (cnt == 2) {
                    r = i;
                    if (l == -1)
                        l = i;
                }
            }
            if (!ok)
                continue;
            if (l == -1 || r == -1)
                continue;
            for (int i = l; i <= r; i++)
                if (first[i] == -1 || last[i] == -1 || first[i] == last[i])
                    ok = false;
            if (!ok)
                continue;
            boolean ok2 = false;
            for (int t = l; t <= r; t++) {
                boolean ok3 = true;
                for (int i = l; i < t; i++) {
                    if (first[i] < first[i + 1] || last[i] > last[i + 1])
                        ok3 = false;
                }
                for (int i = r; i > t; i--)
                    if (first[i] < first[i - 1] || last[i] > last[i - 1])
                        ok3 = false;
                if (ok3)
                    ok2 = true;
            }
            if (ok2)
                sum++;
        }
        return sum;
    }

    void run() {
        try {
            in = new FastScanner(new File("test.in"));
            out = new PrintWriter(new File("test.out"));

            solve();

            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    void runIO() {
        in = new FastScanner(System.in);
        out = new PrintWriter(System.out);

        solve();

        out.close();
    }

    class FastScanner {
        BufferedReader br;
        StringTokenizer st;

        public FastScanner(File f) {
            try {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        public FastScanner(InputStream f) {
            br = new BufferedReader(new InputStreamReader(f));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                String s = null;
                try {
                    s = br.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        boolean hasMoreTokens() {
            while (st == null || !st.hasMoreTokens()) {
                String s = null;
                try {
                    s = br.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (s == null)
                    return false;
                st = new StringTokenizer(s);
            }
            return true;
        }

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

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

    public static void main(String[] args) {
        new CF().runIO();
    }
}

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.io.BufferedWriter;
import java.util.InputMismatchException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.NoSuchElementException;
import java.io.Writer;
import java.math.BigInteger;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author Egor Kulikov (egor@egork.net)
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		OutputWriter out = new OutputWriter(outputStream);
		TaskD solver = new TaskD();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskD {
	static final long MOD = 1000000007;

	public void solve(int testNumber, InputReader in, OutputWriter out) {
		int rowCount = in.readInt();
		int columnCount = in.readInt();
		long[][] strict = new long[rowCount][columnCount];
		long[][] nonStrict = new long[rowCount][columnCount];
		Arrays.fill(strict[0], 1, columnCount, 1);
		Arrays.fill(nonStrict[0], 1, columnCount, 1);
		for (int i = 1; i < rowCount; i++) {
			long sum = 0;
			long result = 0;
			for (int j = 0; j < columnCount; j++) {
				result += sum;
				if (result >= MOD)
					result -= MOD;
				strict[i][j] = result;
				result += nonStrict[i - 1][j];
				sum += nonStrict[i - 1][j];
				if (result >= MOD)
					result -= MOD;
				if (sum >= MOD)
					sum -= MOD;
				nonStrict[i][j] = result;
			}
		}
		long answer = 0;
		for (int i = 0; i < columnCount; i++) {
			long sum = 0;
			long result = 0;
			long current = 0;
			for (int j = 0; j < rowCount; j++) {
				sum += nonStrict[j][i];
				if (sum >= MOD)
					sum -= MOD;
				result += sum;
				if (result >= MOD)
					result -= MOD;
				current += strict[rowCount - j - 1][i] * result % MOD;
			}
			current %= MOD;
			answer += current * (columnCount - i);
		}
		answer %= MOD;
		out.printLine(answer);
    }
}

class InputReader {

	private InputStream stream;
	private byte[] buf = new byte[1024];
	private int curChar;
	private int numChars;
	private SpaceCharFilter filter;

	public InputReader(InputStream stream) {
		this.stream = stream;
	}

	public int read() {
		if (numChars == -1)
			throw new InputMismatchException();
		if (curChar >= numChars) {
			curChar = 0;
			try {
				numChars = stream.read(buf);
			} catch (IOException e) {
				throw new InputMismatchException();
			}
			if (numChars <= 0)
				return -1;
		}
		return buf[curChar++];
	}

	public int readInt() {
		int c = read();
		while (isSpaceChar(c))
			c = read();
		int sgn = 1;
		if (c == '-') {
			sgn = -1;
			c = read();
		}
		int res = 0;
		do {
			if (c < '0' || c > '9')
				throw new InputMismatchException();
			res *= 10;
			res += c - '0';
			c = read();
		} while (!isSpaceChar(c));
		return res * sgn;
	}

	public boolean isSpaceChar(int c) {
		if (filter != null)
			return filter.isSpaceChar(c);
		return isWhitespace(c);
	}

	public static boolean isWhitespace(int c) {
		return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
	}

	public interface SpaceCharFilter {
		public boolean isSpaceChar(int ch);
	}
}

class OutputWriter {
	private final PrintWriter writer;

	public OutputWriter(OutputStream outputStream) {
		writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
	}

	public OutputWriter(Writer writer) {
		this.writer = new PrintWriter(writer);
	}

	public void print(Object...objects) {
		for (int i = 0; i < objects.length; i++) {
			if (i != 0)
				writer.print(' ');
			writer.print(objects[i]);
		}
	}

    public void printLine(Object...objects) {
		print(objects);
		writer.println();
	}

	public void close() {
		writer.close();
	}

	}

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.io.PrintStream;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Random;
import java.io.Reader;
import java.io.Writer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author Niyaz Nigmatullin
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		FastScanner in = new FastScanner(inputStream);
		FastPrinter out = new FastPrinter(outputStream);
		TaskD solver = new TaskD();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskD {

    public void solve(int testNumber, FastScanner in, FastPrinter out) {
//        test();
        int n = in.nextInt();
        int m = in.nextInt();
        out.println(solve(n, m));
//        if (solve(n, m) != solveStupid(n, m)) {
//            throw new AssertionError();
//        }
    }

    static final int MOD = 1000000007;

    static int solve(int n, int m) {
        int[][] dp = new int[m][n + 1];
        int[][] dp2 = new int[m][n + 1];
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int s = 0;
            int ss = 0;
            dp[0][i] = 1;
            for (int j = 1; j < m; j++) {
                dp2[j][i] = (ss + s + 1) % MOD;
                s += dp[j][i - 1];
                if (s >= MOD) {
                    s -= MOD;
                }
                ss += s;
                if (ss >= MOD) {
                    ss -= MOD;
                }
                dp[j][i] = (ss + 1) % MOD;
            }
        }
        int ans = 0;
        for (int i = 0; i < n; i++) {
            int cur = 0;
            for (int j = 1; j < m; j++) {
                cur = (int) ((cur + (long) dp[j][i + 1] * dp2[j][n - i] % MOD * (m - j)) % MOD);
            }
            ans += cur;
            if (ans >= MOD) {
                ans -= MOD;
            }
        }
        return ans;
    }

    }

class FastScanner extends BufferedReader {

    boolean isEOF;

    public FastScanner(InputStream is) {
        super(new InputStreamReader(is));
    }

    public int read() {
        try {
            int ret = super.read();
            if (isEOF && ret < 0) {
                throw new InputMismatchException();
            }
            isEOF = ret == -1;
            return ret;
        } catch (IOException e) {
            throw new InputMismatchException();
        }
    }

    static boolean isWhiteSpace(int c) {
        return c >= 0 && c <= 32;
    }

    public int nextInt() {
        int c = read();
        while (isWhiteSpace(c)) {
            c = read();
        }
        int sgn = 1;
        if (c == '-') {
            sgn = -1;
            c = read();
        }
        int ret = 0;
        while (c >= 0 && !isWhiteSpace(c)) {
            if (c < '0' || c > '9') {
                throw new NumberFormatException("digit expected " + (char) c
                        + " found");
            }
            ret = ret * 10 + c - '0';
            c = read();
        }
        return ret * sgn;
    }

    }

class FastPrinter extends PrintWriter {

    public FastPrinter(OutputStream out) {
        super(out);
    }

    public FastPrinter(Writer out) {
        super(out);
    }

}

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

public class D_fast {

	BufferedReader br;
	PrintWriter out;
	StringTokenizer st;
	boolean eof;

	static final int MOD = 1_000_000_007;

	void solve() throws IOException {
		int n = nextInt();
		int m = nextInt();
		if (m < 2) {
			out.println(0);
			return;
		}

		int[][] dp = new int[n + 1][m + 1];
		for (int len = 2; len <= m; len++) {
			dp[1][len] = 1;
		}

		int[] tmp = new int[m + 1];

		for (int height = 2; height <= n; height++) {
			int[] dpPrev = dp[height - 1];
			tmp[2] = dpPrev[2];
			for (int i = 3; i <= m; i++) {
				tmp[i] = tmp[i - 1] + dpPrev[i];
				if (tmp[i] >= MOD)
					tmp[i] -= MOD;
			}

			int[] dpCur = dp[height];
			dpCur[2] = tmp[2];
			for (int i = 3; i <= m; i++) {
				dpCur[i] = dpCur[i - 1] + tmp[i];
				if (dpCur[i] >= MOD)
					dpCur[i] -= MOD;
			}
		}

		int[] fixedHeight = new int[n + 1];
		int[] up = new int[n + 1];
		int[] down = new int[n + 1];

		int ans = 0;
		for (int len = 2; len <= m; len++) {

			int val = 0;

			for (int h = 1; h <= n; h++) {
				up[h] = down[h] = dp[h][len];
				if (h != 1) {
					up[h] -= dp[h - 1][len];
					if (up[h] < 0)
						up[h] += MOD;
				}
				if (h != 1) {
					up[h] += up[h - 1];
					if (up[h] >= MOD)
						up[h] -= MOD;
					down[h] += down[h - 1];
					if (down[h] >= MOD)
						down[h] -= MOD;
				}
			}

			for (int mid = 0; mid < n; mid++) {
				int maxUp = mid + 1;
				int maxDown = n - mid;
				val += (int)((long)up[maxUp] * down[maxDown] % MOD);
				if (val >= MOD)
					val -= MOD;
			}

			ans += (int) ((long) val * (m - len + 1) % MOD);
			if (ans >= MOD)
				ans -= MOD;
		}

		out.println(ans);

	}

	D_fast() throws IOException {
		br = new BufferedReader(new InputStreamReader(System.in));
		out = new PrintWriter(System.out);
		solve();
		out.close();
	}

	public static void main(String[] args) throws IOException {
		new D_fast();
	}

	String nextToken() {
		while (st == null || !st.hasMoreTokens()) {
			try {
				st = new StringTokenizer(br.readLine());
			} catch (Exception e) {
				eof = true;
				return null;
			}
		}
		return st.nextToken();
	}

	String nextString() {
		try {
			return br.readLine();
		} catch (IOException e) {
			eof = true;
			return null;
		}
	}

	int nextInt() throws IOException {
		return Integer.parseInt(nextToken());
	}

	long nextLong() throws IOException {
		return Long.parseLong(nextToken());
	}

	double nextDouble() throws IOException {
		return Double.parseDouble(nextToken());
	}
}

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.StringTokenizer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Parser in = new Parser(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }
}

class TaskD {
    int mod = 1000000007;
    long[][] sum1d = new long[3000][3000];
    long[][] sum2d = new long[3000][3000];

    public void solve(int testNumber, Parser in, OutputWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();

        for (int rows = 0; rows <= n; rows++) {
            for (int i = 2; i <= m; i++) {
                sum1d[rows][i] = (rows==0) ? 1 : sum2d[rows-1][i]+1;
                if (i != 0) sum1d[rows][i] += sum1d[rows][i-1];

                sum2d[rows][i] = sum1d[rows][i];
                if (i != 0) sum2d[rows][i] += sum2d[rows][i-1];
            }
        }

        long tot = 0;
        for (int i = 0; i < n; i++) {
            for (int mid = 2; mid <= m; mid++) {
                // res=(res+(((((pd[w][t][1]-pd[w][t-1][1]+M)%M)*pd[w][n-t+1][1])%M)*(m-w+1))%M)%M;
                long top = (i==0) ? 1 : (sum2d[i-1][mid-1]+sum1d[i-1][mid-1]+1);
                long bot = (n-i-1==0) ? 1 : sum2d[n-i-2][mid]+1;

                System.out.println("center on (" + i + "," + mid + ") top=" + top + " bot=" + bot + " mult=" + (m-mid+1));
                bot = (bot * top) % mod;
                bot = (bot * (m-mid+1)) % mod;
                tot = (tot + bot) % mod;
            }
        }

        out.println(tot);
    }
}

class Parser
{
    private BufferedReader din;
    private StringTokenizer tokenizer;

    public Parser(InputStream in)
    {
        din = new BufferedReader(new InputStreamReader(in));
        tokenizer = null;
    }

    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(din.readLine());
            } catch (Exception e) {
                throw new UnknownError();
            }
        }
        return tokenizer.nextToken();
    }

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

    }

class OutputWriter extends PrintWriter {
    public OutputWriter(Writer out) {
        super(out);
    }

    public OutputWriter(OutputStream out) {
        super(out);
    }

    }

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.StringTokenizer;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 */
public class Main {
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        OutputStream outputStream = System.out;
        Parser in = new Parser(inputStream);
        OutputWriter out = new OutputWriter(outputStream);
        TaskD solver = new TaskD();
        solver.solve(1, in, out);
        out.close();
    }
}

class TaskD {
    int mod = 1000000007;
    long[][] sum1d = new long[3000][3000];
    long[][] sum2d = new long[3000][3000];

    public void solve(int testNumber, Parser in, OutputWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();

        for (int rows = 0; rows <= n; rows++) {
            for (int i = 2; i <= m; i++) {
                sum1d[rows][i] = (rows==0) ? 1 : sum2d[rows-1][i]+1;
                if (i != 0) sum1d[rows][i] += sum1d[rows][i-1];

                sum2d[rows][i] = sum1d[rows][i];
                if (i != 0) sum2d[rows][i] += sum2d[rows][i-1];
            }
        }

        long tot = 0;
        for (int i = 0; i < n; i++) {
            for (int mid = 2; mid <= m; mid++) {
                // res=(res+(((((pd[w][t][1]-pd[w][t-1][1]+M)%M)*pd[w][n-t+1][1])%M)*(m-w+1))%M)%M;
                long top = (i==0) ? 1 : (sum2d[i-1][mid-1]+sum1d[i-1][mid-1]+1);
                long bot = (n-i-1==0) ? 1 : sum2d[n-i-2][mid]+1;

                bot = (bot * top) % mod;
                bot = (bot * (m-mid+1)) % mod;
                tot = (tot + bot) % mod;
            }
        }

        out.println(tot);
    }
}

class Parser
{
    private BufferedReader din;
    private StringTokenizer tokenizer;

    public Parser(InputStream in)
    {
        din = new BufferedReader(new InputStreamReader(in));
        tokenizer = null;
    }

    public String next() {
        while (tokenizer == null || !tokenizer.hasMoreTokens()) {
            try {
                tokenizer = new StringTokenizer(din.readLine());
            } catch (Exception e) {
                throw new UnknownError();
            }
        }
        return tokenizer.nextToken();
    }

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

    }

class OutputWriter extends PrintWriter {
    public OutputWriter(Writer out) {
        super(out);
    }

    public OutputWriter(OutputStream out) {
        super(out);
    }

    }

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

public class CF {
    FastScanner in;
    PrintWriter out;

    long mod = (long) 1e9 + 7;

    void solve() {
//        for (int i = 1; i <= 4; i++) {
//            for (int j = 1; j <= 4; j++) {
//                System.err.print(sol2(i, j) - stupid(i, j) + " ");
//            }
//            System.err.println();
//        }
         out.println(sol2(in.nextInt(), in.nextInt()));
    }

    long sol2(int n, int m) {
        long res = 0;
        long[][] dp01 = new long[m + 1][n + 1];
        long[][] dp01Sum = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp01[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                dp01Sum[w][h] = (dp01Sum[w - 1][h] + dp01[w][h - 1]) % mod;
                dp01[w][h] = (dp01[w - 1][h] + dp01Sum[w][h]) % mod;
            }
        long[][] dp1 = new long[m + 1][n + 1];
        for (int w = 2; w <= m; w++)
            dp1[w][1] = 1;
        for (int h = 2; h <= n; h++)
            for (int w = 2; w <= m; w++) {
                for (int w1 = 2; w1 < w; w1++) {
                    dp1[w][h] = (dp1[w][h] + (dp01[w1][h - 1] * (w - w1 + 1))
                            % mod)
                            % mod;
                }
            }
        long[][] dpSum01 = new long[m + 1][n + 1];
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++)
                dpSum01[w][h] = (dpSum01[w][h - 1] + dp01[w][h]) % mod;
        long[][] dpSum1 = new long[m + 1][n + 1];
        for (int h = 1; h <= n; h++)
            for (int w = 1; w <= m; w++)
                dpSum1[w][h] = (dpSum1[w][h - 1] + dp1[w][h]) % mod;
        for (int h1 = 1; h1 <= n; h1++)
            for (int w1 = 1; w1 <= m; w1++) {
                long curRes = (dpSum01[w1][h1] * dpSum1[w1][n - h1 + 1]) % mod;
                curRes = (curRes * (m - w1 + 1)) % mod;
                res += curRes;
            }
        return res;
    }

    int stupid(int n, int m) {
        int total = n * m;
        int sum = 0;
        boolean[][] field = new boolean[n][m];
        for (int st = 0; st < (1 << total); st++) {
            int it = 0;
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    if (((1 << it++) & st) != 0)
                        field[i][j] = true;
                    else
                        field[i][j] = false;
            int l = -1, r = -1;
            boolean ok = true;
            int[] first = new int[n];
            int[] last = new int[n];
            Arrays.fill(first, -1);
            Arrays.fill(last, -1);
            for (int i = 0; i < n; i++) {
                int cnt = 0;
                for (int j = 0; j < m; j++)
                    if (field[i][j]) {
                        if (cnt == 0)
                            first[i] = j;
                        last[i] = j;
                        cnt++;
                    }
                if (cnt != 2 && cnt != 0)
                    ok = false;
                if (cnt == 2) {
                    r = i;
                    if (l == -1)
                        l = i;
                }
            }
            if (!ok)
                continue;
            if (l == -1 || r == -1)
                continue;
            for (int i = l; i <= r; i++)
                if (first[i] == -1 || last[i] == -1 || first[i] == last[i])
                    ok = false;
            if (!ok)
                continue;
            boolean ok2 = false;
            for (int t = l; t <= r; t++) {
                boolean ok3 = true;
                for (int i = l; i < t; i++) {
                    if (first[i] < first[i + 1] || last[i] > last[i + 1])
                        ok3 = false;
                }
                for (int i = r; i > t; i--)
                    if (first[i] < first[i - 1] || last[i] > last[i - 1])
                        ok3 = false;
                if (ok3)
                    ok2 = true;
            }
            if (ok2)
                sum++;
        }
        return sum;
    }

    void run() {
        try {
            in = new FastScanner(new File("test.in"));
            out = new PrintWriter(new File("test.out"));

            solve();

            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    void runIO() {
        in = new FastScanner(System.in);
        out = new PrintWriter(System.out);

        solve();

        out.close();
    }

    class FastScanner {
        BufferedReader br;
        StringTokenizer st;

        public FastScanner(File f) {
            try {
                br = new BufferedReader(new FileReader(f));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        public FastScanner(InputStream f) {
            br = new BufferedReader(new InputStreamReader(f));
        }

        String next() {
            while (st == null || !st.hasMoreTokens()) {
                String s = null;
                try {
                    s = br.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (s == null)
                    return null;
                st = new StringTokenizer(s);
            }
            return st.nextToken();
        }

        boolean hasMoreTokens() {
            while (st == null || !st.hasMoreTokens()) {
                String s = null;
                try {
                    s = br.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (s == null)
                    return false;
                st = new StringTokenizer(s);
            }
            return true;
        }

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

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

    public static void main(String[] args) {
        new CF().runIO();
    }
}