diff --git a/include/beman/inplace_vector/inplace_vector.hpp b/include/beman/inplace_vector/inplace_vector.hpp index 320cfd1..8153afb 100644 --- a/include/beman/inplace_vector/inplace_vector.hpp +++ b/include/beman/inplace_vector/inplace_vector.hpp @@ -254,6 +254,7 @@ Software. */ #include // for rotate... #include +#include #include // for lots... #include // for size_t #include // for fixed-width integer types @@ -309,6 +310,11 @@ concept container_compatible_range = template concept satify_constexpr = N == 0 || std::is_trivial_v; +template +concept lessthan_comparable = requires(const T &a, const T &b) { + { a < b } -> std::convertible_to; +}; + } // namespace beman::details::inplace_vector // Types implementing the `inplace_vector`'s storage @@ -1001,27 +1007,25 @@ struct inplace_vector insert_range(begin(), il); } - constexpr friend int /*synth-three-way-result*/ - operator<=>(const inplace_vector & x, const inplace_vector & y) { - if (x.size() < y.size()) - return -1; - if (x.size() > y.size()) - return +1; - - bool all_equal = true; - bool all_less = true; - for (size_type i = 0; i < x.size(); ++i) { - if (x[i] < y[i]) - all_equal = false; - if (x[i] == y[i]) - all_less = false; + constexpr friend auto operator<=>(const inplace_vector &x, + const inplace_vector &y) + requires(beman::details::inplace_vector::lessthan_comparable) + { + if constexpr (std::three_way_comparable) { + return std::lexicographical_compare_three_way(x.begin(), x.end(), + y.begin(), y.end()); + } else { + const auto sz = std::min(x.size(), y.size()); + for (std::size_t i = 0; i < sz; ++i) { + if (x[i] < y[i]) + return std::strong_ordering::less; + if (y[i] < x[i]) + return std::strong_ordering::greater; + // [container.opt.reqmts] < must be total ordering relationship + } + + return x.size() <=> y.size(); } - - if (all_equal) - return 0; - if (all_less) - return -1; - return 1; } }; diff --git a/tests/beman/inplace_vector/CMakeLists.txt b/tests/beman/inplace_vector/CMakeLists.txt index e0a01b4..a746209 100644 --- a/tests/beman/inplace_vector/CMakeLists.txt +++ b/tests/beman/inplace_vector/CMakeLists.txt @@ -37,6 +37,7 @@ endfunction() # Tests for official specs add_gtest(container_requirements) add_gtest(triviality) +add_gtest(compare) add_gtest(constructors) add_gtest(size_n_data) add_gtest(erasure) diff --git a/tests/beman/inplace_vector/compare.test.cpp b/tests/beman/inplace_vector/compare.test.cpp new file mode 100644 index 0000000..cdaa458 --- /dev/null +++ b/tests/beman/inplace_vector/compare.test.cpp @@ -0,0 +1,326 @@ +#include +#include + +#include +#include + +using namespace beman; + +template +concept has_threeway = requires(const T &t) { + { t <=> t }; +}; + +template +concept lessthan_comparable = + beman::details::inplace_vector::lessthan_comparable; + +template struct vec_list { + T empty; + T base; // base + T copy; // identical to base + T greater; // greater number of elements + T lesser; // lesser number of elements + T bigger; // bigger value of the elements + T smaller; // smaller value of the elements + T greater_smaller; // greater number of elements but smaller values + T lesser_bigger; // lesser number of elements but bigger values +}; + +template static void runtests(vec_list &list) { + + static_assert(std::three_way_comparable || lessthan_comparable); + + // if T::value_type is threewaycomparable with ordering X then T must also + // be comparable with ordering X + + using VT = typename T::value_type; + + if constexpr (std::three_way_comparable) + static_assert(std::three_way_comparable); + + if constexpr (std::three_way_comparable) + static_assert(std::three_way_comparable); + + if constexpr (std::three_way_comparable) + static_assert(std::three_way_comparable); + + if constexpr (std::equality_comparable) { + EXPECT_TRUE(list.empty == list.empty); + EXPECT_TRUE(list.empty != list.base); + } + + EXPECT_TRUE((list.base <=> list.copy) == 0); + EXPECT_TRUE((list.base <=> list.greater) < 0); + EXPECT_TRUE((list.base <=> list.lesser) > 0); + + EXPECT_TRUE((list.base <=> list.bigger) < 0); + EXPECT_TRUE((list.base <=> list.smaller) > 0); + + EXPECT_TRUE((list.base <=> list.greater_smaller) < 0); + EXPECT_TRUE((list.base <=> list.lesser_bigger) > 0); + + if constexpr (std::equality_comparable) { + EXPECT_TRUE(list.base == list.copy); + EXPECT_TRUE(list.base != list.greater); + EXPECT_TRUE(list.base != list.lesser); + } + EXPECT_TRUE(list.base <= list.copy); + EXPECT_TRUE(list.base >= list.copy); + EXPECT_TRUE(list.base < list.greater); + EXPECT_TRUE(list.base <= list.greater); + EXPECT_TRUE(list.base > list.lesser); + EXPECT_TRUE(list.base >= list.lesser); + + if constexpr (std::equality_comparable) { + EXPECT_TRUE(list.copy == list.base); + EXPECT_TRUE(list.copy != list.greater); + EXPECT_TRUE(list.copy != list.lesser); + } + EXPECT_TRUE(list.copy <= list.base); + EXPECT_TRUE(list.copy >= list.base); + EXPECT_TRUE(list.greater > list.base); + EXPECT_TRUE(list.greater >= list.base); + EXPECT_TRUE(list.lesser < list.base); + EXPECT_TRUE(list.lesser <= list.base); +}; + +TEST(Compare, threeway_int) { + vec_list> list{ + .empty{}, + .base{1, 2, 3}, + .copy{1, 2, 3}, + .greater{4, 5, 6}, + .lesser{0, 0, 0}, + .bigger{1, 2, 3, 0}, + .smaller{1, 2}, + .greater_smaller{2, 2}, + .lesser_bigger{0, 2, 3, 4}, + }; + + runtests(list); +} + +TEST(Compare, threeway_float) { + vec_list> list{ + .empty{}, + .base{1.0f, 2.0f, 3.0f}, + .copy{1.0f, 2.0f, 3.0f}, + .greater{4.0f, 5.0f, 6.0f}, + .lesser{0.0f, 0.0f, 0.0f}, + .bigger{1.0f, 2.0f, 3.0f, 0.0f}, + .smaller{1.0f, 2.0f}, + .greater_smaller{2.0f, 2.0f}, + .lesser_bigger{0.0f, 2.0f, 3.0f, 4.0f}, + }; + + runtests(list); + + // compare unorderable values + + EXPECT_EQ(std::nanf("") <=> std::nanf(""), std::partial_ordering::unordered); + EXPECT_FALSE(std::nanf("") == std::nanf("")); + EXPECT_FALSE(std::nanf("") < std::nanf("")); + EXPECT_FALSE(std::nanf("") > std::nanf("")); + EXPECT_FALSE(std::nanf("") >= std::nanf("")); + EXPECT_FALSE(std::nanf("") <= std::nanf("")); + + inplace_vector vnan{std::nanf("")}; + inplace_vector vnan2{std::nanf("")}; + + EXPECT_EQ(vnan <=> vnan2, std::partial_ordering::unordered); + EXPECT_FALSE(vnan == vnan2); + EXPECT_FALSE(vnan < vnan2); + EXPECT_FALSE(vnan > vnan2); + EXPECT_FALSE(vnan >= vnan2); + EXPECT_FALSE(vnan <= vnan2); +} + +TEST(Compare, threeway_comparable1) { + struct comparable1 { + int a; + int b; + constexpr auto operator<=>(const comparable1 &) const = default; + }; + + static_assert(std::three_way_comparable); + static_assert(has_threeway); + static_assert(std::three_way_comparable>); + static_assert(has_threeway>); + + vec_list> list{ + .empty{}, + .base{{1, 2}, {3, 4}}, + .copy{{1, 2}, {3, 4}}, + .greater{{5, 6}, {7, 8}}, + .lesser{{0, 0}, {0, 0}}, + .bigger{{1, 2}, {3, 4}, {5, 6}}, + .smaller{{1, 2}}, + .greater_smaller{{2, 2}, {3, 3}}, + .lesser_bigger{{0, 2}, {3, 3}, {4, 4}}, + }; + + runtests(list); +} + +TEST(Compare, threeway_comparable2) { + + struct comparable2 { + int a; + int b; + constexpr bool operator==(const comparable2 &) const = delete; + constexpr bool operator<(const comparable2 &other) const { + return a < other.a || (a == other.a && b < other.b); + }; + }; + + static_assert(!std::three_way_comparable); + static_assert(!has_threeway); + static_assert(lessthan_comparable); + static_assert(std::three_way_comparable>); + static_assert(has_threeway>); + + vec_list> list{ + .empty{}, + .base{{1, 2}, {3, 4}}, + .copy{{1, 2}, {3, 4}}, + .greater{{5, 6}, {7, 8}}, + .lesser{{0, 0}, {0, 0}}, + .bigger{{1, 2}, {3, 4}, {5, 6}}, + .smaller{{1, 2}}, + .greater_smaller{{2, 2}, {3, 3}}, + .lesser_bigger{{0, 2}, {3, 3}, {4, 4}}, + }; + + runtests(list); +} + +TEST(Compare, threeway_strong_ordering) { + + struct weaktype { + int a; + constexpr std::strong_ordering + operator<=>(const weaktype &other) const = default; + }; + + using T = weaktype; + + vec_list> list{ + .empty{}, + .base{T{1}, T{2}, T{3}}, + .copy{T{1}, T{2}, T{3}}, + .greater{T{4}, T{5}, T{6}}, + .lesser{T{0}, T{0}, T{0}}, + .bigger{T{1}, T{2}, T{3}, T{0}}, + .smaller{T{1}, T{2}}, + .greater_smaller{T{2}, T{2}}, + .lesser_bigger{T{0}, T{2}, T{3}, T{4}}, + }; + + runtests(list); +} + +TEST(Compare, threeway_weak_ordering) { + + struct weaktype { + int a; + constexpr std::weak_ordering + operator<=>(const weaktype &other) const = default; + }; + + using T = weaktype; + + vec_list> list{ + .empty{}, + .base{T{1}, T{2}, T{3}}, + .copy{T{1}, T{2}, T{3}}, + .greater{T{4}, T{5}, T{6}}, + .lesser{T{0}, T{0}, T{0}}, + .bigger{T{1}, T{2}, T{3}, T{0}}, + .smaller{T{1}, T{2}}, + .greater_smaller{T{2}, T{2}}, + .lesser_bigger{T{0}, T{2}, T{3}, T{4}}, + }; + + runtests(list); +} + +TEST(Compare, threeway_partial_ordering) { + + struct custom { + int a; + constexpr auto operator<=>(const custom &other) const { + if (a == -1 && other.a == -1) + return std::partial_ordering::unordered; + return std::partial_ordering(a <=> other.a); + } + + constexpr bool operator==(const custom &other) const { + if (a == -1 && other.a == -1) + return false; + return a == other.a; + } + }; + + using T = custom; + + vec_list> list{ + .empty{}, + .base{T{1}, T{2}, T{3}}, + .copy{T{1}, T{2}, T{3}}, + .greater{T{4}, T{5}, T{6}}, + .lesser{T{0}, T{0}, T{0}}, + .bigger{T{1}, T{2}, T{3}, T{0}}, + .smaller{T{1}, T{2}}, + .greater_smaller{T{2}, T{2}}, + .lesser_bigger{T{0}, T{2}, T{3}, T{4}}, + }; + + runtests(list); + + T t1{-1}; + T t2 = t1; + EXPECT_EQ(t1 <=> t2, std::partial_ordering::unordered); + + inplace_vector v1{t1}; + inplace_vector v2{t2}; + + EXPECT_EQ(v1 <=> v2, std::partial_ordering::unordered); + EXPECT_FALSE(v1 == v2); + EXPECT_FALSE(v1 < v2); + EXPECT_FALSE(v1 > v2); + EXPECT_FALSE(v1 >= v2); + EXPECT_FALSE(v1 <= v2); +} + +TEST(Compare, threeway_uncomparable) { + + struct uncomparable1 { + int a; + }; + + static_assert(!std::three_way_comparable); + static_assert(!has_threeway); + static_assert(!std::three_way_comparable>); + static_assert(!has_threeway>); + + struct uncomparable2 { + int a; + constexpr bool operator==(const uncomparable2 &) const = default; + }; + + static_assert(!std::three_way_comparable); + static_assert(!has_threeway); + static_assert(!std::three_way_comparable>); + static_assert(!has_threeway>); + + struct uncomparable3 { + int a; + constexpr auto operator<=>(const uncomparable3 &) const = delete; + }; + + static_assert(!std::three_way_comparable); + static_assert(!has_threeway); + static_assert(!std::three_way_comparable>); + static_assert(!has_threeway>); +}