#include <cstdio>
#include <vector>
#include <cstring>
#include <thread>

using namespace std;

#define REALMAXN 2000000000
#define MAXN 2002000000
#define SQRTN 45000
#define BLOCKSIZE 500000


// Automatically zeroed in G++
// 0 means primes, 1 means composite number
// Threated as block of bits
unsigned long long big_sieve[MAXN/64];

// We are piglors and use global shared variables
vector<pair<int, int>> big_primes;

void do_sieve(int start, int end, vector<pair<int, int>> primes) {
    for (auto &p: primes) {
        auto start_mul = start / p.first + 1;
        if (start_mul % 2 == 0) {
            start_mul += 1;
        }
        p.second = max(p.second, start_mul*p.first);
    }
    for (int i = start; i < end; i+=BLOCKSIZE) {
        for (auto &p: primes) {
            while (p.second < i + BLOCKSIZE) { 
                big_sieve[p.second/64] |= 1ll << (p.second % 64);
                p.second += p.first * 2;
            }
        }
    }
}

int main() {
    vector<bool> sieve(SQRTN, true);
    vector<pair<int, int>> small_primes;
    for (int i = 2; i < SQRTN; i++) {
        if (sieve[i]) {
            for (int j = i*i; j < SQRTN; j += i) {
                sieve[j] = false;
            }
            if (i <= 13) {
                small_primes.push_back(make_pair(i, i*i));
            } else {
                big_primes.push_back(make_pair(i, i*i));
            }
        }
    }

    // Presieving with 2, 3, 5, 7, 11, 13
    int lim = 2 * 3 * 5 * 7 * 11 * 13 * 32;
    int limull = lim / 64;
    
    for (int i = 0; i < lim; i+=2) {
        big_sieve[i/64] |= 1ull << (i % 64);
    }
    for (int i = 0; i < lim; i+=3) {
        big_sieve[i/64] |= 1ll << (i % 64);
    }
    for (int i = 0; i < lim; i+=5) {
        big_sieve[i/64] |= 1ll << (i % 64);
    }
    for (int i = 0; i < lim; i+=7) {
        big_sieve[i/64] |= 1ll << (i % 64);
    }
    for (int i = 0; i < lim; i+=11) {
        big_sieve[i/64] |= 1ll << (i % 64);
    }
    for (int i = 0; i < lim; i+=13) {
        big_sieve[i/64] |= 1ll << (i % 64);
    }

    for (int i = limull; i + limull < MAXN/64; i+=limull) {
        memcpy(big_sieve + i, big_sieve, limull * 8);
    }

    thread t1(do_sieve,          0,  500000000, big_primes);
    thread t2(do_sieve,  500000000, 1000000000, big_primes);
    thread t3(do_sieve, 1000000000, 1500000000, big_primes);
    thread t4(do_sieve, 1500000000, 2000000000, big_primes);
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    int total = 0;
    for (int i = 0; i < REALMAXN/64; i++) {
        total += __builtin_popcountl(big_sieve[i]);
    }
    // Correction by 5 due to 2,3,5,7,11,13 (and 1)
    printf("%d %d\n", 5 + REALMAXN - total, small_primes.size());
}