#include <iostream>
#include <chrono>
#include <pthread.h>
using namespace std;
using namespace std::chrono;

struct Args {
    uint64_t* sum_ptr;
    uint64_t count;
};

void* f1(void* args) {
    Args* p = (Args*)args;
    uint64_t* sum_ptr = p->sum_ptr;
    uint64_t count = p->count;

    while (count--) {
        ++*sum_ptr;
    }
    return NULL;
}

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

void* f2(void* args) {
    Args* p = (Args*)args;
    uint64_t* sum_ptr = p->sum_ptr;
    uint64_t count = p->count;

    while (count--) {
        pthread_mutex_lock(&mtx);
        ++*sum_ptr;
        pthread_mutex_unlock(&mtx);
    }
    return NULL;
}

uint64_t sum1() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;

    Args args1 { &sum, count };
    f1(&args1);

    Args args2 { &sum, count };
    f1(&args2);

    return sum;
}

uint64_t sum2() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;
    pthread_t t1, t2;

    Args args1 { &sum, count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum, count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum;
}

uint64_t sum3() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum[2] = {0};
    pthread_t t1, t2;

    Args args1 { &sum[0], count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum[1], count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum[0] + sum[1];
}

uint64_t sum4() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum[9] = {0};
    pthread_t t1, t2;

    Args args1 { &sum[0], count };
    pthread_create(&t1, NULL, &f1, &args1);

    Args args2 { &sum[8], count };
    pthread_create(&t2, NULL, &f1, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum[0] + sum[8];
}

uint64_t sum5() {
    constexpr uint64_t count = 1'000'000;
    uint64_t sum = 0;
    pthread_t t1, t2;

    Args args1 { &sum, count };
    pthread_create(&t1, NULL, &f2, &args1);

    Args args2 { &sum, count };
    pthread_create(&t2, NULL, &f2, &args2);

    pthread_join(t1, NULL);
    pthread_join(t2, NULL);

    return sum;
}

int main() {
    auto time0 = high_resolution_clock::now();

    uint64_t result = sum1();
    // uint64_t result = sum2();
    // uint64_t result = sum3();
    // uint64_t result = sum4();
    // uint64_t result = sum5();

    auto time1 = high_resolution_clock::now();
    auto dt = duration_cast<microseconds>(time1 - time0);
    cout << "elapsed: " << dt.count() << " microsec\n";
    cout << "result: " << result << '\n';
}
