//  (C) Copyright Antony Polukhin 2012-2021.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/config for most recent version.

//
// Testing variant performance rvalue copy/assign performance
//

#define BOOST_ERROR_CODE_HEADER_ONLY
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono.hpp>

#include <boost/variant.hpp>
#include <string>
#include <vector>

    struct scope {
        typedef boost::chrono::steady_clock test_clock;
        typedef boost::chrono::milliseconds duration_t;
        test_clock::time_point start_;
        const char* const message_;

        explicit scope(const char* const message)
            : start_(test_clock::now())
            , message_(message)
        {}

        ~scope() {
            std::cout << message_ << "   " << boost::chrono::duration_cast<duration_t>(test_clock::now() - start_) << std::endl;
        }
    };



static void do_test(bool do_count_cleanup_time = false) {
    BOOST_STATIC_CONSTANT(std::size_t, c_run_count = 5000000);
    typedef std::vector<char> str_t;
    typedef boost::variant<int, str_t, float> var_t;

    const char hello1_c[] = "hello long word";
    const str_t hello1(hello1_c, hello1_c + sizeof(hello1_c));

    const char hello2_c[] = "Helllloooooooooooooooooooooooooooooooo!!!!!!!!!!!!!";
    const str_t hello2(hello2_c, hello2_c + sizeof(hello2_c));

    if (do_count_cleanup_time) {
        std::cout << "#############################################\n";
        std::cout << "#############################################\n";    
        std::cout << "NOW TIMES WITH DATA DESTRUCTION\n";
        std::cout << "#############################################\n";
    }

    std::vector<var_t> data_from, data_to;
    data_from.resize(c_run_count, hello1);
    data_to.reserve(c_run_count);
    {
        scope sc("boost::variant(const variant&) copying speed");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to.push_back(data_from[i]); 
            
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }
    
    data_from.resize(c_run_count, hello1);
    data_to.clear();
    data_to.reserve(c_run_count);
    {
        scope sc("boost::variant(variant&&) moving speed");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to.push_back(boost::move(data_from[i]));
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    std::cout << "#############################################\n";

    data_from.clear();
    data_from.resize(c_run_count, hello2);
    data_to.clear();
    data_to.resize(c_run_count, hello2);
    {
        scope sc("boost::variant=(const variant&) copying speed on same types");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = data_from[i]; 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    data_from.resize(c_run_count, hello2);
    data_to.clear();
    data_to.resize(c_run_count, hello2);
    {
        scope sc("boost::variant=(variant&&) moving speed on same types");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = boost::move(data_from[i]); 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    std::cout << "#############################################\n";

    data_from.clear();
    data_from.resize(c_run_count, hello2);
    
    data_to.clear();
    data_to.resize(c_run_count, var_t(0));
    {
        scope sc("boost::variant=(const variant&) copying speed on different types");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = data_from[i]; 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    data_from.resize(c_run_count, hello2);
    data_to.clear();
    data_to.resize(c_run_count, var_t(0));
    {
        scope sc("boost::variant=(variant&&) moving speed on different types");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = boost::move(data_from[i]); 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    std::cout << "#############################################\n";

    data_from.clear();
    data_from.resize(c_run_count, var_t(0));
    
    data_to.clear();
    data_to.resize(c_run_count, hello2);
    {
        scope sc("boost::variant=(const variant&) copying speed on different types II");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = data_from[i]; 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }

    data_from.resize(c_run_count, var_t(0));
    data_to.clear();
    data_to.resize(c_run_count, hello2);
    {
        scope sc("boost::variant=(variant&&) moving speed on different types II");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = boost::move(data_from[i]); 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            data_from.clear();
        }
    }


    std::cout << "#############################################\n";
    
    std::vector<str_t> s1(c_run_count, hello2);
    data_to.clear();
    data_to.resize(c_run_count, var_t(0));
    
    {
        scope sc("boost::variant=(const T&) copying speed");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = s1[i]; 
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            s1.clear();
        }
    }

    std::vector<str_t> s2(c_run_count, hello2);
    data_to.clear();
    data_to.resize(c_run_count, var_t(0));
    {
        scope sc("boost::variant=(T&&) moving speed");
        for (std::size_t i = 0; i < c_run_count; ++i) {
            data_to[i] = boost::move(s2[i]);
        }

        if (do_count_cleanup_time) {
            data_to.clear();
            s2.clear();
        }
    }
}


int main () {

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
    std::cout << "# Running tests in C++11 mode (with rvalues).\n";
#else
    std::cout << "# Running tests in C++03 mode (without rvalues).\n";
#endif

    do_test(false);
    do_test(true);
}


