import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.StreamTokenizer; // 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer in = new StreamTokenizer(br); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); while (in.nextToken() != StreamTokenizer.TT_EOF) { int n = (int) in.nval; in.nextToken(); int m = (int) in.nval; // long[][] dp = new long[n + 1][m + 1]; // for (int i = 0; i <= n; i++) { // for (int j = 0; j <= m; j++) { // dp[i][j] = -1; // } // } out.println(compute3(n, m)); } out.flush(); out.close(); br.close(); } // 记忆化搜索动态规划 private static long compute(int n, int m, long[][] dp) { if (n == 0) { return 1; } if (m == 0) { return 0; } if (dp[n][m] != -1) { return dp[n][m]; } long ans = 0; for (int i = 0; i < n; i++) { ans = (ans + compute(i, m - 1, dp) * compute(n - i - 1, m - 1, dp)) % 1000000007; } dp[n][m] = ans; return ans; } // 严格位置依赖的动态规划 private static long compute2(int n, int m, long[][] dp) { for (int j = 0; j <= n; j++) { dp[j][0] = 0; } for (int i = 0; i <= m; i++) { dp[0][i] = 1; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { dp[i][j] = 0; for (int k = 0; k < i; k++) { dp[i][j] = (dp[i][j] + dp[k][j - 1] * dp[i - k - 1][j - 1] % 1000000007) % 1000000007; } } } return dp[n][m]; } //动态规划 + 空间压缩 private static long compute3(int n, int m) { long[] dp = new long[n + 1]; dp[0] = 1; for (int j = 1; j <= m; j++) { for (int i = n; i >= 1; i--) { dp[i] = 0; for (int k = 0; k < i; k++) { dp[i] = (dp[i] + dp[k] * dp[i - k - 1] % 1000000007) % 1000000007; } } } return dp[n]; } }