// Generated by 1.1.6 https://github.com/kyuridenamida/atcoder-tools

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

const i64 MOD = 1e9 + 7;
const i64 INF = i64(1e18) + 7;


template <typename T>
bool chmin(T& x, T y){
    if(x > y){
        x = y;
        return true;
    }
    return false;
}

template <typename T>
bool chmax(T& x, T y){
    if(x < y){
        x = y;
        return true;
    }
    return false;
}

template <i64 mod = MOD>
struct ModInt{
    i64 p;

    ModInt() : p(0){}
    ModInt(i64 x){p = x >= 0 ? x % mod : x + (-x + mod - 1) / mod * mod;}

    ModInt& operator+=(const ModInt& y){p = p + *y - ((p + *y) >= mod ? mod : 0); return *this;}
    ModInt& operator-=(const ModInt& y){p = p - *y + (p - *y < 0 ? mod : 0); return *this;}
    ModInt& operator*=(const ModInt& y){p = (p * *y) % mod; return *this;}
    ModInt& operator%=(const ModInt& y){if(y)p %= *y; return *this;}

    ModInt operator+(const ModInt& y) const{ModInt x = *this; return x += y;}
    ModInt operator-(const ModInt& y) const{ModInt x = *this; return x -= y;}
    ModInt operator*(const ModInt& y) const{ModInt x = *this; return x *= y;}
    ModInt operator%(const ModInt& y) const{ModInt x = *this; return x %= y;}

    friend ostream& operator<<(ostream& stream, const ModInt<mod>& x){
        stream << *x;
        return stream;
    }

    friend ostream& operator>>(ostream& stream, const ModInt<mod>& x){
        stream >> *x;
        return stream;
    }

    ModInt& operator++(){p = (p + 1) % mod; return *this;}
    ModInt& operator--(){p = (p - 1 + mod) % mod; return *this;}

    bool operator==(const ModInt& y) const{return p == *y;}
    bool operator!=(const ModInt& y) const{return p != *y;}

    const i64& operator*() const{return p;}
    i64& operator*(){return p;}

};

using mint = ModInt<2019>;


struct BitVector{
    int n, m;
    vector<int> l;
    vector<uint64_t> s;

    BitVector(int n) : n(n){
        m = (n + 63) >> 6;
        l.assign(m, 0);
        s.assign(m, 0);
    }

    void set(int i, bool flag = true){
        if(flag)
            s[i >> 6] |= (1uLL << (i & 63));
        else
            s[i >> 6] &= ~(1uLL << (i & 63));
    }

    void build(){
        l[0] = 0;
        for(int i = 1; i < m; ++i)
            l[i] = l[i - 1] + __builtin_popcountll(s[i - 1]);
    }

    // [0, r) count flag
    int rank(int r, bool flag = true){
        if(flag)
            return l[r >> 6] + __builtin_popcountll(s[r >> 6] & ((1uLL << (r & 63)) - 1));
        else
            return r - (l[r >> 6] + __builtin_popcountll(s[r >> 6] & ((1uLL << (r & 63)) - 1)));
    }

};

void solve(string s){

    int n = s.size();
    constexpr int p = 2019;
    vector<int> table(n + 1, 0);
    vector<int> table_10(n + 1, 1);
    for(int i = 0; i < n; ++i)
        table_10[i + 1] = table_10[i] * 10 % p;
    int diff = 0;
    for(int i = n - 1; i >= 0; --i){
        table[i + 1] = diff;
        diff += table_10[n - 1 - i] * (s[i] - '0');
        diff %= p;
    }
    table[0] = diff;
    reverse(table.begin(), table.end());

    i64 ans = 0;
    vector<int> v(p, 0);
    ++v[table[n]];
    for(int i = n - 1; i >= 0; --i){
        int q = table[i];
        ans += v[q];
        ++v[table[i]];
    }

    cout << ans << endl;
}


signed main(){
    string S;
    cin >> S;
    solve(S);
    return 0;
}


Measured memory_bytes: 5240