text:
/*
    Copyright 2015 Adobe
    Distributed under 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)
*/

/**************************************************************************************************/

#include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>

#include <stlab/concurrency/default_executor.hpp>
#include <stlab/concurrency/future.hpp>
#include <stlab/concurrency/utility.hpp>
#include <stlab/test/model.hpp>

#include "future_test_helper.hpp"

using namespace std;
using namespace stlab;
using namespace future_test_helper;

BOOST_FIXTURE_TEST_SUITE(future_then_void, test_fixture<void>)

BOOST_AUTO_TEST_CASE(future_void_single_task) {
    BOOST_TEST_MESSAGE("running future void single task");

    int p = 0;

    sut = async(make_executor<0>(), [& _p = p] { _p = 42; });

    check_valid_future(sut);
    wait_until_future_completed(sut);

    BOOST_REQUIRE_EQUAL(42, p);
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_void_single_task_detached) {
    BOOST_TEST_MESSAGE("running future void single task detached");

    atomic_int p{0};
    {
        auto detached = async(make_executor<0>(), [& _p = p] { _p = 42; });
        detached.detach();
    }
    while (p.load() != 42) {
    }
}

BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_same_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE("running future void with two task on same scheduler, then on r-value");

    /* because of a gcc version < 6 bug, it is not possible to use the following
    using task_t = function<void()>;
    using op_t = future<void>(future<void>::*)(task_t&&)&&;

    op_t ops[] = {static_cast<op_t>(&future<void>::then<task_t>),
                  static_cast<op_t>(&future<void>::operator|<task_t>)};

    for (const auto& op : ops)
    */
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] { _p += 42; });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] { _p += 42; };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_same_scheduler_then_on_lvalue) {
    BOOST_TEST_MESSAGE("running future void with two task on same scheduler, then on l-value");

    {
        atomic_int p{0};
        auto interim = async(make_executor<0>(), [& _p = p] { _p = 42; });

        sut = interim.then([& _p = p] { _p += 42; });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};
        auto interim = async(make_executor<0>(), [& _p = p] { _p = 42; });

        sut = interim | [& _p = p] { _p += 42; };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_void_two_tasks_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future int void tasks with same scheduler");

    {
        atomic_int p{0};

        sut =
            async(make_executor<0>(), [] { return 42; }).then([& _p = p](auto x) { _p = x + 42; });
        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [] { return 42; }) | [& _p = p](auto x) { _p = x + 42; };
        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_void_two_tasks_with_different_scheduler) {
    BOOST_TEST_MESSAGE("running future int void tasks with different schedulers");

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [] {
                  return 42;
              }).then(make_executor<1>(), [& _p = p](auto x) { _p = x + 42; });
        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [] { return 42; }) |
              (executor{make_executor<1>()} & [& _p = p](auto x) { _p = x + 42; });

        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_void_two_tasks_with_different_scheduler) {
    BOOST_TEST_MESSAGE("running future void two tasks with different schedulers");

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] {
                  _p = 42;
              }).then(make_executor<1>(), [& _p = p] { _p += 42; });
        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
    }

    custom_scheduler<0>::reset();
    custom_scheduler<1>::reset();

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) |
              (executor{make_executor<1>()} & [& _p = p] { _p += 42; });

        check_valid_future(sut);

        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
    }
}

/*
        f1
       /
    sut
       \
        f2
*/
BOOST_AUTO_TEST_CASE(future_void_Y_formation_tasks_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future void with Y formation with same scheduler");

    {
        atomic_int p{0};
        int r1 = 0;
        int r2 = 0;

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; });
        auto f1 = sut.then([& _p = p, &_r = r1] { _r = 42 + _p; });
        auto f2 = sut.then([& _p = p, &_r = r2] { _r = 4711 + _p; });

        check_valid_future(sut, f1, f2);
        wait_until_future_completed(f1, f2);

        BOOST_REQUIRE_EQUAL(42 + 42, r1);
        BOOST_REQUIRE_EQUAL(42 + 4711, r2);
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};
        int r1 = 0;
        int r2 = 0;

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; });
        auto f1 = sut | [& _p = p, &_r = r1] { _r = 42 + _p; };
        auto f2 = sut | [& _p = p, &_r = r2] { _r = 4711 + _p; };

        check_valid_future(sut, f1, f2);
        wait_until_future_completed(f1, f2);

        BOOST_REQUIRE_EQUAL(42 + 42, r1);
        BOOST_REQUIRE_EQUAL(42 + 4711, r2);
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_void) {
    BOOST_TEST_MESSAGE("running future reduction void to void");

    {
        bool first{false};
        bool second{false};

        sut = async(make_executor<0>(), [&] { first = true; }).then([&] {
            return async(make_executor<0>(), [&] { second = true; });
        });

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
    {
        bool first{false};
        bool second{false};

        sut = async(make_executor<0>(), [&] { first = true; }) |
              [&] { return async(make_executor<0>(), [&] { second = true; }); };

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_int_to_void) {
    BOOST_TEST_MESSAGE("running future reduction int to void");

    {
        atomic_bool first{false};
        atomic_bool second{false};
        atomic_int result{0};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return 42;
              }).then([& _flag = second, &_result = result](auto x) {
            return async(
                default_executor,
                [&_flag, &_result](auto x) {
                    _flag = true;
                    _result = x + 42;
                },
                x);
        });

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(84, result);
    }
    {
        atomic_bool first{false};
        atomic_bool second{false};
        atomic_int result{0};

        sut = async(default_executor,
                    [& _flag = first] {
                        _flag = true;
                        return 42;
                    }) |
              [& _flag = second, &_result = result](auto x) {
                  return async(
                      default_executor,
                      [&_flag, &_result](auto x) {
                          _flag = true;
                          _result = x + 42;
                      },
                      x);
              };

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(84, result);
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_void) {
    BOOST_TEST_MESSAGE("running future reduction move-only to void");
    {
        atomic_bool first{false};
        move_only result;

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _result = result](auto&& x) {
            return async(
                default_executor, [&_result](auto&& x) { _result = std::move(x); },
                forward<move_only>(x));
        });

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE_EQUAL(42, result.member());
    }
    {
        bool first{false};
        move_only result;

        sut = async(immediate_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _result = result](auto&& x) {
            return async(
                immediate_executor, [&_result](auto&& x) { _result = std::move(x); },
                forward<move_only>(x));
        });

        BOOST_REQUIRE(sut.get_try());

        BOOST_REQUIRE(first);
        BOOST_REQUIRE_EQUAL(42, result.member());
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(future_then_non_copyable, test_fixture<move_only>)
BOOST_AUTO_TEST_CASE(future_non_copyable_single_task) {
    BOOST_TEST_MESSAGE("running future non copyable single task");

    sut = async(make_executor<0>(), [] { return move_only(42); });

    check_valid_future(sut);
    auto result = wait_until_future_r_completed(sut);

    BOOST_REQUIRE_EQUAL(42, result->member());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_then_non_copyable_detach) {
    BOOST_TEST_MESSAGE("running future non copyable, detached");
    atomic_bool check{false};
    {
        async(make_executor<0>(), [& _check = check] {
            _check = true;
            return move_only(42);
        }).detach();
    }
    while (!check) {
        this_thread::sleep_for(chrono::milliseconds(1));
    }
}

