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
#include <bits/stdc++.h>
template <typename It>
struct iter_range : std::pair<It, It> {
  using std::pair<It, It>::std::pair<It, It>;
  It begin() { return std::pair<It, It>::first; }
  It end() { return std::pair<It, It>::second; }
};
template <typename It>
iter_range<std::reverse_iterator<It>> rev(const iter_range<It> &origin) {
  using rev_t = std::reverse_iterator<It>;
  return {rev_t(origin.second), rev_t(origin.first)};
}
template <typename Int = int>
struct int_iter {
  Int i;
  explicit int_iter(Int i = 0) : i(i) {}
  Int operator*() const { return i; }
  int_iter &operator++() {
    ++i;
    return *this;
  }
  int_iter &operator--() {
    --i;
    return *this;
  }
  int_iter operator+(Int step) const { return int_iter(i + step); }
  int_iter operator-(Int step) const { return int_iter(i - step); }
  Int operator-(const int_iter &that) const { return i - that.i; }
  int_iter &operator+=(Int step) {
    i += step;
    return *this;
  }
  int_iter &operator-=(Int step) {
    i -= step;
    return *this;
  }
  bool operator==(const int_iter &that) const { return i == that.i; }
  bool operator!=(const int_iter &that) const { return !(*this == that); }
  Int operator[](Int n) { return i + n; }
  typedef Int *pointer;
  typedef Int reference;
};
template <typename Int = int>
using int_range = iter_range<int_iter<Int>>;
template <typename Int>
int_range<Int> range(Int b, Int e) {
  return {int_iter<Int>(b), int_iter<Int>(e > b ? e : b)};
}
template <typename Int>
int_range<Int> range(Int n) {
  return range(Int(), n);
}
template <typename Int>
int_range<Int> nrange(Int n) {
  return range(Int(1), Int(n + 1));
}
template <typename Int>
int_range<Int> inrange(Int b, Int e) {
  return range(b, Int(e + 1));
}
using namespace std;
template <typename T>
bool chmax(T &m, const T &v) {
  if (v > m) return m = v, true;
  return false;
}
template <typename T>
bool chmin(T &m, const T &v) {
  if (v < m) return m = v, true;
  return false;
}
template <typename T>
T max_s(const T &v) {
  return v;
}
template <typename T, typename... Args>
T max_s(const T &v1, const T &v2, Args... arg) {
  return max_s(max(v1, v2), arg...);
}
template <typename T>
T min_s(const T &v) {
  return v;
}
template <typename T, typename... Args>
T min_s(const T &v1, const T &v2, Args... arg) {
  return min_s(min(v1, v2), arg...);
}
static const int HMAX32 = INT32_MAX / 2;
static const int64_t HMAX64 = INT64_MAX / 2;
stringstream line_stream() {
  string str;
  getline(cin, str);
  return stringstream(str);
}
template <typename Int>
Int gcd(Int a, Int b) {
  while (a != 0) {
    b %= a;
    if (b == 0) return a;
    a %= b;
  }
  return b;
}
template <typename Int, typename RInt = Int>
Int lcm(Int a, Int b) {
  return a / gcd(a, b) * static_cast<RInt>(b);
}
template <typename Int, typename XInt>
Int extended_gcd(Int a, Int b, XInt &x, XInt &y) {
  static_assert(std::numeric_limits<XInt>::is_signed, "x, y must be signed");
  if (b == 0) {
    x = 1;
    y = 0;
    return a;
  }
  Int g = extended_gcd(b, a % b, y, x);
  y -= (a / b) * x;
  return g;
}
template <typename Int, typename TInt = int64_t>
Int mul_mod(Int a, Int b, Int m) {
  TInt p = a;
  p = (p * b) % m;
  return (Int)p;
}
template <typename Int, typename PInt, typename TInt = int64_t>
Int pow_mod(Int a, PInt p, Int m) {
  Int r;
  a %= m;
  for (r = 1; p; p >>= 1) {
    if (p & 1) r = mul_mod(r, a, m);
    a = mul_mod(a, a, m);
  }
  return r;
}
template <typename Int, typename XInt = typename std::make_signed<Int>::type>
Int inv_mod(Int a, Int m) {
  XInt x, y;
  extended_gcd(a, m, x, y);
  if (x < 0) return x + m;
  return x;
}
template <typename Int>
static Int addx_mod(Int u, Int v, Int m) {
  Int r = u + v;
  if (r >= m) r -= m;
  return r;
}
template <typename Int>
static Int subx_mod(Int u, Int v, Int m) {
  Int r = u - v;
  if (r < 0) r += m;
  return r;
}
template <typename Int>
Int div_pos_r(Int a, Int b, Int &r) {
  Int q = a / b;
  r = a % b;
  if (r < 0) {
    if (b < 0)
      q += 1, r -= b;
    else
      q -= 1, r += b;
  }
  return q;
}
struct factorial_list {
  std::vector<int> fact;
  std::vector<int> inv;
  std::vector<int> inv_fact;
  int mod;
  factorial_list(int n, int mod) : mod(mod) {
    fact.resize(n + 1);
    inv.resize(n + 1);
    inv_fact.resize(n + 1);
    fact[0] = 1;
    for (int i = 1; i <= n; ++i) fact[i] = mul_mod(fact[i - 1], i, mod);
    inv[1] = 1;
    for (int i = 2; i <= n; ++i)
      inv[i] = mod - mul_mod(mod / i, inv[mod % i], mod);
    inv_fact[0] = 1;
    for (int i = 1; i <= n; ++i)
      inv_fact[i] = mul_mod(inv_fact[i - 1], inv[i], mod);
  }
  int operator()(int n) { return fact[n]; }
  int A(int n, int m) { return mul_mod(fact[n], inv_fact[n - m], mod); }
  int C(int n, int m) {
    if (m > n || n < 0 || m < 0) return 0;
    return mul_mod(A(n, m), inv_fact[m], mod);
  }
  int H(int n, int m) { return C(n + m - 1, m); }
};
static const int MOD = 1000000007;
factorial_list F(5005, MOD);
int down[2005][2005];
int downh[2005][2005];
int uph[2005][2005];
int main() {
  int n;
  scanf("%d", &n);
  int m;
  scanf("%d", &m);
  for (int w : inrange(1, m - 1))
    for (int h : inrange(1, n)) down[w][h] = F.H(2 * h - 1, w - 1);
  for (int w : inrange(1, m - 1))
    for (int h : inrange(0, n))
      downh[w][h] = addx_mod((h == 0 ? 0 : downh[w][h - 1]), (down[w][h]), MOD);
  for (int w : inrange(1, m - 1))
    for (int h : inrange(1, n))
      uph[w][h] = subx_mod((downh[w][h + 1]), (downh[w][h]), MOD);
  for (int w : inrange(1, m - 1)) uph[w][0] = 1;
  int ans = 0;
  for (int t : inrange(1, n)) {
    for (int w : inrange(1, m - 1)) {
      int tmp = mul_mod((downh[w][n - t + 1]), (uph[w][t - 1]), MOD);
      ;
      ;
      ;
      (ans) = addx_mod((ans), (mul_mod((tmp), (m - w), MOD)), MOD);
    }
  }
  printf("%d\n", ans);
  return 0;
}
