#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; i < n; ++i)
#define REPR(i, n) for (int i = n - 1; i >= 0; --i)
#define FOR(i, a, b) for (int i = a; i < b; ++i)
#define FORR(i, a, b) for (int i = b - 1; i >= a; --i)
#define SORT(v) sort(v.begin(), v.end())
#define SORTR(v) sort(v.rbegin(), v.rend())
#define REV(v) reverse(v.begin(), v.end())
#define ITER(v) for (auto itr = v.begin(), itr != v.end(); ++itr)
#define LB(v, x) lower_bound(v.begin(), v.end(), x) - v.begin()
#define UB(v, x) upper_bound(v.begin(), v.end(), x) - v.begin()
#define SZ(v) (int)v.size()
using namespace std;
using ll = long long;
using P = pair<int, int>;

class Zp {
    const int p = 2019;
    ll r;
    void set(ll x) {
        while (x < 0) x += p;
        r = x % p;
    }

public:
    Zp(ll x) { set(x); }
    Zp() { set(0); }

    Zp operator-() const { return Zp(-r); }
    Zp &operator=(ll y) {
        set(y);
        return *this;
    }
    Zp &operator=(const Zp &y) { return *this = y.r; }

    Zp &operator+=(ll y) {
        set(r + y);
        return *this;
    }
    Zp &operator-=(ll y) { return *this += -y; }
    Zp &operator*=(ll y) {
        set(r * y);
        return *this;
    }
    Zp &operator/=(ll y) { return *this *= inv(y); }
    Zp operator+(ll y) const { return Zp(r + y); }
    Zp operator-(ll y) const { return *this + (-y); }
    Zp operator*(ll y) const { return Zp(r * y); }
    Zp operator/(ll y) const { return *this * inv(y); }

    Zp &operator+=(const Zp &y) { return *this += y.r; }
    Zp &operator-=(const Zp &y) { return *this -= y.r; }
    Zp &operator*=(const Zp &y) { return *this *= y.r; }
    Zp &operator/=(const Zp &y) { return *this /= y.r; }
    Zp operator+(const Zp &y) const { return *this + y.r; }
    Zp operator-(const Zp &y) const { return *this - y.r; }
    Zp operator*(const Zp &y) const { return *this * y.r; }
    Zp operator/(const Zp &y) const { return *this / y.r; }

    Zp pow(int n) const {
        if (n == 0) return 1;
        if (n % 2) return *this * pow(n - 1);
        Zp tmp = pow(n / 2);
        return tmp * tmp;
    }
    Zp inv(int x) const { return Zp(x).pow(p - 2); }
    Zp inv() const { return inv(r); }

    ll get() const { return r; }

    friend istream &operator>>(istream &istr, Zp &x) {
        istr >> x.r;
        x.set(x.r);
        return istr;
    }
    friend ostream &operator<<(ostream &ostr, const Zp &x) {
        ostr << x.r;
        return ostr;
    }
};
class Comb {
    vector<Zp> fact, ifact;

public:
    Comb(int N) : fact(N + 1), ifact(N + 1) {
        fact[0] = 1;
        REP(i, N) fact[i + 1] = fact[i] * (i + 1);
        ifact[N] = fact[N].inv();
        REPR(i, N) ifact[i] = ifact[i + 1] * (i + 1);
    }
    Zp operator()(int n, int k) const {
        return fact[n] * ifact[n - k] * ifact[k];
    }
    Zp f(int k) const { return fact[k]; }
    Zp invf(int k) const { return ifact[k]; }
};

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);

    string S;
    cin >> S;
    int n = SZ(S);
    vector<Zp> T(n + 1);
    Zp mul = 1;
    vector<ll> memo(2019);
    memo[0]++;
    REPR(i, n) {
        T[i] = T[i + 1] + mul * (S[i] - '0');
        memo[T[i].get()]++;
        mul *= 10;
    }
    ll res = 0;
    REP(i, 2019) { res += memo[i] * (memo[i] - 1) / 2; }
    cout << res << endl;

    return 0;
}


Measured memory_bytes: 6796