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

#include <stdio.h>

#include <cstring>

#include <cstdio>

#include <math.h>

#include <algorithm>

#include <vector>

#include <string>

#include <stdlib.h>

#include <queue>

#include <stack>

#include <utility>

#include <fstream>

#include <random>

#include <map>

#include <unordered_map>

#include <cstdlib>

#include <functional>

#include <random>

#include <time.h>

#include <chrono>

#include <sstream>

#include <iomanip>

#include <numeric>

#include <iostream>

#include <limits>

#include <numeric>

#include <type_traits>

#define rep(i,n) for(int i=0;i<n;i++)

#define rrep(i,n) for(int i=n-1;i>=0;i--)

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

#define ll long long

#define INF 1000000001

#define MOD 1000000007

#define SORT(s) sort(s.begin(), s.end());

#define REVERSE(s) reverse(s.begin(), s.end());

#define SUM(s) accumulate(s.begin(), s.end(), 0LL);

#define P pair<int,int>

#define mp make_pair

#define valid(x,y,h,w) (0<=x&&x<h&&0<=y&&y<w)

//#define int ll

int dx[4] = { 0,1,0,-1 };

int dy[4] = { 1,0,-1,0 };

int ndx[8] = { 0,1,0,-1, 1,1,-1,-1 };

int ndy[8] = { 1,0,-1,0, 1,-1,-1,1 };

using namespace std;

int POW(int x, int y) { return int(pow(double(x), double(y))); }

double log(double a, double b) { return log(b) / log(a); }





int main() {

	string s;

	cin >> s;

	int n = s.size();



	vector<int> a(n);

	rep(i, n) {

		a[i] = (int)s[i] - 48;

	}



	ll ans = 0;



	vector<unsigned short> pre(2019), now(2019), all0(2019);



	rep(i, n) {

		std::vector<unsigned short>().swap(pre);

		pre = now;

		now = all0;

		if (i == 0) {

			now[a[i]]++;

		}

		else {

			now[a[i]]++;

			rep(j, 2019) {

				int z = (10 * j + a[i]) % 2019;

				now[z] += pre[j];

			}

		}

		ans += now[0];

	}



	cout << ans << endl;

}






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

#include <stdio.h>

#include <cstring>

#include <cstdio>

#include <math.h>

#include <algorithm>

#include <vector>

#include <string>

#include <stdlib.h>

#include <queue>

#include <stack>

#include <utility>

#include <fstream>

#include <random>

#include <map>

#include <unordered_map>

#include <cstdlib>

#include <functional>

#include <random>

#include <time.h>

#include <chrono>

#include <sstream>

#include <iomanip>

#include <numeric>

#include <iostream>

#include <limits>

#include <numeric>

#include <type_traits>

#define rep(i,n) for(int i=0;i<n;i++)

#define rrep(i,n) for(int i=n-1;i>=0;i--)

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

#define ll long long

#define INF 1000000001

#define MOD 1000000007

#define SORT(s) sort(s.begin(), s.end());

#define REVERSE(s) reverse(s.begin(), s.end());

#define SUM(s) accumulate(s.begin(), s.end(), 0LL);

#define P pair<int,int>

#define mp make_pair

#define valid(x,y,h,w) (0<=x&&x<h&&0<=y&&y<w)

//#define int ll

int dx[4] = { 0,1,0,-1 };

int dy[4] = { 1,0,-1,0 };

int ndx[8] = { 0,1,0,-1, 1,1,-1,-1 };

int ndy[8] = { 1,0,-1,0, 1,-1,-1,1 };

using namespace std;

int POW(int x, int y) { return int(pow(double(x), double(y))); }

double log(double a, double b) { return log(b) / log(a); }



//同じ数が何個ずつあるか

vector<int> getSameNoCount(vector<int> list) {

	SORT(list);

	vector<int> result;

	int m = list[0];

	int c = 1;

	FOR(i, 1, list.size()) {

		if (m == list[i]) {

			c++;

		}

		else {

			result.push_back(c);

			c = 1;

			m = list[i];

		}

	}

	result.push_back(c);

	return result;

}



int main() {

	string s;

	cin >> s;

	int n = s.size();



	vector<vector<int>> dp(n);

	vector<int> a(n), c(n);

	rep(i, n) {

		a[i] = (int)s[n - 1 - i] - 48;

	}



	c[0] = a[0];

	int j = 1;

	rep(i, n - 1) {

		j = j * 10 % 2019;

		c[i + 1] = (c[i] + j * a[i + 1]) % 2019;

	}

	c.push_back(0);

	auto z = getSameNoCount(c);

	ll ans = 0;

	rep(i, z.size()) {

		ll m = z[i];

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

	}

	cout << ans << endl;





}






// Fastest known solution for this problem: