//author : raghav_0901

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> 
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds; 

#define int long long int

#define fi first
#define se second
#define pb push_back
#define f(i,s,e) for(int i=s;i<e;++i)
#define fr(i,s,e) for(int i=s;i>=e;i--)
#define all(x) (x).begin(),(x).end()
#define allr(x) (x).rbegin(),(x).rend()
#define _in(v) for(auto &x:v) cin>>x;
#define ffr(i,s,e,f) for(int i=s;i>=e && f;i--)
#define ff(i,s,e,f) for(int i=s;i<e && f;++i)
#define mem(arr,val) memset(arr, val, sizeof(arr));
#define _out(v) {for(auto x:v)cout<<x<<' ';cout<<'\n';}
#define setbit(x) __builtin_popcountll(x)
#define zerobit(x) __builtin_ctzll(x)
#define ps(x,y) fixed<<setprecision(y)<<x
#define inf INT_MAX

#define pi 3.1415926535897932384626433
#define Y cout<<"YES\n";
#define N cout<<"NO\n";

typedef vector<int> vec;
typedef pair<int,int> pr; 

const int mod=1000000007;
const int lim=100000;

inline int add(int a,int b){return((a%mod)+(b%mod))%mod;}
inline int sub(int a,int b){return((a%mod)-(b%mod)+mod)%mod;}
inline int mul(int a,int b){return((a%mod)*(b%mod))%mod;}

int gcd(int a,int b){return b?gcd(b,a%b):a;}
int lcm(int a,int b){return a*(b/gcd(a,b));}
inline int n_sum(int n){return (n*(n+1))/2;}
int power(int a,int b){int res=1;assert(b>=0);for(;b;b>>=1){if(b&1)res=res*a;a=a*a;}return res;}
int powmod(int a,int b){int res=1;a%=mod;assert(b>=0);for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int gcdExtended(int a,int b,int &x,int &y){if(a==0){x=0;y=1;return b;}int x1,y1,gcd=gcdExtended(b%a,a,x1,y1);x=y1-((b/a)*x1);y=x1;return gcd;}
int modInv(int a,int m){int x,y;gcdExtended(a,m,x,y);return (x%m + m)%m;}
int divM(int a,int b){return (modInv(b,mod)*(a%mod))%mod;}
int ncr(int n,int r){int res = 1;if (r > n)return 0;if (r > n - r)r = n - r;for (int i = 0; i < r; i++){res *= (n - i);res /= (i + 1);}return res;}
inline int max(int a,int b,int c){return (a>b)?((a>c)?a:c):((b>c)?b:c);}
inline int min(int a,int b,int c){return (a<b)?((a<c)?a:c):((b<c)?b:c);}

struct custom_hash {
    static uint64_t splitmix64(uint64_t x) {x += 0x9e3779b97f4a7c15;x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;x = (x ^ (x >> 27)) * 0x94d049bb133111eb;return x ^ (x >> 31);}
    size_t operator()(uint64_t x) const {static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();return splitmix64(x + FIXED_RANDOM);}
};
template <typename t1,typename t2>
using hash_table=gp_hash_table<t1,t2,custom_hash>;

template <typename T>
T max(vector<T> v){return *max_element(all(v));}
template <typename T>
T min(vector<T> v){return *min_element(all(v));}

template <typename T>
using ordered_set=tree <T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;

template <typename T>
class triplet
{
    public:
    T a,b,c;
    triplet(){}
    triplet(T a,T b,T c){this->a=a;this->b=b;this->c=c;}
    inline T max(){return (a>b)?((a>c)?a:c):((b>c)?b:c);}
};
#define trip triplet<int>
bool cmp_trip(triplet<int> t1,triplet<int> t2){return t1.a<t2.a;}

class adj_graph
{
    public:
    vector<list<int>> al;bool bi=0;int size;
    vector<bool> vis;
    adj_graph(){}
    adj_graph(int n){al.resize(n+1);size=n;}
    adj_graph(int n,bool bi){al.resize(n+1);this->bi=bi;size=n;}
    void add_edge(int u,int v){al[u].pb(v);if(bi)al[v].pb(u);}
    void add_edge(int m){int u,v;f(i,0,m){cin>>u>>v;add_edge(u,v);}}
    bool has_edge(int u,int v){return find(all(al[u]),v)!=al[u].end();}
    void remove_edge(int u,int v){al[u].remove(v);}
    void bfs(int s);
    void dfs(int s){vis=vector<bool>(size+1,0);dfs_util(s);};
    void dfs_util(int s);
};

void adj_graph::bfs(int s)
{
    vis=vector<bool>(size+1,0);
    queue<int> q;q.push(s);vis[s]=1;
    while(q.size())
    {
        s=q.front();q.pop();
        for(int &x:al[s])
        {
            if(!vis[x])
            {
                q.push(x);vis[x]=1;
            }
        }
    }
}

void adj_graph::dfs_util(int s)
{
    vis[s]=1;
    for(int x:al[s])
        if(!vis[x])
        {
            dfs_util(x);
        }
    return;
}

struct cmp_func
{
    bool operator()(auto a,auto b) 
    {
        return 1;
    }
};

void _main();
signed main(void){
ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1,i=0;
//cin>>t;
while (t--){
//cout<<"Case #"<<(++i)<<": ";

_main();

}return 0;}

//------------------------------------------------------------------------------------


void _main(){
    int ans=0,num=0,cnt=0,sum=0;
    string s;cin>>s;
    int dp[2019];mem(dp,0);
    dp[s[0]-'0']=1;
    for(int i=1;i<s.length();i++)
    {
        int d=s[i]-'0';
        int temp[2019];mem(temp,0);temp[d]=1;
        for(int j=0;j<2019;j++)
        {
            if(dp[j]>0)
            {
                int r=(10*j+d)%2019;
                if(r==0)ans+=dp[j];
                temp[r]+=dp[j];
            }
        }
        for(int i=0;i<2019;i++)dp[i]=temp[i];
    }    
    cout<<ans<<endl;
    return;
}


Measured memory_bytes: 3788