using System;
using System.Linq;
using System.Numerics;
using System.Collections.Generic;
using System.IO;
using System.Text;
using N = System.Int64;

public class Solver
{
    public void Solve()
    {
        N x = rl, y = rl;
        if ((2 * x - y) % 3 != 0 || (2 * y - x) % 3 != 0)
        {
            Write(0);
            return;
        }
        N a = (2 * y - x) / 3, b = (2 * x - y) / 3;
        if (a<0||b<0)
        {
            Write(0);
            return;
        }
        N s = a + b;
        N[] fac = new N[s + 1];
        fac[0] = 1;
        for (N i = 0; i < s; i++)
        {
            fac[i + 1] = (fac[i] * (i + 1)) % mod;
        }
        N[] rfac = new N[s + 1];
        rfac[s] = Reverse(fac[s]);
        for (N i = s; i > 0; i--)
        {
            rfac[i - 1] = (rfac[i] * i) % mod;
        }
        Write(Mul(fac[s], rfac[a], rfac[s - a]));
    }
    

    int ri { get { return sc.Integer(); } }
    long rl { get { return sc.Long(); } }
    double rd { get { return sc.Double(); } }
    string rs { get { return sc.Scan(); } }
    public StreamScanner sc = new StreamScanner(Console.OpenStandardInput());
    public Algorithm alg = new Algorithm();

    private const N mod = 1000000007;
    N Reverse(N n) { return Pow(n, mod - 2); }
    N Mul(params N[] n) { N p = n[0]; for (N i = 1; i < n.Length; i++) { p = (p * n[i]) % mod; } return p; }
    N Pow(N n, N p) { N t = n, q = 1; while (p > 0) { if (p % 2 == 1) { q = (q * t) % mod; } t = (t * t) % mod; p >>= 1; } return q; }
    N PowUpper(N n, N b) { N p = 1; while (p <= n) { p *= b; } return p; }
    N Sign(N n) { return n == 0 ? 0 : n > 0 ? 1 : -1; }
    N Abs(N n) { return (n > 0) ? n : -n; }
    N Gcd(params N[] n) { N p = n[0]; for (N i = 1; i < n.Length; i++) { p = Gcd(p, n[i]); } return p; }
    N Gcd(N a, N b) { return (a == 0) ? b : Gcd(b % a, a); }
    N Lcm(params N[] n) { N p = n[0]; for (N i = 1; i < n.Length; i++) { p = Lcm(p, n[i]); } return p; }
    N Lcm(N a, N b) { return (a / Gcd(a, b) * b) % mod; }
    void Write<T>(T t) { Console.WriteLine(t.ToString()); }
    void YN(bool t) { Console.WriteLine(t ? "YES" : "NO"); }
    void Yn(bool t) { Console.WriteLine(t ? "Yes" : "No"); }
    void yn(bool t) { Console.WriteLine(t ? "yes" : "no"); }
}

static class Program
{
    static public void Main(string[] args)
    {
        Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        var solver = new Solver();
        solver.Solve();
        Console.Out.Flush();
    }
}

public class Algorithm
{
    public int[] ZAlgorithm(string s)
    {
        int[] p = new int[s.Length];
        p[0] = s.Length;
        int i = 1, j = 0;
        while (i < s.Length)
        {
            while (i + j < s.Length && s[j] == s[i + j]) ++j;
            p[i] = j;
            if (j == 0) { ++i; continue; }
            int k = 1;
            while (i + k < s.Length && k + p[k] < j)
            {
                p[i + k] = p[k];
                ++k;
            }
            i += k; j -= k;
        }
        return p;
    }

    public List<int> SieveOfEratosthenes(int n)
    {
        List<int> l = new List<int>();
        if (n >= 2) { l.Add(2); }
        bool[] t = new bool[(n - 1) / 2];
        for (int p = 3; p <= n; p += 2) { if (!t[(p - 3) / 2]) { l.Add(p); for (int q = p * p; q <= n; q += p) { t[(q - 3) / 2] = true; } } }
        return l;
    }

    public Dictionary<int,int> PrimeFactor(int n, List<int> primetable)
    {
        Dictionary<int, int> d = new Dictionary<int, int>();
        int t = 0;
        foreach(int i in primetable)
        {
            if (n % i == 0)
            {
                while (n % 2 == 0) { n /= i; t++; }
                d.Add(i, t);
            }
        }
        if (n > 1) { d.Add(n, 1); }
        return d;
    }
}

