Problem p03581 (C++)

Buggy submission (status: Runtime Error):
/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author RiaD
 */

#include <fstream>
#include <iostream>

#include <iostream>

#include <iterator>

#include <stdexcept>
#include <string>

#ifndef SPCPPL_ASSERT
#ifdef SPCPPL_DEBUG
#define SPCPPL_ASSERT(condition)                                               \
  if (!(condition)) {                                                          \
    throw std::runtime_error(std::string() + #condition + " in line " +        \
                             std::to_string(__LINE__) + " in " +               \
                             __PRETTY_FUNCTION__);                             \
  }
#else
#define SPCPPL_ASSERT(condition)
#endif
#endif

/**
 * Support decrementing and multi-passing, but not declared bidirectional(or
 * even forward) because it's reference type is not a reference.
 *
 * It doesn't return reference because
 * 1. Anyway it'll not satisfy requirement [forward.iterators]/6
 *   If a and b are both dereferenceable, then a == b if and only if *a and
 *   b are bound to the same object.
 * 2. It'll not work with reverse_iterator that returns operator * of temporary
 * which is temporary for this iterator
 *
 * Note, reverse_iterator is not guaranteed to work  now too since it works only
 * with bidirectional iterators, but it's seems to work at least on my
 * implementation.
 *
 * It's not really useful anywhere except iterating anyway.
 */
template <typename T>
class IntegerIterator
    : public std::iterator<std::input_iterator_tag, T, std::ptrdiff_t, T *, T> {
public:
  explicit IntegerIterator(T value) : value(value) {}

  IntegerIterator &operator++() {
    ++value;
    return *this;
  }

  IntegerIterator operator++(int) {
    IntegerIterator copy = *this;
    ++value;
    return copy;
  }

  IntegerIterator &operator--() {
    --value;
    return *this;
  }

  IntegerIterator operator--(int) {
    IntegerIterator copy = *this;
    --value;
    return copy;
  }

  T operator*() const { return value; }

  bool operator==(IntegerIterator rhs) const { return value == rhs.value; }

  bool operator!=(IntegerIterator rhs) const { return !(*this == rhs); }

private:
  T value;
};

template <typename T> class IntegerRange {
public:
  IntegerRange(T begin, T end) : begin_(begin), end_(end) {
    SPCPPL_ASSERT(begin <= end);
  }

  IntegerIterator<T> begin() const { return IntegerIterator<T>(begin_); }

  IntegerIterator<T> end() const { return IntegerIterator<T>(end_); }

private:
  T begin_;
  T end_;
};

template <typename T> class ReversedIntegerRange {
  typedef std::reverse_iterator<IntegerIterator<T>> IteratorType;

public:
  ReversedIntegerRange(T begin, T end) : begin_(begin), end_(end) {
    SPCPPL_ASSERT(begin >= end);
  }

  IteratorType begin() const {
    return IteratorType(IntegerIterator<T>(begin_));
  }

  IteratorType end() const { return IteratorType(IntegerIterator<T>(end_)); }

private:
  T begin_;
  T end_;
};

template <typename T> IntegerRange<T> range(T to) {
  return IntegerRange<T>(0, to);
}

template <typename T> IntegerRange<T> range(T from, T to) {
  return IntegerRange<T>(from, to);
}

template <typename T> IntegerRange<T> inclusiveRange(T to) {
  return IntegerRange<T>(0, to + 1);
}

template <typename T> IntegerRange<T> inclusiveRange(T from, T to) {
  return IntegerRange<T>(from, to + 1);
}

template <typename T> ReversedIntegerRange<T> downrange(T from) {
  return ReversedIntegerRange<T>(from, 0);
}

template <typename T> ReversedIntegerRange<T> downrange(T from, T to) {
  return ReversedIntegerRange<T>(from, to);
}

template <typename T> ReversedIntegerRange<T> inclusiveDownrange(T from) {
  return ReversedIntegerRange<T>(from + 1, 0);
}

template <typename T> ReversedIntegerRange<T> inclusiveDownrange(T from, T to) {
  return ReversedIntegerRange<T>(from + 1, to);
}