BOOST_AUTO_TEST_CASE(future_non_copyable_capture) {
    BOOST_TEST_MESSAGE("running future non copyable capture");

    move_only m{42};

    sut = async(make_executor<0>(), [& _m = m] { return move_only(_m.member()); });

    check_valid_future(sut);
    auto result = wait_until_future_r_completed(sut);

    BOOST_REQUIRE_EQUAL(42, result->member());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(
    future_copyable_with_non_copyable_as_continuation_with_same_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE(
        "running future copyable with non copyable as contination with same scheduler, then on r-value");

    {
        sut =
            async(make_executor<0>(), [] { return 42; }).then([](auto x) { return move_only(x); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return move_only(x); };

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(
    future_copyable_with_non_copyable_as_continuation_with_different_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE(
        "running future copyable with non copyable as contination with different scheduler, then on r-value");

    {
        sut = async(make_executor<0>(), [] { return 42; }).then(make_executor<1>(), [](auto x) {
            return move_only(x);
        });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }

    custom_scheduler<0>::reset();
    custom_scheduler<1>::reset();

    {
        sut = async(make_executor<0>(), [] { return 42; }) |
              (executor{make_executor<1>()} & [](auto x) { return move_only(x); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(
    future_copyable_with_non_copyable_as_continuation_with_same_scheduler_then_on_lvalue) {
    BOOST_TEST_MESSAGE(
        "running future copyable with non copyable as contination with same scheduler, then on l-value");

    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim.then([](auto x) { return move_only(x); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim | [](auto x) { return move_only(x); };

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(
    future_copyable_with_non_copyable_as_continuation_with_different_scheduler_then_on_lvalue) {
    BOOST_TEST_MESSAGE(
        "running future copyable with non copyable as contination with different scheduler, then on l-value");
    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim.then(make_executor<1>(), [](auto x) { return move_only(x); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }
    custom_scheduler<0>::reset();
    custom_scheduler<1>::reset();
    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim | (executor{make_executor<1>()} & [](auto x) { return move_only(x); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_non_copyable_as_continuation_with_same_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE(
        "running future non copyable as contination with same scheduler, then on r-value");

    {
        sut = async(make_executor<0>(), [] { return move_only(42); }).then([](auto&& x) {
            return move_only(x.member() * 2);
        });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42 * 2, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return move_only(42); }) |
              [](auto&& x) { return move_only(x.member() * 2); };

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42 * 2, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_non_copyable_as_continuation_with_different_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE(
        "running future non copyable as contination with different scheduler, then on r-value");
    {
        sut = async(make_executor<0>(), [] {
                  return move_only(42);
              }).then(make_executor<1>(), [](auto x) { return move_only(x.member() * 2); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42 * 2, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }

    custom_scheduler<0>::reset();
    custom_scheduler<1>::reset();

    {
        sut = async(make_executor<0>(), [] { return move_only(42); }) |
              (executor{make_executor<1>()} & [](auto x) { return move_only(x.member() * 2); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42 * 2, result->member());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<0>::usage_counter());
        BOOST_REQUIRE_EQUAL(1, custom_scheduler<1>::usage_counter());
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(future_then_move_only, test_fixture<move_only>)

BOOST_AUTO_TEST_CASE(future_async_move_only_move_captured_to_result) {
    BOOST_TEST_MESSAGE("running future move only move to result");

    {
        sut = async(make_executor<0>(), [] { return move_only{42}; }).then([](auto x) {
            return move(x);
        });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return move_only{42}; }) |
              [](auto x) { return move(x); };

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_async_moving_move_only_capture_to_result) {
    BOOST_TEST_MESSAGE("moving move_only capture to result");

    move_only m{42};

    sut = async(make_executor<0>(), [& _m = m] { return move(_m); });

    check_valid_future(sut);
    auto result = wait_until_future_r_completed(sut);

    BOOST_REQUIRE_EQUAL(42, result->member());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_async_mutable_move_move_only_capture_to_result) {
    BOOST_TEST_MESSAGE("moving move_only capture to result in task");

    move_only m{42};

    sut = async(make_executor<0>(), [& _m = m]() { return move(_m); });

    check_valid_future(sut);
    auto result = wait_until_future_r_completed(sut);

    BOOST_REQUIRE_EQUAL(42, result->member());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_continuation_moving_move_only_capture_to_result) {
    BOOST_TEST_MESSAGE("moving move_only capture to result");

    move_only m{42};

    sut = async(make_executor<0>(), [] { return move_only{10}; }).then([& _m = m](auto) {
        return move(_m);
    });

    check_valid_future(sut);
    auto result = wait_until_future_r_completed(sut);

    BOOST_REQUIRE_EQUAL(42, result->member());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_continuation_async_mutable_move_move_only_capture_to_result) {
    BOOST_TEST_MESSAGE("moving move_only capture to result in task");

    {
        move_only m{42};

        sut = async(make_executor<0>(), []() {
                  return move_only{10};
              }).then([& _m = m](auto) { return move(_m); });

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        move_only m{42};

        sut = async(make_executor<0>(), []() { return move_only{10}; }) |
              [& _m = m](auto) { return move(_m); };

        check_valid_future(sut);
        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE_EQUAL(42, result->member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_move_only) {
    BOOST_TEST_MESSAGE("running future reduction move-only to move-only");
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _flag = second](auto&& x) {
            return async(
                default_executor,
                [&_flag](auto&& x) {
                    _flag = true;
                    return forward<move_only>(x);
                },
                forward<move_only>(x));
        });

        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(42, (*result).member());
    }
    {
        bool first{false};
        bool second{false};

        sut = async(immediate_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _flag = second](auto&& x) {
            return async(
                immediate_executor,
                [&_flag](auto&& x) {
                    _flag = true;
                    return forward<move_only>(x);
                },
                forward<move_only>(x));
        });

        auto result = wait_until_future_r_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(42, (*result).member());
    }
}

BOOST_AUTO_TEST_SUITE_END()

namespace stlab
{

// specializing std::vector, so that the framework can detect correctly
// if std::vector<move_only> is copyable or only moveable
template<template<typename> class test, typename T, typename A>
struct smart_test<test, std::vector<T, A>> : test<T> {};

}

BOOST_FIXTURE_TEST_SUITE(future_then_move_only_container, test_fixture<std::vector<move_only>>)

BOOST_AUTO_TEST_CASE(future_continuation_async_move_only_container) {
    BOOST_TEST_MESSAGE("moving move_only move only container");

    {
        sut = async(make_executor<0>(), []() {
            std::vector<move_only> result;
            result.emplace_back(10);
            result.emplace_back(42);

            return result;
        }).then([](auto x) {
            x.emplace_back(50);
            return x;
        });

        check_valid_future(sut);
        auto result = blocking_get(std::move(sut));

        BOOST_REQUIRE_EQUAL(3, result.size());
        BOOST_REQUIRE_EQUAL(10, result[0].member());
        BOOST_REQUIRE_EQUAL(42, result[1].member());
        BOOST_REQUIRE_EQUAL(50, result[2].member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), []() {
            std::vector<move_only> result;
            result.emplace_back(10);
            result.emplace_back(42);

            return result;
        }) | [](auto x) {
            x.emplace_back(50);
            return x;
        };

        check_valid_future(sut);
        auto result = blocking_get(std::move(sut));

        BOOST_REQUIRE_EQUAL(3, result.size());
        BOOST_REQUIRE_EQUAL(10, result[0].member());
        BOOST_REQUIRE_EQUAL(42, result[1].member());
        BOOST_REQUIRE_EQUAL(50, result[2].member());
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(future_then_int, test_fixture<int>)

BOOST_AUTO_TEST_CASE(future_int_single_task) {
    BOOST_TEST_MESSAGE("running future int single tasks");

    sut = async(make_executor<0>(), [] { return 42; });

    check_valid_future(sut);
    wait_until_future_completed(sut);

    BOOST_REQUIRE_EQUAL(42, *sut.get_try());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_int_single_task_get_try_on_rvalue) {
    BOOST_TEST_MESSAGE("running future int single tasks, get_try on r-value");

    sut = async(make_executor<0>(), [] { return 42; });

    auto test_result_1 = move(sut).get_try(); // test for r-value implementation
    (void)test_result_1;
    wait_until_future_completed(sut);
    auto test_result_2 = move(sut).get_try();

    BOOST_REQUIRE_EQUAL(42, *sut.get_try());
    BOOST_REQUIRE_EQUAL(42, *test_result_2);
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_int_single_task_detached) {
    BOOST_TEST_MESSAGE("running future int single tasks, detached");
    atomic_bool check{false};
    {
        auto detached = async(make_executor<0>(), [& _check = check] {
            _check = true;
            return 42;
        });
        detached.detach();
    }
    while (!check) {
        this_thread::sleep_for(chrono::milliseconds(1));
    }
}

BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_same_scheduler_then_on_rvalue) {
    BOOST_TEST_MESSAGE("running future int two tasks with same scheduler, then on r-value");

    {
        sut = async(make_executor<0>(), [] { return 42; }).then([](auto x) { return x + 42; });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return x + 42; };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_same_scheduler_then_on_lvalue) {
    BOOST_TEST_MESSAGE("running future int two tasks with same scheduler, then on l-value");

    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim.then([](auto x) { return x + 42; });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        auto interim = async(make_executor<0>(), [] { return 42; });

        sut = interim | [](auto x) { return x + 42; };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_two_tasks_with_different_scheduler) {
    BOOST_TEST_MESSAGE("running future int two tasks with different scheduler");

    sut = async(make_executor<0>(), [] { return 42; }).then(make_executor<1>(), [](auto x) {
        return x + 42;
    });

    check_valid_future(sut);
    wait_until_future_completed(sut);

    BOOST_REQUIRE_EQUAL(42 + 42, *sut.get_try());
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_void_int_two_tasks_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future void int tasks with same scheduler");

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {
            _p += 42;
            return _p.load();
        });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] {
            _p += 42;
            return _p.load();
        };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_void_int_two_tasks_with_different_scheduler) {
    BOOST_TEST_MESSAGE("running future void int tasks with different schedulers");

    atomic_int p{0};

    sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then(make_executor<1>(), [& _p = p] {
        _p += 42;
        return _p.load();
    });

    check_valid_future(sut);
    wait_until_future_completed(sut);

    BOOST_REQUIRE_EQUAL(42 + 42, p);
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    BOOST_REQUIRE_LE(1, custom_scheduler<1>::usage_counter());
}

/*
    sut - f - f
*/
BOOST_AUTO_TEST_CASE(future_int_three_tasks_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future int with three tasks with same scheduler");

    {
        sut = async(make_executor<0>(), [] {
                  return 42;
              }).then([](auto x) {
                    return x + 42;
                }).then([](auto x) { return x + 42; });

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return 42; }) | [](auto x) { return x + 42; } |
              [](auto x) { return x + 42; };

        check_valid_future(sut);
        wait_until_future_completed(sut);

        BOOST_REQUIRE_EQUAL(42 + 42 + 42, *sut.get_try());
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
}

/*
        f1
       /
    sut
       \
        f2
*/
BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future int Y formation tasks with same scheduler");

    {
        sut = async(make_executor<0>(), [] { return 42; });
        auto f1 = sut.then([](auto x) { return x + 42; });
        auto f2 = sut.then([](auto x) { return x + 4177; });

        check_valid_future(sut, f1, f2);
        wait_until_future_completed(f1, f2);

        BOOST_REQUIRE_EQUAL(42 + 42, *f1.get_try());
        BOOST_REQUIRE_EQUAL(42 + 4177, *f2.get_try());
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
    {
        sut = async(make_executor<0>(), [] { return 42; });
        auto f1 = sut | [](auto x) { return x + 42; };
        auto f2 = sut | [](auto x) { return x + 4177; };

        check_valid_future(sut, f1, f2);
        wait_until_future_completed(f1, f2);

        BOOST_REQUIRE_EQUAL(42 + 42, *f1.get_try());
        BOOST_REQUIRE_EQUAL(42 + 4177, *f2.get_try());
        BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_void_to_int) {
    BOOST_TEST_MESSAGE("running future reduction void to int");

    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {
            return async(default_executor, [&_flag] {
                _flag = true;
                return 42;
            });
        });

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(42, *sut.get_try());
    }
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] { _flag = true; }) | [& _flag = second] {
            return async(default_executor, [&_flag] {
                _flag = true;
                return 42;
            });
        };

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(42, *sut.get_try());
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_int_to_int) {
    BOOST_TEST_MESSAGE("running future reduction int to int");

    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return 42;
              }).then([& _flag = second](auto x) {
            return async(
                default_executor,
                [&_flag](auto x) {
                    _flag = true;
                    return x + 42;
                },
                x);
        });

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(84, *sut.get_try());
    }
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor,
                    [& _flag = first] {
                        _flag = true;
                        return 42;
                    }) |
              [& _flag = second](auto x) {
                  return async(
                      default_executor,
                      [&_flag](auto x) {
                          _flag = true;
                          return x + 42;
                      },
                      x);
              };

        wait_until_future_completed(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
        BOOST_REQUIRE_EQUAL(84, *sut.get_try());
    }
}
BOOST_AUTO_TEST_SUITE_END()

// ----------------------------------------------------------------------------
//                             Error cases
// ----------------------------------------------------------------------------

BOOST_FIXTURE_TEST_SUITE(future_void_then_error, test_fixture<void>)

BOOST_AUTO_TEST_CASE(future_void_single_task_error) {
    BOOST_TEST_MESSAGE("running future void with single tasks that fails");

    sut = async(make_executor<0>(), [] { throw test_exception("failure"); });

    wait_until_future_fails<test_exception>(sut);
    check_failure<test_exception>(sut, "failure");
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_void_two_tasks_error_in_1st_task_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future void with two tasks which first fails");

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [] { throw test_exception("failure"); }).then([& _p = p] {
            _p = 42;
        });

        wait_until_future_fails<test_exception>(sut);
        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(0, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [] { throw test_exception("failure"); }) |
              [& _p = p] { _p = 42; };

        wait_until_future_fails<test_exception>(sut);
        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(0, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_void_two_tasks_error_in_2nd_task_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future void with two tasks which second fails");

    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([& _p = p] {
            (void)_p;
            throw test_exception("failure");
        });

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) | [& _p = p] {
            (void)_p;
            throw test_exception("failure");
        };

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_void_to_void_error) {
    BOOST_TEST_MESSAGE("running future reduction void to void where the inner future fails");

    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] { _flag = true; }).then([& _flag = second] {
            return async(default_executor, [&_flag] {
                _flag = true;
                throw test_exception("failure");
            });
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] { _flag = true; }) | [& _flag = second] {
            return async(default_executor, [&_flag] {
                _flag = true;
                throw test_exception("failure");
            });
        };

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
}

BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_void_when_inner_future_fails) {
    BOOST_TEST_MESSAGE("running future reduction move-only to void when inner future fails");
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _check = second](auto&& x) {
            return async(
                default_executor,
                [&_check](auto&&) {
                    _check = true;
                    throw test_exception("failure");
                },
                forward<move_only>(x));
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
    {
        bool first{false};
        bool second{false};

        sut = async(immediate_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _check = second](auto&& x) {
            return async(
                immediate_executor,
                [&_check](auto&&) {
                    _check = true;
                    throw test_exception("failure");
                },
                forward<move_only>(x));
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(future_then_int_error, test_fixture<int>)

BOOST_AUTO_TEST_CASE(future_int_single_task_error) {
    BOOST_TEST_MESSAGE("running future int with single tasks that fails");

    sut = async(make_executor<0>(), []() -> int { throw test_exception("failure"); });
    wait_until_future_fails<test_exception>(sut);

    check_failure<test_exception>(sut, "failure");
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_int_two_tasks_error_in_1st_task_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future int with two tasks which first fails");

    {
        custom_scheduler<0>::reset();
        int p = 0;

        sut = async(make_executor<0>(), [] {
                  throw test_exception("failure");
              }).then([& _p = p]() -> int {
            _p = 42;
            return _p;
        });

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(0, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
    {
        custom_scheduler<0>::reset();
        int p = 0;

        sut = async(make_executor<0>(), [] { throw test_exception("failure"); }) |
              [& _p = p]() -> int {
            _p = 42;
            return _p;
        };

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(0, p);
        BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_two_tasks_error_in_2nd_task_with_same_scheduler) {
    BOOST_TEST_MESSAGE("running future void with two tasks which second fails");

    {
        custom_scheduler<0>::reset();
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }).then([]() -> int {
            throw test_exception("failure");
        });

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
    {
        custom_scheduler<0>::reset();
        atomic_int p{0};

        sut = async(make_executor<0>(), [& _p = p] { _p = 42; }) |
              []() -> int { throw test_exception("failure"); };

        wait_until_future_fails<test_exception>(sut);

        check_failure<test_exception>(sut, "failure");
        BOOST_REQUIRE_EQUAL(42, p);
        BOOST_REQUIRE_LE(2, custom_scheduler<0>::usage_counter());
    }
}

BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_with_failing_1st_task) {
    BOOST_TEST_MESSAGE("running future int Y formation tasks where the 1st tasks fails");

    atomic_int p{0};

    sut = async(make_executor<0>(), []() -> int { throw test_exception("failure"); });
    auto f1 = sut.then(make_executor<0>(), [& _p = p](auto x) -> int {
        _p += 1;
        return x + 42;
    });
    auto f2 = sut.then(make_executor<0>(), [& _p = p](auto x) -> int {
        _p += 1;
        return x + 4177;
    });

    wait_until_future_fails<test_exception>(f1, f2);

    check_failure<test_exception>(f1, "failure");
    check_failure<test_exception>(f2, "failure");
    BOOST_REQUIRE_EQUAL(0, p);
    BOOST_REQUIRE_LE(1, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_one_of_the_2nd_task_failing) {
    BOOST_TEST_MESSAGE("running future int Y formation tasks where one of the 2nd tasks fails");

    sut = async(make_executor<0>(), []() -> int { return 42; });
    auto f1 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception("failure"); });
    auto f2 = sut.then(make_executor<0>(), [](auto x) -> int { return x + 4711; });

    wait_until_future_completed(f2);
    wait_until_future_fails<test_exception>(f1);

    check_failure<test_exception>(f1, "failure");
    BOOST_REQUIRE_EQUAL(42 + 4711, *f2.get_try());
    BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(future_int_Y_formation_tasks_where_both_of_the_2nd_task_failing) {
    BOOST_TEST_MESSAGE("running future int Y formation tasks where both of the 2nd tasks fails");

    sut = async(make_executor<0>(), []() -> int { return 42; });
    auto f1 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception("failure"); });
    auto f2 = sut.then(make_executor<0>(), [](auto) -> int { throw test_exception("failure"); });

    wait_until_future_fails<test_exception>(f1, f2);

    check_failure<test_exception>(f1, "failure");
    check_failure<test_exception>(f2, "failure");
    BOOST_REQUIRE_LE(3, custom_scheduler<0>::usage_counter());
}

BOOST_AUTO_TEST_CASE(reduction_future_void_to_int_error) {
    BOOST_TEST_MESSAGE("running future reduction void to int where the outer future fails");
    atomic_bool first{false};
    atomic_bool second{false};

    sut = async(default_executor, [& _flag = first] {
              _flag = true;
          }).then([& _flag = second]() -> future<int> {
        (void)_flag;
        throw test_exception("failure");
    });

    wait_until_future_fails<test_exception>(sut);

    BOOST_REQUIRE(first);
    BOOST_REQUIRE(!second);
}

BOOST_AUTO_TEST_CASE(reduction_future_int_to_int_error) {
    BOOST_TEST_MESSAGE("running future reduction int to int where the inner future fails");

    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return 42;
              }).then([& _flag = second](auto x) {
            return async(
                default_executor,
                [&_flag](auto) -> int {
                    _flag = true;
                    throw test_exception("failure");
                },
                x);
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor,
                    [& _flag = first] {
                        _flag = true;
                        return 42;
                    }) |
              [& _flag = second](auto x) {
                  return async(
                      default_executor,
                      [&_flag](auto) -> int {
                          _flag = true;
                          throw test_exception("failure");
                      },
                      x);
              };

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(future_then_move_only_error, test_fixture<move_only>)

BOOST_AUTO_TEST_CASE(reduction_future_move_only_to_move_only_when_inner_future_fails) {
    BOOST_TEST_MESSAGE("running future reduction move-only to move-only when inner future fails");
    {
        atomic_bool first{false};
        atomic_bool second{false};

        sut = async(default_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _flag = second](auto&& x) {
            return async(
                default_executor,
                [&_flag](auto&&) -> move_only {
                    _flag = true;
                    throw test_exception("failure");
                },
                forward<move_only>(x));
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
    {
        bool first{false};
        bool second{false};

        sut = async(immediate_executor, [& _flag = first] {
                  _flag = true;
                  return move_only(42);
              }).then([& _flag = second](auto&& x) {
            return async(
                immediate_executor,
                [&_flag](auto&&) -> move_only {
                    _flag = true;
                    throw test_exception("failure");
                },
                forward<move_only>(x));
        });

        wait_until_future_fails<test_exception>(sut);

        BOOST_REQUIRE(first);
        BOOST_REQUIRE(second);
    }
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: olnrao/libraries

max_issues_repo_name: olnrao/libraries

max_forks_repo_name: olnrao/libraries


text:
#include <boost/cstdint.hpp>
#include "template_consumer.h"
#include <fstream>
#include <Codecs/XMLTemplateParser.h>

namespace liquibook { namespace examples {

using namespace QuickFAST::Messages;

const FieldIdentity TemplateConsumer::id_seq_num_("SequenceNumber");

const FieldIdentity TemplateConsumer::id_msg_type_("MessageType");

const FieldIdentity TemplateConsumer::id_timestamp_("Timestamp");

const FieldIdentity TemplateConsumer::id_symbol_("Symbol");

const FieldIdentity TemplateConsumer::id_bids_("Bids");

const FieldIdentity TemplateConsumer::id_bids_length_("BidsLength");

const FieldIdentity TemplateConsumer::id_asks_("Asks");

const FieldIdentity TemplateConsumer::id_asks_length_("AsksLength");

const FieldIdentity TemplateConsumer::id_level_num_("LevelNum");

const FieldIdentity TemplateConsumer::id_order_count_("OrderCount");

const FieldIdentity TemplateConsumer::id_size_("AggregateQty");

const FieldIdentity TemplateConsumer::id_price_("Price");

const FieldIdentity TemplateConsumer::id_qty_("Quantity");

const FieldIdentity TemplateConsumer::id_cost_("Cost");

QuickFAST::Codecs::TemplateRegistryPtr
TemplateConsumer::parse_templates(const std::string& template_filename)
{
  std::ifstream template_stream(template_filename.c_str());
  QuickFAST::Codecs::XMLTemplateParser parser;
  return parser.parse(template_stream);
}

} }

ext: cpp

max_stars_repo_name: coinderex/liquibook

max_issues_repo_name: swtcfoundation/liquibook

max_forks_repo_name: swtcfoundation/liquibook


text:
#define BOOST_TEST_MODULE <%= projectName %>_test
#include <boost/test/unit_test.hpp>

max_stars_repo_name: jabaa/generator-wombytes-cpp

max_issues_repo_name: jabaa/generator-wombytes-cpp

max_forks_repo_name: jabaa/generator-wombytes-cpp


text:
#include "stdafx.h"
#include "comutilities.h"

#include <boost/test/unit_test.hpp>

#ifdef UNIT_TEST

BOOST_AUTO_TEST_SUITE( ComUtilitiesTest )

	BOOST_AUTO_TEST_CASE( test_out_ptr )
	{
		boost::scoped_ptr<int> i(new int(0)) ;
		int *val = i.get() ;
		void **data = out_ptr(&val) ;
		BOOST_CHECK((int*)*data == val) ;
	}

BOOST_AUTO_TEST_SUITE_END()

#endif

max_stars_repo_name: ultimatezen/felix

max_issues_repo_name: ultimatezen/felix

max_forks_repo_name: ultimatezen/felix


text:
//  (C) Copyright John Maddock 2017.
//  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/type_traits for most recent version including documentation.

#ifndef BOOST_TT_IS_COMPLETE_HPP_INCLUDED
#define BOOST_TT_IS_COMPLETE_HPP_INCLUDED

#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/config/workaround.hpp>

/*
 * CAUTION:
 * ~~~~~~~~
 *
 * THIS TRAIT EXISTS SOLELY TO GENERATE HARD ERRORS WHEN A ANOTHER TRAIT
 * WHICH REQUIRES COMPLETE TYPES AS ARGUMENTS IS PASSED AN INCOMPLETE TYPE
 *
 * DO NOT MAKE GENERAL USE OF THIS TRAIT, AS THE COMPLETENESS OF A TYPE
 * VARIES ACROSS TRANSLATION UNITS AS WELL AS WITHIN A SINGLE UNIT.
 *
*/

namespace boost {

//
// We will undef this if the trait isn't fully functional:
//
#define BOOST_TT_HAS_WORKING_IS_COMPLETE

#if !defined(BOOST_NO_SFINAE_EXPR) && !BOOST_WORKAROUND(BOOST_MSVC, <= 1900) && !BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40600)

   namespace detail{

      template <unsigned N>
      struct ok_tag { double d; char c[N]; };

      template <class T>
      ok_tag<sizeof(T)> check_is_complete(int);
      template <class T>
      char check_is_complete(...);
   }

   template <class T> struct is_complete
      : public integral_constant<bool, ::boost::is_function<typename boost::remove_reference<T>::type>::value || (sizeof(detail::check_is_complete<T>(0)) != sizeof(char))> {};

#elif !defined(BOOST_NO_SFINAE) && !defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) && !BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40500)

   namespace detail
   {

      template <class T>
      struct is_complete_imp
      {
         template <class U, class = decltype(sizeof(std::declval< U >())) >
         static type_traits::yes_type check(U*);

         template <class U>
         static type_traits::no_type check(...);

         static const bool value = sizeof(check<T>(0)) == sizeof(type_traits::yes_type);
      };

} // namespace detail

   template <class T>
   struct is_complete : boost::integral_constant<bool, ::boost::is_function<typename boost::remove_reference<T>::type>::value || ::boost::detail::is_complete_imp<T>::value>
   {};
   template <class T>
   struct is_complete<T&> : boost::is_complete<T> {};

#else

      template <class T> struct is_complete
         : public boost::integral_constant<bool, true> {};

#undef BOOST_TT_HAS_WORKING_IS_COMPLETE

#endif

} // namespace boost

#endif // BOOST_TT_IS_COMPLETE_HPP_INCLUDED

ext: hpp

max_stars_repo_name: cfsengineering/tigl

max_issues_repo_name: cfsengineering/tigl

max_forks_repo_name: cfsengineering/tigl


text:
#define BOOST_TEST_MAIN
#define  BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#define BOOST_TEST_MODULE NFmiGrib

#include "NFmiGrib.h"
#include <iostream>

const std::string grib1 = "file.grib";
const std::string grib2 = "file.grib2";
const std::string ens = "ens.grib2";

NFmiGrib reader;

void init1()
{
	bool open = reader.Open(grib1);

	BOOST_REQUIRE(open);

	bool next = reader.NextMessage();

	BOOST_REQUIRE(next);
}

void init2()
{
	bool open = reader.Open(grib2);

	BOOST_REQUIRE(open);

	bool next = reader.NextMessage();

	BOOST_REQUIRE(next);
}

void initens()
{
	bool open = reader.Open(ens);

	BOOST_REQUIRE(open);

	bool next = reader.NextMessage();

	BOOST_REQUIRE(next);
}

BOOST_AUTO_TEST_CASE(localDefinitionNumber)
{
	init1();

	BOOST_REQUIRE(reader.Message().LocalDefinitionNumber() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().LocalDefinitionNumber() == 0);
}

BOOST_AUTO_TEST_CASE(productDefinitionTemplateNumber)
{
	init1();

	BOOST_REQUIRE(reader.Message().ProductDefinitionTemplateNumber() == INVALID_INT_VALUE);

	init2();

	reader.Message().ProductDefinitionTemplateNumber(1);
	BOOST_REQUIRE(reader.Message().ProductDefinitionTemplateNumber() == 1);

}

BOOST_AUTO_TEST_CASE(typeOfStatisticalProcessing)
{
	init1();

	BOOST_REQUIRE(reader.Message().TypeOfStatisticalProcessing() == INVALID_INT_VALUE);

	init2();

	reader.Message().ProductDefinitionTemplateNumber(8);
	reader.Message().TypeOfStatisticalProcessing(5);
	BOOST_REQUIRE(reader.Message().TypeOfStatisticalProcessing() == 5);

}

BOOST_AUTO_TEST_CASE(valuesLength)
{
	init1();

	BOOST_REQUIRE(reader.Message().ValuesLength() == 840480);

	init2();

	BOOST_REQUIRE(reader.Message().ValuesLength() == 243869);

}

BOOST_AUTO_TEST_CASE(dataDate)
{
	init1();

	BOOST_REQUIRE(reader.Message().DataDate() == 20120529);
	reader.Message().DataDate(20140912);
	BOOST_REQUIRE(reader.Message().DataDate() == 20140912);

	init2();

	BOOST_REQUIRE(reader.Message().DataDate() == 20120611);

}

BOOST_AUTO_TEST_CASE(dataTime)
{
	init1();

	BOOST_REQUIRE(reader.Message().DataTime() == 0);
	reader.Message().DataTime(600);
	BOOST_REQUIRE(reader.Message().DataTime() == 600);

	init2();

	BOOST_REQUIRE(reader.Message().DataTime() == 0);

}

BOOST_AUTO_TEST_CASE(forecastTime)
{
	init1();

	BOOST_REQUIRE(reader.Message().ForecastTime() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().ForecastTime() == 9);
	reader.Message().ForecastTime(12);
	BOOST_REQUIRE(reader.Message().ForecastTime() == 12);
}

BOOST_AUTO_TEST_CASE(parameterUnit)
{
	init1();

	BOOST_REQUIRE(reader.Message().ParameterUnit() == "K");

	init2();

	BOOST_REQUIRE(reader.Message().ParameterUnit() == "K");
}

BOOST_AUTO_TEST_CASE(parameterNumber)
{
	init1();

	BOOST_REQUIRE(reader.Message().ParameterNumber() == 11);
	reader.Message().ParameterNumber(3);
	BOOST_REQUIRE(reader.Message().ParameterNumber() == 3);

	init2();

	BOOST_REQUIRE(reader.Message().ParameterNumber() == 0);
}

BOOST_AUTO_TEST_CASE(parameterCategory)
{
	init1();

	BOOST_REQUIRE(reader.Message().ParameterCategory() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().ParameterCategory() == 0);
	reader.Message().ParameterCategory(3);
	BOOST_REQUIRE(reader.Message().ParameterCategory() == 3);

}

BOOST_AUTO_TEST_CASE(parameterDiscipline)
{
	init1();

	BOOST_REQUIRE(reader.Message().ParameterDiscipline() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().ParameterDiscipline() == 0);
	reader.Message().ParameterDiscipline(10);
	BOOST_REQUIRE(reader.Message().ParameterDiscipline() == 10);

}

BOOST_AUTO_TEST_CASE(parameterName)
{
	init1();

	BOOST_REQUIRE(reader.Message().ParameterName() == "Temperature");

	init2();

	BOOST_REQUIRE(reader.Message().ParameterName() == "Temperature");

}

BOOST_AUTO_TEST_CASE(gridType)
{
	init1();

	BOOST_REQUIRE(reader.Message().GridType() == 10);
	reader.Message().GridType(1);
	BOOST_REQUIRE(reader.Message().GridType() == 1);

	init2();

	BOOST_REQUIRE(reader.Message().GridType() == 1);

}

BOOST_AUTO_TEST_CASE(gridOrientation)
{
	init1();

	BOOST_REQUIRE(reader.Message().GridOrientation() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().GridOrientation() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(iDirectionIncrement)
{
	init1();

	BOOST_REQUIRE(reader.Message().iDirectionIncrement() == 0.068);
	reader.Message().iDirectionIncrement(2);
	BOOST_REQUIRE(reader.Message().iDirectionIncrement() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().iDirectionIncrement() == 0.125);

}

BOOST_AUTO_TEST_CASE(jDirectionIncrement)
{
	init1();

	BOOST_REQUIRE(reader.Message().jDirectionIncrement() == 0.068);
	reader.Message().jDirectionIncrement(2);
	BOOST_REQUIRE(reader.Message().jDirectionIncrement() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().jDirectionIncrement() == 0.125);

}

BOOST_AUTO_TEST_CASE(levelType)
{
	init1();

	BOOST_REQUIRE(reader.Message().LevelType() == 109);
	reader.Message().LevelType(100);
	BOOST_REQUIRE(reader.Message().LevelType() == 100);

	init2();

	BOOST_REQUIRE(reader.Message().LevelType() == 105);

}

BOOST_AUTO_TEST_CASE(levelValue)
{
	init1();

	BOOST_REQUIRE(reader.Message().LevelValue() == 11);
	reader.Message().LevelValue(1);
	BOOST_REQUIRE(reader.Message().LevelValue() == 1);

	init2();

	BOOST_REQUIRE(reader.Message().LevelValue() == 40);

}

BOOST_AUTO_TEST_CASE(X0)
{
	init1();

	BOOST_REQUIRE(reader.Message().X0() == -33.5);
	reader.Message().X0(10.0);
	BOOST_REQUIRE(reader.Message().X0() == 10.0);

	init2();

	BOOST_REQUIRE(reader.Message().X0() == 334);

}

BOOST_AUTO_TEST_CASE(Y0)
{
	init1();

	BOOST_REQUIRE(reader.Message().Y0() == -24);
	reader.Message().Y0(10.0);
	BOOST_REQUIRE(reader.Message().Y0() == 10.0);

	init2();

	BOOST_REQUIRE(reader.Message().Y0() == 22.5);

}

BOOST_AUTO_TEST_CASE(X1)
{
	init1();

	BOOST_REQUIRE(reader.Message().X1() == 36.472);
	reader.Message().X1(10.0);
	BOOST_REQUIRE(reader.Message().X1() == 10.0);

	init2();

	BOOST_REQUIRE(reader.Message().X1() == 40);

}

BOOST_AUTO_TEST_CASE(Y1)
{
	init1();

	BOOST_REQUIRE(reader.Message().Y1() == 31.42);
	reader.Message().Y1(10.0);
	BOOST_REQUIRE(reader.Message().Y1() == 10.0);

	init2();

	BOOST_REQUIRE(reader.Message().Y1() == -35);

}

BOOST_AUTO_TEST_CASE(southPoleX)
{
	init1();

	BOOST_REQUIRE(reader.Message().SouthPoleX() == 0);
	reader.Message().SouthPoleX(5.0);
	BOOST_REQUIRE(reader.Message().SouthPoleX() == 5.0);

	init2();

	BOOST_REQUIRE(reader.Message().SouthPoleX() == 0);

}

BOOST_AUTO_TEST_CASE(southPoleY)
{
	init1();

	BOOST_REQUIRE(reader.Message().SouthPoleY() == -30);
	reader.Message().SouthPoleY(-40.0);
	BOOST_REQUIRE(reader.Message().SouthPoleY() == -40.0);

	init2();

	BOOST_REQUIRE(reader.Message().SouthPoleY() == -30);

}

BOOST_AUTO_TEST_CASE(edition)
{
	init1();

	BOOST_REQUIRE(reader.Message().Edition() == 1);
	reader.Message().Edition(2);
	BOOST_REQUIRE(reader.Message().Edition() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().Edition() == 2);

}

BOOST_AUTO_TEST_CASE(process)
{
	init1();

	BOOST_REQUIRE(reader.Message().Process() == 1);
	reader.Message().Process(2);
	BOOST_REQUIRE(reader.Message().Process() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().Process() == 141);

}

BOOST_AUTO_TEST_CASE(centre)
{
	init1();

	BOOST_REQUIRE(reader.Message().Centre() == 86);
	reader.Message().Centre(98);
	BOOST_REQUIRE(reader.Message().Centre() == 98);

	init2();

	BOOST_REQUIRE(reader.Message().Centre() == 98);

}

BOOST_AUTO_TEST_CASE(iScansNegatively)
{
	init1();

	BOOST_REQUIRE(!reader.Message().IScansNegatively());
	reader.Message().IScansNegatively(true);
	BOOST_REQUIRE(reader.Message().IScansNegatively());

	init2();

	BOOST_REQUIRE(!reader.Message().IScansNegatively());

}

BOOST_AUTO_TEST_CASE(jScansPositively)
{
	init1();

	BOOST_REQUIRE(reader.Message().JScansPositively());
	reader.Message().JScansPositively(false);
	BOOST_REQUIRE(!reader.Message().JScansPositively());

	init2();

	BOOST_REQUIRE(!reader.Message().JScansPositively());

}

BOOST_AUTO_TEST_CASE(sizeX)
{
	init1();

	BOOST_REQUIRE(reader.Message().SizeX() == 1030);
	reader.Message().SizeX(100);
	BOOST_REQUIRE(reader.Message().SizeX() == 100);

	init2();

	BOOST_REQUIRE(reader.Message().SizeX() == 529);

}

BOOST_AUTO_TEST_CASE(sizeY)
{
	init1();

	BOOST_REQUIRE(reader.Message().SizeY() == 816);
	reader.Message().SizeY(10);
	BOOST_REQUIRE(reader.Message().SizeY() == 10);

	init2();

	BOOST_REQUIRE(reader.Message().SizeY() == 461);

}

BOOST_AUTO_TEST_CASE(table2Version)
{
	init1();

	BOOST_REQUIRE(reader.Message().Table2Version() == 1);
	reader.Message().Table2Version(2);
	BOOST_REQUIRE(reader.Message().Table2Version() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().Table2Version() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(numberOfMissing)
{
	init1();

	BOOST_REQUIRE(reader.Message().NumberOfMissing() == 0);

	init2();

	BOOST_REQUIRE(reader.Message().NumberOfMissing() == 0);

}

BOOST_AUTO_TEST_CASE(normalizedLevelType)
{
	init1();

	BOOST_REQUIRE(reader.Message().NormalizedLevelType(1) == 109);
	BOOST_REQUIRE(reader.Message().NormalizedLevelType(2) == 105);

	init2();

	BOOST_REQUIRE(reader.Message().NormalizedLevelType(1) == 109);
	BOOST_REQUIRE(reader.Message().NormalizedLevelType(2) == 105);

}

BOOST_AUTO_TEST_CASE(normalizedGridType)
{
	init1();

	BOOST_REQUIRE(reader.Message().NormalizedGridType(1) == 10);
	BOOST_REQUIRE(reader.Message().NormalizedGridType(2) == 1);

	init2();

	BOOST_REQUIRE(reader.Message().NormalizedGridType(1) == 10);
	BOOST_REQUIRE(reader.Message().NormalizedGridType(2) == 1);

}

BOOST_AUTO_TEST_CASE(normalizedStep)
{
	init1();

	BOOST_REQUIRE(reader.Message().NormalizedStep(false, false) == 7);
	reader.Message().TimeRangeIndicator(5);
	BOOST_REQUIRE(reader.Message().NormalizedStep(false, true) == 7);
	BOOST_REQUIRE(reader.Message().NormalizedStep(true, true) == 0);
	reader.Message().UnitOfTimeRange(11);
	BOOST_REQUIRE(reader.Message().NormalizedStep(false, true) == 7*6);
	reader.Message().TimeRangeIndicator(10);
	reader.Message().UnitOfTimeRange(1);
	BOOST_REQUIRE(reader.Message().NormalizedStep(true, true) == 1792);

	init2();

	BOOST_REQUIRE(reader.Message().NormalizedStep(false, false) == 9);

}

BOOST_AUTO_TEST_CASE(normalizedUnitOfTimeRange)
{
	init1();

	BOOST_REQUIRE(reader.Message().NormalizedUnitOfTimeRange() == 1);

	init2();

	BOOST_REQUIRE(reader.Message().NormalizedUnitOfTimeRange() == 1);

}

BOOST_AUTO_TEST_CASE(startStep)
{
	init1();

	BOOST_REQUIRE(reader.Message().StartStep() == 7);
	reader.Message().StartStep(4);
	BOOST_REQUIRE(reader.Message().StartStep() == 4);

	init2();

	BOOST_REQUIRE(reader.Message().StartStep() == 9);

}

BOOST_AUTO_TEST_CASE(endStep)
{
	init1();

	BOOST_REQUIRE(reader.Message().EndStep() == 7);
	reader.Message().EndStep(8);
	BOOST_REQUIRE(reader.Message().EndStep() == 8);

	init2();

	BOOST_REQUIRE(reader.Message().EndStep() == 9);

}

BOOST_AUTO_TEST_CASE(stepUnits)
{
	init1();

	BOOST_REQUIRE(reader.Message().StepUnits() == 1);
	reader.Message().StepUnits(8);
	BOOST_REQUIRE(reader.Message().StepUnits() == 8);

	init2();

	BOOST_REQUIRE(reader.Message().StepUnits() == 1);

}

BOOST_AUTO_TEST_CASE(stepRange)
{
	init1();

	BOOST_REQUIRE(reader.Message().StepRange() == 7);
	reader.Message().StepRange(17);
	BOOST_REQUIRE(reader.Message().StepRange() == 17);

	init2();

	BOOST_REQUIRE(reader.Message().StepRange() == 9);

}

BOOST_AUTO_TEST_CASE(year)
{
	init1();

	BOOST_REQUIRE(reader.Message().Year() == 2012);

	init2();

	BOOST_REQUIRE(reader.Message().Year() == 2012);
	reader.Message().Year("2014");
	BOOST_REQUIRE(reader.Message().Year() == 2014);

}

BOOST_AUTO_TEST_CASE(month)
{
	init1();

	BOOST_REQUIRE(reader.Message().Month() == 5);
	reader.Message().Month("7");
	BOOST_REQUIRE(reader.Message().Month() == 7);

	init2();

	BOOST_REQUIRE(reader.Message().Month() == 6);

}

BOOST_AUTO_TEST_CASE(day)
{
	init1();

	BOOST_REQUIRE(reader.Message().Day() == 29);
	reader.Message().Day("7");
	BOOST_REQUIRE(reader.Message().Day() == 7);

	init2();

	BOOST_REQUIRE(reader.Message().Day() == 11);

}

BOOST_AUTO_TEST_CASE(hour)
{
	init1();

	BOOST_REQUIRE(reader.Message().Hour() == 0);
	reader.Message().Hour("7");
	BOOST_REQUIRE(reader.Message().Hour() == 7);

	init2();

	BOOST_REQUIRE(reader.Message().Hour() == 0);

}

BOOST_AUTO_TEST_CASE(minute)
{
	init1();

	BOOST_REQUIRE(reader.Message().Minute() == 0);
	reader.Message().Minute("17");
	BOOST_REQUIRE(reader.Message().Minute() == 17);

	init2();

	BOOST_REQUIRE(reader.Message().Minute() == 0);

}

BOOST_AUTO_TEST_CASE(second)
{
	init1();

	BOOST_REQUIRE(reader.Message().Second() == 0);
	reader.Message().Second("1");
	BOOST_REQUIRE(reader.Message().Second() == 1);

	init2();

	BOOST_REQUIRE(reader.Message().Second() == 0);

}

BOOST_AUTO_TEST_CASE(bitmap)
{
	init1();

	BOOST_REQUIRE(!reader.Message().Bitmap());
	reader.Message().Bitmap(true);
	BOOST_REQUIRE(reader.Message().Bitmap());

	init2();

	BOOST_REQUIRE(!reader.Message().Bitmap());

}

BOOST_AUTO_TEST_CASE(bitsPerValue)
{
	init1();

	BOOST_REQUIRE(reader.Message().BitsPerValue() == 10);
	reader.Message().BitsPerValue(16);
	BOOST_REQUIRE(reader.Message().BitsPerValue() == 16);

	init2();

	BOOST_REQUIRE(reader.Message().BitsPerValue() == 12);

}

BOOST_AUTO_TEST_CASE(uvRelativeToGrid)
{
	init1();

	BOOST_REQUIRE(reader.Message().UVRelativeToGrid());
	reader.Message().UVRelativeToGrid(false);
	BOOST_REQUIRE(!reader.Message().UVRelativeToGrid());

	init2();

	BOOST_REQUIRE(!reader.Message().UVRelativeToGrid());
	reader.Message().UVRelativeToGrid(true);
	BOOST_REQUIRE(reader.Message().UVRelativeToGrid());
	reader.Message().UVRelativeToGrid(false);
	BOOST_REQUIRE(!reader.Message().UVRelativeToGrid());

}

BOOST_AUTO_TEST_CASE(resolutionAndComponentFlags)
{
	init1();

	BOOST_REQUIRE(reader.Message().ResolutionAndComponentFlags() == 136);
	reader.Message().ResolutionAndComponentFlags(16);
	BOOST_REQUIRE(reader.Message().ResolutionAndComponentFlags() == 16);

	init2();

	BOOST_REQUIRE(reader.Message().ResolutionAndComponentFlags() == 48);

}

BOOST_AUTO_TEST_CASE(packingType)
{
	init1();

	BOOST_REQUIRE(reader.Message().PackingType() == "grid_simple");
	reader.Message().PackingType("grid_second_order");
	BOOST_REQUIRE(reader.Message().PackingType() == "grid_second_order");

	init2();

	BOOST_REQUIRE(reader.Message().PackingType() == "grid_simple");

}

BOOST_AUTO_TEST_CASE(typeOfGeneratingProcess)
{
	init1();

	BOOST_REQUIRE(reader.Message().TypeOfGeneratingProcess() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().TypeOfGeneratingProcess() == 2);
	reader.Message().TypeOfGeneratingProcess(1);
	BOOST_REQUIRE(reader.Message().TypeOfGeneratingProcess() == 1);

}

BOOST_AUTO_TEST_CASE(xLengthInMeters)
{
	init1();

	BOOST_REQUIRE(reader.Message().XLengthInMeters() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().XLengthInMeters() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(yLengthInMeters)
{
	init1();

	BOOST_REQUIRE(reader.Message().YLengthInMeters() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().YLengthInMeters() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(unitOfTimeRange)
{
	init1();

	BOOST_REQUIRE(reader.Message().UnitOfTimeRange() == 1);
	reader.Message().UnitOfTimeRange(2);
	BOOST_REQUIRE(reader.Message().UnitOfTimeRange() == 2);

	init2();

	BOOST_REQUIRE(reader.Message().UnitOfTimeRange() == 1);

}

BOOST_AUTO_TEST_CASE(lengthOfTimeRange)
{
	init1();

	BOOST_REQUIRE(reader.Message().LengthOfTimeRange() == INVALID_INT_VALUE);

	init2();

	BOOST_REQUIRE(reader.Message().LengthOfTimeRange() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(timeRangeIndicator)
{
	init1();

	BOOST_REQUIRE(reader.Message().TimeRangeIndicator() == 0);
	reader.Message().TimeRangeIndicator(10);
	BOOST_REQUIRE(reader.Message().TimeRangeIndicator() == 10);

	init2();

	BOOST_REQUIRE(reader.Message().TimeRangeIndicator() == 0);

}

BOOST_AUTO_TEST_CASE(P1)
{
	init1();

	BOOST_REQUIRE(reader.Message().P1() == 7);
	reader.Message().P1(10);
	BOOST_REQUIRE(reader.Message().P1() == 10);

	init2();

	BOOST_REQUIRE(reader.Message().P1() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(P2)
{
	init1();

	BOOST_REQUIRE(reader.Message().P2() == 0);
	reader.Message().P2(10);
	BOOST_REQUIRE(reader.Message().P2() == 10);

	init2();

	BOOST_REQUIRE(reader.Message().P2() == INVALID_INT_VALUE);

}

BOOST_AUTO_TEST_CASE(NV)
{
	init1();

	BOOST_REQUIRE(reader.Message().NV() == 2);
	reader.Message().NV(4);
	BOOST_REQUIRE(reader.Message().NV() == 4);

	init2();

	BOOST_REQUIRE(reader.Message().NV() == 184);

}

BOOST_AUTO_TEST_CASE(PV)
{
	init1();

	BOOST_REQUIRE(reader.Message().PV(2, 0)[0] == 1.5831093750e+04);
	reader.Message().PV({0.1, 0.2}, 2);
	BOOST_CHECK_CLOSE(reader.Message().PV(2, 0)[0], 0.1, 0.01);
	BOOST_CHECK_CLOSE(reader.Message().PV(2, 0)[1], 0.2, 0.01);

	init2();

	BOOST_CHECK_CLOSE(reader.Message().PV(184, 66)[0], 16965.08984375, 0.01);

}

BOOST_AUTO_TEST_CASE(packedValuesLength)
{
	init1();

	BOOST_REQUIRE(reader.Message().PackedValuesLength() == 1050601);

	init2();

	BOOST_REQUIRE(reader.Message().PackedValuesLength() == 365804);

}

BOOST_AUTO_TEST_CASE(packedValues)
{
	init1();

	size_t N = reader.Message().PackedValuesLength();

	unsigned char* arr = new unsigned char[N];

	reader.Message().PackedValues(arr);

	BOOST_REQUIRE(arr[0] == 47);

	delete [] arr;

	init2();

	N = reader.Message().PackedValuesLength();
	arr = new unsigned char[N];

	reader.Message().PackedValues(arr);

	BOOST_REQUIRE(arr[1] == 199);

	delete [] arr;

}

BOOST_AUTO_TEST_CASE(binaryScaleFactor)
{
	init1();

	BOOST_REQUIRE(reader.Message().BinaryScaleFactor() == -5);

	init2();

	BOOST_REQUIRE(reader.Message().BinaryScaleFactor() == -6);

}

BOOST_AUTO_TEST_CASE(decimalScaleFactor)
{
	init1();

	BOOST_REQUIRE(reader.Message().DecimalScaleFactor() == 0);

	init2();

	BOOST_REQUIRE(reader.Message().DecimalScaleFactor() == 0);

}

BOOST_AUTO_TEST_CASE(section4Length)
{
	init1();

	BOOST_REQUIRE(reader.Message().Section4Length() == 1050612);

	init2();

	BOOST_REQUIRE(reader.Message().Section4Length() == 770);

}

BOOST_AUTO_TEST_CASE(keyExists)
{
	init1();

	BOOST_REQUIRE(!reader.Message().KeyExists("asdfeaf"));
	BOOST_REQUIRE(reader.Message().KeyExists("gridType"));

	init2();

	BOOST_REQUIRE(reader.Message().KeyExists("edition"));

}

BOOST_AUTO_TEST_CASE(forecastType)
{
	init1();

	BOOST_REQUIRE(reader.Message().PerturbationNumber() == -999);
	BOOST_REQUIRE(reader.Message().ForecastType() == 1);
	BOOST_REQUIRE(reader.Message().ForecastTypeValue() == -999);

	reader.Message().ForecastType(2);
	BOOST_REQUIRE(reader.Message().ForecastType() == 1); // "analysis" is not set to grib1!

	init2();

	reader.Message().ForecastType(2);
	BOOST_REQUIRE(reader.Message().ForecastType() == 2); // "analysis" is set to grib2!

	reader.Message().ForecastType(4);
	BOOST_REQUIRE(reader.Message().ForecastType() == 4);

	reader.Message().ForecastTypeValue(50);
	BOOST_REQUIRE(reader.Message().ForecastTypeValue() == 50);

	initens();

	BOOST_REQUIRE(reader.Message().ForecastType() == 4);
	BOOST_REQUIRE(reader.Message().PerturbationNumber() == 0);
	BOOST_REQUIRE(reader.Message().ForecastTypeValue() == 0);
}

max_stars_repo_name: fmidev/fmigrib

max_issues_repo_name: fmidev/fmigrib

max_forks_repo_name: fmidev/fmigrib


text:
#include <boost/asio/dispatch.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <botan-2/botan/bcrypt.h>
#include <botan-2/botan/rng.h>
#include <botan-2/botan/system_rng.h>
#include <iomanip>

#include "core/id.hpp"
#include "core/log.hpp"
#include "web/constants/database.hpp"
#include "web/database.hpp"

Database::Database(boost::asio::io_context &context, const std::string &config)
    : mContext(context)
    , mStrand(context)
    , mConnection(config)
{}

void Database::asyncRequestWebToken(const std::string &email, const std::string &password, WebTokenRequestCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::requestWebToken, this, email, password, callback));
}

void Database::asyncValidateWebToken(const std::string &email, const WebToken &token, WebTokenValidationCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::validateWebToken, this, email, token, callback));
}

void Database::asyncRegisterUser(const std::string &email, const std::string &password, UserRegistrationCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::registerUser, this, email, password, callback));
}

void Database::asyncCheckWebName(int userId, const TournamentId &id, const std::string &webName, WebNameCheckCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::checkWebName, this, userId, id, webName, callback));
}

void Database::asyncRegisterWebName(int userId, const TournamentId &id, const std::string &webName, WebNameRegistrationCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::registerWebName, this, userId, id, webName, callback));
}

void Database::asyncSetSynced(const std::string &webName, SyncedSetCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::setSynced, this, webName, callback));
}

void Database::asyncSetSaveTime(const std::string &webName, std::chrono::system_clock::time_point time, SaveTimeSetCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::setSaveTime, this, webName, time, callback));
}

void Database::asyncGetSaveStatus(const std::string &webName, SaveStatusGetCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::getSaveStatus, this, webName, callback));
}

bool Database::hasUser(const std::string &email) {
    pqxx::work work(mConnection);
    pqxx::result r = work.exec("select 1 FROM users where email = " + work.quote(email));
    work.commit();

    return (r.size() > 0);
}

bool Database::checkPassword(const std::string &email, const std::string &password) {
    pqxx::work work(mConnection);
    pqxx::result r = work.exec("select password_hash FROM users where email = " + work.quote(email));
    work.commit();

    if (r.empty())
        return false;

    auto hash = r.front()[0].as<std::string>();
    return Botan::check_bcrypt(password, hash);
}

void Database::validateWebToken(const std::string &email, const WebToken &token, WebTokenValidationCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("select id, token_expiration FROM users where email = "
                                + work.quote(email)
                                + " and token = " + work.quote_raw(token.data(), token.size()));
        work.commit();

        if (r.size() == 0) {
            boost::asio::dispatch(mContext, std::bind(callback, WebTokenValidationResponse::INVALID_TOKEN, std::nullopt));
        }
        else {
            int userId = r[0][0].as<int>();
            std::string expiration = r[0][1].as<std::string>();

            if (!validateWebTokenExpiration(expiration))
                boost::asio::dispatch(mContext, std::bind(callback, WebTokenValidationResponse::EXPIRED_TOKEN, userId));
            else
                boost::asio::dispatch(mContext, std::bind(callback, WebTokenValidationResponse::SUCCESSFUL, userId));
        }
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, WebTokenValidationResponse::SERVER_ERROR, std::nullopt));
    }
}

