#pragma comment(linker, "/STACK:1024000000")
#define _CRT_SECURE_NO_WARNINGS
#undef NDEBUG
//#include "testlib.h"
#include <cstdio>
#include <cassert>
#include <algorithm>
#include <iostream>
#include <memory.h>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <bitset>
#include <deque>
#include <ctime>
#include <stack>
#include <queue>
#include <fstream>
#include <sstream>
#include <functional>
/*#ifndef room111
#include <sys/resource.h>
#endif*/
#include <unordered_set>
#include <unordered_map>
#include <array>
using namespace std;
#define FILENAME ""
#define mp make_pair
#define all(a) a.begin(), a.end()
typedef long long li;
typedef long double ld;
void solve(bool);
void precalc();
clock_t start;
//int timer = 1;

int testNumber = 1;

bool todo = true;

bool stress = false;

int main() {
#ifdef room111
	freopen("in.txt", "r", stdin);
	//freopen("out.txt", "w", stdout);
#else
	//freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
	//freopen(FILENAME".in", "r", stdin);
	//freopen(FILENAME ".out", "w", stdout);
#endif
	start = clock();
	int t = 1;
	cout.sync_with_stdio(0);
	cin.tie(0);
	precalc();
	cout.precision(10);
	cout << fixed;
	/*#ifndef room111
	const rlim_t kStackSize = 1024L * 1024L * 1024L;   // min stack size = 64 Mb
	struct rlimit rl;
	int result;

	result = getrlimit(RLIMIT_STACK, &rl);
	if (result == 0)
	{
	if (rl.rlim_cur < kStackSize)
	{
	rl.rlim_cur = kStackSize;
	result = setrlimit(RLIMIT_STACK, &rl);
	if (result != 0)
	{
	fprintf(stderr, "setrlimit returned result = %d\n", result);
	}
	}
	}
	#endif*/

	//cin >> t;
	int testNum = 1;
	while (t--) {
		//cerr << testNum << endl;
		//cout << "Case #" << testNum++ << ": ";
		solve(true);
		++testNumber;
		//++timer;
	}
#ifdef room1111
	while (true)
		solve(false);
#endif

#ifdef room111
	cerr << "\n\n" << (clock() - start) / 1.0 / CLOCKS_PER_SEC << "\n\n";
#endif

	return 0;
}

//BE CAREFUL: IS INT REALLY INT?

//#define int li

template<typename T>
T binpow(T q, T w, T mod) {
	if (!w)
		return 1 % mod;
	if (w & 1)
		return q * 1LL * binpow(q, w - 1, mod) % mod;
	return binpow(q * 1LL * q % mod, w / 2, mod);
}

template<typename T>
T gcd(T q, T w) {
	while (w) {
		q %= w;
		swap(q, w);
	}
	return q;
}
template<typename T>
T lcm(T q, T w) {
	return q / gcd(q, w) * w;
}
void precalc() {}

template<typename T>
void relax(T& cur, T val) {
	cur = min(cur, val);
}

//#define int li
const int mod = 1000000007;

int dp[3010][6010][2];
//vector<string> all_dp[100][100][2];

void add(int& cur, int val) {
	cur += val;
	cur %= mod;
}

int dp1[6010][3010];

void solve(bool read) {
	int n, m;
	cin >> n >> m;
	for (int red = 0; red <= n; ++red) {
		dp[0][red][0] = 1;
	//	all_dp[0][red][0].push_back("");
	}
	for (int i = 0; i < m; ++i) {
		for (int diff_red = 0; diff_red <= i + n; ++diff_red) {
			int diff_blue = n - diff_red;
			for (int was_before = 0; was_before < 2; ++was_before) {
				int cur_dp = dp[i][diff_red][was_before];
				if (!cur_dp) continue;
				//cout << i << " " << diff_red << " " << was_before << " " << cur_dp << endl;
				if (diff_red >= 1) {
					int new_before = (diff_red == 1 || was_before);
					add(dp[i + 1][diff_red - 1][new_before], cur_dp);
		/*			for (string s : all_dp[i][diff_red][was_before]) {
						s += "rr";
						all_dp[i + 1][diff_red - 1][new_before].push_back(s);
					}*/
					if (diff_blue >= 0) {
						add(dp[i + 1][diff_red][new_before], cur_dp);

						/*for (string s : all_dp[i][diff_red][was_before]) {
							s += "rb";
							all_dp[i + 1][diff_red][new_before].push_back(s);
						}*/

					}
				}
				if (diff_blue > 0) {
					int new_before = (diff_red == 0 || was_before);
					if (!was_before && new_before) {
						//cout << i << " " << diff_red << " " << diff_blue << " " << cur_dp << endl;
					}
					add(dp[i + 1][diff_red][new_before], cur_dp);

					/*for (string s : all_dp[i][diff_red][was_before]) {
						s += "br";
						all_dp[i + 1][diff_red][new_before].push_back(s);
					}*/

					add(dp[i + 1][diff_red + 1][was_before], cur_dp);
					/*for (string s : all_dp[i][diff_red][was_before]) {
						s += "bb";
						all_dp[i + 1][diff_red + 1][was_before].push_back(s);
					}*/
				}
			}
		}
	}

	int ans = 0;
	set<string> have;
	for (int diff_red = 0; diff_red <= n + m; ++diff_red) {
		add(ans, dp[m][diff_red][1]);
		/*cout << diff_red << " " << dp[m][diff_red][1] << endl;
		for (string s : all_dp[m][diff_red][1]) {
			cout << s << " ";
			if (have.count(s)) {
				cout << endl << "have string " << s << " twice" << endl;
			}
			have.insert(s);
		}
		cout << endl;*/
	}
	/*for (string cur : have) {
		cout << cur << endl;
	}
	cout << have.size() << endl;
	cout << ans << endl;*/

	dp1[0][0] = 1;
	for (int i = 0; i < 2 * m; ++i) {
		int have_blue = n + (i + 1) / 2;
		for (int used = 0; used < max(i / 2, 1); ++used) {
			if (have_blue + used >= i + 1) {
				add(dp1[i + 1][used], dp1[i][used]);
			}
			if (used + 1 < (i + 1) / 2 && have_blue + used + 1 >= i + 1) {
				add(dp1[i + 1][used + 1], dp1[i][used]);
			}
		}
	}
	for (int used = 0; used < m; ++used) {
		add(ans, dp1[2 * m][used]);
		//cout << used << " " << dp1[2 * m][used] << endl;
	}

	cout << ans << "\n";

}

Measured memory_bytes: 210304