#include <assert.h>
#include <type_traits>

template <typename T, typename = std::true_type> struct IdentityHelper;

template <typename T>
struct IdentityHelper<T, typename std::is_arithmetic<T>::type> {
  static T identity() { return 1; }
};

template <typename T> T identity() { return IdentityHelper<T>::identity(); }

/**
 * ax + by = result
 */
template <typename T> T extendedGcd(T a, T b, T &x, T &y) {
  if (a == 0) {
    x = 0;
    y = 1;
    return b;
  }
  T d = extendedGcd(b % a, a, y, x);
  x -= (b / a) * y;
  return d;
}

template <typename T> class Zn {
public:
  Zn() : value(0) {}

  /**
   * Instead of ctor, to allow not to normalize in ctor
   */
  static Zn valueOf(int value) {
    int x = value % mod();
    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  static Zn valueOf(long long value) {
    int x = static_cast<int>(value % mod());
    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  static Zn rawValueOf(int value) {
    SPCPPL_ASSERT(value >= 0 && value < mod());
    return Zn(value);
  }

  Zn &operator=(int rhs) { return *this = Zn::valueOf(rhs); }

  Zn &operator=(long long rhs) { return *this = Zn::valueOf(rhs); }

  Zn &operator+=(const Zn &rhs) {
    value += rhs.value;
    if (value >= mod()) {
      value -= mod();
    }
    return *this;
  }

  Zn &operator+=(int rhs) { return *this += Zn::valueOf(rhs); }

  Zn &operator+=(long long rhs) { return *this += Zn::valueOf(rhs); }

  Zn &operator-=(const Zn &rhs) {
    value -= rhs.value;
    if (value < 0) {
      value += mod();
    }
    return *this;
  }

  Zn &operator-=(int rhs) { return *this -= Zn::valueOf(rhs); }

  Zn &operator-=(long long rhs) { return *this -= Zn::valueOf(rhs); }

  Zn &operator*=(const Zn &rhs) {
    long long result =
        static_cast<long long>(value) * static_cast<long long>(rhs.value);
    value = static_cast<int>(result % mod());
    return *this;
  }

  Zn &operator*=(int rhs) { return *this *= Zn::valueOf(rhs); }

  Zn &operator*=(long long rhs) { return *this *= Zn::valueOf(rhs); }

  Zn operator-() const {
    if (value == 0) {
      return *this;
    } else {
      return Zn(mod() - value);
    }
  }

  Zn &operator/=(const Zn &rhs) { return *this *= rhs.inversed(); }

  Zn &operator/=(int rhs) { return *this /= Zn::valueOf(rhs); }

  Zn &operator/=(long long rhs) { return *this /= Zn::valueOf(rhs); }

  bool operator==(const Zn &rhs) const { return value == rhs.value; }

  Zn inversed() const {
    SPCPPL_ASSERT(value != 0);

    int x, y;
    int gcd = extendedGcd(value, mod(), x, y);
    (void)gcd;
    SPCPPL_ASSERT(gcd == 1);

    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  template <typename U>
  friend std::ostream &operator<<(std::ostream &, const Zn<U> &zn);

  template <typename U>
  friend std::istream &operator>>(std::istream &, Zn<U> &zn);

  int intValue() const { return value; }

private:
  /**
   * No normalization performed
   */
  explicit Zn(int value) : value(value) {}

  int value;

  constexpr static int mod() { return T::value; }

  template <int N = T::value> static constexpr bool positive_or_runtime(int) {
    return N > 0;
  }
  static constexpr bool positive_or_runtime(...) { return true; }
  static_assert(
      std::is_same<typename std::decay<decltype(T::value)>::type, int>::value,
      "T::value must be int");
  static_assert(positive_or_runtime(0), "Mod has to be positive integer");
};

template <typename T> bool operator==(const Zn<T> &lhs, int rhs) {
  return lhs == Zn<T>::valueOf(rhs);
}

template <typename T> bool operator==(int lhs, const Zn<T> &rhs) {
  return rhs == lhs;
}
template <typename T> bool operator==(const Zn<T> &lhs, long long rhs) {
  return lhs == Zn<T>::valueOf(rhs);
}

template <typename T> bool operator==(long long lhs, Zn<T> &rhs) {
  return rhs == lhs;
}

template <typename T> bool operator!=(const Zn<T> &lhs, const Zn<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(const Zn<T> &lhs, int rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(int lhs, const Zn<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(const Zn<T> &lhs, long long rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(long long rhs, const Zn<T> &lhs) {
  return !(lhs == rhs);
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(int lhs, const Zn<T> &rhs) {
  return rhs + lhs;
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(long long lhs, const Zn<T> &rhs) {
  return rhs + lhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(int lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) - rhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(long lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) - rhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(int lhs, const Zn<T> &rhs) {
  return rhs * lhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(long long lhs, const Zn<T> &rhs) {
  return rhs * lhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(int lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) / rhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(long long lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) / rhs;
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Zn<T> &zn) {
  return stream << zn.value;
}

template <typename T>
std::istream &operator>>(std::istream &stream, Zn<T> &zn) {
  long long value;
  stream >> value;
  zn.value = static_cast<int>(value % T::value);
  return stream;
}

template <typename T> struct IdentityHelper<Zn<T>> {
  static Zn<T> identity() { return Zn<T>::valueOf(1); }
};

template <int m> using ZnConst = Zn<std::integral_constant<int, m>>;

#include <vector>

template <typename T, size_t N> struct MakeVector {
  template <typename... Args,
            typename R = std::vector<decltype(MakeVector<T, N - 1>::make_vector(
                std::declval<Args>()...))>>
  static R make_vector(std::size_t first, Args... sizes) {
    auto inner = MakeVector<T, N - 1>::make_vector(sizes...);
    return R(first, inner);
  }
};

template <typename T> struct MakeVector<T, 1> {
  /*
   * This template is to fool CLion.
   * Without it CLion thinks that make_vector always returns std::vector<T> and
   * marks code like
   *
   * auto dp = make_vector<int>(n, m, 0);
   * dp[0][0] = 1 as error because it suppose that dp[0] is int
   *
   * TODO: Consider removing it once
   * https://youtrack.jetbrains.com/issue/CPP-3340 is fixed
   */
  template <typename R = std::vector<T>>
  static R make_vector(std::size_t size, const T &value) {
    return R(size, value);
  }
};

template <typename T, typename... Args>
auto make_vector(Args... args)
    -> decltype(MakeVector<T, sizeof...(Args) - 1>::make_vector(args...)) {
  return MakeVector<T, sizeof...(Args) - 1>::make_vector(args...);
}

#include <algorithm>
#include <array>

template <typename T> void updateMin(T &oldValue, const T &newValue) {
  if (newValue < oldValue) {
    oldValue = newValue;
  }
}

template <typename T> void updateMax(T &oldValue, const T &newValue) {
  if (oldValue < newValue) {
    oldValue = newValue;
  }
}

// #define PROBLEM "problem_name.h"
// #include PROBLEM
// #include <message.h>
// #include <spcppl/dgcj.h>

using namespace std;
using Z = ZnConst<1000000007>;

// vector<vector<Z>> cnk;
std::array<std::array<Z, 4005>, 4005> cnk;
std::array<std::array<Z, 4005>, 4006> cnksum;
std::array<std::array<Z, 4005>, 4006> cnksumk;
const int magic = 4005;

class TaskE {
public:
  void solve(std::istream &in, std::ostream &out) {
    int a, b;
    in >> a >> b;
    Z ans;

    // cnk = make_vector<Z>(magic, magic, Z());

    cnk[0][0] = 1;
    for (int i : range(1, magic)) {
      cnk[i][0] = cnk[i][i] = Z::rawValueOf(1);
      for (int j : range(1, i)) {
        cnk[i][j] = cnk[i - 1][j - 1] + cnk[i - 1][j];
      }
    }

    for (int i : range(0, magic)) {
      cnksum[i][0] = 0;
      cnksumk[i][0] = 0;
      for (int j : range(magic)) {
        cnksum[i][j + 1] = cnksum[i][j] + cnk[i][j];
        cnksumk[i][j + 1] = cnksumk[i][j] + cnk[i][j] * j;
      }
    }

    ans += 1; // ssssttttttt
    // uint64_t ans = 1;

    for (int pos1 = 0; pos1 < a; ++pos1) {
      for (int bInFirstBlock : inclusiveRange(1, b)) {
        int left = a - pos1;
        int leftB = b - bInFirstBlock;
        int leftA = left - leftB;
        if (leftB > left) {
          continue;
        }

        Z c = cnk[b - 1][b - bInFirstBlock];
        ans += c;
        if (leftA == 0) {
          continue;
        }
        Z cur; // = 0;
        int border = leftB - leftA + 1;
        updateMax(border, 1);
        updateMin(border, leftB + 1);
        cur += (cnk[leftA + border - 1][leftA] - 1);
        cur += (cnksum[leftB][leftB + 1] - cnksum[leftB][border]);
        cur += (cnksum[leftB - 1][leftB] - cnksum[leftB - 1][border - 1]) *
               (leftA - leftB);
        cur += (cnksumk[leftB - 1][leftB] - cnksumk[leftB - 1][border - 1]);

        ans += cur * c;
      }
    }

    out << ans << "\n";
  }
};

int main() {
  std::ios_base::sync_with_stdio(false);
  TaskE solver;
  std::istream &in(std::cin);
  std::ostream &out(std::cout);
  in.tie(nullptr);
  out << std::fixed;
  out.precision(20);
  solver.solve(in, out);
  return 0;
}


Fixed submission:
/**
 * code generated by JHelper
 * More info: https://github.com/AlexeyDmitriev/JHelper
 * @author RiaD
 */

#include <fstream>
#include <iostream>

#include <iostream>

#include <iterator>

#include <stdexcept>
#include <string>

#ifndef SPCPPL_ASSERT
#ifdef SPCPPL_DEBUG
#define SPCPPL_ASSERT(condition)                                               \
  if (!(condition)) {                                                          \
    throw std::runtime_error(std::string() + #condition + " in line " +        \
                             std::to_string(__LINE__) + " in " +               \
                             __PRETTY_FUNCTION__);                             \
  }
#else
#define SPCPPL_ASSERT(condition)
#endif
#endif

/**
 * Support decrementing and multi-passing, but not declared bidirectional(or
 * even forward) because it's reference type is not a reference.
 *
 * It doesn't return reference because
 * 1. Anyway it'll not satisfy requirement [forward.iterators]/6
 *   If a and b are both dereferenceable, then a == b if and only if *a and
 *   b are bound to the same object.
 * 2. It'll not work with reverse_iterator that returns operator * of temporary
 * which is temporary for this iterator
 *
 * Note, reverse_iterator is not guaranteed to work  now too since it works only
 * with bidirectional iterators, but it's seems to work at least on my
 * implementation.
 *
 * It's not really useful anywhere except iterating anyway.
 */
template <typename T>
class IntegerIterator
    : public std::iterator<std::input_iterator_tag, T, std::ptrdiff_t, T *, T> {
public:
  explicit IntegerIterator(T value) : value(value) {}

  IntegerIterator &operator++() {
    ++value;
    return *this;
  }

  IntegerIterator operator++(int) {
    IntegerIterator copy = *this;
    ++value;
    return copy;
  }

  IntegerIterator &operator--() {
    --value;
    return *this;
  }

  IntegerIterator operator--(int) {
    IntegerIterator copy = *this;
    --value;
    return copy;
  }

  T operator*() const { return value; }

  bool operator==(IntegerIterator rhs) const { return value == rhs.value; }

  bool operator!=(IntegerIterator rhs) const { return !(*this == rhs); }

private:
  T value;
};

template <typename T> class IntegerRange {
public:
  IntegerRange(T begin, T end) : begin_(begin), end_(end) {
    SPCPPL_ASSERT(begin <= end);
  }

  IntegerIterator<T> begin() const { return IntegerIterator<T>(begin_); }

  IntegerIterator<T> end() const { return IntegerIterator<T>(end_); }

private:
  T begin_;
  T end_;
};

template <typename T> class ReversedIntegerRange {
  typedef std::reverse_iterator<IntegerIterator<T>> IteratorType;

public:
  ReversedIntegerRange(T begin, T end) : begin_(begin), end_(end) {
    SPCPPL_ASSERT(begin >= end);
  }

  IteratorType begin() const {
    return IteratorType(IntegerIterator<T>(begin_));
  }

  IteratorType end() const { return IteratorType(IntegerIterator<T>(end_)); }

private:
  T begin_;
  T end_;
};

template <typename T> IntegerRange<T> range(T to) {
  return IntegerRange<T>(0, to);
}

template <typename T> IntegerRange<T> range(T from, T to) {
  return IntegerRange<T>(from, to);
}

template <typename T> IntegerRange<T> inclusiveRange(T to) {
  return IntegerRange<T>(0, to + 1);
}

template <typename T> IntegerRange<T> inclusiveRange(T from, T to) {
  return IntegerRange<T>(from, to + 1);
}

template <typename T> ReversedIntegerRange<T> downrange(T from) {
  return ReversedIntegerRange<T>(from, 0);
}

template <typename T> ReversedIntegerRange<T> downrange(T from, T to) {
  return ReversedIntegerRange<T>(from, to);
}

template <typename T> ReversedIntegerRange<T> inclusiveDownrange(T from) {
  return ReversedIntegerRange<T>(from + 1, 0);
}

template <typename T> ReversedIntegerRange<T> inclusiveDownrange(T from, T to) {
  return ReversedIntegerRange<T>(from + 1, to);
}

#include <assert.h>
#include <type_traits>

template <typename T, typename = std::true_type> struct IdentityHelper;

template <typename T>
struct IdentityHelper<T, typename std::is_arithmetic<T>::type> {
  static T identity() { return 1; }
};

template <typename T> T identity() { return IdentityHelper<T>::identity(); }

/**
 * ax + by = result
 */
template <typename T> T extendedGcd(T a, T b, T &x, T &y) {
  if (a == 0) {
    x = 0;
    y = 1;
    return b;
  }
  T d = extendedGcd(b % a, a, y, x);
  x -= (b / a) * y;
  return d;
}

template <typename T> class Zn {
public:
  Zn() : value(0) {}

  /**
   * Instead of ctor, to allow not to normalize in ctor
   */
  static Zn valueOf(int value) {
    int x = value % mod();
    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  static Zn valueOf(long long value) {
    int x = static_cast<int>(value % mod());
    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  static Zn rawValueOf(int value) {
    SPCPPL_ASSERT(value >= 0 && value < mod());
    return Zn(value);
  }

  Zn &operator=(int rhs) { return *this = Zn::valueOf(rhs); }

  Zn &operator=(long long rhs) { return *this = Zn::valueOf(rhs); }

  Zn &operator+=(const Zn &rhs) {
    value += rhs.value;
    if (value >= mod()) {
      value -= mod();
    }
    return *this;
  }

  Zn &operator+=(int rhs) { return *this += Zn::valueOf(rhs); }

  Zn &operator+=(long long rhs) { return *this += Zn::valueOf(rhs); }

  Zn &operator-=(const Zn &rhs) {
    value -= rhs.value;
    if (value < 0) {
      value += mod();
    }
    return *this;
  }

  Zn &operator-=(int rhs) { return *this -= Zn::valueOf(rhs); }

  Zn &operator-=(long long rhs) { return *this -= Zn::valueOf(rhs); }

  Zn &operator*=(const Zn &rhs) {
    long long result =
        static_cast<long long>(value) * static_cast<long long>(rhs.value);
    value = static_cast<int>(result % mod());
    return *this;
  }

  Zn &operator*=(int rhs) { return *this *= Zn::valueOf(rhs); }

  Zn &operator*=(long long rhs) { return *this *= Zn::valueOf(rhs); }

  Zn operator-() const {
    if (value == 0) {
      return *this;
    } else {
      return Zn(mod() - value);
    }
  }

  Zn &operator/=(const Zn &rhs) { return *this *= rhs.inversed(); }

  Zn &operator/=(int rhs) { return *this /= Zn::valueOf(rhs); }

  Zn &operator/=(long long rhs) { return *this /= Zn::valueOf(rhs); }

  bool operator==(const Zn &rhs) const { return value == rhs.value; }

  Zn inversed() const {
    SPCPPL_ASSERT(value != 0);

    int x, y;
    int gcd = extendedGcd(value, mod(), x, y);
    (void)gcd;
    SPCPPL_ASSERT(gcd == 1);

    if (x < 0) {
      x += mod();
    }
    return Zn(x);
  }

  template <typename U>
  friend std::ostream &operator<<(std::ostream &, const Zn<U> &zn);

  template <typename U>
  friend std::istream &operator>>(std::istream &, Zn<U> &zn);

  int intValue() const { return value; }

private:
  /**
   * No normalization performed
   */
  explicit Zn(int value) : value(value) {}

  int value;

  constexpr static int mod() { return T::value; }

  template <int N = T::value> static constexpr bool positive_or_runtime(int) {
    return N > 0;
  }
  static constexpr bool positive_or_runtime(...) { return true; }
  static_assert(
      std::is_same<typename std::decay<decltype(T::value)>::type, int>::value,
      "T::value must be int");
  static_assert(positive_or_runtime(0), "Mod has to be positive integer");
};

template <typename T> bool operator==(const Zn<T> &lhs, int rhs) {
  return lhs == Zn<T>::valueOf(rhs);
}

template <typename T> bool operator==(int lhs, const Zn<T> &rhs) {
  return rhs == lhs;
}
template <typename T> bool operator==(const Zn<T> &lhs, long long rhs) {
  return lhs == Zn<T>::valueOf(rhs);
}

template <typename T> bool operator==(long long lhs, Zn<T> &rhs) {
  return rhs == lhs;
}

template <typename T> bool operator!=(const Zn<T> &lhs, const Zn<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(const Zn<T> &lhs, int rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(int lhs, const Zn<T> &rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(const Zn<T> &lhs, long long rhs) {
  return !(lhs == rhs);
}

template <typename T> bool operator!=(long long rhs, const Zn<T> &lhs) {
  return !(lhs == rhs);
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(int lhs, const Zn<T> &rhs) {
  return rhs + lhs;
}

template <typename T> Zn<T> operator+(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy += rhs;
}

template <typename T> Zn<T> operator+(long long lhs, const Zn<T> &rhs) {
  return rhs + lhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(int lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) - rhs;
}

template <typename T> Zn<T> operator-(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy -= rhs;
}

template <typename T> Zn<T> operator-(long lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) - rhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(int lhs, const Zn<T> &rhs) {
  return rhs * lhs;
}

template <typename T> Zn<T> operator*(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy *= rhs;
}

template <typename T> Zn<T> operator*(long long lhs, const Zn<T> &rhs) {
  return rhs * lhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, const Zn<T> &rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, int rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(int lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) / rhs;
}

template <typename T> Zn<T> operator/(const Zn<T> &lhs, long long rhs) {
  Zn<T> copy = lhs;
  return copy /= rhs;
}

template <typename T> Zn<T> operator/(long long lhs, const Zn<T> &rhs) {
  return Zn<T>::valueOf(lhs) / rhs;
}

template <typename T>
std::ostream &operator<<(std::ostream &stream, const Zn<T> &zn) {
  return stream << zn.value;
}

template <typename T>
std::istream &operator>>(std::istream &stream, Zn<T> &zn) {
  long long value;
  stream >> value;
  zn.value = static_cast<int>(value % T::value);
  return stream;
}

template <typename T> struct IdentityHelper<Zn<T>> {
  static Zn<T> identity() { return Zn<T>::valueOf(1); }
};

template <int m> using ZnConst = Zn<std::integral_constant<int, m>>;

#include <vector>

template <typename T, size_t N> struct MakeVector {
  template <typename... Args,
            typename R = std::vector<decltype(MakeVector<T, N - 1>::make_vector(
                std::declval<Args>()...))>>
  static R make_vector(std::size_t first, Args... sizes) {
    auto inner = MakeVector<T, N - 1>::make_vector(sizes...);
    return R(first, inner);
  }
};

template <typename T> struct MakeVector<T, 1> {
  /*
   * This template is to fool CLion.
   * Without it CLion thinks that make_vector always returns std::vector<T> and
   * marks code like
   *
   * auto dp = make_vector<int>(n, m, 0);
   * dp[0][0] = 1 as error because it suppose that dp[0] is int
   *
   * TODO: Consider removing it once
   * https://youtrack.jetbrains.com/issue/CPP-3340 is fixed
   */
  template <typename R = std::vector<T>>
  static R make_vector(std::size_t size, const T &value) {
    return R(size, value);
  }
};

template <typename T, typename... Args>
auto make_vector(Args... args)
    -> decltype(MakeVector<T, sizeof...(Args) - 1>::make_vector(args...)) {
  return MakeVector<T, sizeof...(Args) - 1>::make_vector(args...);
}

#include <algorithm>
#include <array>

template <typename T> void updateMin(T &oldValue, const T &newValue) {
  if (newValue < oldValue) {
    oldValue = newValue;
  }
}

template <typename T> void updateMax(T &oldValue, const T &newValue) {
  if (oldValue < newValue) {
    oldValue = newValue;
  }
}

// #define PROBLEM "problem_name.h"
// #include PROBLEM
// #include <message.h>
// #include <spcppl/dgcj.h>

using namespace std;
using Z = ZnConst<1000000007>;

// vector<vector<Z>> cnk;
std::array<std::array<Z, 4005>, 4005> cnk;
std::array<std::array<Z, 4005>, 4006> cnksum;
std::array<std::array<Z, 4005>, 4006> cnksumk;
const int magic = 4005;

class TaskE {
public:
  void solve(std::istream &in, std::ostream &out) {
    int a, b;
    in >> a >> b;
    Z ans;

    // cnk = make_vector<Z>(magic, magic, Z());

    cnk[0][0] = 1;
    for (int i : range(1, magic)) {
      cnk[i][0] = cnk[i][i] = Z::rawValueOf(1);
      for (int j : range(1, i)) {
        cnk[i][j] = cnk[i - 1][j - 1] + cnk[i - 1][j];
      }
    }

    for (int i : range(0, magic)) {
      cnksum[i][0] = 0;
      cnksumk[i][0] = 0;
      for (int j : range(magic)) {
        cnksum[i][j + 1] = cnksum[i][j] + cnk[i][j];
        cnksumk[i][j + 1] = cnksumk[i][j] + cnk[i][j] * j;
      }
    }

    ans += 1; // ssssttttttt
    // uint64_t ans = 1;

    for (int pos1 = 0; pos1 < a; ++pos1) {
      for (int bInFirstBlock : inclusiveRange(1, b)) {
        int left = a - pos1;
        int leftB = b - bInFirstBlock;
        int leftA = left - leftB;
        if (leftB > left) {
          continue;
        }

        Z c = cnk[b - 1][b - bInFirstBlock];
        ans += c;
        if (leftA == 0) {
          continue;
        }
        Z cur; // = 0;
        int border = leftB - leftA + 1;
        updateMax(border, 1);
        updateMin(border, leftB + 1);
        cur += (cnk[leftA + border - 1][leftA] - 1);
        cur += (cnksum[leftB][leftB + 1] - cnksum[leftB][border]);
        if (leftB == 0) {
          continue;
        }
        cur += (cnksum[leftB - 1][leftB] - cnksum[leftB - 1][border - 1]) *
               (leftA - leftB);
        cur += (cnksumk[leftB - 1][leftB] - cnksumk[leftB - 1][border - 1]);

        ans += cur * c;
      }
    }

    out << ans << "\n";
  }
};

int main() {
  std::ios_base::sync_with_stdio(false);
  TaskE solver;
  std::istream &in(std::cin);
  std::ostream &out(std::cout);
  in.tie(nullptr);
  out << std::fixed;
  out.precision(20);
  solver.solve(in, out);
  return 0;
}