void Database::registerUser(const std::string &email, const std::string &password, UserRegistrationCallback callback) {
    try {
        if (hasUser(email)) {
            callback(UserRegistrationResponse::EMAIL_EXISTS, std::nullopt, std::nullopt);
            return;
        }

        WebToken token = generateWebToken();
        std::string tokenExpiration = generateWebTokenExpiration();

        auto &rng = Botan::system_rng();
        std::string passwordHash = Botan::generate_bcrypt(password, rng);

        pqxx::work work(mConnection);
        pqxx::result r = work.exec("insert into users (email, password_hash, token, token_expiration) values ("
                                + work.quote(email)
                                + ", " + work.quote(passwordHash)
                                + ", " + work.quote_raw(token.data(), token.size())
                                + ", " + work.quote(tokenExpiration)
                                + ") returning id");
        work.commit();

        auto userId = r[0][0].as<int>();
        boost::asio::dispatch(mContext, std::bind(callback, UserRegistrationResponse::EMAIL_EXISTS, token, userId));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, UserRegistrationResponse::SERVER_ERROR, std::nullopt, std::nullopt));
    }
}

void Database::requestWebToken(const std::string &email, const std::string &password, WebTokenRequestCallback callback) {
    try {
        if (!checkPassword(email, password)) {
            boost::asio::dispatch(mContext, std::bind(callback, WebTokenRequestResponse::INCORRECT_CREDENTIALS, std::nullopt, std::nullopt));
            return;
        }

        auto token = generateWebToken();
        auto tokenExpiration = generateWebTokenExpiration();

        pqxx::work work(mConnection);
        pqxx::result r = work.exec("update users set token=" + work.quote_raw(token.data(), token.size())
                                + ", token_expiration=" + work.quote(tokenExpiration)
                                + " where email=" + work.quote(email)
                                + " returning id");
        work.commit();

        auto userId = r[0][0].as<int>();
        boost::asio::dispatch(mContext, std::bind(callback, WebTokenRequestResponse::SUCCESSFUL, token, userId));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, WebTokenRequestResponse::SERVER_ERROR, std::nullopt, std::nullopt));
    }
}

