#include <vector>
#include <string>
#include <chrono>
#include <iostream>
#include <random>
#include <algorithm>

using namespace std;

struct citizen_t {
    string first_name;
    string last_name;
    int salary;
    int age;
};

struct soa_citizens_t {
    vector<string> first_names;
    vector<string> last_names;
    vector<int> salary;
    vector<int> age;

    void resize( size_t n ) {
        first_names.resize( n );
        last_names.resize( n );
        salary.resize( n );
        age.resize( n );
    }
};

int main() {
    using clock_t = chrono::high_resolution_clock;

    size_t count = 300'000'000;
    uniform_int_distribution<int> salary_dist( 0, 10000 );

    chrono::duration<double> aos_duration;
    chrono::duration<double> soa_duration;

    int aos_avg = 0;
    int soa_avg = 0;

    // Array of Structures
    {
        const auto seed_val = mt19937::default_seed;
        mt19937 rng( seed_val );

        vector<citizen_t> aos_citizens;
        aos_citizens.resize( count );
        generate_n( aos_citizens.data(), count, [&]() { citizen_t c; c.salary = salary_dist( rng ); return c; } );

        auto start = clock_t::now();

        int avg = 0;
        int t = 1;
        for ( auto & c : aos_citizens ) {
            avg += (c.salary - avg) / t;
            ++t;
        }

        auto finish = clock_t::now();
        aos_duration = finish - start;
        aos_avg = avg;
    }
    
    // Structure of Arrays
    {
        const auto seed_val = mt19937::default_seed;
        mt19937 rng( seed_val );

        soa_citizens_t soa_citizens;
        soa_citizens.resize( count );
        generate_n( soa_citizens.salary.data(), count, [&]() { return salary_dist( rng ); } );

        auto start = clock_t::now();

        int avg = 0;
        int t = 1;
        for ( auto salary : soa_citizens.salary ) {
            avg += (salary - avg) / t;
            ++t;
        }

        auto finish = clock_t::now();
        soa_duration = finish - start;
        soa_avg = avg;
    }

    cout << "AoS (" << aos_avg << ") in " << aos_duration.count() << " seconds" << std::endl;
    cout << "SoA (" << soa_avg << ") in " << soa_duration.count() << " seconds" << std::endl;

    return 0;
}
