// Original C++ solution (slower):
#include <iostream>

#include <iomanip>

#include <cassert>

#include <cstdint>

#include <vector>

#include <string>

#include <set>

#include <map>

#include <unordered_set>

#include <unordered_map>

#include <array>

#include <deque>

#include <stack>

#include <queue>

#include <algorithm>

#include <functional>

#include <utility>

#include <cmath>

#include <numeric>

#include <sstream>



using namespace std;



#ifdef _DEBUG

#define DUMP(x) std::cerr << (#x) << " = " << (x) << "\n"

#else

#define DUMP(x)

#endif

#define REP(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)

#define EREP(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)

#define RREP(i, a, b) for (int i = (int)(a)-1; i >= (int)(b); --i)

#define rep(i, n) REP(i, 0, n)

#define erep(i, n) EREP(i, 0, n)

#define rrep(i, n) RREP(i, n, 0)

#define ALL(r) (r).begin(), (r).end()

#define pb push_back

#define eb emplace_back

#define fi first

#define se second



void dummy_();

inline void YES() { std::cout << "YES\n"; }

inline void Yes() { std::cout << "Yes\n"; }

inline void NO() { std::cout << "NO\n"; }

inline void No() { std::cout << "No\n"; }

inline void M1() { std::cout << "-1\n"; }



template <typename T>

ostream &operator<<(ostream &os, vector<T> &v)

{

    os << "{";

    rep(i, v.size()) os << v[i] << (i == (int)v.size() - 1 ? "" : ", ");

    os << "}";

    return os;

}

template <typename T, typename U>

ostream &operator<<(ostream &os, pair<T, U> &p) { return (os << "(" << p.first << ", " << p.second << ")"); }

template <typename T, typename U>

ostream &operator<<(ostream &os, map<T, U> &m)

{

    bool first = true;

    os << "{";

    for (const auto &e : m)

    {

        if (!first)

            os << ", ";

        os << "{" << e.first << ": " << e.second << "}";

        first = false;

    }

    os << "}";

    return os;

}

template <typename T>

ostream &operator<<(ostream &os, set<T> &s)

{

    os << "{";

    bool first = true;

    for (const auto &e : s)

    {

        if (!first)

            os << ", ";

        os << e;

        first = false;

    }

    os << "}";

    return os;

}

template <typename T>

T dup(T x, T y) { return (x + y - 1) / y; };

template <typename A, size_t N, typename T>

inline void arrayFill(A (&array)[N], const T &val) { std::fill((T *)array, (T *)(array + N), val); }

template <class T>

inline bool chmax(T &a, T b)

{

    if (a < b)

    {

        a = b;

        return true;

    }

    return false;

}

template <class T>

inline bool chmin(T &a, T b)

{

    if (a > b)

    {

        a = b;

        return true;

    }

    return false;

}

struct in

{

    const size_t n = 0;

    in() = default;

    in(size_t n) : n(n){};

    template <typename T>

    operator T()

    {

        T ret;

        cin >> ret;

        return ret;

    }

    template <typename T>

    operator vector<T>()

    {

        assert(n != 0);

        vector<T> ret(n);

        for (T &x : ret)

        {

            T tmp = in();

            x = tmp;

        }

        return ret;

    }

    template <typename T, typename U>

    operator pair<T, U>()

    {

        pair<T, U> ret;

        ret.first = in();

        ret.second = in();

        return ret;

    }

};



using ll = int64_t;

using vint = vector<int32_t>;

using vvint = vector<vint>;

using vll = vector<ll>;

using vvll = vector<vll>;

using vstr = vector<string>;

using pint = pair<int32_t, int32_t>;

using vpint = vector<pint>;

using pll = pair<ll, ll>;

using vpll = vector<pll>;

using setint = set<int32_t>;

using qint = queue<int32_t>;

using qpint = queue<pint>;



constexpr std::int32_t INF = 1001001001;

constexpr std::int64_t LINF = 1001001001001001001;



void Main()

{

    string s = in();

    int l = s.size();

    vll m(2020, 0), m2(2020, 0);

    rep(i, l)

    {

        int d = s[i] - '0';

        vll tmp(2020, 0);

        ++tmp[d];

        rep(j, 2020)

        {

            int x = (j * 10 + d) % 2019;

            tmp[x] += m[j];

            m2[j] += m[j];

        }

        m = tmp;

    }

    rep(i, 2020) m[i] += m2[i];

    cout << m[0] << '\n';

}



signed main()

{

    std::cin.tie(nullptr);

    std::ios_base::sync_with_stdio(false);

    std::cout << std::fixed << std::setprecision(15);

    Main();

    return 0;

}


// Optimized solution to the same problem (faster):
#include <iostream>

