blob: 34dbff7f19cfb06e30fa4c9939c435ad65375833 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/stdcompat/internal/algorithm.h"
#include <lib/stdcompat/algorithm.h>
#include <lib/stdcompat/array.h>
#include <lib/stdcompat/span.h>
#include <lib/stdcompat/string_view.h>
#include <array>
#include <cstdint>
#include <iostream>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace {
template <typename T, typename Comp>
constexpr bool IsHeap(const T& container, Comp& cmp) {
if (container.empty()) {
return true;
}
for (size_t i = 1; i < container.size() - 1; ++i) {
const auto& parent = container[i / 2];
if (cmp(container[i], parent)) {
return false;
}
}
return true;
}
template <typename T, typename Comp>
constexpr bool IsSorted(const T& container, Comp& cmp) {
if (container.empty()) {
return true;
}
for (size_t i = 0; i < container.size() - 1; ++i) {
if (!cmp(container[i], container[i + 1])) {
return false;
}
}
return true;
}
constexpr auto kLessThan = [](const auto& a, const auto& b) { return a < b; };
constexpr auto kGreaterThan = [](const auto& a, const auto& b) { return a > b; };
constexpr auto kLessOrEqual = [](const auto& a, const auto& b) { return a <= b; };
constexpr auto kGreaterOrEqual = [](const auto& a, const auto& b) { return a >= b; };
TEST(InternalMakeHeapTest, GeneratesValidHeap) {
constexpr auto make_heap = [](auto container, auto cmp) {
cpp20::internal::make_heap(container.begin(), container.end(), cmp);
return container;
};
static_assert(IsHeap(make_heap(cpp20::to_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 5}), kLessThan),
kLessThan),
"");
static_assert(IsHeap(make_heap(cpp20::span<uint8_t>(), kLessThan), kLessThan), "");
static_assert(IsHeap(make_heap(cpp20::to_array({2, 3, 1}), kLessThan), kLessThan), "");
static_assert(IsHeap(make_heap(cpp20::to_array({2, 3, 4, 1}), kLessThan), kLessThan), "");
static_assert(IsHeap(make_heap(cpp20::to_array({2, 3, 6, 5, 1}), kLessThan), kLessThan), "");
static_assert(IsHeap(make_heap(cpp20::to_array({2, 3, 6, 5, 6, 1}), kLessThan), kLessThan), "");
}
TEST(InternalSortHeapTest, SortedHeapOrderIsReverseToComparison) {
constexpr auto make_sorted_heap = [](auto container, auto cmp) {
cpp20::internal::make_heap(container.begin(), container.end(), cmp);
cpp20::internal::sort_heap(container.begin(), container.end(), cmp);
return container;
};
static_assert(
IsSorted(make_sorted_heap(cpp20::to_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 5}), kLessThan),
kGreaterOrEqual),
"");
static_assert(IsSorted(make_sorted_heap(cpp20::to_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 5}),
kGreaterThan),
kLessOrEqual),
"");
static_assert(IsSorted(make_sorted_heap(cpp20::to_array<cpp17::string_view>(
{"1", "Foo", "2", "345", "1", "678", "0"}),
kLessThan),
kGreaterOrEqual),
"");
static_assert(IsSorted(make_sorted_heap(cpp20::to_array<cpp17::string_view>(
{"1", "Foo", "2", "345", "1", "678", "0"}),
kGreaterThan),
kLessOrEqual),
"");
}
TEST(SortTest, ElementsAreSorted) {
constexpr auto sort_container = [](auto container, auto& comp) {
cpp20::sort(container.begin(), container.end(), comp);
return container;
};
static_assert(
IsSorted(sort_container(cpp20::to_array({0, 2, 4, 1, 3, 4, 5, 6}), kLessThan), kLessOrEqual),
"");
static_assert(IsSorted(sort_container(cpp20::to_array({0, 2, 4, 1, 3, 4, 5, 6}), kGreaterThan),
kGreaterOrEqual),
"");
}
TEST(IsSortedTest, ElemenstAreSorted) {
constexpr auto kDataAscending = cpp20::to_array({1, 2, 3, 4, -1});
// Subrange is sorted.
static_assert(cpp20::is_sorted(kDataAscending.begin(), kDataAscending.end() - 1), "");
ASSERT_TRUE(std::is_sorted(kDataAscending.begin(), kDataAscending.end() - 1));
// Full array is not.
static_assert(!cpp20::is_sorted(kDataAscending.begin(), kDataAscending.end()), "");
ASSERT_FALSE(std::is_sorted(kDataAscending.begin(), kDataAscending.end()));
// Same but in reverse order with custom comparison.
constexpr auto kDataDescending = cpp20::to_array({4, 3, 2, 1, 5});
// Subrange is sorted.
static_assert(
cpp20::is_sorted(kDataDescending.begin(), kDataDescending.end() - 1, std::greater{}), "");
ASSERT_TRUE(std::is_sorted(kDataDescending.begin(), kDataDescending.end() - 1, std::greater{}));
// Full array is not.
static_assert(!cpp20::is_sorted(kDataDescending.begin(), kDataDescending.end(), std::greater{}),
"");
ASSERT_FALSE(std::is_sorted(kDataDescending.begin(), kDataDescending.end(), std::greater{}));
// Same but in reverse order with custom comparison.
constexpr auto kDataWithRepeatedElements = cpp20::to_array({4, 3, 2, 2, 1, 5});
// Full array is not.
static_assert(cpp20::is_sorted(kDataWithRepeatedElements.begin(),
kDataWithRepeatedElements.end() - 1, std::greater{}),
"");
ASSERT_TRUE(std::is_sorted(kDataWithRepeatedElements.begin(), kDataWithRepeatedElements.end() - 1,
std::greater{}));
}
TEST(RemoveIfTest, ElementsAreRemoved) {
std::vector<int> container = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5};
std::vector<int> expected = {2, 3, 4, 5, 2, 3, 4, 5};
size_t expected_size = container.size();
auto new_end =
cpp20::remove_if(container.begin(), container.end(), [](const auto& a) { return a == 1; });
ASSERT_EQ(container.size(), expected_size);
ASSERT_THAT(expected, testing::UnorderedElementsAreArray(container.begin(), new_end));
}
TEST(RemoveTest, ElementsAreRemoved) {
std::vector<int> container = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5};
std::vector<int> expected = {2, 3, 4, 5, 2, 3, 4, 5};
size_t expected_size = container.size();
auto new_end = cpp20::remove(container.begin(), container.end(), 1);
ASSERT_EQ(container.size(), expected_size);
ASSERT_THAT(expected, testing::UnorderedElementsAreArray(container.begin(), new_end));
}
TEST(RemoveTest, IsAliasWhenStdIsAvailable) {
auto check = [](auto&& container) {
constexpr auto predicate = [](auto& a) { return true; };
using pred = decltype(predicate);
using it = decltype(std::declval<decltype(container)>().begin());
constexpr it (*cpp20_remove_if)(it, it, pred) = cpp20::remove_if;
constexpr it (*std_remove_if)(it, it, pred) = cpp20::remove_if;
static_assert(cpp20_remove_if == std_remove_if, "");
};
check(std::vector<int>());
check(std::array<int, 4>());
}
#if __cpp_lib_constexpr_algorithms >= 201806L && !defined(LIB_STDCOMPAT_USE_POLYFILLS)
TEST(SortTest, IsAliasWhenStdIsAvailable) {
{
using iterator_type = decltype(std::declval<std::vector<int>>().begin());
constexpr void (*sort_cpp20)(iterator_type, iterator_type) = cpp20::sort;
constexpr void (*sort_std)(iterator_type, iterator_type) = std::sort;
static_assert(sort_cpp20 == sort_std, "cpp20::sort should be an alias of std::sort.");
}
{
using iterator_type = decltype(std::declval<std::vector<int>>().begin());
auto comp = [](const int& a, const int& b) -> bool { return false; };
constexpr void (*sort_cpp20)(iterator_type, iterator_type, decltype(comp)) = cpp20::sort;
constexpr void (*sort_std)(iterator_type, iterator_type, decltype(comp)) = std::sort;
static_assert(sort_cpp20 == sort_std, "cpp20::sort should be an alias of std::sort.");
}
}
TEST(IsSortedTest, IsAliasWhenStdIsAvailable) {
{
using iterator_type = decltype(std::declval<std::vector<int>>().begin());
constexpr bool (*is_sorted_cpp20)(iterator_type, iterator_type) = cpp20::is_sorted;
constexpr bool (*is_sorted_std)(iterator_type, iterator_type) = std::is_sorted;
static_assert(is_sorted_cpp20 == is_sorted_std,
"cpp20::is_sorted_std should be an alias of std::sort.");
}
{
using iterator_type = decltype(std::declval<std::vector<int>>().begin());
auto comp = [](const int& a, const int& b) -> bool { return false; };
constexpr bool (*is_sorted_cpp20)(iterator_type, iterator_type, decltype(comp)) =
cpp20::is_sorted;
constexpr bool (*is_sorted_std)(iterator_type, iterator_type, decltype(comp)) = std::is_sorted;
static_assert(is_sorted_cpp20 == is_sorted_std,
"cpp20::is_sorted_std should be an alias of std::sort.");
}
}
#endif // __cpp_lib_constexpr_algorithms >= 201806L && !defined(LIB_STDCOMPAT_USE_POLYFILLS)
} // namespace