#include <bits/stdc++.h>
#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <boost/math/common_factor_rt.hpp>
using namespace std;
using namespace boost::range;
using boost::irange;
using boost::math::gcd;
using boost::math::lcm;

using ll = int64_t;
constexpr char LN = '\n';
template<class T> bool chmax(T& a, const T& b) {if(a<b){a=b;return true;}else{return false;}}
template<class T> bool chmin(T& a, const T& b) {if(b<a){a=b;return true;}else{return false;}}

#ifdef LOCAL
template<class H> void dump_(const char* s, H h) {cout<<s<<"="<<h<<LN; }
template<class H, class... T> void dump_(const char* s, H h, T... t) {for(;*s!=',';++s){cout<<*s;}cout<<"="<<h<<",";dump_(++s,t...);}
#define dump(...) dump_(#__VA_ARGS__, __VA_ARGS__)
#else
#define dump(...)
#endif

struct init_ {init_(){cin.tie(nullptr);ios::sync_with_stdio(false);}} init_;

template<class C> C scan(size_t n) {C c(n);for(auto& v:c){cin>>v;}return c;}
template<class C> C scan(size_t n, size_t m) {C vss(n);for(auto& vs:vss){vs.resize(m);for(auto& v:vs){cin>>v;}}return vss;}

template<class R> struct Joined { const R& r; const char* s; };
template<class R> Joined<R> joined(const R& r, const char* s = " ") {return{r,s};}
template<class R> ostream& operator<<(ostream& out, Joined<R> j) {bool f=true;for(const auto& v:j.r){if(f){f=false;}else{out<<j.s;}out<<v;}return out;}

template<class T> using matrix = vector<vector<T>>;
template<class T> matrix<T> make_matrix(size_t n, size_t m, const T& init = T()) {matrix<T> vss(n);for(auto& vs:vss){vs.resize(m,init);}return vss;}
template<class T> ostream& operator<<(ostream& out, Joined<matrix<T>> j) {bool a=true;for(const auto& vs:j.r){if(a){a=false;}else{out<<LN;}bool f=true;for(const auto& v:vs){if(f){f=false;}else{out<<j.s;}out << v;}}return out;}

template<class T> void show_(const T& t) {cout<<t;}
template<class T> void show_(const vector<T>& v) {cout<<joined(v);}
void show_(bool b) {cout<<(b?"Yes":"No");}
void show_(double d) {cout<<fixed<<setprecision(12)<<d;}
template<class T, class... TS> void show_(const T& t, TS... ts) {show_(t);cout<<' ';show_(ts...);}
template<class T> void show(const T& t) {show_(t);cout<<LN;}
template<class T, class... TS> void show(const T& t, TS... ts) {show_(t, ts...);cout<<LN;}

template<u_int32_t Mod> class modint {
    int64_t val;
public:
    static constexpr u_int32_t mod = Mod;
    constexpr modint(int64_t v = 0) noexcept : val(v % mod) { if (val < 0) val += mod; }
    constexpr int64_t value() const noexcept { return val; }
    friend constexpr istream& operator>>(istream &is, modint<mod>& x) noexcept { int64_t v; is >> v; x = v; return is; }
    friend constexpr ostream& operator<<(ostream &os, const modint<mod>& x) noexcept { return os << x.val; }
    constexpr bool operator==(const modint& r) const noexcept { return val == r.val; }
    constexpr bool operator!=(const modint& r) const noexcept { return val != r.val; }
    constexpr modint operator-() const noexcept { return val ? mod - val : 0; }
    constexpr modint operator+(const modint& r) const noexcept { return modint(*this) += r; }
    constexpr modint operator-(const modint& r) const noexcept { return modint(*this) -= r; }
    constexpr modint operator*(const modint& r) const noexcept { return modint(*this) *= r; }
    constexpr modint operator/(const modint& r) const noexcept { return modint(*this) /= r; }
    constexpr modint& operator+=(const modint& r) noexcept { val += r.val; if (val >= mod) val -= mod; return *this; }
    constexpr modint& operator-=(const modint& r) noexcept { val -= r.val; if (val < 0) val += mod; return *this; }
    constexpr modint& operator*=(const modint& r) noexcept { val = val * r.val % mod; return *this; }
    constexpr modint& operator/=(const modint& r) noexcept {
        int64_t a = r.val, b = mod, u = 1, v = 0, t = 0;
        while (b) { t = a / b; a -= t * b; swap(a, b); u -= t * v; swap(u, v); }
        val = val * u % mod;
        if (val < 0) val += mod;
        return *this;
    }
    friend constexpr modint<mod> pow(const modint<mod> &a, u_int32_t n) noexcept {
        modint<mod> r = 1, t = a;
        for (;n > 0;  n >>= 1) { if (n & 1) { r *= t; } t *= t; }
        return r;
    }
};

template<u_int32_t Mod> class modop {
    vector<int64_t> fs, fis;
public:
    static constexpr u_int32_t mod = Mod;
    modop(u_int32_t max) noexcept : fs(max + 1), fis(max + 1) {
        fs[0] = fs[1] = fis[0] = fis[1] = 1;
        for (u_int32_t i = 2; i <= max; i++) fs[i] = fs[i - 1] * i % mod;
        fis[max] = (modint<mod>(1) / fs[max]).value();
        for (u_int32_t i = max; i > 2; i--) fis[i - 1] = fis[i] * i % mod;
    }
    int64_t fac(u_int32_t n) const noexcept { return fs[n]; }
    int64_t fac_inv(u_int32_t n) const noexcept { return fis[n]; }
    int64_t comb(u_int32_t n, u_int32_t k) const noexcept { return (n < k) ? 0 : fs[n] * (fis[k] * fis[n - k] % mod) % mod; }
};

bool normalize(int& x, int& y) {
    int xx = -x + 2*y, yy = 2*x - y;
    if (xx < 0 || yy < 0 || xx % 3 != 0 || yy % 3 != 0) return false;
    x = xx / 3; y = yy / 3;
    return true;
}

int main() {
    int x, y;
    cin >> x >> y;
    if (!normalize(x, y)) {
        show(0);
        return 0;
    }
    modop<1000000007> mop(x + y);
    show(mop.comb(x + y, x));
}


Measured memory_bytes: 10624