WebToken Database::generateWebToken() {
    WebToken res;
    Botan::system_rng().randomize(res.data(), res.size());

    return res;
}

void Database::checkWebName(int userId, const TournamentId &id, const std::string &webName, WebNameCheckCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("select owner, tournament_id FROM tournaments where web_name = "
                                + work.quote(webName));
        work.commit();

        if (r.empty())
            boost::asio::dispatch(mContext, std::bind(callback, WebNameCheckResponse::FREE));
        else if (r.front()[0].as<int>() != userId)
            boost::asio::dispatch(mContext, std::bind(callback, WebNameCheckResponse::OCCUPIED_OTHER_USER));
        else if (r.front()[1].as<TournamentId::InternalType>() != id.getValue())
            boost::asio::dispatch(mContext, std::bind(callback, WebNameCheckResponse::OCCUPIED_OTHER_TOURNAMENT));
        else
            boost::asio::dispatch(mContext, std::bind(callback, WebNameCheckResponse::OCCUPIED_SAME_TOURNAMENT));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, WebNameCheckResponse::SERVER_ERROR));
    }
}

void Database::registerWebName(int userId, const TournamentId &id, const std::string &webName, WebNameRegistrationCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("select owner, tournament_id FROM tournaments where web_name = "
                                + work.quote(webName));
        work.commit();

        if (!r.empty() && r.front()[0].as<int>() != userId) {
            boost::asio::dispatch(mContext, std::bind(callback, WebNameRegistrationResponse::OCCUPIED_OTHER_USER));
            return;
        }

        if (!r.empty() && r.front()[1].as<TournamentId::InternalType>() == id.getValue()) {
            boost::asio::dispatch(mContext, std::bind(callback, WebNameRegistrationResponse::SUCCESSFUL));
            return;
        }

        if (r.empty()) {
            pqxx::work work(mConnection);
            pqxx::result r = work.exec("insert into tournaments (owner, tournament_id, web_name) values (" + work.quote(userId)
                                        + ", " + work.quote(id.getValue())
                                        + ", " + work.quote(webName)
                                        + ")");
            work.commit();
        }
        else {
            pqxx::work work(mConnection);
            pqxx::result r = work.exec("update tournaments set synced=false, tournament_id=" + work.quote(id.getValue())
                                        + " WHERE web_name=" + work.quote(webName));
            work.commit();
        }

        boost::asio::dispatch(mContext, std::bind(callback, WebNameRegistrationResponse::SUCCESSFUL));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, WebNameRegistrationResponse::SERVER_ERROR));
    }
}

