#include <bits/stdc++.h>
#define TASK "aksdjkads"
#define INT_LIM (int) 2147483647
#define LL_LIM (long long) 9223372036854775807
#define endl '\n'
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define BIT(i,x) (((i)>>(x))&1)
#define FOR(i,a,b) for(int i = (a); i<=(b); i++)
#define FORD(i,a,b) for(int i = (a); i>=(b); i--)
#define ll long long
#define pii pair<int,int>
using namespace std;
///------------------------------------------///
const int MOD = 1e9+7;
class modInt{
private:
    int val;
public:
    modInt(): val(0) {};
    modInt(long long _val): val(int(_val%MOD))
    {
        if (val<0) val+=MOD;
    }
    void operator += (const modInt &other)
    {
        val+=other.val;
        if (val>=MOD) val-=MOD;
    }
    void operator -= (const modInt &other)
    {
        val-=other.val;
        if (val<0) val+=MOD;
    }
    void operator *= (const modInt &other)
    {
        val = 1LL*val*other.val%MOD;
    }
    modInt operator + (const modInt &other)
    {
        modInt ret = *this;
        ret+= other;
        return ret;
    }
    modInt operator - (const modInt &other)
    {
        modInt ret = *this;
        ret-= other;
        return ret;
    }
    modInt operator * (const modInt &other)
    {
        modInt ret = *this;
        ret*= other;
        return ret;
    }
    modInt inverse(modInt x)
    {
        modInt ret = 1; int p = MOD-2;
        while (p>0)
        {
            if (p&1) ret*=x;
            x*=x; p>>=1;
        }
        return ret;
    }
    void operator /= (const modInt &other)
    {
        (*this)*= inverse(other);
    }
    modInt operator / (const modInt &other)
    {
        modInt ret = *this;
        ret/=other;
        return ret;
    }
    explicit operator int()
    {
        return val;
    }
    friend ostream & operator << (ostream &cout, const modInt &x)
    {
        cout << x.val;
        return cout;
    }
    friend istream & operator >> (istream &cin, modInt &x)
    {
        ll tmp;
        cin >> tmp;
        x = modInt(tmp);
        return cin;
    }
};
modInt pw(modInt x, int p)
{
    modInt ret = 1;
    while (p>0)
    {
        if (p&1) ret*=x;
        x*=x; p>>=1;
    }
    return ret;
}
int m,n;
modInt fact[3005], inv[3005];
void prep(int MAXN)
{
    fact[0] = 1;
    FOR(i, 1, MAXN) fact[i] = fact[i-1]*i;
    inv[MAXN] = modInt(1)/fact[MAXN];
    FORD(i, MAXN, 1)
    {
        inv[i-1] = inv[i]*i;
    }
}
modInt nCk(int n, int k)
{
    if (k<0 || k>n) return 0;
    return int(fact[n]*inv[n-k]*inv[k]);
}
void inp()
{
    prep(3000);
    cin >> m >> n;
}
int f[3001][3001];
int calc(int x, int y)
{
    if (x<1 || y<1) return 1;
    if (f[x][y]!=-1) return f[x][y];
    modInt ret = 1;
    FOR(j, 1, y)
        ret+= nCk(x, 2)*calc(x-2, j-1);
    return f[x][y] = (int)ret;
}
void solve()
{
    memset(f, -1, sizeof(f));
    modInt ans = 0;
    FOR(k, 0, min(n,m))
    {
        modInt term = nCk(m,k)*nCk(n,k)*fact[k]*pw(4,k);
        FOR(l, 0, min(m-k, (n-k)/2))
        {
            modInt term2 = nCk(m-k,l)*nCk(n-k,2*l)*fact[2*l]/pw(2,l);
//            cout << k << ' ' << l << ' ' << term << ' ' << term2 << ' ' << calc(m-k-l, n-k-2*l) << endl;
            ans+= term*term2*calc(m-k-l, n-k-2*l);
        }
    }
    ans-=1;
    cout << ans;
}
signed main()
{
    ///--------------------------///
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    if (fopen(TASK".INP","r")!=NULL)
    {
        freopen(TASK".INP","r",stdin);
        freopen(TASK".OUT","w",stdout);
    }
    ///--------------------------///
    int NTEST = 1;
    //cin >> NTEST;
    while (NTEST--)
    {
        inp();
        solve();
    }
    return 0;
}
///------------------------------------------///
컴파일 시 표준 에러 (stderr) 메시지
tents.cpp: In function 'int main()':
tents.cpp:172:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
  172 |         freopen(TASK".INP","r",stdin);
      |         ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
tents.cpp:173:16: warning: ignoring return value of 'FILE* freopen(const char*, const char*, FILE*)' declared with attribute 'warn_unused_result' [-Wunused-result]
  173 |         freopen(TASK".OUT","w",stdout);
      |         ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict | Execution time | Memory | Grader output | 
|---|
| Fetching results... |