From d6e85b6b6d16ed9708f55f11e02e10c297ad295f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Cruz?= Date: Sat, 29 Jul 2023 14:04:38 -0300 Subject: [PATCH] Add collection wrapper --- robocin/CMakeLists.txt | 1 + robocin/collection/CMakeLists.txt | 12 + robocin/collection/README.md | 484 ++++++++ robocin/collection/collection.cpp | 6 + robocin/collection/collection.h | 1074 +++++++++++++++++ .../collection/internal/collection_internal.h | 77 ++ 6 files changed, 1654 insertions(+) create mode 100644 robocin/collection/CMakeLists.txt create mode 100644 robocin/collection/README.md create mode 100644 robocin/collection/collection.cpp create mode 100644 robocin/collection/collection.h create mode 100644 robocin/collection/internal/collection_internal.h diff --git a/robocin/CMakeLists.txt b/robocin/CMakeLists.txt index f8474db..6060646 100644 --- a/robocin/CMakeLists.txt +++ b/robocin/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(utility) +add_subdirectory(collection) diff --git a/robocin/collection/CMakeLists.txt b/robocin/collection/CMakeLists.txt new file mode 100644 index 0000000..3b361dd --- /dev/null +++ b/robocin/collection/CMakeLists.txt @@ -0,0 +1,12 @@ +robocin_cpp_library( + NAME collection + HDRS collection.h + SRCS collection.cpp + DEPS tbb +) + +robocin_cpp_test( + NAME collection_test + SRCS collection_test.cpp + DEPS collection +) diff --git a/robocin/collection/README.md b/robocin/collection/README.md new file mode 100644 index 0000000..ede7483 --- /dev/null +++ b/robocin/collection/README.md @@ -0,0 +1,484 @@ +# Collection + +## Overview + +The `Collection` class is a versatile container adaptor designed to provide the programmer with an extended interface +for handling sequences of elements, resembling the functionalities of both static and dynamic size arrays. It serves as +a powerful alternative to the standard `std::vector` and `std::array` containers, enhancing their capabilities with +additional map/reduce-like methods and several other utilities. + +### Key Features + +* **Versatility**: The Collection class can accommodate both static-size and dynamic-size collections, making it + adaptable to different use cases. Whether you need a fixed-size collection or one that can grow or shrink as needed, + the Collection class has you covered. + +* **Map/Reduce-like Methods**: The class offers map, reduce, filter, and transform methods, inspired by functional + programming paradigms. These methods allow you to perform element-wise operations, aggregate results, and selectively + filter elements based on conditions with ease and readability. + +* **Efficient Parallel Operations**: Certain methods, such as `stable_sort` and `reverse`, support parallel execution + using the provided execution policies. This feature leverages modern hardware capabilities, such as multi-core + processors, to perform computations faster on large datasets. + +* **Sorting and Reversing**: The Collection class provides various sorting algorithms, including both stable and + non-stable sorts, allowing you to organize the elements according to specific criteria. Additionally, you can easily + reverse the order of elements with a single function call. + +* **Implicit Conversions**: The class supports implicit conversions to other data structures, such as standard + containers like `std::vector`, ranges, pairs, and tuples. This convenient feature simplifies interoperability with + existing codebases and enables easy integration with other standard library components. + +## Aliases + +- `template using ArrayCollection = Collection>`: The `ArrayCollection` + alias creates a new template specialization of the Collection class that uses std::array as its underlying container + type. This alias allows users to work with static-size arrays using the familiar Collection interface. + +## Member Types + +- `container_type`: The type of the underlying container used to store elements. +- `value_type`: The type of the elements held in the collection. +- `size_type`: An unsigned integral type used for representing sizes and indices. +- `reference`: A reference type to the elements in the container. +- `const_reference`: A const reference type to the elements in the container. +- `iterator`: An iterator type to traverse the collection. +- `const_iterator`: A const iterator type to traverse the collection. +- `reverse_iterator`: A reverse iterator type to traverse the collection in reverse. +- `const_reverse_iterator`: A const reverse iterator type to traverse the collection in reverse. + +## Constructors + +- `Collection()`: Default constructor, constructs an empty collection. +- `Collection(const Collection& other)`: Copy constructor, creates a new collection by copying elements from another + collection. +- `Collection(Collection&& other) noexcept`: Move constructor, creates a new collection by moving elements from another + collection. +- `explicit Collection(size_type count)`: Constructs the collection with `count` default-inserted instances + of `value_type`. +- `Collection(size_type count, const value_type& value)`: Constructs the collection with `count` copies of `value`. +- `Collection(std::initializer_list il)`: Constructs the collection with the elements from the initializer + list `il`. +- `explicit Collection(const container_type& container)`: Constructs the collection by copying the elements from the + provided `container`. +- `explicit Collection(container_type&& container)`: Constructs the collection by moving the elements from the + provided `container`. +- `template Collection(FirstIt first, LastIt last)`: Constructs the collection with + elements in the range `[first, last)`. +- `template explicit Collection(R&& range)`: Constructs the collection with elements from the + range `range`. + +## Assignment Operators + +- `Collection& operator=(const Collection& other)`: Copy assignment operator, assigns the elements from another + collection to this collection. +- `Collection& operator=(Collection&& other) noexcept`: Move assignment operator, moves the elements from another + collection to this collection. +- `Collection& operator=(std::initializer_list il)`: Assigns the elements from the initializer list `il` to + the collection. +- `Collection& operator=(const container_type& container)`: Assigns the elements from the provided `container` to this + collection by copying them. +- `Collection& operator=(container_type&& container)`: Assigns the elements from the provided `container` to this + collection by moving them. +- `template Collection& operator=(R&& range)`: Assigns the elements from the range `range` to + the collection. + +## Element Access + +- `reference at(size_type pos)`: Returns a reference to the element at the specified position `pos` with bounds + checking. +- `const_reference at(size_type pos) const`: Returns a const reference to the element at the specified position `pos` + with bounds checking. +- `reference operator[](size_type pos)`: Returns a reference to the element at the specified position `pos`. No bounds + checking is performed. +- `const_reference operator[](size_type pos) const`: Returns a const reference to the element at the specified + position `pos`. No bounds checking is performed. +- `reference front()`: Returns a reference to the first element in the collection. +- `const_reference front() const`: Returns a const reference to the first element in the collection. +- `reference back()`: Returns a reference to the last element in the collection. +- `const_reference back() const`: Returns a const reference to the last element in the collection. + +## Capacity + +- `bool empty() const`: Returns a boolean indicating whether the collection is empty (i.e., contains no elements). +- `size_type size() const`: Returns the number of elements currently stored in the collection. +- `size_type max_size() const`: Returns the maximum possible number of elements the collection can hold. The actual + limit may vary depending on the system and memory constraints. +- `void reserve(size_type new_cap)`: Requests that the collection's underlying container reserve memory for at + least `new_cap` elements. This can help to reduce reallocations during insertions and prevent unnecessary memory + reallocation. +- `size_type capacity() const`: Returns the current capacity of the collection, which is the maximum number + of elements it can hold without triggering a reallocation. +- `void shrink_to_fit()`: Requests the collection's underlying container to deallocate any unused memory + beyond the current size. This operation may or may not result in a reallocation. + +## Modifiers + +> **Note**: The following modifiers are not available for collections that use `std::array` as their underlying +> container type. + +- `void append_range(const Collection& other)`: Appends the elements from another collection `other` at the + end of this collection. +- `void append_range(Collection&& other)`: Appends the elements from another collection `other` at the end of + this collection, using move semantics when applicable. Note that the original `other` collection is not modified. +- `void append_range(std::initializer_list il)`: Appends elements from the initializer list `il` + at the end of this collection. +- `template void append_range(R&& range)`: Appends elements from the range `range` at + the end of this collection. + +- `void clear()`: Removes all elements from the collection, leaving it with a size of 0. + +- `template iterator emplace(Args&&... args)`: Constructs an element in-place at the given + position using the provided arguments `args`. + +- `template reference emplace_back(Args&&... args)`: Constructs an element in-place at the + end of the collection using the provided arguments `args` and returns a reference to the newly constructed element. + +- `void erase(const_iterator pos)`: Removes the element at the specified position `pos`. + +- `void erase(const_iterator first, const_iterator last)`: Removes elements in the range `[first, last)` from + the collection. + +- `iterator insert(const_iterator pos, const value_type& value)`: Inserts a copy of `value` before the + specified position `pos` and returns an iterator pointing to the inserted element. + +- `iterator insert(const_iterator pos, value_type&& value)`: Inserts a moved element `value` before the + specified position `pos` and returns an iterator pointing to the inserted element. + +- `iterator insert(const_iterator pos, size_type count, const value_type& value)`: Inserts `count` copies + of `value` before the specified position `pos` and returns an iterator pointing to the first inserted element. + +- `template iterator insert(const_iterator pos, FirstIt first, LastIt last)`: + Inserts elements from the range `[first, last)` before the specified position `pos` and returns an iterator pointing + to the first inserted element. + +- `iterator insert(const_iterator pos, std::initializer_list il)`: Inserts elements from the + initializer list `il` before the specified position `pos` and returns an iterator pointing to the first inserted + element. + +- `template iterator insert(const_iterator pos, R&& range)`: Inserts elements from + the range `range` before the specified position `pos` and returns an iterator pointing to the first inserted + element. + +- `void push_back(const value_type& value)`: Appends a copy of `value` to the end of the collection. + +- `void push_back(value_type&& value)`: Appends a moved element `value` to the end of the collection. + +- `void pop_back()`: Removes the last element of the collection. + +- `void resize(size_type count)`: Resizes the collection to contain `count` elements. If `count` is smaller + than the current size, elements are removed from the end. If `count` is greater, additional default-constructed + elements are appended. + +- `void resize(size_type count, const value_type& value)`: Resizes the collection to contain `count` + elements. If `count` is smaller than the current size, elements are removed from the end. If `count` is greater, + additional copies of `value` are appended. + +- `Collection resized(size_type count) &&`: Resizes the collection to contain `count` elements and returns a + reference to the resized collection using move semantics. If `count` is smaller than the current size, elements are + removed from the end. If `count` is greater, additional default-constructed elements are appended. + +- `Collection resized(size_type count) const&`: Creates a copy of the collection, resizes it to + contain `count` elements, and returns the resized copy. If `count` is smaller than the current size, elements are + removed from the end. If `count` is greater, additional default-constructed elements are appended. + +- `Collection resized(size_type count, const value_type& value) &&`: Resizes the collection to + contain `count` elements, setting each new element to `value`, and returns a reference to the resized collection + using move semantics. If `count` is smaller than the current size, elements are removed from the end. If `count` is + greater, additional copies of `value` are appended. + +- `Collection resized(size_type count, const value_type& value) const&`: Creates a copy of the collection, + resizes it to contain `count` elements, setting each new element to `value`, and returns the resized copy. + If `count` is smaller than the current size, elements are removed from the end. If `count` is greater, additional + copies of `value` are appended. + +- `void swap(Collection& other)`: Swaps the contents of this collection with another collection `other`. + +## Comparison Operators + +- `inline bool operator==(const Collection& other) const`: Compares this collection with another + collection `other` for equality. Returns `true` if both collections have the same elements in the same order; + otherwise, returns `false`. + +- `inline auto operator<=>(const Collection& other) const`: Compares this collection with another + collection `other`. Returns a three-way comparison result indicating whether this collection is less than, equal to, + or greater than `other`. + +## Iterators + +- `iterator begin() noexcept`: Returns an iterator pointing to the first element of the collection. +- `const_iterator begin() const noexcept`: Returns a constant iterator pointing to the first element of the + collection. +- `iterator end() noexcept`: Returns an iterator pointing to the element following the last element of the + collection. +- `const_iterator end() const noexcept`: Returns a constant iterator pointing to the element following the + last element of the collection. + +- `reverse_iterator rbegin() noexcept`: Returns a reverse iterator pointing to the last element of the + collection (reverse beginning). +- `const_reverse_iterator rbegin() const noexcept`: Returns a constant reverse iterator pointing to the last + element of the collection (reverse beginning). +- `reverse_iterator rend() noexcept`: Returns a reverse iterator pointing to the element preceding the first + element of the collection (reverse end). +- `const_reverse_iterator rend() const noexcept`: Returns a constant reverse iterator pointing to the element + preceding the first element of the collection (reverse end). + +- `const_iterator cbegin() const noexcept`: Returns a constant iterator pointing to the first element of the + collection. Similar to `begin()`. +- `const_iterator cend() const noexcept`: Returns a constant iterator pointing to the element following the + last element of the collection. Similar to `end()`. +- `const_reverse_iterator crbegin() const noexcept`: Returns a constant reverse iterator pointing to the + last element of the collection (reverse beginning). Similar to `rbegin()`. +- `const_reverse_iterator crend() const noexcept`: Returns a constant reverse iterator pointing to the + element preceding the first element of the collection (reverse end). Similar to `rend()`. + +## Reduce + +- `template value_type reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) &&`: + Reduces the elements in the collection using the provided binary operation `op` and an initial value `init`. The + reduction is performed using the specified execution policy `policy`. The collection is modified using + move semantics during the reduction process. + +- `template value_type reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) const&`: + Reduces the elements in the collection using the provided binary operation `op` and an initial value `init`. The + reduction is performed using the specified execution policy `policy`. A copy of the collection is made + for the reduction process to keep the original collection unchanged. + +- `template value_type reduce(BinaryOperation&& op, value_type init) &&`: + Reduces the elements in the collection using the provided binary operation `op` and an initial value `init`. The + collection is modified using move semantics during the reduction process. The reduction is performed sequentially. + +- `template value_type reduce(BinaryOperation&& op, value_type init) const&`: + Reduces the elements in the collection using the provided binary operation `op` and an initial value `init`. A copy + of the collection is made for the reduction process to keep the original collection unchanged. The reduction is + performed sequentially. + +## Filter + +> **Note**: The following filtering methods are not available for collections that use `std::array` as the underlying +> container type. + +- `template void filter(ExecutionPolicy&& policy, UnaryPredicate&& predicate) &`: + Modifies the collection, removing elements for which the unary predicate `predicate` returns false. The filtering is + performed using the specified execution policy `policy`. + +- `template Collection filtered(ExecutionPolicy&& policy, UnaryPredicate&& predicate) &&`: + Modifies the collection and returns a reference to the modified collection using move semantics, removing elements + for which the unary predicate `predicate` returns false. The filtering is performed using the specified execution + policy `policy`. + +- `template Collection filtered(ExecutionPolicy&& policy, UnaryPredicate&& predicate) const&`: + Creates a copy of the collection and applies filtering to the copy. The original collection is unchanged. The + filtering is performed using the specified execution policy `policy`. + +- `template void filter(UnaryPredicate&& predicate) &`: Modifies the collection, + removing elements for which the unary predicate `predicate` returns false. The filtering is performed sequentially. + +- `template Collection filtered(UnaryPredicate&& predicate) &&`: Modifies the + collection and returns a reference to the modified collection using move semantics, removing elements for which the + unary predicate `predicate` returns false. The filtering is performed sequentially. + +- `template Collection filtered(UnaryPredicate&& predicate) const&`: + Creates a copy of the collection and applies filtering to the copy. The original collection is unchanged. The + filtering is performed sequentially. + +## Transform + +- `template auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) &&`: + Transforms the elements in the collection using the provided unary operation `op`. The result of the transformation + is stored in a new `Collection`. The transformation is performed using the specified execution + policy `policy`. + +- `template auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) const&`: + Transforms the elements in the collection using the provided unary operation `op`. The result of the transformation + is stored in a new `Collection`. A copy of the collection is made for the transformation process to keep the original + collection unchanged. The transformation is performed using the specified execution policy `policy`. + +- `template auto transform(UnaryOperation&& op) &&`: Transforms the + elements in the collection using the provided unary operation `op`. The result of the transformation is stored in a + new `Collection`. The transformation is performed sequentially. + +- `template auto transform(UnaryOperation&& op) const&`: Transforms the + elements in the collection using the provided unary operation `op`. The result of the transformation is stored in a + new `Collection`. A copy of the collection is made for the transformation process to keep the original collection + unchanged. The transformation is performed sequentially. + +## Flat Transform + +> **Note**: These methods are only available if the elements in the collection satisfy the `std::ranges::range` concept +> and does not use `std::array` as the underlying container type. + +- `template auto flat_transform(ExecutionPolicy&& policy, UnaryOperation&& op) &&`: + Flattens and transforms the elements in the collection using the provided unary operation `op`. The result of the + transformation is stored in a new `Collection`. The transformation is performed using the specified + execution policy `policy`. + +- `template auto flat_transform(ExecutionPolicy&& policy, UnaryOperation&& op) + const&`: Flattens and transforms the elements in the collection using the provided unary operation `op`. The result of + the transformation is stored in a new `Collection`. A copy of the collection is made for the transformation process to + keep the original collection unchanged. The transformation is performed using the specified execution + policy `policy`. + +- `template auto flat_transform(UnaryOperation&& op) &&`: Flattens and transforms the elements in + the collection using the provided unary operation `op`. The result of the transformation is stored in a + new `Collection`. The transformation is performed sequentially. + +- `template auto flat_transform(UnaryOperation&& op) const&`: Flattens and transforms the + elements in the collection using the provided unary operation `op`. The result of the transformation is stored in a + new `Collection`. A copy of the collection is made for the transformation process to keep the original collection + unchanged. The transformation is performed sequentially. + +## Sorts + +- `template void sort(ExecutionPolicy&& policy, Cmp cmp) &`: Sorts the + elements in the collection using the provided comparison function `cmp`. The sorting is performed using + the specified execution policy `policy`. + +- `template Collection sorted(ExecutionPolicy&& policy, Cmp cmp) &&`: + Sorts the elements in the collection using the provided comparison function `cmp`. The original collection is + modified using move semantics during the sorting process. The sorting is performed using the specified execution + policy `policy`. + +- `template Collection sorted(ExecutionPolicy&& policy, Cmp cmp) const&`: + Creates a copy of the collection and sorts the copy using the provided comparison function `cmp`. The original + collection is unchanged. The sorting is performed using the specified execution policy `policy`. + +- `template void sort(Cmp cmp = {}) &`: Sorts the elements in the collection + using the default comparison function `std::ranges::less`. The sorting is performed sequentially. + +- `template Collection sorted(Cmp cmp = {}) &&`: Sorts the elements in the + collection using the default comparison function `std::ranges::less`. The original collection is modified using move + semantics during the sorting process. The sorting is performed sequentially. + +- `template Collection sorted(Cmp cmp = {}) const&`: Creates a + copy of the collection and sorts the copy using the default comparison function `std::ranges::less`. The original + collection is unchanged. The sorting is performed sequentially. + +- `template void stable_sort(ExecutionPolicy&& policy, Cmp cmp) &`: Sorts + the elements in the collection using the provided comparison function `cmp`. The sorting is stable, meaning that the + relative order of equal elements is preserved. The sorting is performed using the specified execution + policy `policy`. + +- `template Collection stable_sorted(ExecutionPolicy&& policy, Cmp cmp) &&`: + Sorts the elements in the collection using the provided comparison function `cmp`. The original collection is + modified using move semantics during the sorting process. The sorting is stable, meaning that the relative order of + equal elements is preserved. The sorting is performed using the specified execution policy `policy`. + +- `template Collection stable_sorted(ExecutionPolicy&& policy, Cmp cmp) const&`: + Creates a copy of the collection and sorts the copy using the provided comparison function `cmp`. The sorting is + stable, meaning that the relative order of equal elements is preserved. The original collection is unchanged. The + sorting is performed using the specified execution policy `policy`. + +- `template void stable_sort(Cmp cmp = {}) &`: Sorts the elements in the + collection using the default comparison function `std::ranges::less`. The sorting is stable, meaning that the + relative order of equal elements is preserved. The sorting is performed sequentially. + +- `template Collection stable_sorted(Cmp cmp = {}) &&`: + Sorts the elements in the collection using the default comparison function `std::ranges::less`. The original + collection is modified using move semantics during the sorting process. The sorting is stable, meaning that the + relative order of equal elements is preserved. The sorting is performed sequentially. + +- `template Collection stable_sorted(Cmp cmp = {}) const&`: + Creates a copy of the collection and sorts the copy using the default comparison function `std::ranges::less`. The + sorting is stable, meaning that the relative order of equal elements is preserved. The original collection is + unchanged. The sorting is performed sequentially. + +## Reverse + +- `template void reverse(ExecutionPolicy&& policy) &`: Reverses the order of elements + in the collection in-place. The reversing is performed using the specified execution policy `policy`. + +- `template Collection reversed(ExecutionPolicy&& policy) &&`: Reverses the order of elements + in the collection in-place. The original collection is modified using move semantics during the reversing process. The + reversing is performed using the specified execution policy `policy`. + +- `template Collection reversed(ExecutionPolicy&& policy) const&`: Creates a copy of the + collection and reverses the order of elements in the copy. The original collection is unchanged. The reversing is + performed using the specified execution policy `policy`. + +- `void reverse() &`: Reverses the order of elements in the collection in-place. + +- `Collection reversed() &&`: Reverses the order of elements in the collection in-place. The original collection + is modified using move semantics during the reversing process. + +- `Collection reversed() const&`: Creates a copy of the collection and reverses the order of + elements in the copy. The original collection is unchanged. + +## Contains + +- `bool contains(const value_type& value) const`: Returns `true` if the `value` is present in the collection, + otherwise returns `false`. + +- `template bool contains(const U& value) const`: A template overload of `contains` that allows + checking if a value convertible to `value_type` is present in the collection. Returns `true` if the value is present, + otherwise returns `false`. + +## Implicit Conversions + +- `explicit operator bool() const noexcept`: Converts the collection to a `bool` value. Returns `true` if the + collection is not empty, otherwise returns `false`. + +- `operator container_type() const&`: Converts the collection to the underlying container type (`container_type`) when + used with a lvalue `Collection`. + +- `operator container_type() &&`: Converts the collection to the underlying container type (`container_type`) + when used with a rvalue `Collection`. This conversion allows moving the underlying container out of + the `Collection`. + +- `template operator R() const&`: Converts the collection to another range type (`R`) when used + with a lvalue `Collection`. The elements are copied to the new range. + +- `template operator R() &&`: Converts the collection to another range type (`R`) when + used with a rvalue `Collection`. The elements are moved to the new range. + +- `template operator std::pair() const&`: Converts the collection to a `std::pair` when + used with a lvalue `Collection`. The first two elements of the collection are used to initialize the pair. + +- `template operator std::pair() &&`: Converts the collection to a `std::pair` when used + with a rvalue `Collection`. The first two elements of the collection are moved to initialize the pair. + +- `template operator std::tuple() const&`: Converts the collection to a `std::tuple` when + used with a lvalue `Collection`. The first `sizeof...(Ts)` elements of the collection are used to initialize the + tuple. + +- `template operator std::tuple() &&`: Converts the collection to a `std::tuple` when used + with a rvalue `Collection`. The first `sizeof...(Ts)` elements of the collection are moved to initialize the tuple. + +## Deduction Guides + +- `template Collection(T (&)[N]) -> Collection>`: This deduction guide + allows the Collection class to be deduced when the source data is a C-style array (T[N]). + +- `template Collection(const T (&)[N]) -> Collection>`: Similar to the + previous guide, this deduction guide covers const C-style arrays (const T[N]). + +- `template Collection(T (&&)[N]) -> Collection>`: This guide handles + rvalue C-style arrays (T(&&)[N]). + +- `template Collection(const T (&&)[N]) -> Collection>`: This guide is for + const rvalue C-style arrays (const T(&&)[N]). + +- `template Collection(std::array&) -> Collection>`: This deduction + guide enables the Collection class to be deduced from an lvalue std::array. + +- `template Collection(const std::array&) -> Collection>`: This guide + handles const lvalue std::array. + +- `template Collection(std::array&&) -> Collection>`: This guide is + for rvalue std::array. + +- `template Collection(const std::array&&) -> Collection>`: This + guide handles const rvalue std::array. + +- `template Collection(std::initializer_list) -> Collection`: This deduction guide allows the Collection + class to be deduced when using an initializer list. + +- `template Collection(const std::vector&) -> Collection>`: This guide handles const + lvalue std::vector. + +- `template Collection(std::vector&&) -> Collection>`: This guide is for rvalue std:: + vector. + +- `template Collection(R&&) -> Collection>>`: + This guide enables the Collection class to be deduced from a range (R), making it compatible with various range-based + data sources. diff --git a/robocin/collection/collection.cpp b/robocin/collection/collection.cpp new file mode 100644 index 0000000..6974b7e --- /dev/null +++ b/robocin/collection/collection.cpp @@ -0,0 +1,6 @@ +// +// Created by José Cruz on 27/03/23. +// Copyright (c) 2023 RobôCIn. +// + +#include "robocin/collection/collection.h" diff --git a/robocin/collection/collection.h b/robocin/collection/collection.h new file mode 100644 index 0000000..7359fc3 --- /dev/null +++ b/robocin/collection/collection.h @@ -0,0 +1,1074 @@ +// +// Created by José Cruz on 27/03/23. +// Copyright (c) 2023 RobôCIn. +// + +#ifndef ROBOCIN_COLLECTION_COLLECTION_H +#define ROBOCIN_COLLECTION_COLLECTION_H + +#include "robocin/collection/internal/collection_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace robocin { + +template > +class Collection { + public: + // Member types ---------------------------------------------------------------------------------- + using container_type = Container; + + using value_type = typename container_type::value_type; + using size_type = typename container_type::size_type; + + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using reverse_iterator = typename container_type::reverse_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + // Friendships ----------------------------------------------------------------------------------- + template + friend class Collection; + + // Constructors ---------------------------------------------------------------------------------- + constexpr Collection() = default; + constexpr Collection(const Collection& other) = default; + constexpr Collection(Collection&& other) noexcept = default; + + constexpr explicit Collection(size_type count) : container_(count) {} + constexpr Collection(size_type count, const value_type& value) : container_(count, value) {} + + constexpr Collection(std::initializer_list il) : container_(il) {} + constexpr explicit Collection(const container_type& container) : container_(container) {} + constexpr explicit Collection(container_type&& container) : container_(std::move(container)) {} + + template + constexpr Collection(FirstIt first, LastIt last) : container_(first, last) {} + + template + constexpr explicit Collection(R&& range) : // NOLINT(bugprone-forwarding-reference-overload) + Collection(std::begin(std::forward(range)), std::end(std::forward(range))) {} + + // Destructor ------------------------------------------------------------------------------------ + constexpr ~Collection() = default; + + // Assignment operators -------------------------------------------------------------------------- + constexpr Collection& operator=(const Collection& other) = default; + constexpr Collection& operator=(Collection&& other) noexcept = default; + + // NOLINTBEGIN(*-assign*) + constexpr Collection& operator=(std::initializer_list il) { + return container_ = il, *this; + } + constexpr Collection& operator=(const container_type& container) { + return container_ = container, *this; + } + constexpr Collection& operator=(container_type&& container) { + return container_ = std::move(container), *this; + } + template + constexpr Collection& operator=(R&& range) { + return *this = Collection(std::forward(range)); + } + // NOLINTEND(*-assign*) + + // Element access -------------------------------------------------------------------------------- + constexpr reference at(size_type pos) { return container_.at(pos); } + constexpr const_reference at(size_type pos) const { return container_.at(pos); } + constexpr reference operator[](size_type pos) { return container_[pos]; } + constexpr const_reference operator[](size_type pos) const { return container_[pos]; } + constexpr reference front() { return container_.front(); } + constexpr const_reference front() const { return container_.front(); } + constexpr reference back() { return container_.back(); } + constexpr const_reference back() const { return container_.back(); } + + // Capacity -------------------------------------------------------------------------------------- + [[nodiscard]] constexpr bool empty() const { return container_.empty(); } + constexpr size_type size() const { return container_.size(); } + constexpr size_type max_size() const { return container_.max_size(); } // NOLINT(*-naming*) + constexpr void reserve(size_type new_cap) { container_.reserve(new_cap); } + constexpr size_type capacity() const { return container_.capacity(); } + constexpr void shrink_to_fit() { container_.shrink_to_fit(); } // NOLINT(*-naming*) + + // Modifiers ------------------------------------------------------------------------------------- + // NOLINTBEGIN(*-naming*) + constexpr void append_range(const Collection& other) { + container_.insert(container_.end(), other.container_.begin(), other.container_.end()); + } + constexpr void append_range(Collection&& other) { // NOLINT(*-rvalue*) + container_.insert(container_.end(), + std::make_move_iterator(other.container_.begin()), + std::make_move_iterator(other.container_.end())); + } + constexpr void append_range(std::initializer_list il) { + container_.insert(container_.end(), il); + } + template + constexpr void append_range(R&& range) { + container_.insert(container_.end(), + std::begin(std::forward(range)), + std::end(std::forward(range))); + } + // NOLINTEND(*-naming*) + + constexpr void clear() { container_.clear(); } + + template + constexpr iterator emplace(Args&&... args) { + return container_.emplace(std::forward(args)...); + } + + template // NOLINTNEXTLINE(*-naming*) + constexpr reference emplace_back(Args&&... args) { + return container_.emplace_back(std::forward(args)...); + } + + constexpr void erase(const_iterator pos) { container_.erase(pos); } + constexpr void erase(const_iterator first, const_iterator last) { container_.erase(first, last); } + + constexpr iterator insert(const_iterator pos, const value_type& value) { + return container_.insert(pos, value); + } + constexpr iterator insert(const_iterator pos, value_type&& value) { + return container_.insert(pos, std::move(value)); + } + constexpr iterator insert(const_iterator pos, size_type count, const value_type& value) { + return container_.insert(pos, count, value); + } + template + constexpr iterator insert(const_iterator pos, FirstIt first, LastIt last) { + return container_.insert(pos, first, last); + } + constexpr iterator insert(const_iterator pos, std::initializer_list il) { + return container_.insert(pos, il); + } + template + constexpr iterator insert(const_iterator pos, R&& range) { + return container_.insert(pos, + std::begin(std::forward(range)), + std::end(std::forward(range))); + } + + constexpr void push_back(const value_type& value) // NOLINT(*-naming*) + { + container_.push_back(value); + } + constexpr void push_back(value_type&& value) // NOLINT(*-naming*) + { + container_.push_back(std::move(value)); + } + + constexpr void pop_back() { container_.pop_back(); } // NOLINT(*-naming*) + + constexpr void resize(size_type count) { container_.resize(count); } + constexpr void resize(size_type count, const value_type& value) { + container_.resize(count, value); + } + + constexpr Collection resized(size_type count) && { + return container_.resize(count), std::move(*this); + } + constexpr Collection resized(size_type count) const& { return Collection(*this).resized(count); } + + constexpr Collection resized(size_type count, const value_type& value) && { + return container_.resize(count, value), std::move(*this); + } + constexpr Collection resized(size_type count, const value_type& value) const& { + return Collection(*this).resized(count, value); + } + + constexpr void swap(Collection& other) { container_.swap(other.container_); } + + // Comparison operators -------------------------------------------------------------------------- + inline constexpr bool operator==(const Collection& other) const { + return container_ == other.container_; + } + inline constexpr auto operator<=>(const Collection& other) const { + return container_ <=> other.container_; + } + + // Iterators ------------------------------------------------------------------------------------- + constexpr iterator begin() noexcept { return container_.begin(); } + constexpr const_iterator begin() const noexcept { return container_.begin(); } + constexpr iterator end() noexcept { return container_.end(); } + constexpr const_iterator end() const noexcept { return container_.end(); } + + constexpr reverse_iterator rbegin() noexcept { return container_.rbegin(); } + constexpr const_reverse_iterator rbegin() const noexcept { return container_.rbegin(); } + constexpr reverse_iterator rend() noexcept { return container_.rend(); } + constexpr const_reverse_iterator rend() const noexcept { return container_.rend(); } + + constexpr const_iterator cbegin() const noexcept { return container_.cbegin(); } + constexpr const_iterator cend() const noexcept { return container_.cend(); } + constexpr const_reverse_iterator crbegin() const noexcept { return container_.crbegin(); } + constexpr const_reverse_iterator crend() const noexcept { return container_.crend(); } + + // Reduce ---------------------------------------------------------------------------------------- + template + [[nodiscard]] constexpr value_type + reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) && { + return std::reduce(std::forward(policy), + std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + std::move(init), + std::forward(op)); + } + template + [[nodiscard]] constexpr value_type + reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) const& { + return std::reduce(std::forward(policy), + container_.begin(), + container_.end(), + std::move(init), + std::forward(op)); + } + + template + [[nodiscard]] constexpr value_type reduce(BinaryOperation&& op, value_type init) && { + return std::reduce(std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + std::move(init), + std::forward(op)); + } + template + [[nodiscard]] constexpr value_type reduce(BinaryOperation&& op, value_type init) const& { + return std::reduce(container_.begin(), + container_.end(), + std::move(init), + std::forward(op)); + } + + // Filter ---------------------------------------------------------------------------------------- + template + constexpr void filter(ExecutionPolicy&& policy, UnaryPredicate&& predicate) & { + iterator last = std::remove_if(std::forward(policy), + container_.begin(), + container_.end(), + std::not_fn(std::forward(predicate))); + + container_.erase(last, container_.end()); + } + template + [[nodiscard]] constexpr Collection filtered(ExecutionPolicy&& policy, + UnaryPredicate&& predicate) && { + filter(std::forward(policy), std::forward(predicate)); + return std::move(*this); + } + template + [[nodiscard]] constexpr Collection filtered(ExecutionPolicy&& policy, + UnaryPredicate&& predicate) const& { + return Collection(*this).filtered(std::forward(policy), + std::forward(predicate)); + } + + template + constexpr void filter(UnaryPredicate&& predicate) & { + iterator last = std::remove_if(container_.begin(), + container_.end(), + std::not_fn(std::forward(predicate))); + + container_.erase(last, container_.end()); + } + template + [[nodiscard]] constexpr Collection filtered(UnaryPredicate&& predicate) && { + return filter(std::forward(predicate)), std::move(*this); + } + template + [[nodiscard]] constexpr Collection filtered(UnaryPredicate&& predicate) const& { + return Collection(*this).filtered(std::forward(predicate)); + } + + // Transform ------------------------------------------------------------------------------------- + template + [[nodiscard]] constexpr auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) && { + using result_type = std::invoke_result_t; + + if constexpr (std::is_same_v) { + std::for_each(std::forward(policy), + container_.begin(), + container_.end(), + [map_value = std::forward(op)](value_type& value) { + value = std::move(map_value(value)); + }); + + return std::move(*this); + } else { + collection_internal::ResultCollection result(container_.size()); + + std::transform(std::forward(policy), + std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + result.begin(), + std::forward(op)); + + return result; + } + } + template + [[nodiscard]] constexpr auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) const& { + return Collection(*this).transform(std::forward(policy), + std::forward(op)); + } + + template + [[nodiscard]] constexpr auto transform(UnaryOperation&& op) && { + using result_type = std::invoke_result_t; + + if constexpr (std::is_same_v) { + auto&& map_value = std::forward(op); + for (value_type& value : container_) { + value = std::move(map_value(value)); + } + return std::move(*this); + } else { + collection_internal::ResultCollection result; + result.reserve(container_.size()); + + std::transform(std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + std::back_inserter(result.container_), + std::forward(op)); + + return result; + } + } + template + [[nodiscard]] constexpr auto transform(UnaryOperation&& op) const& { + return Collection(*this).transform(std::forward(op)); + } + + // Flat transform -------------------------------------------------------------------------------- + // NOLINTBEGIN(*-naming*) + template + requires(std::ranges::range) + [[nodiscard]] constexpr auto flat_transform(ExecutionPolicy&& policy, UnaryOperation&& op) && { + using input_type = std::ranges::range_value_t; + using result_type = std::invoke_result_t; + + auto&& pol = std::forward(policy); + + auto sizes_view = std::views::transform(container_, std::size); + size_type result_size = std::reduce(pol, sizes_view.begin(), sizes_view.end(), size_type{0}); + + collection_internal::ResultCollection result(result_size); + auto&& out = result.begin(); + + auto&& fun = std::forward(op); + + for (auto&& element : container_) { + out = std::transform(pol, + std::make_move_iterator(std::begin(element)), + std::make_move_iterator(std::end(element)), + out, + fun); + } + + return result; + } + template + requires(std::ranges::range) + [[nodiscard]] constexpr auto flat_transform(ExecutionPolicy&& policy, + UnaryOperation&& op) const& { + return Collection(*this).flat_transform(std::forward(policy), + std::forward(op)); + } + + template + requires(std::ranges::range) + [[nodiscard]] constexpr auto flat_transform(UnaryOperation&& op) && { + using input_type = std::ranges::range_value_t; + using result_type = std::invoke_result_t; + + auto sizes_view = std::views::transform(container_, std::size); + size_type result_size = std::reduce(sizes_view.begin(), sizes_view.end(), size_type{0}); + + collection_internal::ResultCollection result; + result.reserve(result_size); + + auto&& fun = std::forward(op); + for (auto&& element : container_) { + auto&& transformed = std::views::transform(element, fun); + result.insert(result.end(), + std::make_move_iterator(std::begin(transformed)), + std::make_move_iterator(std::end(transformed))); + } + + return result; + } + template + requires(std::ranges::range) + [[nodiscard]] constexpr auto flat_transform(UnaryOperation&& op) const& { + return Collection(*this).flat_transform(std::forward(op)); + } + // NOLINTEND(*-naming*) + + // Sorts ----------------------------------------------------------------------------------------- + template + constexpr void sort(ExecutionPolicy&& policy, Cmp cmp) & { + std::sort(std::forward(policy), container_.begin(), container_.end(), cmp); + } + template + [[nodiscard]] constexpr Collection sorted(ExecutionPolicy&& policy, Cmp cmp) && { + return sort(std::forward(policy), cmp), std::move(*this); + } + template + [[nodiscard]] constexpr Collection sorted(ExecutionPolicy&& policy, Cmp cmp) const& { + return Collection(*this).sorted(std::forward(policy), cmp); + } + + template + constexpr void sort(Cmp cmp = {}) & { + std::ranges::sort(container_, cmp); + } + template + [[nodiscard]] constexpr Collection sorted(Cmp cmp = {}) && { + return sort(cmp), std::move(*this); + } + template + [[nodiscard]] constexpr Collection sorted(Cmp cmp = {}) const& { + return Collection(*this).sorted(cmp); + } + + // NOLINTBEGIN(*-naming*) + template + constexpr void stable_sort(ExecutionPolicy&& policy, Cmp cmp) & { + std::stable_sort(std::forward(policy), + container_.begin(), + container_.end(), + cmp); + } + template + [[nodiscard]] constexpr Collection stable_sorted(ExecutionPolicy&& policy, Cmp cmp) && { + return stable_sort(std::forward(policy), cmp), std::move(*this); + } + template + [[nodiscard]] constexpr Collection stable_sorted(ExecutionPolicy&& policy, Cmp cmp) const& { + return Collection(*this).stable_sorted(std::forward(policy), cmp); + } + + template + constexpr void stable_sort(Cmp cmp = {}) & { + std::ranges::stable_sort(container_, cmp); + } + template + [[nodiscard]] constexpr Collection stable_sorted(Cmp cmp = {}) && { + return stable_sort(cmp), std::move(*this); + } + template + [[nodiscard]] constexpr Collection stable_sorted(Cmp cmp = {}) const& { + return Collection(*this).stable_sorted(cmp); + } + // NOLINTEND(*-naming*) + + // Reverse --------------------------------------------------------------------------------------- + template + constexpr void reverse(ExecutionPolicy&& policy) & { + std::reverse(std::forward(policy), container_.begin(), container_.end()); + } + template + [[nodiscard]] constexpr Collection reversed(ExecutionPolicy&& policy) && { + return reverse(std::forward(policy)), std::move(*this); + } + template + [[nodiscard]] constexpr Collection reversed(ExecutionPolicy&& policy) const& { + return Collection(*this).reversed(std::forward(policy)); + } + + constexpr void reverse() & { std::ranges::reverse(container_); } + [[nodiscard]] constexpr Collection reversed() && { return reverse(), std::move(*this); } + [[nodiscard]] constexpr Collection reversed() const& { return Collection(*this).reversed(); } + + // Contains -------------------------------------------------------------------------------------- + template + constexpr bool contains(ExecutionPolicy&& policy, const value_type& value) const { + return std::find(std::forward(policy), + container_.begin(), + container_.end(), + value) + != container_.end(); + } + + template + constexpr bool contains(ExecutionPolicy&& policy, const U& value) const { + return std::find(std::forward(policy), + container_.begin(), + container_.end(), + value) + != container_.end(); + } + + constexpr bool contains(const value_type& value) const { + return std::ranges::find(container_, value) != container_.end(); + } + + template + constexpr bool contains(const U& value) const { + return std::ranges::find(container_, value) != container_.end(); + } + + // Implicit conversions -------------------------------------------------------------------------- + constexpr explicit operator bool() const noexcept { return not container_.empty(); } + + constexpr operator container_type() const& { return container_; } // NOLINT(*-explicit*) + constexpr operator container_type() && { return std::move(container_); } // NOLINT(*-explicit*) + + template + constexpr operator R() const& { // NOLINT(*-explicit*) + return R(container_.begin(), container_.end()); + } + template + constexpr operator R() && { // NOLINT(*-explicit*) + return R(std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end())); + } + + template + constexpr operator std::pair() const& { // NOLINT(*-explicit*) + value_type first{}; + value_type second{}; + if (const_iterator it = container_.begin(); it != container_.end()) { + first = *it; + if (++it != end()) { + second = *it; + } + } + return {F(std::move(first)), S(std::move(second))}; + } + template + constexpr operator std::pair() && { // NOLINT(*-explicit*) + value_type first{}; + value_type second{}; + if (iterator it = container_.begin(); it != container_.end()) { + first = std::move(*it); + if (++it != end()) { + second = std::move(*it); + } + } + return {F(std::move(first)), S(std::move(second))}; + } + + template + constexpr operator std::tuple() const& { // NOLINT(*-explicit*) + std::tuple result{}; + collection_internal::assign(result, *this); + return result; + } + template + constexpr operator std::tuple() && { // NOLINT(*-explicit*) + std::tuple result{}; + collection_internal::assign(result, std::move(*this)); + return result; + } + + private: + container_type container_; +}; + +// Specialization for std::array ------------------------------------------------------------------- +template +class Collection> { + public: + // Member types ---------------------------------------------------------------------------------- + using container_type = std::array; + + using value_type = typename container_type::value_type; + using size_type = typename container_type::size_type; + + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using reverse_iterator = typename container_type::reverse_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + // Constructors ---------------------------------------------------------------------------------- + constexpr Collection() = default; + constexpr Collection(const Collection& other) = default; + constexpr Collection(Collection&& other) noexcept = default; + + constexpr explicit Collection(const container_type& container) : container_(container) {} + constexpr explicit Collection(container_type&& container) noexcept : + container_(std::move(container)) {} + + template + constexpr Collection(FirstIt first, LastIt last) { + for (iterator it = container_.begin(); first != last && it != container_.end(); ++it, ++first) { + *it = std::move(*first); + } + } + + constexpr Collection(std::initializer_list il) : Collection(il.begin(), il.end()) {} + + template + constexpr explicit Collection(R&& range) : // NOLINT(bugprone-forwarding-reference-overload) + Collection(std::begin(std::forward(range)), std::end(std::forward(range))) {} + + // Destructor ------------------------------------------------------------------------------------ + constexpr ~Collection() = default; + + // Assignment operators -------------------------------------------------------------------------- + constexpr Collection& operator=(const Collection& other) = default; + constexpr Collection& operator=(Collection&& other) noexcept = default; + + // NOLINTBEGIN(*-assign*) + constexpr Collection& operator=(std::initializer_list il) { + return container_ = il, *this; + } + constexpr Collection& operator=(const container_type& container) { + return container_ = container, *this; + } + constexpr Collection& operator=(container_type&& container) { + return container_ = std::move(container), *this; + } + template + constexpr Collection& operator=(R&& range) { + return *this = Collection(std::forward(range)); + } + // NOLINTEND(*-assign*) + + // Element access -------------------------------------------------------------------------------- + constexpr reference at(size_type pos) { return container_.at(pos); } + constexpr const_reference at(size_type pos) const { return container_.at(pos); } + constexpr reference operator[](size_type pos) { return container_[pos]; } + constexpr const_reference operator[](size_type pos) const { return container_[pos]; } + constexpr reference front() { return container_.front(); } + constexpr const_reference front() const { return container_.front(); } + constexpr reference back() { return container_.back(); } + constexpr const_reference back() const { return container_.back(); } + + // Capacity -------------------------------------------------------------------------------------- + [[nodiscard]] constexpr bool empty() const { return container_.empty(); } + constexpr size_type size() const { return container_.size(); } + + // Modifiers ------------------------------------------------------------------------------------- + constexpr void swap(Collection& other) { container_.swap(other.container_); } + + // Comparison operators -------------------------------------------------------------------------- + inline constexpr bool operator==(const Collection& other) const { + return container_ == other.container_; + } + inline constexpr auto operator<=>(const Collection& other) const { + return container_ <=> other.container_; + } + + // Iterators ------------------------------------------------------------------------------------- + constexpr iterator begin() noexcept { return container_.begin(); } + constexpr const_iterator begin() const noexcept { return container_.begin(); } + constexpr iterator end() noexcept { return container_.end(); } + constexpr const_iterator end() const noexcept { return container_.end(); } + + constexpr reverse_iterator rbegin() noexcept { return container_.rbegin(); } + constexpr const_reverse_iterator rbegin() const noexcept { return container_.rbegin(); } + constexpr reverse_iterator rend() noexcept { return container_.rend(); } + constexpr const_reverse_iterator rend() const noexcept { return container_.rend(); } + + constexpr const_iterator cbegin() const noexcept { return container_.cbegin(); } + constexpr const_iterator cend() const noexcept { return container_.cend(); } + constexpr const_reverse_iterator crbegin() const noexcept { return container_.crbegin(); } + constexpr const_reverse_iterator crend() const noexcept { return container_.crend(); } + + // Reduce ---------------------------------------------------------------------------------------- + template + [[nodiscard]] constexpr value_type + reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) && { + return std::reduce(std::forward(policy), + std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + std::move(init), + std::forward(op)); + } + template + [[nodiscard]] constexpr value_type + reduce(ExecutionPolicy&& policy, BinaryOperation&& op, value_type init) const& { + return std::reduce(std::forward(policy), + container_.begin(), + container_.end(), + std::move(init), + std::forward(op)); + } + + template + [[nodiscard]] constexpr value_type reduce(BinaryOperation&& op, value_type init) && { + return std::reduce(std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + std::move(init), + std::forward(op)); + } + template + [[nodiscard]] constexpr value_type reduce(BinaryOperation&& op, value_type init) const& { + return std::reduce(container_.begin(), + container_.end(), + std::move(init), + std::forward(op)); + } + + // Transform ------------------------------------------------------------------------------------- + template + [[nodiscard]] constexpr auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) && { + using result_type = std::invoke_result_t; + + if constexpr (std::is_same_v) { + std::for_each(std::forward(policy), + container_.begin(), + container_.end(), + [map_value = std::forward(op)](value_type& value) { + value = std::move(map_value(value)); + }); + + return std::move(*this); + } else { + collection_internal::ResultCollection result; + + std::transform(std::forward(policy), + std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + result.begin(), + std::forward(op)); + + return result; + } + } + template + [[nodiscard]] constexpr auto transform(ExecutionPolicy&& policy, UnaryOperation&& op) const& { + return Collection(*this).transform(std::forward(policy), + std::forward(op)); + } + + template + [[nodiscard]] constexpr auto transform(UnaryOperation&& op) && { + using result_type = std::invoke_result_t; + + if constexpr (std::is_same_v) { + auto&& map_value = std::forward(op); + for (value_type& value : container_) { + value = std::move(map_value(value)); + } + return std::move(*this); + } else { + collection_internal::ResultCollection result; + + std::transform(std::make_move_iterator(container_.begin()), + std::make_move_iterator(container_.end()), + result.begin(), + std::forward(op)); + + return result; + } + } + template + [[nodiscard]] constexpr auto transform(UnaryOperation&& op) const& { + return Collection(*this).transform(std::forward(op)); + } + + // Dynamicize ------------------------------------------------------------------------------------ + template