diff --git a/benchmark/sort_benchmark.cpp b/benchmark/sort_benchmark.cpp index 2ec45d0d..75a38119 100644 --- a/benchmark/sort_benchmark.cpp +++ b/benchmark/sort_benchmark.cpp @@ -72,7 +72,11 @@ int main() auto bench = an::Bench().relative(true).minEpochIterations(10); test_sort("random doubles (std)", std::ranges::sort, vec, bench); - test_sort("random doubles (flux)", flux::sort, vec, bench); + // Use a custom comparator because we know we don't have NaNs + auto custom_sort = [](auto& arg) { + return flux::sort(arg, flux::cmp::compare_floating_point_unchecked); + }; + test_sort("random doubles (flux)", custom_sort, vec, bench); } { diff --git a/docs/reference/adaptors.rst b/docs/reference/adaptors.rst index 1bed0397..ea0f6d76 100644 --- a/docs/reference/adaptors.rst +++ b/docs/reference/adaptors.rst @@ -1406,8 +1406,8 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o ^^^^^^^^^^^^^^^^^^ .. function:: - template \ - requires strict_weak_order_for && strict_weak_order_for \ + template \ + requires weak_ordering_for && weak_ordering_for \ auto set_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto; Returns a sequence adaptor which yields the set difference of the two input sequences :var:`seq1` and :var:`seq2`, ordered by the given comparison function :var:`cmp`. @@ -1418,7 +1418,7 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o :param seq1: The first sorted sequence. :param seq2: The second sorted sequence. - :param cmp: A binary predicate that takes two elements as arguments and returns true if the first element is less than the second. + :param cmp: A binary comparator whose return type is convertible to :type:`std::weak_ordering`. Both sequences must be sorted with respect to this comparator. :returns: A sequence adaptor that yields those elements of `seq1` which do not also appear in `seq2`. @@ -1466,8 +1466,8 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o ^^^^^^^^^^^^^^^^^^^^ .. function:: - template \ - requires strict_weak_order_for && strict_weak_order_for \ + template \ + requires weak_ordering_for && weak_ordering_for \ auto set_intersection(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto; Returns a sequence adaptor which yields the set intersection of the two input sequences :var:`seq1` and :var:`seq2`, ordered by the given comparison function :var:`cmp`. @@ -1478,7 +1478,7 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o :param seq1: The first sorted sequence. :param seq2: The second sorted sequence. - :param cmp: A binary predicate that takes two elements as arguments and returns true if the first element is less than the second. + :param cmp: A binary comparator whose return type is convertible to :type:`std::weak_ordering`. Both sequences must be sorted with respect to this comparator. :returns: A sequence adaptor that represents the set intersection of the two input sequences. @@ -1526,7 +1526,7 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. function:: - template \ + template \ requires see_below \ auto set_symmetric_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto; @@ -1546,11 +1546,13 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o std::common_reference_with, element_t> && std::common_reference_with, rvalue_element_t> && requires { typename std::common_type_t, value_t>; } && - strict_weak_order_for && - strict_weak_order_for + weak_ordering_for && + weak_ordering_for :param seq1: The first sequence to merge. :param seq2: The second sequence to merge. + :param cmp: A binary comparator whose return type is convertible to :type:`std::weak_ordering`. Both sequences must be sorted with respect to this comparator. + :returns: A sequence adaptor that yields elements of `seq1` and `seq2` which do not appear in both sequences. :models: @@ -1597,7 +1599,7 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o ^^^^^^^^^^^^^ .. function:: - template \ + template \ requires see_below \ auto set_union(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto; @@ -1613,12 +1615,12 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o std::common_reference_with, element_t> && std::common_reference_with, rvalue_element_t> && requires { typename std::common_type_t, value_t>; } && - strict_weak_order_for && - strict_weak_order_for + weak_ordering_for && + weak_ordering_for :param seq1: The first sorted sequence to merge. :param seq2: The second sorted sequence to merge. - :param cmp: A binary predicate that takes two elements as arguments and returns true if the first element is less than the second. + :param cmp: A binary comparator whose return type is convertible to :type:`std::weak_ordering`. Both sequences must be sorted with respect to this comparator. :returns: A sequence adaptor that represents the set union of the two input sequences. diff --git a/docs/reference/algorithms.rst b/docs/reference/algorithms.rst index 58f8e8d0..fd09b0e4 100644 --- a/docs/reference/algorithms.rst +++ b/docs/reference/algorithms.rst @@ -349,7 +349,7 @@ Algorithms ------------ .. function:: - template Cmp = std::ranges::less> \ + template Cmp = std::compare_three_way> \ auto find_max(Seq&& seq, Cmp cmp = {}) -> cursor_t; Returns a cursor to the maximum element of :var:`seq`, compared using :var:`cmp`. @@ -359,7 +359,7 @@ Algorithms .. note:: This behaviour differs from :func:`std::max_element()`, which returns an iterator to the *first* maximal element. :param seq: A multipass sequence - :param cmp: A comparator to use to find the maximum element, defaulting to :type:`std::ranges::less` + :param cmp: A comparator to use to find the maximum element, defaulting to :type:`std::compare_three_way` :returns: A cursor pointing to the maximum element of :var:`seq`. @@ -380,7 +380,7 @@ Algorithms ------------ .. function:: - template Cmp = std::ranges::less> \ + template Cmp = std::compare_three_way> \ auto find_min(Seq&& seq, Cmp cmp = {}) -> cursor_t; Returns a cursor to the minimum element of :var:`seq`, compared using :var:`cmp`. @@ -388,7 +388,7 @@ Algorithms If several elements are equally minimal, :func:`find_min` returns a cursor to the **first** such element. :param seq: A multipass sequence - :param cmp: A comparator to use to find the minimum element, defaulting to :type:`std::ranges::less` + :param cmp: A comparator to use to find the minimum element, defaulting to :type:`std::compare_three_way` :returns: A cursor pointing to the minimum element of :var:`seq`. @@ -409,7 +409,7 @@ Algorithms --------------- .. function:: - template Cmp = std::ranges::less> \ + template Cmp = std::compare_three_way> \ auto find_minmax(Seq&& seq, Cmp cmp = {}) -> minmax_result>; Returns a pair of cursors to the minimum and maximum elements of :var:`seq`, compared using :var:`cmp`. @@ -424,7 +424,7 @@ Algorithms but only does a single pass over :var:`seq`. :param seq: A multipass sequence - :param cmp: A comparator to use to find the maximum element, defaulting to :type:`std::ranges::less` + :param cmp: A comparator to use to find the maximum element, defaulting to :type:`std::compare_three_way` :returns: A cursor pointing to the maximum element of :var:`seq`. @@ -489,15 +489,14 @@ Algorithms ------- .. function:: - template \ - requires std::predicate, element_t> \ + template Cmp = std::compare_three_way> \ auto max(Seq&& seq, Cmp cmp = {}) -> optional>; ``min`` ------- .. function:: - template \ + template Cmp = std::compare_three_way> \ requires std::predicate, element_t> \ auto min(Seq&& seq, Cmp cmp = {}) -> optional>; @@ -507,7 +506,7 @@ Algorithms .. struct:: template minmax_result; .. function:: - template \ + template Cmp = std::compare_three_way> \ requires std::predicate, element_t> \ auto minmax(Seq&& seq, Cmp cmp = {}) -> optional>; @@ -547,7 +546,7 @@ Algorithms -------- .. function:: - template \ + template \ requires see_below \ auto sort(Seq&& seq, Cmp cmp = {}) -> void; diff --git a/docs/reference/concepts.rst b/docs/reference/concepts.rst index 301dfab5..d144e363 100644 --- a/docs/reference/concepts.rst +++ b/docs/reference/concepts.rst @@ -348,3 +348,88 @@ Concepts .. concept:: template writable_sequence_of + + A sequence :var:`Seq` models :expr:`writable_sequence_t` for a type :var:`T` if :expr:`element_t` is assignable from an object of type :var:`T`. + + The :concept:`writable_sequence_of` concept is defined as:: + + template + concept writable_sequence_of = + sequence && + requires (element_t e, T&& item) { + { e = std::forward(item) } -> std::same_as&>; + }; + +``element_swappable_with`` +-------------------------- + +.. concept:: + template element_swappable_with + + A pair of sequences :var:`Seq1` and :var:`Seq2` model :concept:`element_swappable_with` if their respective elements can be swapped, that is, we can assign to an element of :var:`Seq1` from an rvalue element of :var:`Seq2` and vice-versa. + + Formally, the :concept:`element_swappable_with` concept is defined as:: + + template + concept element_swappable_with = + std::constructible_from, rvalue_element_t> && + std::constructible_from, rvalue_element_t> && + writable_sequence_of> && + writable_sequence_of&&> && + writable_sequence_of> && + writable_sequence_of&&>; + + +``ordering_invocable`` +---------------------- + +.. concept:: + template \ + ordering_invocable + + The concept :concept:`ordering_invocable` signifies that the binary invocable :var:`Fn` return a value of one of the standard comparison categories, convertible to :var:`Cat`, for all combinations of arguments of types :var:`T` and :var:`U` + + Semantic requirements: + + * Let :expr:`r1 = fn(a, b)` and :expr:`r2 = fn(b, c)`. If :expr:`r1 == r2` and :expr:`r1 != std::partial_ordering::unordered`, then :expr:`fn(a, c) == r1`. + * :expr:`fn(a, b) == std::partial_ordering::less` if and only if :expr:`fn(b, a) == std::partial_ordering::greater` + + The :concept:`ordering_invocable` concept is defined as:: + + template + concept ordering_invocable_ = // exposition-only + std::regular_invocable && + std::same_as< + std::common_comparison_category_t>>, + Cat>, + Cat>; + + template + concept ordering_invocable = + ordering_invocable_ && + ordering_invocable_ && + ordering_invocable_ && + ordering_invocable_; + + +``weak_ordering_for`` +---------------------------- + +.. concept:: + template \ + weak_ordering_for + + Signifies that a binary callable :var:`Fn` forms a strict weak order over the elements of sequences :var:`Seq1` and :var:`Seq2`. + + It is defined as:: + + template + concept weak_ordering_for = + sequence && + sequence && + ordering_invocable, element_t, std::weak_ordering> && + ordering_invocable&, element_t, std::weak_ordering> && + ordering_invocable, value_t&, std::weak_ordering> && + ordering_invocable&, value_t&, std::weak_ordering> && + ordering_invocable, common_element_t, std::weak_ordering>; + diff --git a/example/docs/find_max.cpp b/example/docs/find_max.cpp index b86121a3..3427167a 100644 --- a/example/docs/find_max.cpp +++ b/example/docs/find_max.cpp @@ -21,7 +21,7 @@ int main() }; // Get a cursor to the maximum of the people vector, according to age - auto max_cur = flux::find_max(people, flux::proj(std::less{}, &Person::age)); + auto max_cur = flux::find_max(people, flux::proj(flux::cmp::compare, &Person::age)); // The oldest person is 63 assert(flux::read_at(people, max_cur).age == 63); diff --git a/example/docs/find_min.cpp b/example/docs/find_min.cpp index d5704573..ab9e31e7 100644 --- a/example/docs/find_min.cpp +++ b/example/docs/find_min.cpp @@ -21,7 +21,7 @@ int main() }; // Get a cursor to the maximum of the people vector, according to age - auto min_cur = flux::find_min(people, flux::proj(std::less{}, &Person::age)); + auto min_cur = flux::find_min(people, flux::proj(flux::cmp::compare, &Person::age)); // The youngest person is 29 assert(flux::read_at(people, min_cur).age == 29); diff --git a/example/docs/find_minmax.cpp b/example/docs/find_minmax.cpp index 950ab47e..42a84ea8 100644 --- a/example/docs/find_minmax.cpp +++ b/example/docs/find_minmax.cpp @@ -22,7 +22,7 @@ int main() // find_minmax() returns a pair of cursors which we can destructure // Here we'll get the min and max of the people vector, according to age - auto [min, max] = flux::find_minmax(people, flux::proj(std::less{}, &Person::age)); + auto [min, max] = flux::find_minmax(people, flux::proj(flux::cmp::compare, &Person::age)); // The "minimum" is Chris. Dani is the same age, but Chris appears earlier // in the sequence diff --git a/example/merge_intervals.cpp b/example/merge_intervals.cpp index afbc1648..e26a59dd 100644 --- a/example/merge_intervals.cpp +++ b/example/merge_intervals.cpp @@ -27,7 +27,7 @@ bool is_overlapped(interval_t a, interval_t b) auto merge = [](flux::sequence auto seq) -> interval_t { auto begin = flux::front(seq)->begin; - auto end = flux::max(seq, flux::proj(std::less<>{}, &interval_t::end))->end; + auto end = flux::max(seq, flux::proj(flux::cmp::compare, &interval_t::end))->end; return {begin, end}; }; @@ -36,7 +36,7 @@ int main() std::vector intervals = {{2, 4}, {7, 9}, {11, 13}, {6, 7}, {0, 3}}; // sort intervals according to begin - flux::sort(intervals, flux::proj(std::less{}, &interval_t::begin)); + flux::sort(intervals, flux::proj(flux::cmp::compare, &interval_t::begin)); flux::ref(intervals) .chunk_by(is_overlapped) diff --git a/include/flux/core/default_impls.hpp b/include/flux/core/default_impls.hpp index 744818b0..2ad50342 100644 --- a/include/flux/core/default_impls.hpp +++ b/include/flux/core/default_impls.hpp @@ -6,6 +6,7 @@ #ifndef FLUX_CORE_DEFAULT_IMPLS_HPP_INCLUDED #define FLUX_CORE_DEFAULT_IMPLS_HPP_INCLUDED +#include #include #include diff --git a/include/flux/core/functional.hpp b/include/flux/core/functional.hpp index 54751088..3520a163 100644 --- a/include/flux/core/functional.hpp +++ b/include/flux/core/functional.hpp @@ -120,9 +120,56 @@ struct unpack_fn { } }; +struct flip_fn { + template + struct flipped { + Fn fn; + + template + requires std::invocable + constexpr auto operator()(T&& t, U&& u, Args&&... args) & + -> decltype(auto) + { + return std::invoke(fn, FLUX_FWD(u), FLUX_FWD(t), FLUX_FWD(args)...); + } + + template + requires std::invocable + constexpr auto operator()(T&& t, U&& u, Args&&... args) const& + -> decltype(auto) + { + return std::invoke(fn, FLUX_FWD(u), FLUX_FWD(t), FLUX_FWD(args)...); + } + + template + requires std::invocable + constexpr auto operator()(T&& t, U&& u, Args&&... args) && + -> decltype(auto) + { + return std::invoke(std::move(fn), FLUX_FWD(u), FLUX_FWD(t), FLUX_FWD(args)...); + } + + template + requires std::invocable + constexpr auto operator()(T&& t, U&& u, Args&&... args) const && + -> decltype(auto) + { + return std::invoke(std::move(fn), FLUX_FWD(u), FLUX_FWD(t), FLUX_FWD(args)...); + } + }; + + template + [[nodiscard]] + constexpr auto operator()(Fn func) const + { + return flipped{std::move(func)}; + } +}; + } // namespace detail FLUX_EXPORT inline constexpr auto unpack = detail::unpack_fn{}; +FLUX_EXPORT inline constexpr auto flip = detail::flip_fn{}; namespace pred { @@ -260,32 +307,102 @@ namespace cmp { namespace detail { +struct compare_floating_point_unchecked_fn { + template + [[nodiscard]] + constexpr auto operator()(T a, T b) const noexcept + -> std::weak_ordering + { + return a < b ? std::weak_ordering::less + : a > b ? std::weak_ordering::greater + : std::weak_ordering::equivalent; + } +}; + struct min_fn { - template - requires std::strict_weak_order + template + requires same_decayed && + std::common_reference_with && + ordering_invocable [[nodiscard]] - constexpr auto operator()(T&& t, U&& u, Cmp cmp = Cmp{}) const + constexpr auto operator()(T&& a, U&& b, Cmp cmp = Cmp{}) const -> std::common_reference_t { - return std::invoke(cmp, u, t) ? FLUX_FWD(u) : FLUX_FWD(t); + return std::invoke(cmp, b, a) < 0 ? FLUX_FWD(b) : FLUX_FWD(a); } }; struct max_fn { - template - requires std::strict_weak_order + template + requires same_decayed && + std::common_reference_with && + ordering_invocable + [[nodiscard]] + constexpr auto operator()(T&& a, U&& b, Cmp cmp = Cmp{}) const + -> std::common_reference_t + { + return !(std::invoke(cmp, b, a) < 0) ? FLUX_FWD(b) : FLUX_FWD(a); + } +}; + +struct partial_min_fn { + template + requires same_decayed && + std::common_reference_with && + ordering_invocable + [[nodiscard]] + constexpr auto operator()(T&& a, U&& b) const + -> std::common_reference_t + { + return (b < a) ? FLUX_FWD(b) : FLUX_FWD(a); + } + + template + requires same_decayed && + std::common_reference_with && + ordering_invocable + [[nodiscard]] + constexpr auto operator()(T&& a, U&& b, Cmp cmp) const + -> std::common_reference_t + { + return std::invoke(cmp, b, a) < 0 ? FLUX_FWD(b) : FLUX_FWD(a); + } +}; + +struct partial_max_fn { + template + requires same_decayed && + std::common_reference_with && + ordering_invocable + [[nodiscard]] + constexpr auto operator()(T&& a, U&& b) const + -> std::common_reference_t + { + return !(b < a) ? FLUX_FWD(b) : FLUX_FWD(a); + } + + template + requires same_decayed && + std::common_reference_with && + ordering_invocable [[nodiscard]] - constexpr auto operator()(T&& t, U&& u, Cmp cmp = Cmp{}) const + constexpr auto operator()(T&& a, U&& b, Cmp cmp) const -> std::common_reference_t { - return !std::invoke(cmp, u, t) ? FLUX_FWD(u) : FLUX_FWD(t); + return !(std::invoke(cmp, b, a) < 0) ? FLUX_FWD(b) : FLUX_FWD(a); } }; } // namespace detail +FLUX_EXPORT inline constexpr auto compare = std::compare_three_way{}; +FLUX_EXPORT inline constexpr auto reverse_compare = flip(compare); +FLUX_EXPORT inline constexpr auto compare_floating_point_unchecked + = detail::compare_floating_point_unchecked_fn{}; FLUX_EXPORT inline constexpr auto min = detail::min_fn{}; FLUX_EXPORT inline constexpr auto max = detail::max_fn{}; +FLUX_EXPORT inline constexpr auto partial_min = detail::partial_min_fn{}; +FLUX_EXPORT inline constexpr auto partial_max = detail::partial_max_fn{}; } // namespace cmp diff --git a/include/flux/core/inline_sequence_base.hpp b/include/flux/core/inline_sequence_base.hpp index ab3db8af..11554fa9 100644 --- a/include/flux/core/inline_sequence_base.hpp +++ b/include/flux/core/inline_sequence_base.hpp @@ -431,18 +431,18 @@ struct inline_sequence_base { [[nodiscard]] constexpr auto find_if_not(Pred pred); - template - requires strict_weak_order_for + template + requires weak_ordering_for [[nodiscard]] constexpr auto find_max(Cmp cmp = Cmp{}); - template - requires strict_weak_order_for + template + requires weak_ordering_for [[nodiscard]] constexpr auto find_min(Cmp cmp = Cmp{}); - template - requires strict_weak_order_for + template + requires weak_ordering_for [[nodiscard]] constexpr auto find_minmax(Cmp cmp = Cmp{}); @@ -470,16 +470,16 @@ struct inline_sequence_base { requires bounded_sequence && detail::element_swappable_with; - template - requires strict_weak_order_for + template + requires weak_ordering_for constexpr auto max(Cmp cmp = Cmp{}); - template - requires strict_weak_order_for + template + requires weak_ordering_for constexpr auto min(Cmp cmp = Cmp{}); - template - requires strict_weak_order_for + template + requires weak_ordering_for constexpr auto minmax(Cmp cmp = Cmp{}); template @@ -496,11 +496,11 @@ struct inline_sequence_base { requires foldable, value_t> && std::default_initializable>; - template + template requires random_access_sequence && bounded_sequence && detail::element_swappable_with && - strict_weak_order_for + weak_ordering_for constexpr void sort(Cmp cmp = {}); constexpr auto product() diff --git a/include/flux/core/utils.hpp b/include/flux/core/utils.hpp index 1258d82d..a48ce1ef 100644 --- a/include/flux/core/utils.hpp +++ b/include/flux/core/utils.hpp @@ -6,16 +6,11 @@ #ifndef FLUX_CORE_UTILS_HPP_INCLUDED #define FLUX_CORE_UTILS_HPP_INCLUDED -#include - +#include #include -#include -#include -#include -#include #include -#include +#include namespace flux { @@ -26,6 +21,11 @@ FLUX_EXPORT template concept decays_to = std::same_as, To>; +FLUX_EXPORT +template +concept same_decayed = std::same_as, + std::remove_cvref_t>; + namespace detail { struct copy_fn { @@ -75,8 +75,24 @@ namespace detail { template concept any_of = (std::same_as || ...); +template +concept compares_as = std::same_as, Cat>; + +template +concept ordering_invocable_ = + std::regular_invocable && + compares_as>, Cat>; + } // namespace detail +FLUX_EXPORT +template +concept ordering_invocable = + detail::ordering_invocable_ && + detail::ordering_invocable_ && + detail::ordering_invocable_ && + detail::ordering_invocable_; + } // namespace flux #endif diff --git a/include/flux/op/compare.hpp b/include/flux/op/compare.hpp index fc2c206a..e92a8327 100644 --- a/include/flux/op/compare.hpp +++ b/include/flux/op/compare.hpp @@ -16,12 +16,6 @@ namespace flux { namespace detail { -template -concept is_comparison_category = - std::same_as || - std::same_as || - std::same_as; - struct compare_fn { private: template @@ -47,14 +41,11 @@ struct compare_fn { } public: - template - requires std::invocable, element_t> && - is_comparison_category, element_t>>> + template + requires ordering_invocable, element_t> constexpr auto operator()(Seq1 &&seq1, Seq2 &&seq2, Cmp cmp = {}) const -> std::decay_t< - std::invoke_result_t, element_t>> + std::invoke_result_t, element_t>> { constexpr bool can_memcmp = std::same_as && diff --git a/include/flux/op/detail/pdqsort.hpp b/include/flux/op/detail/pdqsort.hpp index 967056a3..0ba254bb 100644 --- a/include/flux/op/detail/pdqsort.hpp +++ b/include/flux/op/detail/pdqsort.hpp @@ -40,15 +40,12 @@ inline constexpr int pdqsort_cacheline_size = 64; template inline constexpr bool is_default_compare_v = false; -template -inline constexpr bool is_default_compare_v> = true; -template -inline constexpr bool is_default_compare_v> = true; template <> -inline constexpr bool is_default_compare_v = true; +inline constexpr bool is_default_compare_v = true; template <> -inline constexpr bool is_default_compare_v = true; - +inline constexpr bool is_default_compare_v = true; +template <> +inline constexpr bool is_default_compare_v = true; // Returns floor(log2(n)), assumes n > 0. template @@ -619,10 +616,14 @@ constexpr void pdqsort(Seq& seq, Comp& comp) is_default_compare_v> && std::is_arithmetic_v>; + auto comp_wrapper = [&comp](auto&& lhs, auto&& rhs) -> bool { + return std::is_lt(std::invoke(comp, FLUX_FWD(lhs), FLUX_FWD(rhs))); + }; + detail::pdqsort_loop(seq, first(seq), last(seq), - comp, + comp_wrapper, detail::log2(size(seq))); } diff --git a/include/flux/op/find_min_max.hpp b/include/flux/op/find_min_max.hpp index fc91e500..953f8d42 100644 --- a/include/flux/op/find_min_max.hpp +++ b/include/flux/op/find_min_max.hpp @@ -15,14 +15,14 @@ namespace detail { struct find_min_fn { template Cmp = std::ranges::less> + weak_ordering_for Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const -> cursor_t { 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))) { + if (std::invoke(cmp, read_at(seq, cur), read_at(seq, min)) < 0) { min = cur; } } @@ -34,14 +34,14 @@ struct find_min_fn { struct find_max_fn { template Cmp = std::ranges::less> + weak_ordering_for Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const -> cursor_t { 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))) { + if (!(std::invoke(cmp, read_at(seq, cur), read_at(seq, max)) < 0)) { max = cur; } } @@ -53,7 +53,7 @@ struct find_max_fn { struct find_minmax_fn { template Cmp = std::ranges::less> + weak_ordering_for Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const -> minmax_result> @@ -64,10 +64,10 @@ struct find_minmax_fn { 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))) { + if (std::invoke(cmp, elem, read_at(seq, min)) < 0) { min = cur; } - if (!std::invoke(cmp, elem, read_at(seq, max))) { + if (!(std::invoke(cmp, elem, read_at(seq, max)) < 0)) { max = cur; } } @@ -85,7 +85,7 @@ FLUX_EXPORT inline constexpr auto find_minmax = detail::find_minmax_fn{}; template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::find_min(Cmp cmp) { return flux::find_min(derived(), std::move(cmp)); @@ -93,7 +93,7 @@ constexpr auto inline_sequence_base::find_min(Cmp cmp) template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::find_max(Cmp cmp) { return flux::find_max(derived(), std::move(cmp)); @@ -101,7 +101,7 @@ constexpr auto inline_sequence_base::find_max(Cmp cmp) template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::find_minmax(Cmp cmp) { return flux::find_minmax(derived(), std::move(cmp)); diff --git a/include/flux/op/minmax.hpp b/include/flux/op/minmax.hpp index 027c8c2b..113205b0 100644 --- a/include/flux/op/minmax.hpp +++ b/include/flux/op/minmax.hpp @@ -23,13 +23,13 @@ struct minmax_result { namespace detail { struct min_op { - template Cmp = std::ranges::less> + template Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const -> flux::optional> { return flux::fold_first(FLUX_FWD(seq), [&](auto min, auto&& elem) -> value_t { - if (std::invoke(cmp, elem, min)) { + if (std::invoke(cmp, elem, min) < 0) { return value_t(FLUX_FWD(elem)); } else { return min; @@ -39,13 +39,13 @@ struct min_op { }; struct max_op { - template Cmp = std::ranges::less> + template Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const -> flux::optional> { return flux::fold_first(FLUX_FWD(seq), [&](auto max, auto&& elem) -> value_t { - if (!std::invoke(cmp, elem, max)) { + if (!(std::invoke(cmp, elem, max) < 0)) { return value_t(FLUX_FWD(elem)); } else { return max; @@ -55,7 +55,7 @@ struct max_op { }; struct minmax_op { - template Cmp = std::ranges::less> + template Cmp = std::compare_three_way> [[nodiscard]] constexpr auto operator()(Seq&& seq, Cmp cmp = Cmp{}) const -> flux::optional>> @@ -71,10 +71,10 @@ struct minmax_op { value_t(flux::read_at(seq, cur))}; auto fold_fn = [&](R mm, auto&& elem) -> R { - if (std::invoke(cmp, elem, mm.min)) { + if (std::invoke(cmp, elem, mm.min) < 0) { mm.min = value_t(elem); } - if (!std::invoke(cmp, elem, mm.max)) { + if (!(std::invoke(cmp, elem, mm.max) < 0)) { mm.max = value_t(FLUX_FWD(elem)); } return mm; @@ -93,7 +93,7 @@ FLUX_EXPORT inline constexpr auto minmax = detail::minmax_op{}; template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::max(Cmp cmp) { return flux::max(derived(), std::move(cmp)); @@ -101,7 +101,7 @@ constexpr auto inline_sequence_base::max(Cmp cmp) template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::min(Cmp cmp) { return flux::min(derived(), std::move(cmp)); @@ -109,7 +109,7 @@ constexpr auto inline_sequence_base::min(Cmp cmp) template template - requires strict_weak_order_for + requires weak_ordering_for constexpr auto inline_sequence_base::minmax(Cmp cmp) { return flux::minmax(derived(), std::move(cmp)); diff --git a/include/flux/op/requirements.hpp b/include/flux/op/requirements.hpp index 545f632a..93c7299c 100644 --- a/include/flux/op/requirements.hpp +++ b/include/flux/op/requirements.hpp @@ -57,14 +57,14 @@ concept foldable = FLUX_EXPORT template -concept strict_weak_order_for = +concept weak_ordering_for = sequence && sequence && - std::strict_weak_order, element_t> && - std::strict_weak_order&, element_t> && - std::strict_weak_order, value_t&> && - std::strict_weak_order&, value_t&> && - std::strict_weak_order, common_element_t>; + ordering_invocable, element_t, std::weak_ordering> && + ordering_invocable&, element_t, std::weak_ordering> && + ordering_invocable, value_t&, std::weak_ordering> && + ordering_invocable&, value_t&, std::weak_ordering> && + ordering_invocable, common_element_t, std::weak_ordering>; } // namespace flux diff --git a/include/flux/op/set_adaptors.hpp b/include/flux/op/set_adaptors.hpp index beb9d41d..30e5c3ca 100644 --- a/include/flux/op/set_adaptors.hpp +++ b/include/flux/op/set_adaptors.hpp @@ -59,14 +59,13 @@ struct set_union_adaptor return; } - if (std::invoke(self.cmp_, flux::read_at(self.base2_, cur.base2_cursor), - flux::read_at(self.base1_, cur.base1_cursor))) { + auto r = std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), + flux::read_at(self.base2_, cur.base2_cursor)); + + if (r == std::weak_ordering::greater) { cur.active_ = cursor_type::second; return; - } - - if (not std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), - flux::read_at(self.base2_, cur.base2_cursor))) { + } else if (r == std::weak_ordering::equivalent) { flux::inc(self.base2_, cur.base2_cursor); } @@ -188,13 +187,12 @@ struct set_difference_adaptor return; } - if(std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), - flux::read_at(self.base2_, cur.base2_cursor))) { - return; - } + auto r = std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), + flux::read_at(self.base2_, cur.base2_cursor)); - if(not std::invoke(self.cmp_, flux::read_at(self.base2_, cur.base2_cursor), - flux::read_at(self.base1_, cur.base1_cursor))) { + if (r == std::weak_ordering::less) { + return; + } else if (r == std::weak_ordering::equivalent) { flux::inc(self.base1_, cur.base1_cursor); } @@ -294,19 +292,17 @@ struct set_symmetric_difference_adaptor return; } - if(std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), - flux::read_at(self.base2_, cur.base2_cursor))) { + auto r = std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), + flux::read_at(self.base2_, cur.base2_cursor)); + + if (r == std::weak_ordering::less) { cur.state_ = cursor_type::first; return; + } else if (r == std::weak_ordering::greater) { + cur.state_ = cursor_type::second; + return; } else { - if(std::invoke(self.cmp_, flux::read_at(self.base2_, cur.base2_cursor), - flux::read_at(self.base1_, cur.base1_cursor))) { - cur.state_ = cursor_type::second; - return; - } else { - flux::inc(self.base1_, cur.base1_cursor); - } - + flux::inc(self.base1_, cur.base1_cursor); flux::inc(self.base2_, cur.base2_cursor); } } @@ -436,16 +432,15 @@ struct set_intersection_adaptor while(not flux::is_last(self.base1_, cur.base1_cursor) && not flux::is_last(self.base2_, cur.base2_cursor)) { - if(std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), - flux::read_at(self.base2_, cur.base2_cursor))) { - flux::inc(self.base1_, cur.base1_cursor); - } else { + auto r = std::invoke(self.cmp_, flux::read_at(self.base1_, cur.base1_cursor), + flux::read_at(self.base2_, cur.base2_cursor)); - if(not std::invoke(self.cmp_, flux::read_at(self.base2_, cur.base2_cursor), - flux::read_at(self.base1_, cur.base1_cursor))) { - return; - } + if (r == std::weak_ordering::less) { + flux::inc(self.base1_, cur.base1_cursor); + } else if (r == std::weak_ordering::greater) { flux::inc(self.base2_, cur.base2_cursor); + } else { + return; } } } @@ -508,10 +503,10 @@ concept set_op_compatible = requires { typename std::common_type_t, value_t>; }; struct set_union_fn { - template + template requires set_op_compatible && - strict_weak_order_for && - strict_weak_order_for + weak_ordering_for && + weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -520,9 +515,9 @@ struct set_union_fn { }; struct set_difference_fn { - template - requires strict_weak_order_for && - strict_weak_order_for + template + requires weak_ordering_for && + weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -531,10 +526,10 @@ struct set_difference_fn { }; struct set_symmetric_difference_fn { - template + template requires set_op_compatible && - strict_weak_order_for && - strict_weak_order_for + weak_ordering_for && + weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { @@ -543,9 +538,9 @@ struct set_symmetric_difference_fn { }; struct set_intersection_fn { - template - requires strict_weak_order_for && - strict_weak_order_for + template + requires weak_ordering_for && + weak_ordering_for [[nodiscard]] constexpr auto operator()(Seq1&& seq1, Seq2&& seq2, Cmp cmp = {}) const { diff --git a/include/flux/op/sort.hpp b/include/flux/op/sort.hpp index 939067f1..93392c11 100644 --- a/include/flux/op/sort.hpp +++ b/include/flux/op/sort.hpp @@ -11,10 +11,10 @@ namespace flux { namespace detail { struct sort_fn { - template + template requires bounded_sequence && element_swappable_with && - strict_weak_order_for + weak_ordering_for constexpr auto operator()(Seq&& seq, Cmp cmp = {}) const { auto wrapper = flux::unchecked(flux::from_fwd_ref(FLUX_FWD(seq))); @@ -31,7 +31,7 @@ template requires random_access_sequence && bounded_sequence && detail::element_swappable_with && - strict_weak_order_for + weak_ordering_for constexpr void inline_sequence_base::sort(Cmp cmp) { return flux::sort(derived(), std::ref(cmp)); diff --git a/test/test_find_min_max.cpp b/test/test_find_min_max.cpp index 3db62c47..63bb3045 100644 --- a/test/test_find_min_max.cpp +++ b/test/test_find_min_max.cpp @@ -38,7 +38,7 @@ constexpr bool test_find_min() { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - auto cur = flux::find_min(arr, flux::proj(std::greater{}, &IntPair::a)); + auto cur = flux::find_min(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)); STATIC_CHECK(not flux::is_last(arr, cur)); STATIC_CHECK(flux::read_at(arr, cur) == IntPair{5, 6}); @@ -49,7 +49,7 @@ constexpr bool test_find_min() { IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; - auto cur = flux::find_min(arr, flux::proj(std::less{}, &IntPair::a)); + auto cur = flux::find_min(arr, flux::proj(flux::cmp::compare, &IntPair::a)); STATIC_CHECK(not flux::is_last(arr, cur)); STATIC_CHECK(flux::read_at(arr, cur).b == 2); @@ -80,7 +80,7 @@ constexpr bool test_find_max() { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - auto cur = flux::find_max(arr, flux::proj(std::greater{}, &IntPair::a)); + auto cur = flux::find_max(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)); STATIC_CHECK(flux::read_at(arr, cur) == IntPair{1, 2}); } @@ -89,7 +89,7 @@ constexpr bool test_find_max() { IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; - auto cur = flux::find_max(arr, flux::proj(std::less{}, &IntPair::b)); + auto cur = flux::find_max(arr, flux::proj(flux::cmp::compare, &IntPair::b)); STATIC_CHECK(flux::read_at(arr, cur).b == 4); } @@ -122,7 +122,7 @@ constexpr bool test_find_minmax() { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - auto result = flux::find_minmax(arr, flux::proj(std::greater<>{}, &IntPair::a)); + auto result = flux::find_minmax(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)); STATIC_CHECK(flux::read_at(arr, result.min) == IntPair{5, 6}); @@ -132,7 +132,7 @@ constexpr bool test_find_minmax() // 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)); + auto [min, max] = flux::find_minmax(arr, flux::proj(flux::cmp::compare, &IntPair::a)); STATIC_CHECK(flux::read_at(arr, min) == IntPair{1, 2}); STATIC_CHECK(flux::read_at(arr, max) == IntPair{1, 4}); diff --git a/test/test_minmax.cpp b/test/test_minmax.cpp index e2c06b7b..d57806c4 100644 --- a/test/test_minmax.cpp +++ b/test/test_minmax.cpp @@ -33,13 +33,13 @@ constexpr bool test_min() // Can use custom comparator and projection { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - STATIC_CHECK(flux::min(arr, flux::proj(std::greater<>{}, &IntPair::a)).value() == IntPair{5, 6}); + STATIC_CHECK(flux::min(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)).value() == IntPair{5, 6}); } // If several elements are equally minimal, returns the first { IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; - STATIC_CHECK(flux::min(arr, flux::proj(std::ranges::less{}, &IntPair::a))->b == 2); + STATIC_CHECK(flux::min(arr, flux::proj(flux::cmp::compare, &IntPair::a))->b == 2); } return true; @@ -64,13 +64,13 @@ constexpr bool test_max() // Can use custom comparator and projection { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - STATIC_CHECK(flux::max(arr, flux::proj(std::greater<>{}, &IntPair::a)).value() == IntPair{1, 2}); + STATIC_CHECK(flux::max(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)).value() == IntPair{1, 2}); } // If several elements are equally maximal, returns the last { IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; - STATIC_CHECK(flux::max(arr, flux::proj(std::ranges::less{}, &IntPair::a))->b == 4); + STATIC_CHECK(flux::max(arr, flux::proj(flux::cmp::compare, &IntPair::a))->b == 4); } return true; @@ -91,7 +91,7 @@ constexpr bool test_minmax() STATIC_CHECK(result.min == 1); STATIC_CHECK(result.max == 5); - result = flux::from(std::array{5, 4, 3, 2, 1}).minmax(std::greater{}).value(); + result = flux::from(std::array{5, 4, 3, 2, 1}).minmax(flux::cmp::reverse_compare).value(); STATIC_CHECK(result.min == 5); STATIC_CHECK(result.max == 1); } @@ -99,7 +99,7 @@ constexpr bool test_minmax() // Can use custom comparator and projection { IntPair arr[] = { {1, 2}, {3, 4}, {5, 6}}; - auto result = flux::minmax(arr, flux::proj(std::greater<>{}, &IntPair::a)).value(); + auto result = flux::minmax(arr, flux::proj(flux::cmp::reverse_compare, &IntPair::a)).value(); STATIC_CHECK(result.min == IntPair{5, 6}); STATIC_CHECK(result.max == IntPair{1, 2}); } @@ -107,7 +107,7 @@ constexpr bool test_minmax() // If several elements are equally minimal/maximal, returns the first/last resp. { IntPair arr[] = { {1, 2}, {1, 3}, {1, 4}}; - auto result = flux::minmax(arr, flux::proj(std::ranges::less{}, &IntPair::a)).value(); + auto result = flux::minmax(arr, flux::proj(flux::cmp::compare, &IntPair::a)).value(); STATIC_CHECK(result.min == IntPair{1, 2}); STATIC_CHECK(result.max == IntPair{1, 4}); } diff --git a/test/test_overflow.cpp b/test/test_overflow.cpp index e052cfe5..4f95728b 100644 --- a/test/test_overflow.cpp +++ b/test/test_overflow.cpp @@ -11,6 +11,7 @@ #ifdef USE_MODULES import flux; #else +#include #include #endif diff --git a/test/test_predicates.cpp b/test/test_predicates.cpp index f232dd21..a171d54a 100644 --- a/test/test_predicates.cpp +++ b/test/test_predicates.cpp @@ -152,21 +152,12 @@ constexpr bool test_comparisons() STATIC_CHECK(cmp::min(i, i + 1) == 1); } - // mixed-type min is a prvalue - { - int const i = 10; - long const j = 5; - using M = decltype(cmp::min(i, j)); - static_assert(std::same_as); - STATIC_CHECK(cmp::min(i, j) == 5); - } - // Custom comparators work okay with min() { - Test t1{1, 3.0}; - Test t2{1, 2.0}; + Test t1{3, 1.0}; + Test t2{2, 1.0}; - auto cmp_test = [](Test t1, Test t2) { return t1.d < t2.d; }; + auto cmp_test = [](Test t1, Test t2) { return t1.i <=> t2.i; }; STATIC_CHECK(cmp::min(t1, t2, cmp_test) == t2); } @@ -180,7 +171,7 @@ constexpr bool test_comparisons() Test t1{1, 3.0}; Test t2{1, 2.0}; - STATIC_CHECK(cmp::min(t1, t2, flux::proj(std::less{}, &Test::i)) == t1); + STATIC_CHECK(cmp::min(t1, t2, flux::proj(cmp::compare, &Test::i)) == t1); } // max of two same-type non-const lvalue references is an lvalue @@ -208,23 +199,14 @@ constexpr bool test_comparisons() STATIC_CHECK(cmp::max(i, i + 1) == 2); } - // mixed-type max is a prvalue - { - int const i = 10; - long const j = 5; - using M = decltype(cmp::max(i, j)); - static_assert(std::same_as); - STATIC_CHECK(cmp::max(i, j) == 10); - } - // Custom comparators work okay with max() { Test t1{1, 3.0}; Test t2{1, 2.0}; - auto cmp_test = [](Test t1, Test t2) { return t1.d < t2.d; }; + auto cmp_test = [](Test t1, Test t2) { return t1.i <=> t2.i; }; - STATIC_CHECK(cmp::max(t1, t2, cmp_test) == t1); + STATIC_CHECK(cmp::max(t1, t2, cmp_test) == t2); } // If arguments are equal, max() returns the second @@ -236,13 +218,79 @@ constexpr bool test_comparisons() Test t1{1, 3.0}; Test t2{1, 2.0}; - STATIC_CHECK(cmp::max(t1, t2, flux::proj(std::less{}, &Test::i)) == t2); + STATIC_CHECK(cmp::max(t1, t2, flux::proj(cmp::compare, &Test::i)) == t2); + } + + // Reverse comparisons give the expected answer + { + int i = 1, j = 2; + int& min = cmp::min(i, j, cmp::reverse_compare); + int& max = cmp::max(i, j, cmp::reverse_compare); + STATIC_CHECK(&min == &j); + STATIC_CHECK(&max == &i); + + Test t1{1, 3.0}; + Test t2{1, 2.0}; + + STATIC_CHECK(&cmp::min(t1, t2, flux::proj(cmp::reverse_compare, &Test::i)) == &t1); + STATIC_CHECK(&cmp::max(t1, t2, flux::proj(cmp::reverse_compare, &Test::i)) == &t2); } return true; } static_assert(test_comparisons()); +struct Test { + bool operator==(Test const&) const = default; + constexpr auto operator<=>(Test const&) const { + return std::partial_ordering::unordered; + } +}; + +constexpr bool test_partial_min_max() +{ + namespace cmp = flux::cmp; + + // partial_min works just like min for sensible types + { + int i = 100, j = 10; + int& r = cmp::partial_min(i, j); + + STATIC_CHECK(&r == &j); + } + + // for partially ordered types, partial_min returns the first element + // if the arguments are unordered + { + Test const t1, t2; + + Test const& r = cmp::partial_min(t1, t2); + + STATIC_CHECK(&r == &t1); + } + + // partial_max works just like min for sensible types + { + int i = 100, j = 10; + int& r = cmp::partial_max(i, j); + + STATIC_CHECK(&r == &i); + } + + // for partially ordered types, partial_max returns the second element + // if the arguments are unordered + { + Test const t1, t2; + + Test const& r = cmp::partial_max(t1, t2); + + STATIC_CHECK(&r == &t2); + } + + return true; +} +static_assert(test_partial_min_max()); + } TEST_CASE("predicates") @@ -254,5 +302,6 @@ TEST_CASE("predicates") TEST_CASE("comparators") { REQUIRE(test_comparisons()); + REQUIRE(test_partial_min_max()); } diff --git a/test/test_set_adaptors.cpp b/test/test_set_adaptors.cpp index 93499834..b52ad197 100644 --- a/test/test_set_adaptors.cpp +++ b/test/test_set_adaptors.cpp @@ -95,7 +95,7 @@ constexpr bool test_set_union() // custom compare { - auto union_seq = flux::set_union(std::array{4, 2, 0}, std::array{5, 3, 1}, std::ranges::greater{}); + auto union_seq = flux::set_union(std::array{4, 2, 0}, std::array{5, 3, 1}, flux::cmp::reverse_compare); using T = decltype(union_seq); static_assert(flux::sequence); @@ -111,7 +111,7 @@ constexpr bool test_set_union() std::array, 3> arr2{{{1, 'x'}, {3, 'y'}, {5, 'z'}}}; auto union_seq = flux::set_union(flux::ref(arr1), flux::ref(arr2), - flux::proj(std::ranges::less{}, [] (auto v) { return v.first; })); + flux::proj(flux::cmp::compare, [] (auto v) { return v.first; })); using T = decltype(union_seq); static_assert(flux::sequence); @@ -135,18 +135,18 @@ constexpr bool test_set_union() // test with different (but compatible) types { std::array arr1{1, 2, 3, 4, 5}; - std::array arr2{4.0, 5.0, 6.0}; + std::array arr2{4L, 5L, 6L}; auto union_seq = flux::set_union(arr1, arr2); using T = decltype(union_seq); - static_assert(std::same_as, double>); - static_assert(std::same_as, double>); - static_assert(std::same_as, double>); - static_assert(std::same_as, double>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); - STATIC_CHECK(check_equal(union_seq, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0})); + STATIC_CHECK(check_equal(union_seq, {1L, 2L, 3L, 4L, 5L, 6L})); } // test cursor iteration @@ -251,7 +251,8 @@ constexpr bool test_set_difference() // custom compare { - auto diff_seq = flux::set_difference(std::array{5, 4, 3, 2, 1, 0}, std::array{4, 2, 0}, std::ranges::greater{}); + auto diff_seq = flux::set_difference(std::array{5, 4, 3, 2, 1, 0}, std::array{4, 2, 0}, + flux::cmp::reverse_compare); using T = decltype(diff_seq); static_assert(flux::sequence); @@ -267,7 +268,7 @@ constexpr bool test_set_difference() std::array, 3> arr2{{{1, 'x'}, {2, 'y'}, {5, 'z'}}}; auto diff_seq = flux::set_difference(flux::ref(arr1), flux::ref(arr2), - flux::proj(std::ranges::less{}, [] (auto v) { return v.first; })); + flux::proj(flux::cmp::compare, [] (auto v) { return v.first; })); using T = decltype(diff_seq); static_assert(flux::sequence); @@ -395,7 +396,8 @@ constexpr bool test_set_symmetric_difference() // custom compare { - auto diff_seq = flux::set_symmetric_difference(std::array{5, 4, 3, 2, 1, 0}, std::array{6, 4, 2, 0}, std::ranges::greater{}); + auto diff_seq = flux::set_symmetric_difference(std::array{5, 4, 3, 2, 1, 0}, std::array{6, 4, 2, 0}, + flux::cmp::reverse_compare); using T = decltype(diff_seq); static_assert(flux::sequence); @@ -411,7 +413,7 @@ constexpr bool test_set_symmetric_difference() std::array, 3> arr2{{{1, 'x'}, {2, 'y'}, {5, 'z'}}}; auto diff_seq = flux::set_symmetric_difference(flux::ref(arr1), flux::ref(arr2), - flux::proj(std::ranges::less{}, [] (auto v) { return v.first; })); + flux::proj(flux::cmp::compare, [] (auto v) { return v.first; })); using T = decltype(diff_seq); static_assert(flux::sequence); @@ -434,7 +436,7 @@ constexpr bool test_set_symmetric_difference() // test different value types { std::array arr1{1, 2, 3, 4}; - std::array arr2{2.0f, 3.0f, 5.0f}; + std::array arr2{2L, 3L, 5L}; auto diff_seq = flux::set_symmetric_difference(arr1, arr2); @@ -444,12 +446,12 @@ constexpr bool test_set_symmetric_difference() static_assert(flux::multipass_sequence); static_assert(not flux::sized_sequence); - static_assert(std::same_as, float>); - static_assert(std::same_as, float>); - static_assert(std::same_as, float>); - static_assert(std::same_as, float>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); + static_assert(std::same_as, long>); - STATIC_CHECK(check_equal(diff_seq, {1.0f, 4.0f, 5.0f})); + STATIC_CHECK(check_equal(diff_seq, {1L, 4L, 5L})); } return true; @@ -540,7 +542,8 @@ constexpr bool test_set_intersection() // custom compare { - auto inter_seq = flux::set_intersection(std::array{3, 2, 1, 0}, std::array{5, 3, 1}, std::ranges::greater{}); + auto inter_seq = flux::set_intersection(std::array{3, 2, 1, 0}, std::array{5, 3, 1}, + flux::cmp::reverse_compare); using T = decltype(inter_seq); static_assert(flux::sequence); @@ -556,7 +559,7 @@ constexpr bool test_set_intersection() std::array, 3> arr2{{{1, 'x'}, {2, 'y'}, {5, 'z'}}}; auto inter_seq = flux::set_intersection(flux::ref(arr1), flux::ref(arr2), - flux::proj(std::ranges::less{}, [] (auto v) { return v.first; })); + flux::proj(flux::cmp::compare, [] (auto v) { return v.first; })); using T = decltype(inter_seq); static_assert(flux::sequence); diff --git a/test/test_sort.cpp b/test/test_sort.cpp index f64d0873..c0c9a1f9 100644 --- a/test/test_sort.cpp +++ b/test/test_sort.cpp @@ -62,7 +62,7 @@ constexpr bool test_sort_contexpr() }; flux::sort(arr, [](auto lhs, auto rhs) { - return rhs < lhs; + return rhs <=> lhs; }); STATIC_CHECK(std::is_sorted(arr.crbegin(), arr.crend())); @@ -78,7 +78,7 @@ constexpr bool test_sort_contexpr() }; flux::zip(std::array{3, 2, 4, 1}, flux::mut_ref(arr)) - .sort(flux::proj(std::ranges::greater{}, + .sort(flux::proj(flux::cmp::reverse_compare, [](auto const& elem) { return std::get<0>(elem); })); STATIC_CHECK(check_equal(arr, {"charlie"sv, "alpha"sv, "bravo"sv, "delta"sv})); @@ -154,7 +154,7 @@ void test_sort_projected(unsigned sz) std::iota(ptr, ptr + sz, Int{0}); std::shuffle(ptr, ptr + sz, gen); - flux::sort(span_seq(ptr, sz), flux::proj(std::less<>{}, &Int::i)); + flux::sort(span_seq(ptr, sz), flux::proj(flux::cmp::compare, &Int::i)); CHECK(std::is_sorted(ptr, ptr + sz, [](Int lhs, Int rhs) { return lhs.i < rhs.i; diff --git a/test/test_unchecked.cpp b/test/test_unchecked.cpp index d96032fa..78d60f1e 100644 --- a/test/test_unchecked.cpp +++ b/test/test_unchecked.cpp @@ -42,7 +42,7 @@ constexpr bool test_unchecked() static_assert(std::same_as, std::pair>); static_assert(std::same_as, std::pair>); - seq.sort(flux::proj(std::less<>{}, [](auto p) { return p.second; })); + seq.sort(flux::proj(std::weak_order, [](auto p) { return p.second; })); STATIC_CHECK(check_equal(doubles, {1.0, 2.0, 3.0})); STATIC_CHECK(check_equal(ints, {3, 4, 5, 2, 1}));