void Database::setSynced(const std::string &webName, SyncedSetCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("update tournaments set synced=true where web_name=" + work.quote(webName));
        work.commit();

        boost::asio::dispatch(mContext, std::bind(callback, (r.affected_rows() > 0)));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, false));
    }
}

std::string timeToString(std::chrono::system_clock::time_point time) {
    auto timeT = std::chrono::system_clock::to_time_t(time);

    std::stringstream ss;
    ss << std::put_time(std::localtime(&timeT), "%Y-%m-%d %H:%M:%S");
    return ss.str();
}

std::chrono::system_clock::time_point stringToTime(const std::string &str) {
    std::tm tm = {};
    std::stringstream ss(str);
    ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
    return std::chrono::system_clock::from_time_t(std::mktime(&tm));
}

void Database::setSaveTime(const std::string &webName, std::chrono::system_clock::time_point time, SaveTimeSetCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("update tournaments set save_time=" + work.quote(timeToString(time))
                                    + " where web_name=" + work.quote(webName));
        work.commit();

        boost::asio::dispatch(mContext, std::bind(callback, (r.affected_rows() > 0)));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, false));
    }
}

void Database::getSaveStatus(const std::string &webName, SaveStatusGetCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("select 1 FROM tournaments where web_name = " + work.quote(webName)
                                   + " and synced = true and save_time IS NOT NULL");
        work.commit();

        boost::asio::dispatch(mContext, std::bind(callback, !r.empty()));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, false));
    }
}

