<|start_filename|>tests/foldable/test_std_containers.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "test_helpers.h"

#include "cefal/everything.h"

#include "catch2/catch.hpp"

#include <deque>
#include <list>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>

using namespace cefal;

TEMPLATE_PRODUCT_TEST_CASE("ops::foldLeft()", "",
                           (std::vector, std::list, std::deque, std::set, std::unordered_set, std::multiset,
                            std::unordered_multiset),
                           (int)) {
    std::string result;
    std::string expected = "result=";
    auto folder = [](std::string&& s, int x) {
        s += std::to_string(x);
        return std::move(s);
    };
    for (auto&& x : TestType{1, 2, 3})
        expected += std::to_string(x);
    SECTION("Lvalue") {
        const auto left = TestType{1, 2, 3};
        SECTION("Pipe") { result = left | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(left); }
    }
    SECTION("Rvalue") {
        auto left = TestType{1, 2, 3};
        SECTION("Pipe") { result = std::move(left) | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(std::move(left)); }
    }

    CHECK(result == expected);
}

TEMPLATE_PRODUCT_TEST_CASE("ops::foldLeft()", "",
                           (std::vector, std::list, std::deque, std::set, std::unordered_set, std::multiset,
                            std::unordered_multiset),
                           (std::string)) {
    std::string result;
    std::string expected = "result=";
    for (auto&& x : TestType{"abc", "de", "f"})
        expected += x;
    SECTION("Lvalue") {
        auto folder = [](std::string&& s, const std::string& x) { return std::move(s) + x; };
        const auto left = TestType{"abc", "de", "f"};
        SECTION("Pipe") { result = left | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(left); }
    }
    SECTION("Rvalue") {
        auto folder = [](std::string&& s, std::string&& x) { return std::move(s) + std::move(x); };
        auto left = TestType{"abc", "de", "f"};
        SECTION("Pipe") { result = std::move(left) | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(std::move(left)); }
    }

    CHECK(result == expected);
}

TEMPLATE_PRODUCT_TEST_CASE("ops::foldLeft()", "", (std::map, std::unordered_map, std::multimap, std::unordered_multimap),
                           ((std::string, int))) {
    std::string result;
    std::string expected = "result=";
    for (auto&& x : TestType{{"abc", 1}, {"de", 2}, {"f", 3}})
        expected += x.first + std::string(' ', x.second);
    SECTION("Lvalue") {
        auto folder = [](std::string&& s, const std::pair<std::string, int>& x) {
            return std::move(s) + x.first + std::string(' ', x.second);
        };
        const auto left = TestType{{"abc", 1}, {"de", 2}, {"f", 3}};
        SECTION("Pipe") { result = left | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(left); }
    }
    SECTION("Rvalue") {
        auto folder = [](std::string&& s, std::pair<std::string, int>&& x) {
            return std::move(s) + std::move(x.first) + std::string(' ', x.second);
        };
        auto left = TestType{{"abc", 1}, {"de", 2}, {"f", 3}};
        SECTION("Pipe") { result = std::move(left) | ops::foldLeft(std::string("result="), folder); }
        SECTION("Curried") { result = ops::foldLeft(std::string("result="), folder)(std::move(left)); }
    }

    CHECK(result == expected);
}


<|start_filename|>include/cefal/instances/converter/from_std_containers.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/detail/std_concepts.h"

#include "cefal/common.h"
#include "cefal/converter.h"
#include "cefal/helpers/std_ranges.h"

#include <numeric>
#include <ranges>
#include <type_traits>

namespace cefal::instances {
namespace detail {

template <typename Dest, std::ranges::sized_range Src>
requires cefal::detail::TransferableSize<Src, Dest> void prepareConvertFromRangeDestination(const Src& src, Dest& dest) {
    dest.reserve(src.size());
}

template <typename Dest, typename Src>
void prepareConvertFromRangeDestination(const Src& src, Dest& dest) {
}

template <cefal::detail::StdContainer Dest, typename Src>
Dest createConvertFromRangeDestination(const Src& src) {
    Dest dest;
    prepareConvertFromRangeDestination(src, dest);
    return dest;
}

template <cefal::detail::SetLikeContainer Dest, typename T>
void addToConvertFromRangeDestination(Dest& dest, T&& x) {
    dest.insert(std::forward<T>(x));
}

template <cefal::detail::VectorLikeContainer Dest, typename T>
void addToConvertFromRangeDestination(Dest& dest, T&& x) {
    dest.push_back(std::forward<T>(x));
}

template <cefal::detail::DoubleSocketedStdContainer Dest, typename K, typename V>
void addToConvertFromRangeDestination(Dest& dest, const std::pair<K, V>& x) {
    dest.insert(x);
}
template <cefal::detail::DoubleSocketedStdContainer Dest, typename K, typename V>
void addToConvertFromRangeDestination(Dest& dest, std::pair<K, V>&& x) {
    dest.insert(std::move(x));
}
template <cefal::detail::DoubleSocketedStdContainer Dest, typename K, typename V>
void addToConvertFromRangeDestination(Dest& dest, const std::tuple<K, V>& x) {
    dest.emplace(std::get<0>(x), std::get<1>(x));
}
template <cefal::detail::DoubleSocketedStdContainer Dest, typename K, typename V>
void addToConvertFromRangeDestination(Dest& dest, std::tuple<K, V>&& x) {
    dest.emplace(std::move(std::get<0>(x)), std::move(std::get<1>(x)));
}
} // namespace detail

template <std::ranges::range Src, cefal::detail::StdContainer Dest>
struct Converter<Src, Dest> {
    template <typename = void>
    requires cefal::detail::VectorLikeContainer<Src> static auto convert(Src&& src) {
        Dest dest = detail::createConvertFromRangeDestination<Dest>(src);
        for (auto&& x : src)
            detail::addToConvertFromRangeDestination(dest, std::move(x));
        return dest;
    }

    template <typename = void>
    requires cefal::detail::SetLikeContainer<Src> static auto convert(Src&& src) {
        using InnerT = std::ranges::range_value_t<Src>;
        Dest dest = detail::createConvertFromRangeDestination<Dest>(src);
        if constexpr (std::is_trivial_v<InnerT> && sizeof(InnerT) <= 8) {
            for (auto x : src)
                detail::addToConvertFromRangeDestination(dest, std::move(x));
        } else {
            while (!src.empty())
                detail::addToConvertFromRangeDestination(dest, std::move(src.extract(src.begin()).value()));
        }
        return dest;
    }

    template <typename = void>
    requires cefal::detail::DoubleSocketedStdContainer<Src> static auto convert(Src&& src) {
        using InnerT = std::ranges::range_value_t<Src>;
        using KeyT = std::remove_cvref_t<std::tuple_element_t<0, InnerT>>;
        Dest dest = detail::createConvertFromRangeDestination<Dest>(src);
        if constexpr (std::is_trivial_v<KeyT> && sizeof(KeyT) <= 8) {
            for (auto& x : src) {
                KeyT key = x.first;
                detail::addToConvertFromRangeDestination(dest, std::make_pair(key, std::move(x.second)));
            }
        } else {
            while (!src.empty()) {
                auto node = src.extract(src.begin());
                detail::addToConvertFromRangeDestination(dest, std::make_pair(std::move(node.key()), std::move(node.mapped())));
            }
        }
        return dest;
    }

    static auto convert(const Src& src) {
        Dest dest = detail::createConvertFromRangeDestination<Dest>(src);
        for (const auto& x : src)
            detail::addToConvertFromRangeDestination(dest, x);
        return dest;
    }
};

} // namespace cefal::instances


<|start_filename|>benchmarks/helpers/expensive.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include <algorithm>
#include <unordered_set>

template <typename T>
struct Expensive {
    Expensive(T x = T()) : value(std::move(x)), payload(new char[102400]) {}
    Expensive(T x, char* payload) : value(std::move(x)), payload(payload) {}
    Expensive(const Expensive& other) {
        payload = new char[102400];
        value = other.value;
    }
    Expensive(Expensive&& other) {
        payload = other.payload;
        value = std::move(other.value);
        other.payload = nullptr;
    }
    Expensive& operator=(const Expensive& other) {
        delete[] payload;
        payload = new char[102400];
        value = other.value;
        return *this;
    }
    Expensive& operator=(Expensive&& other) {
        std::swap(payload, other.payload);
        value = std::move(other.value);
        return *this;
    }
    ~Expensive() { delete[] payload; }

    operator T() const { return value; }

    template <typename U>
    bool operator%(U&& other) const {
        return value % other;
    }

    template <typename U>
    Expensive<std::remove_cvref_t<U>> operator+(U&& other) const& {
        return Expensive<std::remove_cvref_t<U>>(value + other);
    }

    template <typename U>
    Expensive<std::remove_cvref_t<U>> operator+(U&& other) && {
        auto result = Expensive<std::remove_cvref_t<U>>(value + other, payload);
        payload = nullptr;
        return result;
    }

