#include <bits/stdc++.h>
using namespace std;

typedef long long ll; // -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807

template <class T> using vec = std::vector<T>;
template <class T> using vec2 = vec<vec<T>>;
template <class T> using vec3 = vec<vec<vec<T>>>;

// #define DEBUG

struct GCDResult{long long x; long long y; long long res;};
GCDResult gcd(long long a, long long b){
    if(a < 0){
        auto sub = gcd(-a, b);
        return GCDResult{-sub.x, sub.y, sub.res};
    }
    if(b < 0){
        auto sub = gcd(a, -b);
        return GCDResult{sub.x, -sub.y, sub.res};
    }
    if(a < b){
        auto sub = gcd(b, a);
        return GCDResult{sub.y, sub.x, sub.res};
    }
    // a >= b AND a >= 0 AND b >= 0
    if(b == 0){
        return GCDResult{1LL, 0LL, a};
    }
    // a >= b AND a > 0 AND b > 0
    auto sub = gcd(b, a % b);
    return GCDResult{sub.y, sub.x - sub.y*(a/b), sub.res};
}

// Requires ATC_GCD
const int COM_MAX = 510000;
template <long long p>
class Zp{
    long long content;

    // for comb()
    static array<long long, COM_MAX> fac;
    static array<long long, COM_MAX> finv;
    static array<long long, COM_MAX> inv_;
    static bool is_initialized_com_table;

    // By rng_2 (See https://drken1215.hatenablog.com/entry/2018/06/08/210000)
    // テーブルを作る前処理
    static void COMinit() {
        auto MOD = p;
        auto MAX = COM_MAX;
        fac.at(0) = fac.at(1) = 1;
        finv.at(0) = finv.at(1) = 1;
        inv_.at(1) = 1;
        for (int i = 2; i < MAX; i++){
            fac.at(i) = fac.at(i - 1) * i % MOD;
            inv_.at(i) = MOD - inv_.at(MOD%i) * (MOD / i) % MOD;
            finv.at(i) = finv.at(i - 1) * inv_.at(i) % MOD;
        }
        is_initialized_com_table = true;
    }

    // 二項係数計算
    static long long COM(int n, int k){
        auto MOD = p;
        auto MAX = COM_MAX;
        if(!is_initialized_com_table){
            assert(false);
        }
        if (n < k) return 0;
        if (n < 0 || k < 0) return 0;
        return fac.at(n) * (finv.at(k) * finv.at(n - k) % MOD) % MOD;
    }



    public:
    Zp(long long content_){
        if(content_ >= 0){
            content = content_ % p;
        }else{
            long long temp = p;
            while(temp < -content_){
                temp *= p;
            }
            content = (temp + content_) % p;
        }
    }

    bool operator==(const Zp<p>& rhs) const{
        return this->content == rhs.content;
    }

    bool operator!=(const Zp<p>& rhs) const{
        return !(*this == rhs);
    }

    Zp<p> operator+(const Zp<p>& rhs) const{
        return Zp<p>(this->content + rhs.content);
    }

    Zp<p> operator-() const{
        return Zp<p>(p - this->content);
    }

    Zp<p> operator-(const Zp<p>& rhs) const{
        return Zp<p>(this->content - rhs.content + p);
    }

    Zp<p> operator*(const Zp<p>& rhs) const{
        return Zp<p>(this->content * rhs.content);
    }

    Zp<p> inv() const{
        if(this->content == 0){
            assert(false);
        }

        auto res = gcd(this->content, p);
        return Zp<p>(res.x);
    }

    Zp<p> operator/(const Zp<p>& rhs) const{
        if(rhs.content == 0){
            assert(false);
        }
        
        return (*this)*rhs.inv();
    }

    Zp<p> pow(int n){
        auto res = Zp<p>(1);
        Zp<p> mult = (*this);
        while(n > 0){
            if(n & 1 == 1){
                res = res*mult;
            }
            n >>= 1;
            mult = mult*mult;
        }
        return res;
    }

    long long raw(){
        return this->content;
    }

    static void initializeCom(){
        COMinit();
    }

    static Zp<p> com(int n, int k){
        return Zp<p>(COM(n, k));
    }

};

template <long long p>
bool Zp<p>::is_initialized_com_table = false;
template <long long p>
array<long long, COM_MAX> Zp<p>::fac;
template <long long p>
array<long long, COM_MAX> Zp<p>::finv;
template <long long p>
array<long long, COM_MAX> Zp<p>::inv_;


void solve(string& S){
    set<ll> hoge;

    Zp<2019> sum(0);
    int starting = 21234567;
    vec<int> appeared(2019, -2);
    appeared.at(0) = -1;
    ll len = 0;
    vec<ll> counter(2019, 0);
    counter.at(0) = 1;
    for(int i=0; i < S.size(); i++){
        int dig = S.at(i) - '0';
        sum = Zp<2019>(10)*sum + Zp<2019>(dig);
        auto temp = sum/Zp<2019>(10).pow(i);

        if(appeared.at(temp.raw()) != -2){
            // cout << i << endl;
            starting = appeared.at(temp.raw());
            len = i - starting;
            // break;
        }

        // hoge.insert(temp.raw());
        appeared.at(temp.raw()) = i;
        counter.at(temp.raw())++;

    }

    ll res = 0;
    for(int i=0; i < counter.size(); i++){
        if(counter.at(i) > 1){
            res += counter.at(i)*(counter.at(i)-1)/2;
        // cout << i << " " << counter.at(i) << endl;
        }
    }

    if(starting == 21234567){
        cout << 0 << endl;
        return;
    }
    cout << res << endl;

    // cout << starting << endl;
    // cout << len << endl;
    // cout << (S.size() - (starting+1) - 1)/(len - 1) << endl;

}

// void solve(long long S){
//     string strS = to_string(S);
//     ll lenS = to_string(S).size();
//     set<pair<int, int>> hoge;
//     // 2019の倍数xについてループをまわし、i文字目～i+|X|が2019の倍数かどうかをみる。
//     for(int i=2019; i <= 200000; i+= 2019){
//         int lenI = to_string(i).size();
//         for(int j=0; j + lenI <= lenS; j++){
//             // cout << lenI << endl;
//             string subst = strS.substr(j, lenI);
//             ll val = stoi(subst);
//             // cout << val << endl;
//             if(val % i == 0){
//                 hoge.insert(pair<int, int>(j, j+lenI));
//                 // hoge.insert(val);
//                 // res++;
//             }

//         }
//     }
//     for(auto iter: hoge){
//         cout << iter.first << " " << iter.second << endl;
//     }
//     cout << hoge.size() << endl;
// }

// Generated by 1.1.6 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
int main(){
    string S;
    cin >> S;
    // scanf("%s",&S);
    solve(S);
    return 0;
}


Measured memory_bytes: 3704