void Database::asyncUpdateTournament(const std::string &webName, const std::string &name, const std::string &location, const std::string &date, UpdateTournamentCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::updateTournament, this, webName, name, location, date, callback));
}

void Database::updateTournament(const std::string &webName, const std::string &name, const std::string &location, const std::string &date, UpdateTournamentCallback callback) {
    try {
        pqxx::work work(mConnection);
        pqxx::result r = work.exec("update tournaments set name=" + work.quote(name)
                                    + ", location=" + work.quote(location)
                                    + ", date=" + work.quote(date)
                                    + " where web_name=" + work.quote(webName));
        work.commit();

        boost::asio::dispatch(mContext, std::bind(callback, (r.affected_rows() > 0)));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, false));
    }
}

void Database::asyncListTournaments(ListTournamentsCallback callback) {
    boost::asio::dispatch(mStrand, std::bind(&Database::listTournaments, this, callback));
}

void Database::listTournaments(ListTournamentsCallback callback) {
    auto current_time = boost::posix_time::second_clock::universal_time();
    std::string current_date = boost::gregorian::to_iso_extended_string(current_time.date());

    std::vector<TournamentListing> upcomingTournaments;
    std::vector<TournamentListing> pastTournaments;

    try {
        pqxx::work work(mConnection);
        pqxx::result upcomingResult = work.exec("select web_name, name, location, date FROM tournaments where date >= "
                                + work.quote(current_date)
                                + " order by date asc limit 20");
        pqxx::result pastResult = work.exec("select web_name, name, location, date FROM tournaments where date < "
                                + work.quote(current_date)
                                + " order by date desc limit 20");
        work.commit();

        for (int i = 0; i < upcomingResult.size(); ++i) {
            TournamentListing listing;
            listing.webName = upcomingResult[i][0].as<std::string>();
            listing.name = upcomingResult[i][1].as<std::string>();
            listing.location = upcomingResult[i][2].as<std::string>();
            listing.date = upcomingResult[i][3].as<std::string>();
            upcomingTournaments.push_back(listing);
        }

        for (int i = 0; i < pastResult.size(); ++i) {
            TournamentListing listing;
            listing.webName = pastResult[i][0].as<std::string>();
            listing.name = pastResult[i][1].as<std::string>();
            listing.location = pastResult[i][2].as<std::string>();
            listing.date = pastResult[i][3].as<std::string>();
            pastTournaments.push_back(listing);
        }

        boost::asio::dispatch(mContext, std::bind(callback, true, std::move(pastTournaments), std::move(upcomingTournaments)));
    }
    catch (const std::exception &e) {
        log_error().field("what", e.what()).msg("PQXX exception caught");
        boost::asio::dispatch(mContext, std::bind(callback, false, std::move(pastTournaments), std::move(upcomingTournaments)));
    }
}

std::string Database::generateWebTokenExpiration() {
    auto time = boost::posix_time::second_clock::universal_time();
    return boost::posix_time::to_simple_string(time);
}

bool Database::validateWebTokenExpiration(const std::string &expiration) {
    auto time = boost::posix_time::time_from_string(expiration);
    auto max_time = boost::posix_time::second_clock::universal_time() + boost::gregorian::days(Constants::TOKEN_DURATION);

    return time <= max_time;
}

max_stars_repo_name: svendcsvendsen/judoassistant

max_issues_repo_name: svendcsvendsen/judoassistant

max_forks_repo_name: judoassistant/judoassistant


text:
// Boost.Geometry

// Copyright (c) 2018-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_LINE_INTERPOLATE_HPP
#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_LINE_INTERPOLATE_HPP

#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/coordinate_dimension.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/strategies/line_interpolate.hpp>
#include <boost/geometry/strategies/cartesian/distance_pythagoras.hpp>
#include <boost/geometry/util/algorithm.hpp>
#include <boost/geometry/util/select_calculation_type.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace line_interpolate
{

/*!
\brief Interpolate point on a cartesian segment.
\ingroup strategies
\tparam CalculationType \tparam_calculation
\tparam DistanceStrategy The underlying point-point distance strategy

\qbk{
[heading See also]
\* [link geometry.reference.algorithms.line_interpolate.line_interpolate_4_with_strategy line_interpolate (with strategy)]
}

*/
template
<
    typename CalculationType = void,
    typename DistanceStrategy = distance::pythagoras<CalculationType>
>
class cartesian
{
public:

    // point-point strategy getters
    struct distance_pp_strategy
    {
        typedef DistanceStrategy type;
    };

    inline typename distance_pp_strategy::type get_distance_pp_strategy() const
    {
        typedef typename distance_pp_strategy::type distance_type;
        return distance_type();
    }

    template <typename Point, typename Fraction, typename Distance>
    inline void apply(Point const& p0,
                      Point const& p1,
                      Fraction const& fraction,
                      Point & p,
                      Distance const&) const
    {
        typedef typename select_calculation_type_alt
            <
                CalculationType, Point
            >::type calc_t;
        typedef typename coordinate_type<Point>::type coord_t;

        //segment convex combination: p0*fraction + p1*(1-fraction)
        Fraction const one_minus_fraction = 1-fraction;
        geometry::detail::for_each_dimension<Point>([&](auto dimension)
        {
            // NOTE: numeric_cast is a leftover from convert, it could probably be ommited.
            // NOTE: the order of points is different than in the formula above
            //       this is also a leftover from the previous implementation
            calc_t coord0 = boost::numeric_cast<calc_t>(get<dimension>(p0));
            calc_t coord1 = boost::numeric_cast<calc_t>(get<dimension>(p1));
            calc_t result = calc_t(coord1 * fraction) + calc_t(coord0 * one_minus_fraction);
            set<dimension>(p, boost::numeric_cast<coord_t>(result));
        });
    }
};

#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
namespace services
{

template <>
struct default_strategy<cartesian_tag>
{
    typedef strategy::line_interpolate::cartesian<> type;
};

} // namespace services
#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS

}} // namespace strategy::line_interpolate

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_LINE_INTERPOLATE_HPP

max_stars_repo_name: pranavgo/RRT

max_issues_repo_name: pranavgo/RRT

max_forks_repo_name: pranavgo/RRT