    Expensive& operator+=(T other) {
        value += other;
        return *this;
    }

    auto operator<=>(const Expensive<T>& other) const { return value <=> other.value; }
    bool operator==(const Expensive<T>& other) const { return std::abs(value - other.value) < 0.0001; }

    T value = T();
    char* payload = nullptr;
};

namespace std {
template <typename T>
struct hash<Expensive<T>> {
    size_t operator()(const Expensive<T>& x) const { return std::hash<T>()(x.value); }
};
} // namespace std


<|start_filename|>tests/functor/test_from_foldable.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "counter.h"

#include "cefal/everything.h"

#include "catch2/catch.hpp"

#include <deque>
#include <list>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>

using namespace cefal;

template <typename T>
struct TemplatedWithFunctions : public Counter {
    TemplatedWithFunctions() : Counter() {}
    TemplatedWithFunctions(T value) : Counter(), value(value) {}
    T value = T();

    static TemplatedWithFunctions empty() {
        addCustom("empty");
        return TemplatedWithFunctions();
    }
    TemplatedWithFunctions append(const TemplatedWithFunctions& other) const& {
        addCustom("lvalue_append");
        return TemplatedWithFunctions(value + other.value);
    }
    TemplatedWithFunctions append(TemplatedWithFunctions&& other) && {
        addCustom("rvalue_append");
        value += other.value;
        return std::move(*this);
    }

    template <typename Result, typename Func>
    Result foldLeft(Result&& init, Func&& f) const& {
        addCustom("lvalue_foldLeft");
        return f(std::move(init), value);
    }
    template <typename Result, typename Func>
    Result foldLeft(Result&& init, Func&& f) && {
        addCustom("rvalue_foldLeft");
        return f(std::move(init), std::move(value));
    }
};

TEMPLATE_PRODUCT_TEST_CASE("ops::unit()", "",
                           (std::vector, std::list, std::deque, std::set, std::unordered_set, std::multiset,
                            std::unordered_multiset),
                           (int)) {
    TestType result = ops::unit<TestType>(42);
    REQUIRE(result.size() == 1);
    CHECK(*result.begin() == 42);
}

TEMPLATE_PRODUCT_TEST_CASE("ops::unit()", "", (std::map, std::unordered_map, std::multimap, std::unordered_multimap),
                           ((std::string, int))) {
    TestType result;
    SECTION("pair") { result = ops::unit<TestType>(std::make_pair(std::string("abc"), 42)); }
    SECTION("tuple") { result = ops::unit<TestType>(std::make_tuple(std::string("abc"), 42)); }
    REQUIRE(result.size() == 1);
    CHECK(result.begin()->first == "abc");
    CHECK(result.begin()->second == 42);
}

TEST_CASE("ops::unit() - TemplatedWithFunctions<int>") {
    auto result = ops::unit<TemplatedWithFunctions>(42);
    CHECK(result.value == 42);
}

TEMPLATE_PRODUCT_TEST_CASE("ops::map()", "",
                           (std::vector, std::list, std::deque, std::set, std::unordered_set, std::multiset,
                            std::unordered_multiset),
                           (std::string)) {
    WithInnerType_T<TestType, int> result;
    SECTION("Lvalue") {
        auto func = [](const std::string& s) { return std::stoi(s); };
        const auto left = TestType{"1", "2", "3"};
        SECTION("Pipe") { result = left | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(left); }
    }
    SECTION("Rvalue") {
        auto func = [](std::string&& s) { return std::stoi(std::move(s)); };
        auto left = TestType{"1", "2", "3"};
        SECTION("Pipe") { result = std::move(left) | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(std::move(left)); }
    }

    CHECK(result == WithInnerType_T<TestType, int>{1, 2, 3});
}

TEMPLATE_PRODUCT_TEST_CASE("ops::map()", "", (std::map, std::unordered_map, std::multimap, std::unordered_multimap),
                           ((std::string, int))) {
    WithInnerType_T<TestType, std::pair<int, std::string>> result;
    SECTION("Lvalue") {
        auto func = [](const std::pair<std::string, int>& x) { return make_pair(x.second, x.first); };
        const auto left = TestType{{"abc", 1}, {"de", 2}, {"f", 3}};
        SECTION("Pipe") { result = left | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(left); }
    }
    SECTION("Rvalue") {
        auto func = [](std::pair<std::string, int>&& x) { return make_pair(x.second, std::move(x.first)); };
        auto left = TestType{{"abc", 1}, {"de", 2}, {"f", 3}};
        SECTION("Pipe") { result = std::move(left) | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(std::move(left)); }
    }

    CHECK(result == WithInnerType_T<TestType, std::pair<int, std::string>>{{1, "abc"}, {2, "de"}, {3, "f"}});
}

TEST_CASE("ops::map() - TemplatedWithFunctions<std::string>") {
    TemplatedWithFunctions<int> result;
    SECTION("Lvalue") {
        auto func = [](const std::string& s) { return std::stoi(s); };
        const auto left = TemplatedWithFunctions<std::string>("3");
        SECTION("Pipe") { result = left | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(left); }
    }
    SECTION("Rvalue") {
        auto func = [](std::string&& s) { return std::stoi(std::move(s)); };
        auto left = TemplatedWithFunctions<std::string>("3");
        SECTION("Pipe") { result = std::move(left) | ops::map(func); }
        SECTION("Curried") { result = ops::map(func)(std::move(left)); }
    }

    CHECK(result.value == 3);
}


<|start_filename|>tests/helpers/counter.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include <atomic>
#include <mutex>
#include <string>
#include <unordered_map>

class Counter {
public:
    Counter() noexcept { _created++; }
    Counter(const Counter&) noexcept { _copied++; }
    Counter(Counter&&) noexcept { _moved++; }
    Counter& operator=(const Counter&) noexcept {
        _copied++;
        return *this;
    }
    Counter& operator=(Counter&&) noexcept {
        _moved++;
        return *this;
    }
    ~Counter() noexcept { _deleted++; }

    auto operator<=>(const Counter&) const = default;

    static uint64_t created() { return _created; }
    static uint64_t copied() { return _copied; }
    static uint64_t moved() { return _moved; }
    static uint64_t deleted() { return _deleted; }
    static uint64_t customCount() {
        std::unique_lock<std::mutex> lock(customMutex);
        return _custom.size();
    }
    static uint64_t custom(const std::string& name) {
        std::unique_lock<std::mutex> lock(customMutex);
        return _custom[name];
    }

    static void addCustom(const std::string& name) {
        std::unique_lock<std::mutex> lock(customMutex);
        ++_custom[name];
    }

    static void resetCreated() { _created = 0; }
    static void resetCopied() { _copied = 0; }
    static void resetMoved() { _moved = 0; }
    static void resetDeleted() { _deleted = 0; }
    static void resetCustom(const std::string& name) {
        std::unique_lock<std::mutex> lock(customMutex);
        _custom[name] = 0;
    }
    static void reset() {
        resetCreated();
        resetCopied();
        resetMoved();
        resetDeleted();
        std::unique_lock<std::mutex> lock(customMutex);
        _custom.clear();
    }

private:
    static std::atomic_uint64_t _created;
    static std::atomic_uint64_t _copied;
    static std::atomic_uint64_t _moved;
    static std::atomic_uint64_t _deleted;

    static std::mutex customMutex;
    static std::unordered_map<std::string, uint64_t> _custom;
};

struct CountedValue : public Counter {
    CountedValue() : Counter() {}
    CountedValue(int value) : Counter(), value(value) {}
    CountedValue(const CountedValue&) noexcept = default;
    CountedValue(CountedValue&&) noexcept = default;
    CountedValue& operator=(const CountedValue&) noexcept = default;
    CountedValue& operator=(CountedValue&&) noexcept = default;
    int value = 0;
    int64_t u1 = 0;
    int64_t u2 = 0;
    auto operator<=>(const CountedValue&) const = default;
};

inline std::ostream& operator<<(std::ostream& os, const CountedValue& value) {
    os << std::to_string(value.value);
    return os;
}
inline std::ostream& operator<<(std::ostream& os, const std::pair<int, CountedValue>& value) {
    os << std::to_string(value.first) << std::string(":") << value.second;
    return os;
}
inline std::ostream& operator<<(std::ostream& os, const std::pair<CountedValue, CountedValue>& value) {
    os << value.first << std::string(":") << value.second;
    return os;
}

namespace std {
template <>
struct hash<CountedValue> {
    std::size_t operator()(const CountedValue& x) const noexcept { return std::hash<int>{}(x.value); }
};
template <>
struct hash<pair<int, CountedValue>> {
    std::size_t operator()(const pair<int, CountedValue>& x) const noexcept {
        return std::hash<int>{}(x.first) ^ (std::hash<CountedValue>{}(x.second) << 1);
    }
};
template <>
struct hash<pair<CountedValue, CountedValue>> {
    std::size_t operator()(const pair<CountedValue, CountedValue>& x) const noexcept {
        return std::hash<CountedValue>{}(x.first) ^ (std::hash<CountedValue>{}(x.second) << 1);
    }
};
} // namespace std


<|start_filename|>tests/filterable/test_with_functions.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "counter.h"

#include "cefal/everything.h"

#include "catch2/catch.hpp"

#include <string>

using namespace cefal;

struct WithFunctions : public Counter {
    WithFunctions(int value) : Counter(), value(value) {}
    int value = 0;
    template <typename Func>
    auto filter(Func&& f) const& {
        addCustom("lvalue_filter");
        if (f(value))
            return WithFunctions(value);
        return WithFunctions(0);
    }
    template <typename Func>
    auto filter(Func&& f) && {
        addCustom("rvalue_filter");
        if (f(value))
            return WithFunctions(std::move(value));
        return WithFunctions(0);
    }
};

namespace cefal {
template <>
struct InnerType<WithFunctions> {
    using type = int;
};
template <typename T>
struct WithInnerType<WithFunctions, T> {
    using type = WithFunctions;
};
} // namespace cefal

template <typename T>
struct TemplatedWithFunctions : public Counter {
    TemplatedWithFunctions(T value) : Counter(), value(value) {}
    T value = 0;
    template <typename Func>
    auto filter(Func&& f) const& {
        addCustom("lvalue_filter");
        if (f(value))
            return TemplatedWithFunctions(value);
        return TemplatedWithFunctions(T());
    }
    template <typename Func>
    auto filter(Func&& f) && {
        addCustom("rvalue_filter");
        if (f(value))
            return TemplatedWithFunctions(std::move(value));
        return TemplatedWithFunctions(T());
    }
};

TEMPLATE_TEST_CASE("ops::filter() - RValue", "", WithFunctions, TemplatedWithFunctions<int>) {
    Counter::reset();
    auto func = [](int x) -> bool { return true; };
    SECTION("Pipe") {
        TestType result = TestType(4) | ops::filter(func);
        CHECK(result.value == 4);
    }
    SECTION("Curried") {
        TestType result = ops::filter(func)(TestType(4));
        CHECK(result.value == 4);
    }
    CHECK(Counter::created() == 2);
    CHECK(Counter::copied() == 0);
    CHECK(Counter::moved() == 0);
    CHECK(Counter::customCount() == 1);
    CHECK(Counter::custom("rvalue_filter") == 1);
}

TEMPLATE_TEST_CASE("ops::filter() - LValue", "", WithFunctions, TemplatedWithFunctions<int>) {
    Counter::reset();
    const auto a = TestType(4);
    auto func = [](int x) -> bool { return true; };
    SECTION("Pipe") {
        TestType result = a | ops::filter(func);
        CHECK(result.value == 4);
    }
    SECTION("Curried") {
        TestType result = ops::filter(func)(a);
        CHECK(result.value == 4);
    }
    CHECK(Counter::created() == 2);
    CHECK(Counter::copied() == 0);
    CHECK(Counter::moved() == 0);
    CHECK(Counter::customCount() == 1);
    CHECK(Counter::custom("lvalue_filter") == 1);
}


<|start_filename|>tests/filterable/test_std_ranges.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "ranges_helpers.h"

#include "cefal/everything.h"

#include "catch2/catch.hpp"

#include <ranges>
#include <set>
#include <string>
#include <vector>

using namespace cefal;

template <typename... T>
struct FromVector {
    constexpr static bool hasBase = true;
    std::vector<std::string> base = {"1", "2", "3"};
    std::vector<std::string> result = HasOp<Reverse, T...>::value ? std::vector<std::string>{"3", "1"}
                                                                  : std::vector<std::string>{"1", "3"};
    auto get() { return (std::views::all(base) | ... | T()); }
};

template <typename... T>
struct FromSet {
    constexpr static bool hasBase = true;
    std::set<std::string> base = {"1", "2", "3"};
    std::vector<std::string> result = HasOp<Reverse, T...>::value ? std::vector<std::string>{"3", "1"}
                                                                  : std::vector<std::string>{"1", "3"};
    auto get() { return (std::views::all(base) | ... | T()); }
};

template <typename... T>
struct FromSingle {
    constexpr static bool hasBase = false;
    std::vector<std::string> result = {"13"};
    auto get() { return (std::views::single(std::string("13")) | ... | T()); }
};

// TODO: join_view cases weird compilation error which could be ranges implementation bug
// Check after gcc release
TEMPLATE_PRODUCT_TEST_CASE("ops::filter()", "", (FromVector, FromSet, FromSingle),
                           ((Transform), (Filter), (Take), (TakeWhile), (Drop), (DropWhile), (Reverse), //(SplitAndJoin),
                            (Transform, Filter, Take, TakeWhile), (Drop, Filter, Take, TakeWhile))) {
    TestType tester;
    std::vector<std::string> result;
    auto func = [](const std::string& x) -> bool { return std::stoi(x) % 2; };
    auto left = tester.get();

    SECTION("Lvalue") {
        SECTION("Pipe") { result = materialize(left | ops::filter(func)); }
        SECTION("Curried") { result = materialize(ops::filter(func)(left)); }
    }
    SECTION("Rvalue") {
        SECTION("Pipe") { result = materialize(std::move(left) | ops::filter(func)); }
        SECTION("Curried") { result = materialize(ops::filter(func)(std::move(left))); }
    }

    if constexpr (TestType::hasBase) {
        CHECK(tester.base.size() == 3);
        CHECK(!tester.base.begin()->empty());
    }
    CHECK(result == tester.result);
}


<|start_filename|>tests/helpers/ranges_helpers.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#pragma once

#include "cefal/helpers/std_ranges.h"

#include <ranges>
#include <vector>

struct Transform {
    template <typename T>
    friend auto operator|(T&& prev, Transform) {
        return std::forward<T>(prev) | std::views::transform([](auto x) { return x; });
    }
};
struct Filter {
    template <typename T>
    friend auto operator|(T&& prev, Filter) {
        return std::forward<T>(prev) | std::views::filter([](auto) { return true; });
    }
};
struct Take {
    template <typename T>
    friend auto operator|(T&& prev, Take) {
        return std::forward<T>(prev) | std::views::take(10);
    }
};
struct TakeWhile {
    template <typename T>
    friend auto operator|(T&& prev, TakeWhile) {
        return std::forward<T>(prev) | std::views::take_while([](auto) { return true; });
    }
};
struct Drop {
    template <typename T>
    friend auto operator|(T&& prev, Drop) {
        return std::forward<T>(prev) | std::views::drop(0);
    }
};
struct DropWhile {
    template <typename T>
    friend auto operator|(T&& prev, DropWhile) {
        return std::forward<T>(prev) | std::views::drop_while([](auto) { return false; });
    }
};
struct SplitAndJoin {
    template <typename T>
    friend auto operator|(T&& prev, SplitAndJoin) {
        return std::views::join(std::forward<T>(prev) | std::views::split(std::string()));
    }
};
struct Reverse {
    template <typename T>
    friend auto operator|(T&& prev, Reverse) {
        return std::views::reverse(std::forward<T>(prev));
    }
};

template <typename... T>
struct HasOp;

template <typename Op, typename... Ts>
struct HasOp<Op, Op, Ts...> : std::true_type {};

template <typename Op, typename Head, typename... Ts>
struct HasOp<Op, Head, Ts...> : HasOp<Op, Ts...> {};

template <typename Op>
struct HasOp<Op> : std::false_type {};

template <std::ranges::view V>
auto materialize(V&& view) {
    std::vector<cefal::InnerType_T<V>> result;
    for (auto&& x : view)
        result.push_back(x);
    return result;
}


<|start_filename|>include/cefal/instances/monoid/std_containers.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/detail/std_concepts.h"

#include "cefal/common.h"
#include "cefal/monoid.h"

#include <type_traits>

namespace cefal {
namespace helpers {
template <cefal::detail::SingleSocketedStdContainer Src>
struct SingletonFrom<Src> {
    using exists = void;
    typename Src::value_type value;
};

template <cefal::detail::DoubleSocketedStdContainer Src>
struct SingletonFrom<Src> {
    using exists = void;
    SingletonFrom(const InnerType_T<Src>& x) : key(x.first), value(x.second) {}
    SingletonFrom(InnerType_T<Src>&& x) : key(std::move(x.first)), value(std::move(x.second)) {}
    typename Src::key_type key;
    typename Src::mapped_type value;
};
} // namespace helpers

namespace instances {
namespace detail {
template <cefal::detail::Reservable C>
void reserveContainer(C& c, size_t size) {
    c.reserve(size);
}
template <typename C>
void reserveContainer(C& c, size_t size) {
}
} // namespace detail

template <cefal::detail::VectorLikeContainer Src>
struct Monoid<Src> {
    static Src empty() { return Src(); }

    static Src append(const Src& left, const Src& right) {
        if (!left.size())
            return right;
        if (!right.size())
            return left;
        Src result;
        detail::reserveContainer(result, left.size() + right.size());
        result.insert(result.end(), left.begin(), left.end());
        result.insert(result.end(), right.begin(), right.end());
        return result;
    }

    static Src append(Src&& left, const Src& right) {
        if (!left.size())
            return right;
        if (!right.size())
            return std::move(left);
        detail::reserveContainer(left, left.size() + right.size());
        left.insert(left.end(), right.begin(), right.end());
        return std::move(left);
    }

    static Src append(const Src& left, Src&& right) {
        if (!left.size())
            return std::move(right);
        if (!right.size())
            return left;
        detail::reserveContainer(right, left.size() + right.size());
        right.insert(right.begin(), left.begin(), left.end());
        return std::move(right);
    }

    static Src append(Src&& left, Src&& right) {
        if (!left.size())
            return std::move(right);
        if (!right.size())
            return std::move(left);
        if (left.size() < right.size()) {
            detail::reserveContainer(right, left.size() + right.size());
            right.insert(right.begin(), left.begin(), left.end());
            return std::move(right);
        } else {
            detail::reserveContainer(left, left.size() + right.size());
            left.insert(left.end(), right.begin(), right.end());
            return std::move(left);
        }
    }

    template <typename T>
    static Src append(T&& left, helpers::SingletonFrom<Src>&& right) {
        static_assert(std::is_same_v<std::remove_cvref_t<T>, Src>, "Argument type should be the same as monoid");
        Src result = std::forward<T>(left);
        result.push_back(std::move(right.value));
        return result;
    }
};

template <cefal::detail::SetLikeContainer Src>
struct Monoid<Src> {
    static Src empty() { return Src(); }

    template <typename T1, typename T2>
    static Src append(T1&& left, T2&& right) {
        static_assert(std::is_same_v<std::remove_cvref_t<T1>, Src>, "Argument type should be the same as monoid");
        static_assert(std::is_same_v<std::remove_cvref_t<T2>, Src>, "Argument type should be the same as monoid");
        Src result = std::forward<T1>(left);
        result.merge(std::forward<T2>(right));
        return result;
    }

    template <typename T>
    static Src append(T&& left, helpers::SingletonFrom<Src>&& right) {
        static_assert(std::is_same_v<std::remove_cvref_t<T>, Src>, "Argument type should be the same as monoid");
        Src result = std::forward<T>(left);
        result.insert(std::move(right.value));
        return result;
    }
};

template <cefal::detail::DoubleSocketedStdContainer Src>
struct Monoid<Src> {
    static Src empty() { return Src(); }

    template <typename T1, typename T2>
    static Src append(T1&& left, T2&& right) {
        static_assert(std::is_same_v<std::remove_cvref_t<T1>, Src>, "Argument type should be the same as monoid");
        static_assert(std::is_same_v<std::remove_cvref_t<T2>, Src>, "Argument type should be the same as monoid");
        Src result = std::forward<T1>(left);
        result.merge(std::forward<T2>(right));
        return result;
    }

    template <typename T>
    static Src append(T&& left, helpers::SingletonFrom<Src>&& right) {
        static_assert(std::is_same_v<std::remove_cvref_t<T>, Src>, "Argument type should be the same as monoid");
        Src result = std::forward<T>(left);
        result.emplace(std::move(right.key), std::move(right.value));
        return result;
    }
};
} // namespace instances
} // namespace cefal


<|start_filename|>tests/converter/test_from_std_containers.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "counter.h"
#include "test_helpers.h"

#include "cefal/everything.h"

#include "catch2/catch.hpp"

#include <deque>
#include <list>
#include <set>
#include <string>
#include <tuple>
#include <unordered_set>
#include <vector>

template <typename Src, typename Dest>
struct PrepareResult {
    static constexpr Dest prepare(Dest dest) { return std::move(dest); }
};
template <typename T, std::ranges::random_access_range Dest>
struct PrepareResult<std::unordered_set<T>, Dest> {
    static constexpr Dest prepare(Dest dest) {
        std::ranges::sort(dest);
        return std::move(dest);
    }
};
template <typename T, std::ranges::random_access_range Dest>
struct PrepareResult<std::unordered_multiset<T>, Dest> {
    static constexpr Dest prepare(Dest dest) {
        std::ranges::sort(dest);
        return std::move(dest);
    }
};
template <typename K, typename V, std::ranges::random_access_range Dest>
struct PrepareResult<std::unordered_map<K, V>, Dest> {
    static constexpr Dest prepare(Dest dest) {
        std::ranges::sort(dest);
        return std::move(dest);
    }
};
template <typename K, typename V, std::ranges::random_access_range Dest>
struct PrepareResult<std::unordered_multimap<K, V>, Dest> {
    static constexpr Dest prepare(Dest dest) {
        std::ranges::sort(dest);
        return std::move(dest);
    }
};

template <typename T, typename U>
struct PrepareResult<std::unordered_set<T>, std::list<U>> {
    static constexpr std::list<U> prepare(std::list<U> dest) {
        std::vector<U> tmp(dest.begin(), dest.end());
        std::ranges::sort(tmp);
        return std::list<U>(tmp.begin(), tmp.end());
    }
};
template <typename T, typename U>
struct PrepareResult<std::unordered_multiset<T>, std::list<U>> {
    static constexpr std::list<U> prepare(std::list<U> dest) {
        std::vector<U> tmp(dest.begin(), dest.end());
        std::ranges::sort(tmp);
        return std::list<U>(tmp.begin(), tmp.end());
    }
};
template <typename K, typename V, typename U>
struct PrepareResult<std::unordered_map<K, V>, std::list<U>> {
    static constexpr std::list<U> prepare(std::list<U> dest) {
        std::vector<U> tmp(dest.begin(), dest.end());
        std::ranges::sort(tmp);
        return std::list<U>(tmp.begin(), tmp.end());
    }
};
template <typename K, typename V, typename U>
struct PrepareResult<std::unordered_multimap<K, V>, std::list<U>> {
    static constexpr std::list<U> prepare(std::list<U> dest) {
        std::vector<U> tmp(dest.begin(), dest.end());
        std::ranges::sort(tmp);
        return std::list<U>(tmp.begin(), tmp.end());
    }
};

template <typename...>
struct TypesProduct;

template <typename... Ts, typename U, typename... Us>
struct TypesProduct<std::tuple<Ts...>, std::tuple<U, Us...>> {
    using type = decltype(
        std::tuple_cat(std::tuple<std::tuple<Ts, U>...>(), typename TypesProduct<std::tuple<Ts...>, std::tuple<Us...>>::type()));
};

template <typename... Ts>
struct TypesProduct<std::tuple<Ts...>, std::tuple<>> {
    using type = std::tuple<>;
};

using SingleSocketContainers =
    std::tuple<std::vector<CountedValue>, std::list<CountedValue>, std::deque<CountedValue>, std::set<CountedValue>,
               std::unordered_set<CountedValue>, std::multiset<CountedValue>, std::unordered_multiset<CountedValue>>;
using DoubleSocketContainers = std::tuple<std::map<int, CountedValue>, std::unordered_map<int, CountedValue>,
                                          std::multimap<int, CountedValue>, std::unordered_multimap<int, CountedValue>>;
using HeavyDoubleSocketContainers =
    std::tuple<std::map<CountedValue, CountedValue>, std::unordered_map<CountedValue, CountedValue>,
               std::multimap<CountedValue, CountedValue>, std::unordered_multimap<CountedValue, CountedValue>>;

using SingleToSingle = typename TypesProduct<SingleSocketContainers, SingleSocketContainers>::type;
using SingleToDouble = typename TypesProduct<SingleSocketContainers, DoubleSocketContainers>::type;
using DoubleToSingle = typename TypesProduct<DoubleSocketContainers, SingleSocketContainers>::type;
using DoubleToDouble = typename TypesProduct<DoubleSocketContainers, DoubleSocketContainers>::type;
using HeavyDoubleToSingle = typename TypesProduct<HeavyDoubleSocketContainers, SingleSocketContainers>::type;
using HeavyDoubleToDouble = typename TypesProduct<HeavyDoubleSocketContainers, HeavyDoubleSocketContainers>::type;

using namespace cefal;

TEMPLATE_LIST_TEST_CASE("ops::as()", "", SingleToSingle) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = std::tuple_element_t<1, TestType>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{CountedValue(1), CountedValue(2), CountedValue(3)};
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    SECTION("Rvalue") {
        auto left = Src{CountedValue(1), CountedValue(2), CountedValue(3)};
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 3);
    }
    result = PrepareResult<Src, Dest>::prepare(std::move(result));
    CHECK(result == Dest{CountedValue(1), CountedValue(2), CountedValue(3)});
}

TEMPLATE_LIST_TEST_CASE("ops::as()", "", DoubleToDouble) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = std::tuple_element_t<1, TestType>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    SECTION("Rvalue") {
        auto left = Src{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 6);
    }
    CHECK(result == Dest{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as()", "", SingleToDouble) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = std::tuple_element_t<1, TestType>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{CountedValue(1), CountedValue(2), CountedValue(3)}
                          | ops::map([](CountedValue&& x) { return std::make_pair(x.value, x); });
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    SECTION("Rvalue") {
        auto left = Src{CountedValue(1), CountedValue(2), CountedValue(3)}
                    | ops::map([](CountedValue&& x) { return std::make_pair(x.value, x); });
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 3);
    }
    CHECK(result == Dest{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as()", "", DoubleToSingle) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = WithInnerType_T<std::tuple_element_t<1, TestType>, std::pair<int, CountedValue>>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    SECTION("Rvalue") {
        auto left = Src{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 6);
    }
    result = PrepareResult<Src, Dest>::prepare(std::move(result));
    CHECK(result == Dest{{1, CountedValue(1)}, {2, CountedValue(2)}, {3, CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as()", "", HeavyDoubleToDouble) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = std::tuple_element_t<1, TestType>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{{CountedValue(11), CountedValue(1)},
                              {CountedValue(12), CountedValue(2)},
                              {CountedValue(13), CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    SECTION("Rvalue") {
        auto left = Src{{CountedValue(11), CountedValue(1)},
                        {CountedValue(12), CountedValue(2)},
                        {CountedValue(13), CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 12);
    }
    CHECK(result
          == Dest{{CountedValue(11), CountedValue(1)}, {CountedValue(12), CountedValue(2)}, {CountedValue(13), CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as()", "", HeavyDoubleToSingle) {
    using Src = std::tuple_element_t<0, TestType>;
    using Dest = WithInnerType_T<std::tuple_element_t<1, TestType>, std::pair<CountedValue, CountedValue>>;
    Dest result;
    SECTION("Lvalue") {
        const auto left = Src{{CountedValue(11), CountedValue(1)},
                              {CountedValue(12), CountedValue(2)},
                              {CountedValue(13), CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    SECTION("Rvalue") {
        auto left = Src{{CountedValue(11), CountedValue(1)},
                        {CountedValue(12), CountedValue(2)},
                        {CountedValue(13), CountedValue(3)}};
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 0);
        CHECK(Counter::moved() <= 12);
    }
    result = PrepareResult<Src, Dest>::prepare(std::move(result));
    CHECK(result
          == Dest{{CountedValue(11), CountedValue(1)}, {CountedValue(12), CountedValue(2)}, {CountedValue(13), CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as() - from view", "", SingleSocketContainers) {
    TestType result;
    SECTION("Lvalue") {
        const std::vector<CountedValue> src{CountedValue(1), CountedValue(2), CountedValue(3)};
        const auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<TestType>(); }
        SECTION("Curried") { result = ops::as<TestType>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    SECTION("Rvalue") {
        const std::vector<CountedValue> src{CountedValue(1), CountedValue(2), CountedValue(3)};
        auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<TestType>(); }
        SECTION("Curried") { result = ops::as<TestType>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 3);
    }
    CHECK(result == TestType{CountedValue(1), CountedValue(2), CountedValue(3)});
}

TEMPLATE_LIST_TEST_CASE("ops::as() - from view", "", HeavyDoubleSocketContainers) {
    TestType result;
    SECTION("Lvalue") {
        const std::vector<std::pair<CountedValue, CountedValue>> src{{CountedValue(11), CountedValue(1)},
                                                                     {CountedValue(12), CountedValue(2)},
                                                                     {CountedValue(13), CountedValue(3)}};
        const auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<TestType>(); }
        SECTION("Curried") { result = ops::as<TestType>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    SECTION("Rvalue") {
        const std::vector<std::pair<CountedValue, CountedValue>> src{{CountedValue(11), CountedValue(1)},
                                                                     {CountedValue(12), CountedValue(2)},
                                                                     {CountedValue(13), CountedValue(3)}};
        const auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<TestType>(); }
        SECTION("Curried") { result = ops::as<TestType>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    CHECK(result
          == TestType{{CountedValue(11), CountedValue(1)}, {CountedValue(12), CountedValue(2)}, {CountedValue(13), CountedValue(3)}});
}

TEMPLATE_LIST_TEST_CASE("ops::as() - from view (from map)", "", SingleSocketContainers) {
    using Dest = WithInnerType_T<TestType, std::pair<CountedValue, CountedValue>>;
    Dest result;
    SECTION("Lvalue") {
        const std::map<CountedValue, CountedValue> src{{CountedValue(11), CountedValue(1)},
                                                       {CountedValue(12), CountedValue(2)},
                                                       {CountedValue(13), CountedValue(3)}};
        const auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = left | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(left); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    SECTION("Rvalue") {
        const std::map<CountedValue, CountedValue> src{{CountedValue(11), CountedValue(1)},
                                                       {CountedValue(12), CountedValue(2)},
                                                       {CountedValue(13), CountedValue(3)}};
        auto left = std::views::all(src);
        Counter::reset();
        SECTION("Pipe") { result = std::move(left) | ops::as<Dest>(); }
        SECTION("Curried") { result = ops::as<Dest>()(std::move(left)); }
        CHECK(Counter::created() == 0);
        CHECK(Counter::copied() == 6);
    }
    CHECK(result
          == Dest{{CountedValue(11), CountedValue(1)}, {CountedValue(12), CountedValue(2)}, {CountedValue(13), CountedValue(3)}});
}


<|start_filename|>include/cefal/functor.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/common.h"

#include <concepts>
#include <functional>
#include <utility>

namespace cefal {
namespace instances {
template <typename T>
struct Functor;

namespace detail {
template <typename>
struct FunctorFromFunctionsExists;
// clang-format off
template <typename T, typename InnerT = InnerType_T<T>>
concept HasFunctorMethods = requires(T x, InnerT value, std::function<InnerT(InnerT)> f) {
    typename FunctorFromFunctionsExists<T>::type;
    { T::unit(std::move(value)) } -> cefal::detail::ValidInnerTypeTransformationFrom<T>;
    { x.map(std::move(f)) } -> cefal::detail::ValidInnerTypeTransformationFrom<T>;
};
// clang-format on
} // namespace detail
} // namespace instances

namespace concepts {
// clang-format off
template <typename T, typename CleanT = std::remove_cvref_t<T>, typename InnerT = InnerType_T<CleanT>>
concept Functor =
requires(CleanT x, InnerT value, std::function<InnerT(InnerT)> converter) {
    { instances::Functor<CleanT>::unit(value) } -> cefal::detail::ValidInnerTypeTransformationFrom<CleanT>;
    { instances::Functor<CleanT>::unit(std::move(value)) } -> cefal::detail::ValidInnerTypeTransformationFrom<CleanT>;
    { instances::Functor<CleanT>::map(x, std::move(converter)) } -> cefal::detail::ValidInnerTypeTransformationFrom<CleanT>;
    { instances::Functor<CleanT>::map(std::move(x), std::move(converter)) } -> cefal::detail::ValidInnerTypeTransformationFrom<CleanT>;
};
// clang-format on
} // namespace concepts

namespace ops {
template <concepts::Functor F, typename T>
inline F unit(T&& x) {
    return instances::Functor<F>::unit(std::forward<T>(x));
}

template <template <typename...> typename F, typename T, typename CleanT = std::remove_cvref_t<T>>
inline auto unit(T&& x) requires concepts::Functor<F<CleanT>> {
    return unit<F<CleanT>>(std::forward<T>(x));
}

template <typename Func>
struct map {
    map(Func&& func) : func(std::move(func)) {}
    map(const Func& func) : func(func) {}

    template <concepts::Functor F>
    auto operator()(F&& left) && {
        return instances::Functor<std::remove_cvref_t<F>>::map(std::forward<F>(left), std::move(func));
    }
    template <concepts::Functor F>
    auto operator()(F&& left) const& {
        return instances::Functor<std::remove_cvref_t<F>>::map(std::forward<F>(left), func);
    }

private:
    Func func;
};

template <typename Func>
struct innerMap {
    innerMap(Func&& func) : func(std::move(func)) {}
    innerMap(const Func& func) : func(func) {}

    template <concepts::Functor F, concepts::Functor InnerF = InnerType_T<std::remove_cvref_t<F>>>
    auto operator()(F&& left) const& {
        return std::forward<F>(left) | map([this]<typename T>(T&& x) { return std::forward<T>(x) | map(func); });
    }

private:
    Func func;
};

template <typename Func>
map(Func &&) -> map<std::remove_cvref_t<Func>>;
template <typename Func>
innerMap(Func &&) -> innerMap<std::remove_cvref_t<Func>>;

} // namespace ops
} // namespace cefal


<|start_filename|>include/cefal/converter.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/detail/instantiator.h"

#include "cefal/common.h"

#include <concepts>
#include <functional>
#include <utility>

namespace cefal {
namespace instances {
template <typename T, typename U>
struct Converter;
} // namespace instances

namespace concepts {
// clang-format off
template <typename T, typename U>
concept CanConvert = requires(T x) {
    { instances::Converter<T, U>::convert(x) } -> std::same_as<U>;
    { instances::Converter<T, U>::convert(std::move(x)) } -> std::same_as<U>;
};
// clang-format on
} // namespace concepts

namespace ops {
namespace detail {
template <template <typename...> typename U>
struct as_templated {
    template <typename T, typename CleanT = std::remove_cvref_t<T>,
              typename FullU = WithInnerType_T<cefal::detail::Instantiator_T<U>, NakedInnerType_T<CleanT>>>
    requires concepts::CanConvert<CleanT, FullU> auto operator()(T&& left) const {
        return instances::Converter<CleanT, FullU>::convert(std::forward<T>(left));
    }
};
template <typename U>
struct as_full {
    template <typename T, typename CleanT = std::remove_cvref_t<T>, typename CleanU = std::remove_cvref_t<U>>
    requires concepts::CanConvert<CleanT, CleanU> auto operator()(T&& left) const {
        static_assert(std::is_same_v<NakedInnerType_T<CleanU>, NakedInnerType_T<CleanT>>,
                      "cefal::ops::as can be called only for destination with same inner type as source");
        return instances::Converter<CleanT, CleanU>::convert(std::forward<T>(left));
    }
};
} // namespace detail

template <template <typename...> typename U>
inline auto as() {
    return detail::as_templated<U>();
}
template <typename U>
inline auto as() {
    return detail::as_full<U>();
}

} // namespace ops
} // namespace cefal


<|start_filename|>include/cefal/detail/common_concepts.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/helpers/inner_type.h"

#include <concepts>

namespace cefal::detail {
template <typename To, typename From>
struct IsValidInnerTypeTransformation {
    static constexpr bool value = std::same_as<From, To>;
};
template <typename To, typename From>
constexpr inline bool IsValidInnerTypeTransformation_V = IsValidInnerTypeTransformation<To, From>::value;

template <typename To, typename From>
concept ValidInnerTypeTransformationFrom = IsValidInnerTypeTransformation_V<To, From>;
} // namespace cefal::detail


<|start_filename|>src/dummy.cpp<|end_filename|>
#include "cefal/everything.h"

#include <cefal/helpers/std_containers.h>

#include <iostream>
#include <set>
#include <unordered_set>
#include <vector>
#include <chrono>
#include <ranges>

using namespace cefal;

template <concepts::Monoid T>
struct ImplementedMethods {
    T value;
    static ImplementedMethods unit(T x) { return {x}; }
    static ImplementedMethods empty() { return {T()}; }

    auto append(ImplementedMethods other) const {
        return ImplementedMethods{value | ops::append(other.value)};
    };

    auto append(helpers::SingletonFrom<ImplementedMethods> other) const {
        return ImplementedMethods{value | ops::append(other.value)};
    };

    template <typename Result, typename Func>
    auto foldLeft(Result&& initial, Func func) const& {
        return func(std::forward<Result>(initial), value);
    };

    template <typename Result, typename Func>
    auto foldLeft(Result&& initial, Func func) && {
        return func(std::forward<Result>(initial), std::move(value));
    };

    template <typename Func>
    auto filter(Func f) const {
        return ImplementedMethods{value};
    };

    template <typename Func>
    auto map(Func f) const {
        return ImplementedMethods{f(value)};
    };

    template <typename Func>
    auto flatMap(Func f) const {
        return ImplementedMethods{f(value).value};
    };
};

namespace cefal::helpers {
template <typename Src>
struct SingletonFrom<ImplementedMethods<Src>> {
    Src value;
};
}

struct SimpleImplementedMethods {
    int value;
    static SimpleImplementedMethods unit(int x) { return {x}; }

    template <typename Func>
    auto map(Func f) const {
        return SimpleImplementedMethods{f(value)};
    };

    template <typename Func>
    auto flat_map(Func f) const & {
        return SimpleImplementedMethods{f(value).value};
    };

    template <typename Func>
    auto flat_map(Func f) && {
        return SimpleImplementedMethods{f(value).value};
    };
};

namespace cefal {
template <>
struct InnerType<SimpleImplementedMethods> {
    using type = int;
};
template <>
struct WithInnerType<SimpleImplementedMethods, int> {
    using type = SimpleImplementedMethods;
};
}

template <concepts::Monad M>
int testContainers(M&& m) {
    using CleanM = std::remove_cvref_t<M>;
    auto flatMapper = ops::flatMap([](auto) { return CleanM{4, 5}; });
    auto rounder = ops::map([](double x) { return int(x); });
    auto filter = ops::filter([](double x) { return x > 4; });
    auto intermediate = std::forward<M>(m) | ops::map([](int x) { return x + 5; })
                                           | flatMapper
                                           | ops::flatMap([](auto) { return CleanM{4, 5}; })
                                           | std::move(filter);
    auto final = std::move(rounder)(intermediate);
    return *final.begin();
}

double testOptional(const std::optional<int>& opt) {
    auto result = opt | ops::map([](auto x) {return x * 2;})
                      | ops::flatMap([](auto x) -> std::optional<double> {if (x < 5) return std::nullopt; return x;})
                      | ops::map([](auto x) {return x * 3;})
                      | ops::filter([](auto x) {return true;});
    return result ? *result : -1;
}

int getInt(int seed) {
    return testContainers(ops::unit<std::set<int>>(seed));
}

template <concepts::Functor C>
struct ContainerTester {
    ContainerTester() {
        C a = ops::unit<C>(5);
        testContainers(a);
        testContainers(std::move(a));
    }
};

int main() {
    std::multimap<std::string, int> m {{"12", 34}, {"56", 78}};
    std::string mapResult1 = m | ops::foldLeft(std::string(), [](std::string c, auto&& x) {
                                    return std::move(c) + std::get<0>(x) + std::to_string(std::get<1>(x)) + " | ";
                                 });
    std::cout << mapResult1 << std::endl;

    std::multimap<std::string, int> mSum = m | ops::append(m);

    std::string mapResult = m | ops::map([](auto&& x) {return std::make_pair(std::get<0>(x), std::to_string(std::get<1>(x)));})
                              | ops::foldLeft(std::string(), [](std::string c, auto&& x) {
                                    return std::move(c) + std::get<0>(x) + std::get<1>(x) + " | ";
                                });
    std::cout << mapResult << std::endl;

    mapResult = std::move(m) | ops::map([](auto&& x) {return std::make_pair(std::get<0>(x), std::to_string(std::get<1>(x)));})
                             | ops::foldLeft(std::string(), [](std::string c, auto&& x) {
                                    return std::move(c) + std::get<0>(x) + std::get<1>(x) + " | ";
                               });
    std::cout << mapResult << std::endl;

    std::set<int> vec = {1, 2, 3};
    for (int x : std::views::all(vec))
        std::cout << x << " ";
    std::cout << std::endl;
    std::string rangeResult = std::views::all(vec) | ops::map([](int x) {return x + 2;})
                                                   | ops::filter([](int x) {return x % 2;})
                                                   | ops::foldLeft(std::string(), [](std::string c, int x) {
                                                        return std::move(c) + std::to_string(x);
                                                     });
    std::cout << rangeResult << std::endl;

    {
        std::set<int> converted = std::vector{1, 2, 3} | ops::as<std::set<int>>();
        std::cout << "1->1 rvalue converted: " << converted.size() << ": ";
        for (auto&& x : converted) std::cout << x << " ";
        std::cout << std::endl;

        auto convertSrc = std::vector{1, 2, 3};
        std::set<int> lvalueConverted = convertSrc | ops::as<std::set>();
        std::cout << "1->1 lvalue converted: " << lvalueConverted.size() << ": ";
        for (auto&& x : lvalueConverted) std::cout << x << " ";
        std::cout << std::endl;

        std::set<double> viewConverted = convertSrc | std::views::transform([](auto x) {return x * 2.5;}) | ops::as<std::set>();
        std::cout << "1->1 view converted: " << viewConverted.size() << ": ";
        for (auto&& x : viewConverted) std::cout << x << " ";
        std::cout << std::endl;
    }

    {
        std::vector<std::pair<std::string, int>> converted = std::map<std::string, int>{{"12", 34}, {"56", 78}} | ops::as<std::vector>();
        std::cout << "2->1 rvalue converted: " << converted.size() << ": ";
        for (auto&& [x, y] : converted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        auto convertSrc = std::map<std::string, int> {{"12", 34}, {"56", 78}};
        std::vector<std::pair<std::string, int>> lvalueConverted = convertSrc | ops::as<std::vector>();
        std::cout << "2->1 lvalue converted: " << lvalueConverted.size() << ": ";
        for (auto&& [x, y] : lvalueConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        std::set<std::pair<std::string, double>> viewConverted =
            convertSrc | std::views::transform([](auto x) {return std::make_pair(std::get<0>(x), std::get<1>(x) * 2.5);})
                       | ops::as<std::set>();
        std::cout << "2->1 view converted: " << viewConverted.size() << ": ";
        for (auto&& [x, y] : viewConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;
    }

    {
        std::unordered_map<std::string, int> converted = std::map<std::string, int>{{"12", 34}, {"56", 78}} | ops::as<std::unordered_map>();
        std::cout << "2->2 rvalue converted: " << converted.size() << ": ";
        for (auto&& [x, y] : converted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        auto convertSrc = std::map<std::string, int> {{"12", 34}, {"56", 78}};
        std::unordered_map<std::string, int> lvalueConverted = convertSrc | ops::as<std::unordered_map>();
        std::cout << "2->2 lvalue converted: " << lvalueConverted.size() << ": ";
        for (auto&& [x, y] : lvalueConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        std::unordered_map<std::string, double> viewConverted =
            convertSrc | std::views::transform([](auto x) {return std::make_pair(std::get<0>(x), std::get<1>(x) * 2.5);})
                       | ops::as<std::unordered_map>();
        std::cout << "2->2 view converted: " << viewConverted.size() << ": ";
        for (auto&& [x, y] : viewConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;
    }

    {
        std::unordered_map<std::string, int> converted = std::vector<std::pair<std::string, int>>{{"12", 34}, {"56", 78}} | ops::as<std::unordered_map>();
        std::cout << "1t->2 rvalue converted: " << converted.size() << ": ";
        for (auto&& [x, y] : converted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        auto convertSrc = std::vector<std::pair<std::string, int>> {{"12", 34}, {"56", 78}};
        std::unordered_map<std::string, int> lvalueConverted = convertSrc | ops::as<std::unordered_map>();
        std::cout << "1t->2 lvalue converted: " << lvalueConverted.size() << ": ";
        for (auto&& [x, y] : lvalueConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        std::unordered_map<std::string, double> viewConverted =
            convertSrc | std::views::transform([](auto x) {return std::make_pair(std::get<0>(x), std::get<1>(x) * 2.5);})
                       | ops::as<std::unordered_map>();
        std::cout << "1t->2 view converted: " << viewConverted.size() << ": ";
        for (auto&& [x, y] : viewConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;
    }

    {
        std::unordered_map<std::string, int> converted = std::vector<std::pair<std::string, int>>{{"12", 34}, {"56", 78}} | ops::as<std::unordered_map>();
        std::cout << "1p->2 rvalue converted: " << converted.size() << ": ";
        for (auto&& [x, y] : converted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        auto convertSrc = std::vector<std::pair<std::string, int>> {{"12", 34}, {"56", 78}};
        std::unordered_map<std::string, int> lvalueConverted = convertSrc | ops::as<std::unordered_map>();
        std::cout << "1p->2 lvalue converted: " << lvalueConverted.size() << ": ";
        for (auto&& [x, y] : lvalueConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;

        std::unordered_map<std::string, double> viewConverted =
            convertSrc | std::views::transform([](auto x) {return std::make_pair(std::get<0>(x), std::get<1>(x) * 2.5);})
                       | ops::as<std::unordered_map>();
        std::cout << "1p->2 view converted: " << viewConverted.size() << ": ";
        for (auto&& [x, y] : viewConverted) std::cout << x << ":" << y << " ";
        std::cout << std::endl;
    }

    std::cout << std::endl;
    std::cout << getInt(42) << std::endl;
    std::cout << testOptional(3) << "; " <<  testOptional(2) << "; " <<  testOptional(std::nullopt) << std::endl;
    { ContainerTester<std::vector<int>> x; }
    { ContainerTester<std::vector<double>> x; }
    { ContainerTester<std::set<int>> x; }
    { ContainerTester<std::set<double>> x; }
    { ContainerTester<std::unordered_set<int>> x; }
    { ContainerTester<std::unordered_set<double>> x; }
    std::vector<std::vector<int>> innerMapped = std::vector{1, 2, 3} | ops::map([](int x) { return ops::unit<std::vector>(x); })
                                                                     | ops::innerFilter([](int x){ return x % 2; })
                                                                     | ops::innerFlatMap([](int x){ return std::vector{x + 1, x + 2}; })
                                                                     | ops::innerMap([](int x){ return x * 3; });
    std::cout << "innerMapped: " << innerMapped.size() << ": | ";
    for (auto&& inner : innerMapped) {
        for (int x : inner)
            std::cout << x << " ";
        std::cout << "| ";
    }
    std::cout << std::endl;

    ops::empty<ImplementedMethods<Sum<int>>>() | ops::foldLeft(0, [](int a, Sum<int> b) {return a + b.value;});
    ops::unit<ImplementedMethods<Sum<int>>>(Sum(42));
    ImplementedMethods<Sum<int>> impl =
        ops::unit<ImplementedMethods>(Sum(42)) | ops::map([](int x) -> Sum<int> {return x + 2;})
                                               | ops::filter([](auto x) {return true;})
                                               | ops::flatMap([](int x) {return ImplementedMethods<Sum<int>>{x * 3};});
    std::cout << impl.value.value << std::endl;

    SimpleImplementedMethods nonTemplatedImpl =
        ops::unit<SimpleImplementedMethods>(42) | ops::map([](int x) {return x + 2;})
                                                | ops::flatMap([](int x) {return SimpleImplementedMethods{x * 3};});
    std::cout << nonTemplatedImpl.value << std::endl;
}


<|start_filename|>include/cefal/helpers/inner_type.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include <tuple>

namespace cefal {
namespace detail {
template <typename...>
struct SimpleInnerType;
template <template <typename...> typename C, typename T, typename... Ts>
struct SimpleInnerType<C<T, Ts...>> {
    using type = T;
};

template <typename T>
struct FullDecay {
    using type = std::remove_cvref_t<T>;
};
template <typename T1, typename T2>
struct FullDecay<std::pair<T1, T2>> {
    using type = std::pair<std::remove_cvref_t<T1>, std::remove_cvref_t<T2>>;
};
template <typename... T>
struct FullDecay<std::tuple<T...>> {
    using type = std::tuple<std::remove_cvref_t<T>...>;
};
} // namespace detail

template <typename T>
struct InnerType {
    using type = detail::SimpleInnerType<T>::type;
};
template <typename T>
using InnerType_T = InnerType<T>::type;

template <typename T>
struct NakedInnerType {
    using type = detail::FullDecay<typename InnerType<T>::type>::type;
};
template <typename T>
using NakedInnerType_T = NakedInnerType<T>::type;

template <typename...>
struct WithInnerType;
template <template <typename...> typename C, typename T, typename... Ts, typename NewT>
struct WithInnerType<C<T, Ts...>, NewT> {
    using type = C<NewT>;
};
template <typename... Ts>
using WithInnerType_T = WithInnerType<Ts...>::type;
} // namespace cefal


<|start_filename|>include/cefal/helpers/std_containers.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include "cefal/detail/common_concepts.h"

#include <map>
#include <unordered_map>
#include <utility>

namespace cefal {
template <typename K, typename V>
struct InnerType<std::map<K, V>> {
    using type = std::pair<K, V>;
};

template <typename K, typename V>
struct InnerType<std::unordered_map<K, V>> {
    using type = std::pair<K, V>;
};

template <typename K, typename V>
struct InnerType<std::multimap<K, V>> {
    using type = std::pair<K, V>;
};

template <typename K, typename V>
struct InnerType<std::unordered_multimap<K, V>> {
    using type = std::pair<K, V>;
};

template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::map<K, V>, std::tuple<NewK, NewV>> {
    using type = std::map<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::unordered_map<K, V>, std::tuple<NewK, NewV>> {
    using type = std::unordered_map<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::multimap<K, V>, std::tuple<NewK, NewV>> {
    using type = std::multimap<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::unordered_multimap<K, V>, std::tuple<NewK, NewV>> {
    using type = std::unordered_multimap<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};

template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::map<K, V>, std::pair<NewK, NewV>> {
    using type = std::map<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::unordered_map<K, V>, std::pair<NewK, NewV>> {
    using type = std::unordered_map<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::multimap<K, V>, std::pair<NewK, NewV>> {
    using type = std::multimap<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
template <typename K, typename V, typename NewK, typename NewV>
struct WithInnerType<std::unordered_multimap<K, V>, std::pair<NewK, NewV>> {
    using type = std::unordered_multimap<std::remove_cvref_t<NewK>, std::remove_cvref_t<NewV>>;
};
} // namespace cefal


<|start_filename|>include/cefal/detail/instantiator.h<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#pragma once

#include <concepts>

namespace cefal::detail {
// clang-format off
template <template <typename ...> typename T>
concept ZeroSockets = requires {typename T<>;};
template <template <typename ...> typename T>
concept OneSocket = (!ZeroSockets<T>) && requires {typename T<int>;};
template <template <typename ...> typename T>
concept TwoSockets = (!ZeroSockets<T>) && (!OneSocket<T>) && requires {typename T<int, double>;};
template <template <typename ...> typename T>
concept ThreeSockets = (!ZeroSockets<T>) && (!OneSocket<T>) && (!TwoSockets<T>) && requires {typename T<int, double, float>;};
template <template <typename ...> typename T>
concept FourSockets = (!ZeroSockets<T>) && (!OneSocket<T>) && (!TwoSockets<T>) && (!ThreeSockets<T>)
                      && requires {typename T<int, double, float, bool>;};
template <template <typename ...> typename T>
concept FiveSockets = (!ZeroSockets<T>) && (!OneSocket<T>) && (!TwoSockets<T>) && (!ThreeSockets<T>) && (!FourSockets<T>)
                      && requires {typename T<int, double, float, bool, char>;};
// clang-format on

template <template <typename...> typename T>
struct Instantiator {};

template <template <typename...> typename T>
requires ZeroSockets<T> struct Instantiator<T> {
    using type = T<>;
};

template <template <typename...> typename T>
requires OneSocket<T> struct Instantiator<T> {
    using type = T<int>;
};

template <template <typename...> typename T>
requires TwoSockets<T> struct Instantiator<T> {
    using type = T<int, double>;
};

template <template <typename...> typename T>
requires ThreeSockets<T> struct Instantiator<T> {
    using type = T<int, double, float>;
};

template <template <typename...> typename T>
requires FourSockets<T> struct Instantiator<T> {
    using type = T<int, double, float, bool>;
};

template <template <typename...> typename T>
requires FiveSockets<T> struct Instantiator<T> {
    using type = T<int, double, float, bool, char>;
};

template <template <typename...> typename T>
using Instantiator_T = Instantiator<T>::type;
} // namespace cefal::detail


<|start_filename|>benchmarks/functor/benchmark_map_std_ranges.cpp<|end_filename|>
/* Copyright 2020, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *     * Neither the name of the copyright holders nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "cefal/everything.h"

#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include "catch2/catch.hpp"

#include <iostream>
#include <list>
#include <ranges>
#include <string>
#include <vector>

using namespace cefal;

// It has its own Expensive due to heavy optimization that could be done for ranges operations by compiler
// Regular expensive structure kept as is for purposes of containers benchmarking
template <typename T>
struct Expensive {
    Expensive(T x) : value(std::move(x)) { init(); }
    Expensive(T x, int* payload) : value(std::move(x)), payload(payload) {}
    Expensive(const Expensive& other) {
        init();
        value = other.value;
    }
    Expensive(Expensive&& other) {
        payload = other.payload;
        value = std::move(other.value);
        other.payload = nullptr;
    }
    Expensive& operator=(const Expensive& other) {
        init();
        value = other.value;
        return *this;
    }
    Expensive& operator=(Expensive&& other) {
        std::swap(payload, other.payload);
        value = std::move(other.value);
        other.payload = nullptr;
        return *this;
    }
    ~Expensive() { delete[] payload; }

    operator T() const { return value + payload[std::chrono::system_clock::now().time_since_epoch().count() & 0xFF]; }

    template <typename U>
    bool operator%(U&& other) const {
        return value % other;
    }

    template <typename U>
    Expensive<std::remove_cvref_t<U>> operator+(U&& other) const& {
        return Expensive<std::remove_cvref_t<U>>(value + other);
    }

    template <typename U>
    Expensive<std::remove_cvref_t<U>> operator+(U&& other) && {
        auto oldPayload = payload;
        payload = 0;
        return Expensive<std::remove_cvref_t<U>>(value + other, oldPayload);
    }

    auto operator<=>(const Expensive<T>& other) const { return value <=> other.value; }
    bool operator==(const Expensive<T>& other) const { return std::abs(value - other.value) < 0.0001; }

    void init() {
        delete[] payload;
        payload = new int[1024];
        auto seed = std::chrono::system_clock::now().time_since_epoch().count();
        for (int i = 0; i < 1024; ++i)
            payload[i] = seed + i;
    }

    T value = T();
    int* payload = nullptr;
};

template <typename C>
struct ContainerSize {
    static constexpr size_t value = std::is_same_v<cefal::InnerType_T<C>, int> ? 1'000'000 : 20'000;
};

template <typename T>
constexpr inline size_t ContainerSize_V = ContainerSize<T>::value;

TEMPLATE_PRODUCT_TEST_CASE("cefal::map() to same type", "", (std::vector), (int, Expensive<int>)) {
    auto seed = std::chrono::system_clock::now();
    auto copyFunc = [seed](InnerType_T<TestType>& x) { return x + (int)seed.time_since_epoch().count(); };
    auto moveFunc = [seed](InnerType_T<TestType>& x) { return std::move(x) + (int)seed.time_since_epoch().count(); };

    BENCHMARK_ADVANCED("cefal::map() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | ops::map(copyFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | std::views::transform(copyFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("cefal::map() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | ops::map(moveFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | std::views::transform(moveFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };
}

TEMPLATE_PRODUCT_TEST_CASE("cefal::map() to different type", "", (std::vector), (int, Expensive<int>)) {
    auto seed = std::chrono::system_clock::now();
    auto copyFunc = [seed](InnerType_T<TestType>& x) { return x + (double)seed.time_since_epoch().count(); };
    auto moveFunc = [seed](InnerType_T<TestType>& x) { return std::move(x) + (double)seed.time_since_epoch().count(); };
    using Dest = WithInnerType_T<TestType, std::invoke_result_t<decltype(copyFunc), InnerType_T<TestType>&>>;

    BENCHMARK_ADVANCED("cefal::map() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | ops::map(copyFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | std::views::transform(copyFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("cefal::map() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | ops::map(moveFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | std::views::transform(moveFunc) | std::views::drop(50) | std::views::take(1);
            return *dest.begin();
        });
    };
}

TEMPLATE_PRODUCT_TEST_CASE("cefal::map() to same type - full consume", "", (std::vector), (int, Expensive<int>)) {
    auto seed = std::chrono::system_clock::now();
    auto copyFunc = [seed](InnerType_T<TestType>& x) { return x + (int)seed.time_since_epoch().count(); };
    auto moveFunc = [seed](InnerType_T<TestType>& x) { return std::move(x) + (int)seed.time_since_epoch().count(); };

    BENCHMARK_ADVANCED("cefal::map() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | ops::map(copyFunc);
            int result = 0;
            for (int x : dest)
                result += x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | std::views::transform(copyFunc);
            int result = 0;
            for (int x : dest)
                result += x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("cefal::map() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | ops::map(moveFunc);
            int result = 0;
            for (int x : dest)
                result += x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | std::views::transform(moveFunc);
            int result = 0;
            for (int x : dest)
                result += x;
            return result;
        });
    };
}

TEMPLATE_PRODUCT_TEST_CASE("cefal::map() to different type - full consume", "", (std::vector), (int, Expensive<int>)) {
    auto seed = std::chrono::system_clock::now();
    auto copyFunc = [seed](InnerType_T<TestType>& x) { return x + (double)seed.time_since_epoch().count(); };
    auto moveFunc = [seed](InnerType_T<TestType>& x) { return std::move(x) + (double)seed.time_since_epoch().count(); };
    using Dest = WithInnerType_T<TestType, std::invoke_result_t<decltype(copyFunc), InnerType_T<TestType>&>>;

    BENCHMARK_ADVANCED("cefal::map() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | ops::map(copyFunc);
            double result = 0;
            for (const auto& x : dest)
                result += (double)x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - immutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &copyFunc](int i) {
            auto dest = src[i] | std::views::transform(copyFunc);
            double result = 0;
            for (double x : dest)
                result += x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("cefal::map() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | ops::map(moveFunc);
            double result = 0;
            for (const auto& x : dest)
                result += (double)x;
            return result;
        });
    };

    BENCHMARK_ADVANCED("std::views::transform() - mutable - x" + std::to_string(ContainerSize_V<TestType>))
    (Catch::Benchmark::Chronometer meter) {
        auto seed = std::chrono::system_clock::now();
        std::vector<TestType> rawSrc(meter.runs());
        std::vector<decltype(std::views::all(*rawSrc.begin()))> src;
        for (auto&& x : rawSrc) {
            x.reserve(ContainerSize_V<TestType>);
            for (int j = 0; j < ContainerSize_V<TestType>; ++j)
                x.push_back(seed.time_since_epoch().count() + j);
            src.push_back(std::views::all(x));
        }
        meter.measure([&src, &moveFunc](int i) {
            auto dest = src[i] | std::views::transform(moveFunc);
            double result = 0;
            for (double x : dest)
                result += x;
            return result;
        });
    };
}