public class PriorityQueue<T> where T : IComparable
{
    private IComparer<T> _comparer = null;
    private int _type = 0;

    private T[] _heap;
    private int _sz = 0;

    private int _count = 0;

    /// <summary>
    /// Priority Queue with custom comparer
    /// </summary>
    public PriorityQueue(IComparer<T> comparer)
    {
        _heap = new T[128];
        _comparer = comparer;
    }

    /// <summary>
    /// Priority queue
    /// </summary>
    /// <param name="type">0: asc, 1:desc</param>
    public PriorityQueue(int type = 0)
    {
        _heap = new T[128];
        _type = type;
    }

    private int Compare(T x, T y)
    {
        if (_comparer != null) return _comparer.Compare(x, y);
        return _type == 0 ? x.CompareTo(y) : y.CompareTo(x);
    }

    public void Push(T x)
    {
        _count++;
        if (_count > _heap.Length)
        {
            var newheap = new T[_heap.Length * 2];
            for (int n = 0; n < _heap.Length; n++) newheap[n] = _heap[n];
            _heap = newheap;
        }

        //node number
        var i = _sz++;

        while (i > 0)
        {
            //parent node number
            var p = (i - 1) / 2;

            if (Compare(_heap[p], x) <= 0) break;

            _heap[i] = _heap[p];
            i = p;
        }

        _heap[i] = x;
    }

    public T Pop()
    {
        _count--;

        T ret = _heap[0];
        T x = _heap[--_sz];

        int i = 0;
        while (i * 2 + 1 < _sz)
        {
            //children
            int a = i * 2 + 1;
            int b = i * 2 + 2;

            if (b < _sz && Compare(_heap[b], _heap[a]) < 0) a = b;

            if (Compare(_heap[a], x) >= 0) break;

            _heap[i] = _heap[a];
            i = a;
        }

        _heap[i] = x;

        return ret;
    }

    public int Count()
    {
        return _count;
    }

    public T Peek()
    {
        return _heap[0];
    }

    public bool Contains(T x)
    {
        for (int i = 0; i < _sz; i++) if (x.Equals(_heap[i])) return true;
        return false;
    }

    public void Clear()
    {
        while (this.Count() > 0) this.Pop();
    }

    public IEnumerator<T> GetEnumerator()
    {
        var ret = new List<T>();

        while (this.Count() > 0)
        {
            ret.Add(this.Pop());
        }

        foreach (var r in ret)
        {
            this.Push(r);
            yield return r;
        }
    }

    public T[] ToArray()
    {
        T[] array = new T[_sz];
        int i = 0;

        foreach (var r in this)
        {
            array[i++] = r;
        }

        return array;
    }
}

public class StreamScanner
{
    public StreamScanner(Stream stream) { str = stream; }
    private readonly Stream str;
    private readonly byte[] buf = new byte[1024];
    private int len, ptr;
    public bool isEof = false;
    public bool IsEndOfStream { get { return isEof; } }
    private byte read()
    {
        if (isEof) throw new EndOfStreamException();
        if (ptr >= len)
        {
            ptr = 0;
            if ((len = str.Read(buf, 0, 1024)) <= 0)
            {
                isEof = true;
                return 0;
            }
        }
        return buf[ptr++];
    }
    public char Char()
    {
        byte b = 0;
        do b = read();
        while (b < 33 || 126 < b);
        return (char)b;
    }
    public string Scan()
    {
        var sb = new StringBuilder();
        for (var b = Char(); b >= 33 && b <= 126; b = (char)read())
            sb.Append(b);
        return sb.ToString();
    }
    public long Long()
    {
        long ret = 0; byte b = 0; var ng = false;
        do b = read();
        while (b != '-' && (b < '0' || '9' < b));
        if (b == '-') { ng = true; b = read(); }
        for (; true; b = read())
        {
            if (b < '0' || '9' < b)
                return ng ? -ret : ret;
            else ret = ret * 10 + b - '0';
        }
    }
    public int Integer() { return (int)Long(); }
    public double Double() { return double.Parse(Scan()); }
}


Measured memory_bytes: 21344