forked from tcbrindle/flux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
These do the same as `min()`/`max()`/`minmax()`, but take multipass sequences and return a cursor to the requisite elements rather than an optional value, returning a past-the-end cursor if the input sequence is empty. Fixes tcbrindle#112
- Loading branch information
Showing
7 changed files
with
280 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
|
||
// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) | ||
// Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|
||
#ifndef FLUX_OP_FIND_MIN_MAX_HPP_INCLUDED | ||
#define FLUX_OP_FIND_MIN_MAX_HPP_INCLUDED | ||
|
||
#include <flux/core.hpp> | ||
#include <flux/op/minmax.hpp> | ||
|
||
namespace flux { | ||
|
||
namespace detail { | ||
|
||
struct find_min_fn { | ||
template <multipass_sequence Seq, | ||
strict_weak_order_for<Seq> Cmp = std::ranges::less> | ||
[[nodiscard]] | ||
constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const -> cursor_t<Seq> | ||
{ | ||
auto min = first(seq); | ||
if (!is_last(seq, min)) { | ||
for (auto cur = next(seq, min); !is_last(seq, cur); inc(seq, cur)) { | ||
if (std::invoke(cmp, read_at(seq, cur), read_at(seq, min))) { | ||
min = cur; | ||
} | ||
} | ||
} | ||
|
||
return min; | ||
} | ||
}; | ||
|
||
struct find_max_fn { | ||
template <multipass_sequence Seq, | ||
strict_weak_order_for<Seq> Cmp = std::ranges::less> | ||
[[nodiscard]] | ||
constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const -> cursor_t<Seq> | ||
{ | ||
auto max = first(seq); | ||
if (!is_last(seq, max)) { | ||
for (auto cur = next(seq, max); !is_last(seq, cur); inc(seq, cur)) { | ||
if (!std::invoke(cmp, read_at(seq, cur), read_at(seq, max))) { | ||
max = cur; | ||
} | ||
} | ||
} | ||
|
||
return max; | ||
} | ||
}; | ||
|
||
struct find_minmax_fn { | ||
template <multipass_sequence Seq, | ||
strict_weak_order_for<Seq> Cmp = std::ranges::less> | ||
[[nodiscard]] | ||
constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const | ||
-> minmax_result<cursor_t<Seq>> | ||
{ | ||
auto min = first(seq); | ||
auto max = min; | ||
if (!is_last(seq, min)) { | ||
for (auto cur = next(seq, min); !is_last(seq, cur); inc(seq, cur)) { | ||
auto&& elem = read_at(seq, cur); | ||
|
||
if (std::invoke(cmp, elem, read_at(seq, min))) { | ||
min = cur; | ||
} | ||
if (!std::invoke(cmp, elem, read_at(seq, max))) { | ||
max = cur; | ||
} | ||
} | ||
} | ||
|
||
return {std::move(min), std::move(max)}; | ||
} | ||
}; | ||
|
||
} // namespace detail | ||
|
||
FLUX_EXPORT inline constexpr auto find_min = detail::find_min_fn{}; | ||
FLUX_EXPORT inline constexpr auto find_max = detail::find_max_fn{}; | ||
FLUX_EXPORT inline constexpr auto find_minmax = detail::find_minmax_fn{}; | ||
|
||
template <typename D> | ||
template <typename Cmp> | ||
requires strict_weak_order_for<Cmp, D> | ||
constexpr auto inline_sequence_base<D>::find_min(Cmp cmp) | ||
{ | ||
return flux::find_min(derived(), std::move(cmp)); | ||
} | ||
|
||
template <typename D> | ||
template <typename Cmp> | ||
requires strict_weak_order_for<Cmp, D> | ||
constexpr auto inline_sequence_base<D>::find_max(Cmp cmp) | ||
{ | ||
return flux::find_max(derived(), std::move(cmp)); | ||
} | ||
|
||
template <typename D> | ||
template <typename Cmp> | ||
requires strict_weak_order_for<Cmp, D> | ||
constexpr auto inline_sequence_base<D>::find_minmax(Cmp cmp) | ||
{ | ||
return flux::find_minmax(derived(), std::move(cmp)); | ||
} | ||
|
||
} // namespace flux | ||
|
||
#endif // FLUX_OP_FIND_MIN_MAX_HPP_INCLUDED |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
#include <flux/core.hpp> | ||
|
||
#include <flux/op/fold.hpp> | ||
#include <flux/op/slice.hpp> | ||
|
||
namespace flux { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
|
||
// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com) | ||
// Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|
||
#include "catch.hpp" | ||
|
||
#include <flux.hpp> | ||
|
||
#include <array> | ||
|
||
#include "test_utils.hpp" | ||
|
||
namespace { | ||
|
||
struct IntPair { | ||
int a, b; | ||
friend bool operator==(IntPair const&, IntPair const&) = default; | ||
}; | ||
|
||
|
||
constexpr bool test_find_min() | ||
{ | ||
// Empty range -> no min value | ||
{ | ||
auto seq = flux::empty<int>; | ||
auto cur = flux::find_min(seq); | ||
STATIC_CHECK(seq.is_last(cur)); | ||
} | ||
|
||
// Basic min works as expected | ||
{ | ||
auto arr = std::array{5, 4, 3, 2, 1}; | ||
|
||
STATIC_CHECK(flux::read_at(arr, flux::find_min(arr)) == 1); | ||
|
||
auto ref = flux::ref(arr); | ||
STATIC_CHECK(ref[ref.find_min()] == 1); // much better! | ||
} | ||
|
||
// Can use custom comparator and projection | ||
{ | ||
IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; | ||
|
||
auto cur = flux::find_min(arr, flux::proj(std::greater{}, &IntPair::a)); | ||
|
||
STATIC_CHECK(not flux::is_last(arr, cur)); | ||
STATIC_CHECK(flux::read_at(arr, cur) == IntPair{5, 6}); | ||
|
||
} | ||
|
||
// If several elements are equally minimal, returns the first | ||
{ | ||
IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; | ||
|
||
auto cur = flux::find_min(arr, flux::proj(std::less{}, &IntPair::a)); | ||
|
||
STATIC_CHECK(not flux::is_last(arr, cur)); | ||
STATIC_CHECK(flux::read_at(arr, cur).b == 2); | ||
} | ||
|
||
return true; | ||
} | ||
static_assert(test_find_min()); | ||
|
||
constexpr bool test_find_max() | ||
{ | ||
// Empty range -> no max value | ||
{ | ||
auto seq = flux::filter(std::array{2, 4, 6, 8, 10}, flux::pred::odd); | ||
auto max = seq.find_max(); | ||
STATIC_CHECK(seq.is_last(max)); | ||
} | ||
|
||
// Basic max works as expected | ||
{ | ||
auto seq = flux::from(std::array{5, 4, 3, 2, 1}); | ||
auto cur = seq.find_max(); | ||
STATIC_CHECK(cur == 0); | ||
STATIC_CHECK(seq[cur] == 5); | ||
} | ||
|
||
// Can use custom comparator and projection | ||
{ | ||
IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; | ||
|
||
auto cur = flux::find_max(arr, flux::proj(std::greater{}, &IntPair::a)); | ||
|
||
STATIC_CHECK(flux::read_at(arr, cur) == IntPair{1, 2}); | ||
} | ||
|
||
// If several elements are equally maximal, returns the last | ||
{ | ||
IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; | ||
|
||
auto cur = flux::find_max(arr, flux::proj(std::less{}, &IntPair::b)); | ||
|
||
STATIC_CHECK(flux::read_at(arr, cur).b == 4); | ||
} | ||
|
||
return true; | ||
} | ||
static_assert(test_find_max()); | ||
|
||
constexpr bool test_find_minmax() | ||
{ | ||
// Empty range -> no minmax | ||
{ | ||
auto seq = flux::filter(std::array{2, 4, 6, 8, 10}, flux::pred::odd); | ||
auto [min, max] = seq.find_minmax(); | ||
STATIC_CHECK(seq.is_last(min)); | ||
STATIC_CHECK(seq.is_last(max)); | ||
} | ||
|
||
// Basic minmax works as expected | ||
{ | ||
auto seq = flux::from(std::array{5, 4, 3, 2, 1}); | ||
|
||
auto result = seq.find_minmax(); | ||
|
||
STATIC_CHECK(seq[result.min] == 1); | ||
STATIC_CHECK(seq[result.max] == 5); | ||
} | ||
|
||
// Can use custom comparator and projection | ||
{ | ||
IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; | ||
|
||
auto result = flux::find_minmax(arr, flux::proj(std::greater<>{}, &IntPair::a)); | ||
|
||
|
||
STATIC_CHECK(flux::read_at(arr, result.min) == IntPair{5, 6}); | ||
STATIC_CHECK(flux::read_at(arr, result.max) == IntPair{1, 2}); | ||
} | ||
|
||
// If several elements are equally minimal/maximal, returns the first/last resp. | ||
{ | ||
IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; | ||
auto [min, max] = flux::find_minmax(arr, flux::proj(std::ranges::less{}, &IntPair::a)); | ||
|
||
STATIC_CHECK(flux::read_at(arr, min) == IntPair{1, 2}); | ||
STATIC_CHECK(flux::read_at(arr, max) == IntPair{1, 4}); | ||
} | ||
|
||
return true; | ||
} | ||
static_assert(test_find_minmax()); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters