Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ranges::minmax* #67

Merged
merged 2 commits into from
Dec 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ Implementation available in C++14 ~ :

| Header | Total | | C++17 | C++20 | C++23 | C++26 |
|-------------------------------------|---------------------------------------------------------|---|--------------------------------------------------------|---------------------------------------------------------|--------------------------------------------------------|--------------------------------------------------------|
| | ![](https://img.shields.io/badge/312/694-grey)![][p045] | | ![](https://img.shields.io/badge/44/113-grey)![][p039] | ![](https://img.shields.io/badge/231/429-grey)![][p054] | ![](https://img.shields.io/badge/59/144-grey)![][p041] | ![](https://img.shields.io/badge/18/135-grey)![][p013] |
| | ![](https://img.shields.io/badge/314/694-grey)![][p045] | | ![](https://img.shields.io/badge/44/113-grey)![][p039] | ![](https://img.shields.io/badge/233/429-grey)![][p054] | ![](https://img.shields.io/badge/59/144-grey)![][p041] | ![](https://img.shields.io/badge/18/135-grey)![][p013] |
| | | | | | | |
| [algorithm](#algorithm) | ![](https://img.shields.io/badge/53/115-grey)![][p046] | | ![](https://img.shields.io/badge/2/4-grey)![][p050] | ![](https://img.shields.io/badge/45/96-grey)![][p047] | ![](https://img.shields.io/badge/8/18-grey)![][p044] | ![](https://img.shields.io/badge/7/23-grey)![][p030] |
| [algorithm](#algorithm) | ![](https://img.shields.io/badge/55/115-grey)![][p048] | | ![](https://img.shields.io/badge/2/4-grey)![][p050] | ![](https://img.shields.io/badge/47/96-grey)![][p049] | ![](https://img.shields.io/badge/8/18-grey)![][p044] | ![](https://img.shields.io/badge/7/23-grey)![][p030] |
| [any](#any) | ![](https://img.shields.io/badge/5/5-grey)![][p100] | | ![](https://img.shields.io/badge/5/5-grey)![][p100] | | | |
| [array](#array) | ![](https://img.shields.io/badge/1/1-grey)![][p100] | | | ![](https://img.shields.io/badge/1/1-grey)![][p100] | | |
| [bit](#bit) | ![](https://img.shields.io/badge/2/14-grey)![][p014] | | | ![](https://img.shields.io/badge/1/13-grey)![][p008] | ![](https://img.shields.io/badge/1/1-grey)![][p100] | |
Expand Down Expand Up @@ -294,8 +294,8 @@ Description
| `ranges::max_element` | ![][c20ok] | |
| `ranges::min` | ![][c20ok] | |
| `ranges::min_element` | ![][c20ok] | |
| `ranges::minmax` | ![][c20no] | |
| `ranges::minmax_element` | ![][c20no] | |
| `ranges::minmax` | ![][c20ok] | |
| `ranges::minmax_element` | ![][c20ok] | |
| `ranges::clamp` | ![][c20no] | |
| `ranges::equal` | ![][c20ok] | |
| `ranges::lexicographical_compare` | ![][c20ok] | |
Expand Down
80 changes: 80 additions & 0 deletions include/preview/__algorithm/ranges/minmax.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// Created by yonggyulee on 2024.12.01
//

#ifndef PREVIEW_ALGORITHM_RANGES_MINMAX_H_
#define PREVIEW_ALGORITHM_RANGES_MINMAX_H_

#include <initializer_list>
#include <type_traits>

#include "preview/__algorithm/ranges/min_max_result.h"
#include "preview/__algorithm/ranges/minmax_element.h"
#include "preview/__concepts/copyable.h"
#include "preview/__core/inline_variable.h"
#include "preview/__functional/identity.h"
#include "preview/__functional/invoke.h"
#include "preview/__functional/less.h"
#include "preview/__functional/wrap_functor.h"
#include "preview/__iterator/indirectly_copyable_storable.h"
#include "preview/__iterator/indirect_strict_weak_order.h"
#include "preview/__iterator/projected.h"
#include "preview/__ranges/input_range.h"
#include "preview/__ranges/iterator_t.h"
#include "preview/__ranges/range_value_t.h"
#include "preview/__type_traits/conjunction.h"

namespace preview {
namespace ranges {

template<typename T>
using minmax_result = min_max_result<T>;

namespace detail {

struct minmax_niebloid {
private:
template<typename I, typename Proj, typename Comp, bool = /* false */ projectable<I, Proj>::value>
struct check_range : std::false_type {};
template<typename I, typename Proj, typename Comp>
struct check_range<I, Proj, Comp, true> : indirect_strict_weak_order<Comp, projected<I, Proj>> {};

public:
template<typename T, typename Proj = identity, typename Comp = ranges::less, std::enable_if_t<
indirect_strict_weak_order<Comp, projected<const T*, Proj>>::value, int> = 0>
constexpr ranges::minmax_result<const T&>
operator()(const T& a, const T& b, Comp comp = {}, Proj proj = {}) const {
return preview::invoke(comp, preview::invoke(proj, b), preview::invoke(proj, a))
? ranges::minmax_result<const T&>{b, a}
: ranges::minmax_result<const T&>{a, b};
}

template<typename T, typename Proj = identity, typename Comp = ranges::less, std::enable_if_t<conjunction_v<
copyable<T>,
check_range<const T*, Proj, Comp>>, int> = 0>
constexpr ranges::minmax_result<const T&>
operator()(std::initializer_list<T> il, Comp comp = {}, Proj proj = {}) const {
auto result = ranges::minmax_element(il, preview::wrap_functor(comp), preview::wrap_functor(proj));
return {*result.min, *result.max};
}

template<typename R, typename Proj = identity, typename Comp = ranges::less, std::enable_if_t<conjunction_v<
input_range<R>,
check_range<ranges::iterator_t<R>, Proj, Comp>,
indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*>
>, int> = 0>
constexpr ranges::minmax_result<range_value_t<R>>
operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
auto result = ranges::minmax_element(r, preview::wrap_functor(comp), preview::wrap_functor(proj));
return {std::move(*result.min), std::move(*result.max)};
}
};

} // namespace detail

PREVIEW_INLINE_VARIABLE constexpr detail::minmax_niebloid minmax{};

} // namespace ranges
} // namespace preview

#endif // PREVIEW_ALGORITHM_RANGES_MINMAX_H_
118 changes: 118 additions & 0 deletions include/preview/__algorithm/ranges/minmax_element.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// Created by yonggyulee on 2024.12.01
//

#ifndef PREVIEW_ALGORITHM_RANGES_MINMAX_ELEMENT_H_
#define PREVIEW_ALGORITHM_RANGES_MINMAX_ELEMENT_H_

#include <type_traits>

#include "preview/__algorithm/ranges/min_max_result.h"
#include "preview/__core/inline_variable.h"
#include "preview/__functional/identity.h"
#include "preview/__functional/invoke.h"
#include "preview/__functional/less.h"
#include "preview/__functional/wrap_functor.h"
#include "preview/__iterator/forward_iterator.h"
#include "preview/__iterator/indirect_strict_weak_order.h"
#include "preview/__iterator/projected.h"
#include "preview/__iterator/sentinel_for.h"
#include "preview/__ranges/begin.h"
#include "preview/__ranges/borrowed_iterator_t.h"
#include "preview/__ranges/end.h"
#include "preview/__ranges/iterator_t.h"
#include "preview/__type_traits/conjunction.h"
#include "preview/__utility/cxx20_rel_ops.h"

namespace preview {
namespace ranges {

template<typename I>
using minmax_element_result = min_max_result<I>;

namespace detail {

struct minmax_element_niebloid {
private:
template<typename I, typename Proj, typename Comp, bool = /* false */ projectable<I, Proj>::value>
struct check_range : std::false_type {};
template<typename I, typename Proj, typename Comp>
struct check_range<I, Proj, Comp, true> : indirect_strict_weak_order<Comp, projected<I, Proj>> {};

// lambda is not constexpr until C++17
template<typename Proj, typename Comp>
struct minmax_element_less {
Proj& proj;
Comp& comp;

template<typename I>
constexpr auto operator()(const I& x, const I& y) const {
return preview::invoke(comp, preview::invoke(proj, *x), preview::invoke(proj, *y));
}
};

public:
template<typename I, typename S, typename Proj = identity, typename Comp = ranges::less, std::enable_if_t<conjunction<
forward_iterator<I>,
sentinel_for<S, I>,
check_range<I, Proj, Comp>
>::value, int> = 0>
constexpr minmax_element_result<I> operator()(I first, S last, Comp comp = {}, Proj proj = {}) const {
using namespace preview::rel_ops;

I min_ = first;
I max_ = first;
if (first == last || ++first == last) {
return {min_, max_};
}

minmax_element_less<Proj, Comp> less{proj, comp};

if (less(first, min_))
min_ = first;
else
max_ = first;

while (++first != last) {
I i = first;
if (++first == last) {
if (less(i, min_))
min_ = i;
else if (!less(i, max_))
max_ = i;
break;
}

if (less(first, i)) {
if (less(first, min_))
min_ = first;
if (!less(i, max_))
max_ = i;
} else {
if (less(i, min_))
min_ = i;
if (!less(first, max_))
max_ = first;
}
}

return {min_, max_};
}

template<typename R, typename Proj = identity, typename Comp = ranges::less, std::enable_if_t<conjunction<
forward_range<R>,
check_range<iterator_t<R>, Proj, Comp>
>::value, int> = 0>
constexpr minmax_element_result<borrowed_iterator_t<R>> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
return (*this)(ranges::begin(r), ranges::end(r), preview::wrap_functor(comp), preview::wrap_functor(proj));
}
};

} // namespace detail

PREVIEW_INLINE_VARIABLE constexpr detail::minmax_element_niebloid minmax_element{};

} // namespace ranges
} // namespace preview

#endif // PREVIEW_ALGORITHM_RANGES_MINMAX_ELEMENT_H_
2 changes: 2 additions & 0 deletions include/preview/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
#include "preview/__algorithm/ranges/max_element.h"
#include "preview/__algorithm/ranges/min.h"
#include "preview/__algorithm/ranges/min_element.h"
#include "preview/__algorithm/ranges/minmax.h"
#include "preview/__algorithm/ranges/minmax_element.h"
#include "preview/__algorithm/ranges/min_max_result.h"
#include "preview/__algorithm/ranges/mismatch.h"
#include "preview/__algorithm/ranges/none_of.h"
Expand Down
83 changes: 83 additions & 0 deletions test/algorithm_ranges.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#include <map>
#include <numeric>
#include <string>
#include <random>
#include <unordered_map>
#include <vector>

#include "preview/array.h"
#include "preview/core.h"
#include "preview/functional.h"
#include "preview/ranges.h"
Expand Down Expand Up @@ -559,3 +561,84 @@ TEST(VERSIONED(AlgorithmRanges), search_n) {
auto it = ranges::search_n(nums2, 2, {4, 2});
EXPECT_EQ(it.size(), 2);
}


#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif

TEST(VERSIONED(AlgorithmRanges), minmax_element) {
{
// leftmost minimum v
const auto v = {3, 9, 1, 4, 1, 2, 5, 9};
// rightmost maximum ^

#if PREVIEW_CXX_VERSION >= 17
const auto [min, max] = ranges::minmax_element(v);
#else
const auto p = ranges::minmax_element(v);
const auto min = p.min;
const auto max = p.max;
#endif
EXPECT_EQ(min, v.begin() + 2);
EXPECT_EQ(max, v.begin() + 7);
}
{
static constexpr int v[] = {3, 9, 1, 4, 1, 2, 5, 9};
constexpr auto minmax = ranges::minmax_element(v);
static_assert(minmax.min == preview::ranges::begin(v) + 2, "");
static_assert(minmax.max == preview::ranges::begin(v) + 7, "");
}
{
const int v[] = {3, 9, 1, 4, 1, 2, 5, 9};
const auto r = v | views::take(5);
#if PREVIEW_CXX_VERSION >= 17
const auto [min, max] = ranges::minmax_element(r);
#else
const auto p = ranges::minmax_element(r);
const auto min = p.min;
const auto max = p.max;
#endif
EXPECT_EQ(min, r.begin() + 2);
EXPECT_EQ(max, r.begin() + 1);
}
}

TEST(VERSIONED(AlgorithmRanges), minmax) {
{
constexpr auto v = preview::to_array({3, 1, 4, 1, 5, 9, 2, 6, 5});
#if PREVIEW_CXX_VERSION >= 17
auto [min, max] = ranges::minmax(v);
#else
auto p = ranges::minmax(v);
auto min = p.min;
auto max = p.max;
#endif
EXPECT_EQ(min, 1);
EXPECT_EQ(max, 9);
}

{
std::random_device rd;
std::mt19937_64 generator(rd());
std::uniform_int_distribution<> distribution(0, 9);

for (int i = 0; i < 100; ++i) {
const int x1 = distribution(generator);
const int x2 = distribution(generator);
#if PREVIEW_CXX_VERSION >= 17
auto [min, max] = ranges::minmax(x1, x2);
#else
auto p = ranges::minmax(x1, x2);
auto min = p.min;
auto max = p.max;
#endif
ASSERT_EQ(min, (std::min)(x1, x2));
ASSERT_EQ(max, (std::max)(x1, x2));
}
}
}
Loading