#include <iostream>
 
#include <chrono>
#include <random>
#include <list>
#include <deque>
#include <queue>
#include <memory>
#include <algorithm>
#include <set>
#include <queue>
 
using namespace std;
 
/* // Кто ж мать его тестит вставки/удаления/сортировки на малых массивах?
const uint64_t iter = 1000000;
const uint64_t elems = 100;
//*/
 
const uint64_t iter = 10000;
const uint64_t elems = 10000;
 
const std::string s = "qwiuehgflkjasgdfkjasgdfkjhagsdkjf23452345hgaskjhfdgaasdfasdfasdfasdfasdfasdfasdfasdfa234523452345sdfasddkjshdgfkjashgfdkjhgsadfkj";
 
class TimeMeasurer
{
public:
    TimeMeasurer(const std::string &text) :
        _text(text),
        _time(std::chrono::steady_clock::now())
    {}
 
    ~TimeMeasurer() {
        using namespace std::chrono;
        auto t = steady_clock::now();
        // Давайте в миллисекундах, а?
        auto ms = duration_cast<milliseconds>(t - _time).count();
        cout << _text << " " << ms << " ms" << endl;
    }
 
private:
    std::string _text;
    std::chrono::steady_clock::time_point _time;
};
 
struct Ad {
    Ad(const int a, const std::string &s, float b) :
        a(a), s(s), b(b)
    {}
 
    // Не забываем про noexcept
    Ad(const Ad &) = default;
    Ad(Ad &&) noexcept = default;
    Ad &operator = (const Ad &) = default;
    Ad &operator = (Ad &&) noexcept = default;
 
    int a;
    std::string s;
    float b;
    bool operator<(const Ad &a) const {
        return (b < a.b);
    }
    bool operator>(const Ad &a) const {
        return (b > a.b);
    }
};
 
//void list_smart() {
void list_stupid() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::list<std::unique_ptr<Ad>> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace_back(new Ad(rand(), s, rand()));
        }
        l.sort();
        //l.resize(5);
    }
}
 
void list_smart() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::list<Ad> l;
        for (uint64_t j = 0; j < elems; ++j)
            l.emplace_back(rand(), s, rand());
        l.sort();
        //l.resize(5);
    }
}
 
void vector_smart() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::vector<std::unique_ptr<Ad>> l;
        l.reserve(elems);
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace_back(new Ad(rand(), s, rand()));
        }
        std::sort(l.begin(), l.end());
        //l.resize(5);
    }
}
 
void deque_smart() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::deque<std::unique_ptr<Ad>> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace_back(new Ad(rand(), s, rand()));
        }
        std::sort(l.begin(), l.end());
        //l.resize(5);
    }
}
 
void set_stupid() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::set<std::unique_ptr<Ad>> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace(new Ad(rand(), s, rand()));
        }
        //std::sort(l.begin(), l.end());
        //l.resize(5);
    }
}
 
void set_smart() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::set<Ad> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace(rand(), s, rand());
        }
        //std::sort(l.begin(), l.end());
        //l.resize(5);
    }
}
 
void pqueue_smart() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::priority_queue<std::unique_ptr<Ad>> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace(new Ad(rand(), s, rand()));
        }
        //std::sort(l.begin(), l.end());
        //l.resize(5);
    }
}
 
//void list_smart_ins_rm() {
void list_stupid_ins_rm() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::list<std::unique_ptr<Ad>> l;
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace_back(new Ad(rand(), s, rand()));
        }
        for (uint64_t j = 0; j < 10; ++j) {
            auto rnd = rand() % elems;
            auto it = l.begin();
            std::advance(it, rnd);
            l.erase(it);
            rnd = rand() % (elems-1);
            it = l.begin();
            std::advance(it, rnd);
//            l.insert(it, std::make_unique<Ad>(Ad(rand(), s, rand()))); // Тут копирование, зачем?
            l.insert(it, std::make_unique<Ad>(rand(), s, rand()));
        }
    }
}
 
void list_smart_ins_rm() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::list<Ad> l;
        for (uint64_t i = 0; i < elems; ++i) {
            l.emplace_back(rand(), s, rand());
        }
        for (uint64_t j = 0; j < 10; ++j) {
            auto rnd = rand() % elems;
            auto it = l.begin();
            std::advance(it, rnd);
            l.erase(it);
            rnd = rand() % (elems-1);
            it = l.begin();
            std::advance(it, rnd);
            l.emplace(it, rand(), s, rand());
        }
    }
}
 
void vector_ins_rm() {
    for (uint64_t i = 0; i < iter; ++i) {
        std::vector<std::unique_ptr<Ad>> l;
        l.reserve(elems);
        for (uint64_t j = 0; j < elems; ++j) {
            l.emplace_back(new Ad(rand(), s, rand()));
        }
        for (uint64_t j = 0; j < 10; ++j) {
            auto rnd = rand() % elems;
            l.erase(l.begin() + rnd);
            rnd = rand() % (elems-1);
//            l.insert(l.begin() + rnd, std::make_unique<Ad>(Ad(rand(), s, rand()))); // Тут тоже. WTF?
            l.insert(l.begin() + rnd, std::make_unique<Ad>(rand(), s, rand()));
        }
    }
}
 
int main() {
    {
        TimeMeasurer _{"list<unique_ptr<T>>"};
        list_stupid();
    }
    {
        TimeMeasurer _{"list<T>"};
        list_smart();
    }
    {
        TimeMeasurer _{"vector"};
        vector_smart();
    }
    {
        TimeMeasurer _{"deque"};
        deque_smart();
    }
    {
        TimeMeasurer _{"set<unique_ptr<T>>"};
        set_stupid();
    }
    {
        TimeMeasurer _{"set<T>"};
        set_smart();
    }
    {
        TimeMeasurer _{"priority_queue"};
        pqueue_smart();
    }
    {
        TimeMeasurer _{"list<unique_ptr<T>> ins/rm"};
        list_stupid_ins_rm();
    }
    {
        TimeMeasurer _{"list<T> ins/rm"};
        list_smart_ins_rm();
    }
    {
        TimeMeasurer _{"vector ins/rm"};
        vector_ins_rm();
    }
    std::cout << "finished" << std::endl;
}