#include <iomanip>

#include <cassert>

#include <cstdint>

#include <vector>

#include <string>

#include <set>

#include <map>

#include <unordered_set>

#include <unordered_map>

#include <array>

#include <deque>

#include <stack>

#include <queue>

#include <algorithm>

#include <functional>

#include <utility>

#include <cmath>

#include <numeric>

#include <sstream>



using namespace std;



#ifdef _DEBUG

#define DUMP(x) std::cerr << (#x) << " = " << (x) << "\n"

#else

#define DUMP(x)

#endif

#define REP(i, a, b) for (int i = (int)(a); i < (int)(b); ++i)

#define EREP(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)

#define RREP(i, a, b) for (int i = (int)(a)-1; i >= (int)(b); --i)

#define rep(i, n) REP(i, 0, n)

#define erep(i, n) EREP(i, 0, n)

#define rrep(i, n) RREP(i, n, 0)

#define ALL(r) (r).begin(), (r).end()

#define pb push_back

#define eb emplace_back

#define fi first

#define se second



template <typename T>

ostream &operator<<(ostream &os, vector<T> &v)

{

    os << "{";

    rep(i, v.size()) os << v[i] << (i == (int)v.size() - 1 ? "" : ", ");

    os << "}";

    return os;

}

template <typename T, typename U>

ostream &operator<<(ostream &os, pair<T, U> &p) { return (os << "(" << p.first << ", " << p.second << ")"); }

template <typename T, typename U>

ostream &operator<<(ostream &os, map<T, U> &m)

{

    bool first = true;

    os << "{";

    for (const auto &e : m)

    {

        if (!first)

            os << ", ";

        os << "{" << e.first << ": " << e.second << "}";

        first = false;

    }

    os << "}";

    return os;

}

template <typename T>

ostream &operator<<(ostream &os, set<T> &s)

{

    os << "{";

    bool first = true;

    for (const auto &e : s)

    {

        if (!first)

            os << ", ";

        os << e;

        first = false;

    }

    os << "}";

    return os;

}

template <typename T>

T dup(T x, T y) { return (x + y - 1) / y; };

template <typename A, size_t N, typename T>

inline void arrayFill(A (&array)[N], const T &val) { std::fill((T *)array, (T *)(array + N), val); }

template <class T>

inline bool chmax(T &a, T b)

{

    if (a < b)

    {

        a = b;

        return true;

    }

    return false;

}

template <class T>

inline bool chmin(T &a, T b)

{

    if (a > b)

    {

        a = b;

        return true;

    }

    return false;

}

struct in

{

    const size_t n = 0;

    in() = default;

    in(size_t n) : n(n){};

    template <typename T>

    operator T()

    {

        T ret;

        cin >> ret;

        return ret;

    }

    template <typename T>

    operator vector<T>()

    {

        assert(n != 0);

        vector<T> ret(n);

        for (T &x : ret)

        {

            T tmp = in();

            x = tmp;

        }

        return ret;

    }

    template <typename T, typename U>

    operator pair<T, U>()

    {

        pair<T, U> ret;

        ret.first = in();

        ret.second = in();

        return ret;

    }

};

template <typename T>

inline void out(T x) { std::cout << x << '\n'; };



using ll = int64_t;

using vint = vector<int32_t>;

using vvint = vector<vint>;

using vll = vector<ll>;

using vvll = vector<vll>;

using vstr = vector<string>;

using pint = pair<int32_t, int32_t>;

using vpint = vector<pint>;

using pll = pair<ll, ll>;

using vpll = vector<pll>;

using setint = set<int32_t>;

using qint = queue<int32_t>;

using qpint = queue<pint>;



constexpr std::int32_t INF = 1001001001;

constexpr std::int64_t LINF = 1001001001001001001;



constexpr int mod = 2019;



void Main()

{

    string s = in();

    int n = s.size();

    int tmp = 1;

    vint a(n);

    rrep(i, n)

    {

        int d = s[i] - '0';

        a[i] = d * tmp % mod;



        tmp *= 10;

        tmp %= mod;

    }



    vint sum(n + 1);

    rep(i, n)

        sum[i + 1] = (sum[i] + a[i]) % mod;



    DUMP(sum);



    map<int, int> m;

    rep(i, n + 1)++ m[sum[i] % mod];



    int ans = 0;

    for (auto [_, cnt] : m)

        ans += cnt * (cnt - 1) / 2;



    out(ans);

}



signed main()

{

    std::cin.tie(nullptr);

    std::ios_base::sync_with_stdio(false);

    std::cout << std::fixed << std::setprecision(15);

    Main();

    return 0;

}


// Fastest known solution for this problem: