#include <algorithm>
#include <cmath>
#include <cstdio>
#include <ctime>

using LL = long long;
using ULL = unsigned long long;
#ifdef __SIZEOF_INT128__
using LLL = __int128;
using ULLL = unsigned __int128;
#else
using LLL = long long;
using ULLL = unsigned long long;
#endif

namespace std {
    void swap(int& x, int& y) { x ^= y ^= x ^= y; }
    void swap(LL& x, LL& y) { x ^= y ^= x ^= y; }
};  // namespace std

#ifdef LX_LOCAL
class Time {
public:
    clock_t begin;
    long duration;
    static clock_t currentTime() { return clock(); }
    Time() {
        begin = currentTime();
        duration = 0;
    }
    ~Time() {
        auto end = currentTime();
        auto d = end - begin;
        duration += d;
        double t = duration * 1000.0 / static_cast<double>(CLOCKS_PER_SEC);
        if (t >= 60000)
            fprintf(stderr, "Time used = %.2lfmin\n", t / 60000.0);
        else if (t >= 1000)
            fprintf(stderr, "Time used = %.2lfs\n", t / 1000.0);
        else
            fprintf(stderr, "Time used = %.0lfms\n", t);
    }
} timeout;
#endif

using std::swap;
#if __cplusplus >= 201402L
template <typename T, typename P>
inline auto max(const T& x, const P& y) { return x < y ? y : x; }
template <typename T, typename P>
inline auto min(const T& x, const P& y) { return x < y ? x : y; }
template <typename T, typename... Args>
inline auto max(const T& x, const Args&... args) { return max(x, max(args...)); }
template <typename T, typename... Args>
inline auto min(const T& x, const Args&... args) { return min(x, min(args...)); }
#else
template <typename T, typename P>
inline auto max(const T& x, const P& y) -> decltype(x < y ? y : x) { return x < y ? y : x; }
template <typename T, typename P>
inline auto min(const T& x, const P& y) -> decltype(x < y ? x : y) { return x < y ? x : y; }
template <typename T, typename... Args>
inline auto max(const T& x, const Args&... args) -> decltype(max(x, max(args...))) { return max(x, max(args...)); }
template <typename T, typename... Args>
inline auto min(const T& x, const Args&... args) -> decltype(min(x, min(args...))) { return min(x, min(args...)); }
#endif
template <typename T>
inline T max(const T& x) { return x; }
template <typename T>
inline T min(const T& x) { return x; }
template <typename T, typename... Args>
inline void chkmax(T& d, const Args&... args) { d = max(d, args...); }
template <typename T, typename... Args>
inline void chkmin(T& d, const Args&... args) { d = min(d, args...); }

#define lc (o << 1)
#define rc (o << 1 | 1)
#define lowbit(x) ((x) & (-(x)))
#define all(x) x.begin(), x.end()
#define SZ(x) int(x.size())

class IO {
#define MY_DEBUG 0
#if !MY_DEBUG
    static const int MAXSIZE = 1 << 20;
    char buf[MAXSIZE], *p1, *p2;
    char pbuf[MAXSIZE], *pp;
#endif
    long double eps = 1e-8L;
    LLL pow10 = 1000000;
    int precision;
    FILE *infile, *outfile;

public:
    IO(FILE* in = nullptr, FILE* out = nullptr) : precision(6), infile(in), outfile(out) {
#if !MY_DEBUG
        p1 = p2 = buf;
        pp = pbuf;
#endif
    }
#if !MY_DEBUG
    ~IO() { fwrite(pbuf, 1, pp - pbuf, outfile); }
#endif
    inline static constexpr bool blank(char ch);
    inline void flush();
    inline void input(const char* str);
    inline void output(const char* str);
    inline void input(FILE* f);
    inline void output(FILE* f);
    inline int getch();
    template <typename T, typename... Args>
    bool read(T& x, Args&... args);
    inline bool read() const { return true; }
    template <typename T>
    typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value || std::is_same<T, LLL>::value || std::is_same<T, ULLL>::value, bool>::type read(T& x);
    bool read(char& ch);
    bool read(char* str);
    bool readline(char* str);
    inline void putch(const char c);
    inline void putback(const char c);
    inline void setprecision(int n = 6);
    template <typename T, typename... Args>
    void write(const T& x, const Args&... args);
    inline void write() const {}
    template <typename T>
    typename std::enable_if<std::is_integral<T>::value || std::is_same<T, LLL>::value || std::is_same<T, ULLL>::value, void>::type write(T x);
    inline void write(char c);
    template <typename T>
    typename std::enable_if<std::is_floating_point<T>::value, void>::type write(T x);
    inline void write(bool x);
    void write(char* str);
    void write(const char* str);
    template <typename... Args>
    void writeln(Args... x) { write(x...), putch('\n'); }
} io(stdin, stdout), err(nullptr, stderr);
template <typename... Args>
inline void writeln(Args... x) { io.write(x...), io.putch('\n'); }
#define dbg(x) err.write(#x " = "), err.writeln(x)
// fast io
#include <cstring>
constexpr int maxn = 200005, MOD = 2019;

char str[maxn];
LL dp[MOD], tdp[MOD];
int main() {
    io.read(str + 1);
    int n = strlen(str + 1);
    LL ans = 0;
    dp[str[1] ^ 48] = 1;
    for (int i = 2; i <= n; i++) {
        memset(tdp, 0, sizeof(tdp));
        tdp[str[i] ^ 48] = 1;
        for (int j = 0; j < MOD; j++)
            if (dp[j]) {
                LL t = (j * 10 + (str[i] ^ 48)) % MOD;
                if (!t)
                    ans += dp[j];
                tdp[t] += dp[j];
            }
        memcpy(dp, tdp, sizeof(dp));
    }
    writeln(ans);
    return 0;
}

#define isdigit(x) (x >= '0' && x <= '9')
inline constexpr bool IO::blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
inline void IO::flush() {
#if !MY_DEBUG
    fwrite(pbuf, 1, pp - pbuf, outfile), pp = pbuf;
#endif
    fflush(outfile);
}
inline void IO::input(const char* str) {
    FILE* file = fopen(str, "rb");
    infile = file;
}
inline void IO::output(const char* str) {
    FILE* file = fopen(str, "wb");
    outfile = file;
}
inline void IO::input(FILE* f) { infile = f; }
inline void IO::output(FILE* f) { outfile = f; }
inline int IO::getch() {
#if MY_DEBUG
    return fgetc(infile);
#else
    return (p1 == p2 ? (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, infile)) : 0), (p1 == p2) ? EOF : *p1++;
#endif
}
inline void IO::putback(const char c) {
#if MY_DEBUG
    ungetc(c, infile);
#else
    *(--p1) = c;
#endif
}
template <typename T, typename... Args>
bool IO::read(T& x, Args&... args) { return read(x) && read(args...); }
template <typename T>
typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value || std::is_same<T, LLL>::value || std::is_same<T, ULLL>::value, bool>::type IO::read(T& x) {
    T tmp = 1;
    bool sign = false;
    x = 0;
    int ch = getch();
    for (; !isdigit(ch) && ~ch; ch = getch())
        sign = !(ch ^ '-');
    if (!(~ch))
        return false;
    for (; isdigit(ch); ch = getch())
        x = x * 10 + (ch ^ '0');
    if (ch == '.' && std::is_floating_point<T>::value)
        for (ch = getch(); isdigit(ch); ch = getch())
            tmp /= 10.0, x += tmp * (ch ^ '0');
    if (~ch)
        putback(ch);
    if (sign)
        x = -x;
    return true;
}
bool IO::read(char& ch) {
    for (ch = getch(); blank(ch) && ~ch; ch = getch())
        ;
    return ~ch;
}
bool IO::read(char* str) {
    int ch = getch();
    while (blank(ch))
        ch = getch();
    if (!(~ch))
        return false;
    while (!blank(ch) && ~ch)
        *str++ = ch, ch = getch();
    *str = 0;
    if (~ch)
        putback(ch);
    return true;
}
bool IO::readline(char* str) {
    int ch = getch();
    while (blank(ch) && (ch ^ '\n'))
        ch = getch();
    if (!(~ch))
        return false;
    while ((ch ^ '\n') && ~ch)
        *str++ = ch, ch = getch();
    *str = 0;
    if (~ch)
        putback(ch);
    return true;
}
inline void IO::putch(const char c) {
#if MY_DEBUG
    fputc(c, outfile);
#else
    ((pp - pbuf == MAXSIZE) ? fwrite(pbuf, 1, MAXSIZE, outfile), pp = pbuf : 0), *pp++ = c;
#endif
}
inline void IO::setprecision(int n) {
    precision = n;
    eps = powl(10.0L, -precision - 2);
    pow10 = powl(10.0L, precision) + eps;
}
template <typename T, typename... Args>
void IO::write(const T& x, const Args&... args) {
    write(x);
    if (sizeof...(args))
        write(args...);
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value || std::is_same<T, LLL>::value || std::is_same<T, ULLL>::value, void>::type IO::write(T x) {
    if (x < 0)
        x = ~x + 1, putch('-');
    static T sta[100];
    int top = 0;
    do
        sta[top++] = x % 10, x /= 10;
    while (x);
    while (top)
        putch(sta[--top] + '0');
}
inline void IO::write(bool x) { putch(x ^ 48); }
inline void IO::write(char c) { putch(c); }
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value>::type IO::write(T x) {
    if (x == 0) {
        putch('0'), putch('.');
        for (int i = 1; i <= precision; i++)
            putch('0');
        return;
    }
    if (x < 0)
        putch('-'), x = -x;
    T res = (LLL)(x * pow10 + 0.5) / (pow10 * 1.0);
    LLL y = LLL(res * pow10 + eps) % pow10;
    write(LLL(res + eps));
    if (precision) {
        putch('.');
        static int sta[100], p = 0;
        for (; p < precision; y /= 10)
            sta[++p] = y % 10;
        for (int i = p; i >= 1; i--)
            putch(sta[i] ^ 48);
    }
}
void IO::write(char* str) {
    while (*str)
        putch(*str++);
}
void IO::write(const char* str) {
    while (*str)
        putch(*str++);
}
#undef isdigit

Measured memory_bytes: 3392