diff --git a/src/algorithm/nd_sort.cpp b/src/algorithm/nd_sort.cpp index 5d5e3fa0..3f23dea0 100644 --- a/src/algorithm/nd_sort.cpp +++ b/src/algorithm/nd_sort.cpp @@ -204,7 +204,10 @@ namespace gapp::algorithm::dtl std::for_each(last_idx, indices.rend(), [&](size_t col) noexcept { - if (dmat(row, col).load(std::memory_order_relaxed) == MAXIMAL) dmat(row, col).store(NONMAXIMAL, std::memory_order_relaxed); + if (dmat(row, col).load(std::memory_order_relaxed) == MAXIMAL) + { + dmat(row, col).store(NONMAXIMAL, std::memory_order_relaxed); + } }); }); }); diff --git a/src/utility/algorithm.hpp b/src/utility/algorithm.hpp index 23ee91c7..7d5ba2b9 100644 --- a/src/utility/algorithm.hpp +++ b/src/utility/algorithm.hpp @@ -43,7 +43,7 @@ namespace gapp::detail } template>> - requires std::strict_weak_order, std::iter_value_t> + requires std::strict_weak_order, std::iter_reference_t> std::vector argsort(Iter first, Iter last, Comp&& comp = {}) { GAPP_ASSERT(std::distance(first, last) >= 0); @@ -70,7 +70,7 @@ namespace gapp::detail } template>> - requires std::strict_weak_order, std::iter_value_t> + requires std::strict_weak_order, std::iter_reference_t> std::vector partial_argsort(Iter first, Iter middle, Iter last, Comp&& comp = {}) { GAPP_ASSERT(std::distance(first, middle) >= 0); @@ -104,33 +104,57 @@ namespace gapp::detail return indices; } - template>> - requires std::strict_weak_order, std::iter_value_t> - constexpr size_t argmax(Iter first, Iter last, Comp&& comp = {}) + template + requires std::invocable> + constexpr Iter max_element(Iter first, Iter last, F&& transform = {}) { - GAPP_ASSERT(std::distance(first, last) > 0); + GAPP_ASSERT(std::distance(first, last) >= 0); - const auto it = std::max_element(first, last, std::forward(comp)); - const size_t idx = std::distance(first, it); + if (first == last) return first; - if constexpr (detail::is_reverse_iterator_v) + Iter max_elem = first; + auto max_value = std::invoke(transform, *first); + + for (++first; first != last; ++first) { - const size_t last_idx = std::distance(first, last) - 1; - return last_idx - idx; + auto value = std::invoke(transform, *first); + if (!(max_value < value)) continue; + max_value = std::move(value); + max_elem = first; } - else + + return max_elem; + } + + template + requires std::invocable> + constexpr Iter min_element(Iter first, Iter last, F&& transform = {}) + { + GAPP_ASSERT(std::distance(first, last) >= 0); + + if (first == last) return first; + + Iter min_elem = first; + auto min_value = std::invoke(transform, *first); + + for (++first; first != last; ++first) { - return idx; + auto value = std::invoke(transform, *first); + if (!(value < min_value)) continue; + min_value = std::move(value); + min_elem = first; } + + return min_elem; } - template>> - requires std::strict_weak_order, std::iter_value_t> - constexpr size_t argmin(Iter first, Iter last, Comp&& comp = {}) + template + requires std::invocable> + constexpr size_t argmax(Iter first, Iter last, F&& transform = {}) { GAPP_ASSERT(std::distance(first, last) > 0); - - const auto it = std::min_element(first, last, std::forward(comp)); + + const auto it = detail::max_element(first, last, std::forward(transform)); const size_t idx = std::distance(first, it); if constexpr (detail::is_reverse_iterator_v) @@ -144,51 +168,28 @@ namespace gapp::detail } } - template + template requires std::invocable> - constexpr Iter max_element(Iter first, Iter last, F&& transform) + constexpr size_t argmin(Iter first, Iter last, F&& transform = {}) { GAPP_ASSERT(std::distance(first, last) > 0); - if (first == last) return first; - - Iter max_elem = first; - auto&& max_value = std::invoke(transform, *first); + const auto it = detail::min_element(first, last, std::forward(transform)); + const size_t idx = std::distance(first, it); - while (++first != last) + if constexpr (detail::is_reverse_iterator_v) { - auto&& value = std::invoke(transform, *first); - if (value <= max_value) continue; - max_value = std::forward(value); - max_elem = first; + const size_t last_idx = std::distance(first, last) - 1; + return last_idx - idx; } - - return max_elem; - } - - template - requires std::invocable> - constexpr Iter min_element(Iter first, Iter last, F&& transform) - { - GAPP_ASSERT(std::distance(first, last) > 0); - - if (first == last) return first; - - Iter min_elem = first; - auto&& min_value = std::invoke(transform, *first); - - while (++first != last) + else { - auto&& value = std::invoke(transform, *first); - if (value >= min_value) continue; - min_value = std::forward(value); - min_elem = first; + return idx; } - - return min_elem; } template + requires std::uniform_random_bit_generator> constexpr void partial_shuffle(Iter first, Iter middle, Iter last, URBG&& gen) { GAPP_ASSERT(std::distance(first, middle) >= 0); @@ -204,30 +205,15 @@ namespace gapp::detail } } - template - constexpr bool contains(Iter first, Iter last, const std::iter_value_t& val) + template + constexpr bool contains(Iter first, Iter last, const T& val) { return std::find(first, last, val) != last; } - template> Pred> - std::vector find_all(Iter first, Iter last, Pred&& pred) - { - GAPP_ASSERT(std::distance(first, last) >= 0); - - std::vector result; - result.reserve(last - first); - - for (; first != last; ++first) - { - if (std::invoke(pred, *first)) result.push_back(first); - } - - return result; - } - - template> Pred> - auto find_all_v(Iter first, Iter last, Pred&& pred) + template + requires std::predicate> + auto find_all(Iter first, Iter last, Pred&& pred) { GAPP_ASSERT(std::distance(first, last) >= 0); @@ -244,8 +230,26 @@ namespace gapp::detail return result; } + template + constexpr std::optional index_of(const Container& container, const T& val) + { + const auto found = std::find(container.begin(), container.end(), val); + const size_t idx = std::distance(container.begin(), found); + + return (found != container.end()) ? idx : std::optional{}; + } template Pred> + constexpr std::optional find_index(const Container& container, Pred&& pred) + { + const auto found = std::find_if(container.begin(), container.end(), std::forward(pred)); + const size_t idx = std::distance(container.begin(), found); + + return (found != container.end()) ? idx : std::optional{}; + } + + template + requires std::predicate std::vector find_indices(const Container& container, Pred&& pred) { std::vector indices; @@ -259,24 +263,6 @@ namespace gapp::detail return indices; } - template - constexpr std::optional index_of(const Container& container, const typename Container::value_type& val) - { - const auto found = std::find(container.begin(), container.end(), val); - const size_t idx = std::distance(container.begin(), found); - - return (found == container.end()) ? std::optional{} : idx; - } - - template Pred> - constexpr std::optional find_index(const Container& container, Pred&& pred) - { - const auto found = std::find_if(container.begin(), container.end(), std::forward(pred)); - const size_t idx = std::distance(container.begin(), found); - - return (found == container.end()) ? std::optional{} : idx; - } - template std::vector elementwise_min(std::vector left, std::span> right) { @@ -305,11 +291,11 @@ namespace gapp::detail return left; } - template - constexpr bool erase_first_stable(T& container, const typename T::value_type& value) + template + constexpr bool erase_first_stable(Container& container, const T& value) { - const auto found = std::find(container.cbegin(), container.cend(), value); - if (found != container.cend()) + const auto found = std::find(container.begin(), container.end(), value); + if (found != container.end()) { container.erase(found); return true; @@ -321,8 +307,6 @@ namespace gapp::detail requires detail::IndexableContainer> auto select(Container&& container, const std::vector& indices) { - GAPP_ASSERT(std::all_of(indices.begin(), indices.end(), [&](size_t idx) { return idx < container.size(); })); - using ValueType = typename std::remove_cvref_t::value_type; std::vector selected; diff --git a/src/utility/functional.hpp b/src/utility/functional.hpp index 761bd2aa..239f4601 100644 --- a/src/utility/functional.hpp +++ b/src/utility/functional.hpp @@ -28,7 +28,7 @@ namespace gapp::detail template F> auto map(const std::vector& cont, F&& f) { - using MappedType = std::invoke_result_t, ValueType>; + using MappedType = std::invoke_result_t; using ResultType = std::vector>; ResultType result; @@ -44,7 +44,7 @@ namespace gapp::detail template F> auto map(const std::vector& cont, F&& f) requires std::is_scalar_v { - using MappedType = std::invoke_result_t, ValueType>; + using MappedType = std::invoke_result_t; using ResultType = std::vector>; ResultType result(cont.size()); @@ -57,10 +57,10 @@ namespace gapp::detail } template - requires std::invocable, ValueType&> + requires std::invocable constexpr auto map(const std::array& cont, F&& f) { - using MappedType = std::invoke_result_t, ValueType>; + using MappedType = std::invoke_result_t; using ResultType = std::array, N>; ResultType result; diff --git a/test/unit/algorithm.cpp b/test/unit/algorithm.cpp index 765d5a9c..8648ed54 100644 --- a/test/unit/algorithm.cpp +++ b/test/unit/algorithm.cpp @@ -102,6 +102,30 @@ TEST_CASE("partial_argsort", "[algorithm]") } } +TEST_CASE("max_element", "[algorithm]") +{ + const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + + REQUIRE(*detail::max_element(nums.begin(), nums.end()) == 5.0); + REQUIRE(*detail::max_element(nums.rbegin(), nums.rend()) == 5.0); + + REQUIRE(detail::max_element(nums.begin(), nums.begin()) == nums.begin()); + + REQUIRE(*detail::max_element(nums.begin(), nums.end(), std::negate{}) == 0.0); +} + +TEST_CASE("min_element", "[algorithm]") +{ + const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; + + REQUIRE(*detail::min_element(nums.begin(), nums.end()) == 0.0); + REQUIRE(*detail::min_element(nums.rbegin(), nums.rend()) == 0.0); + + REQUIRE(detail::min_element(nums.begin(), nums.begin()) == nums.begin()); + + REQUIRE(*detail::min_element(nums.begin(), nums.end(), std::negate{}) == 5.0); +} + TEST_CASE("argmax", "[algorithm]") { const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 }; @@ -109,7 +133,7 @@ TEST_CASE("argmax", "[algorithm]") REQUIRE(detail::argmax(nums.begin(), nums.end()) == 3); REQUIRE(detail::argmax(nums.rbegin(), nums.rend()) == 3); - REQUIRE(detail::argmax(nums.begin(), nums.end(), std::greater<>{}) == 1); + REQUIRE(detail::argmax(nums.begin(), nums.end(), std::negate{}) == 1); REQUIRE(detail::argmax(nums.begin(), nums.begin() + 3) == 0); REQUIRE(detail::argmax(nums.begin() + 1, nums.end()) == 2); @@ -122,7 +146,7 @@ TEST_CASE("argmin", "[algorithm]") REQUIRE(detail::argmin(nums.begin(), nums.end()) == 1); REQUIRE(detail::argmin(nums.rbegin(), nums.rend()) == 1); - REQUIRE(detail::argmin(nums.begin(), nums.end(), std::greater<>{}) == 3); + REQUIRE(detail::argmin(nums.begin(), nums.end(), std::negate{}) == 3); REQUIRE(detail::argmin(nums.begin() + 2, nums.end()) == 2); } @@ -166,7 +190,7 @@ TEST_CASE("find_all", "[algorithm]") const std::vector nums = { 4, 0, 2, 5, 1 }; const auto odd_nums = detail::find_all(nums.begin(), nums.end(), is_odd); - REQUIRE(odd_nums == std::vector{ nums.end() - 2, nums.end() - 1 }); + REQUIRE(odd_nums == std::vector{ 5, 1 }); const auto big_nums = detail::find_all(nums.begin(), nums.end(), is_big); REQUIRE(big_nums.empty()); @@ -175,20 +199,6 @@ TEST_CASE("find_all", "[algorithm]") REQUIRE(detail::find_all(nums.begin(), nums.end(), always_false).size() == 0); } -TEST_CASE("find_all_v", "[algorithm]") -{ - const std::vector nums = { 4, 0, 2, 5, 1 }; - - const auto odd_nums = detail::find_all_v(nums.begin(), nums.end(), is_odd); - REQUIRE(odd_nums == std::vector{ 5, 1 }); - - const auto big_nums = detail::find_all_v(nums.begin(), nums.end(), is_big); - REQUIRE(big_nums.empty()); - - REQUIRE(detail::find_all_v(nums.begin(), nums.end(), always_true).size() == nums.size()); - REQUIRE(detail::find_all_v(nums.begin(), nums.end(), always_false).size() == 0); -} - TEST_CASE("find_indices", "[algorithm]") { const std::vector nums = { 4, 0, 2, 5, 1 };