Skip to content

Commit

Permalink
utility algorithms cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
KRM7 committed Aug 26, 2023
1 parent 375c908 commit 59e648a
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 117 deletions.
5 changes: 4 additions & 1 deletion src/algorithm/nd_sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ namespace gapp::algorithm::dtl

std::for_each(last_idx, indices.rend(), [&](size_t col) noexcept
{
if (dmat(row, col).load(std::memory_order_relaxed) == MAXIMAL) dmat(row, col).store(NONMAXIMAL, std::memory_order_relaxed);
if (dmat(row, col).load(std::memory_order_relaxed) == MAXIMAL)
{
dmat(row, col).store(NONMAXIMAL, std::memory_order_relaxed);
}
});
});
});
Expand Down
174 changes: 79 additions & 95 deletions src/utility/algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace gapp::detail
}

template<std::random_access_iterator Iter, typename Comp = std::less<std::iter_value_t<Iter>>>
requires std::strict_weak_order<Comp, std::iter_value_t<Iter>, std::iter_value_t<Iter>>
requires std::strict_weak_order<Comp&, std::iter_reference_t<Iter>, std::iter_reference_t<Iter>>
std::vector<size_t> argsort(Iter first, Iter last, Comp&& comp = {})
{
GAPP_ASSERT(std::distance(first, last) >= 0);
Expand All @@ -70,7 +70,7 @@ namespace gapp::detail
}

template<std::random_access_iterator Iter, typename Comp = std::less<std::iter_value_t<Iter>>>
requires std::strict_weak_order<Comp, std::iter_value_t<Iter>, std::iter_value_t<Iter>>
requires std::strict_weak_order<Comp&, std::iter_reference_t<Iter>, std::iter_reference_t<Iter>>
std::vector<size_t> partial_argsort(Iter first, Iter middle, Iter last, Comp&& comp = {})
{
GAPP_ASSERT(std::distance(first, middle) >= 0);
Expand Down Expand Up @@ -104,33 +104,57 @@ namespace gapp::detail
return indices;
}

template<std::random_access_iterator Iter, typename Comp = std::less<std::iter_value_t<Iter>>>
requires std::strict_weak_order<Comp, std::iter_value_t<Iter>, std::iter_value_t<Iter>>
constexpr size_t argmax(Iter first, Iter last, Comp&& comp = {})
template<std::forward_iterator Iter, typename F = std::identity>
requires std::invocable<F&, std::iter_reference_t<Iter>>
constexpr Iter max_element(Iter first, Iter last, F&& transform = {})
{
GAPP_ASSERT(std::distance(first, last) > 0);
GAPP_ASSERT(std::distance(first, last) >= 0);

const auto it = std::max_element(first, last, std::forward<Comp>(comp));
const size_t idx = std::distance(first, it);
if (first == last) return first;

if constexpr (detail::is_reverse_iterator_v<Iter>)
Iter max_elem = first;
auto max_value = std::invoke(transform, *first);

for (++first; first != last; ++first)
{
const size_t last_idx = std::distance(first, last) - 1;
return last_idx - idx;
auto value = std::invoke(transform, *first);
if (!(max_value < value)) continue;
max_value = std::move(value);
max_elem = first;
}
else

return max_elem;
}

template<std::forward_iterator Iter, typename F = std::identity>
requires std::invocable<F&, std::iter_reference_t<Iter>>
constexpr Iter min_element(Iter first, Iter last, F&& transform = {})
{
GAPP_ASSERT(std::distance(first, last) >= 0);

if (first == last) return first;

Iter min_elem = first;
auto min_value = std::invoke(transform, *first);

for (++first; first != last; ++first)
{
return idx;
auto value = std::invoke(transform, *first);
if (!(value < min_value)) continue;
min_value = std::move(value);
min_elem = first;
}

return min_elem;
}

template<std::random_access_iterator Iter, typename Comp = std::less<std::iter_value_t<Iter>>>
requires std::strict_weak_order<Comp, std::iter_value_t<Iter>, std::iter_value_t<Iter>>
constexpr size_t argmin(Iter first, Iter last, Comp&& comp = {})
template<std::random_access_iterator Iter, typename F = std::identity>
requires std::invocable<F&, std::iter_reference_t<Iter>>
constexpr size_t argmax(Iter first, Iter last, F&& transform = {})
{
GAPP_ASSERT(std::distance(first, last) > 0);

const auto it = std::min_element(first, last, std::forward<Comp>(comp));
const auto it = detail::max_element(first, last, std::forward<F>(transform));
const size_t idx = std::distance(first, it);

if constexpr (detail::is_reverse_iterator_v<Iter>)
Expand All @@ -144,51 +168,28 @@ namespace gapp::detail
}
}

template<std::forward_iterator Iter, typename F>
template<std::random_access_iterator Iter, typename F = std::identity>
requires std::invocable<F&, std::iter_reference_t<Iter>>
constexpr Iter max_element(Iter first, Iter last, F&& transform)
constexpr size_t argmin(Iter first, Iter last, F&& transform = {})
{
GAPP_ASSERT(std::distance(first, last) > 0);

if (first == last) return first;

Iter max_elem = first;
auto&& max_value = std::invoke(transform, *first);
const auto it = detail::min_element(first, last, std::forward<F>(transform));
const size_t idx = std::distance(first, it);

while (++first != last)
if constexpr (detail::is_reverse_iterator_v<Iter>)
{
auto&& value = std::invoke(transform, *first);
if (value <= max_value) continue;
max_value = std::forward<decltype(value)>(value);
max_elem = first;
const size_t last_idx = std::distance(first, last) - 1;
return last_idx - idx;
}

return max_elem;
}

template<std::forward_iterator Iter, typename F>
requires std::invocable<F&, std::iter_reference_t<Iter>>
constexpr Iter min_element(Iter first, Iter last, F&& transform)
{
GAPP_ASSERT(std::distance(first, last) > 0);

if (first == last) return first;

Iter min_elem = first;
auto&& min_value = std::invoke(transform, *first);

while (++first != last)
else
{
auto&& value = std::invoke(transform, *first);
if (value >= min_value) continue;
min_value = std::forward<decltype(value)>(value);
min_elem = first;
return idx;
}

return min_elem;
}

template<std::random_access_iterator Iter, typename URBG>
requires std::uniform_random_bit_generator<std::remove_cvref_t<URBG>>
constexpr void partial_shuffle(Iter first, Iter middle, Iter last, URBG&& gen)
{
GAPP_ASSERT(std::distance(first, middle) >= 0);
Expand All @@ -204,30 +205,15 @@ namespace gapp::detail
}
}

template<std::input_iterator Iter>
constexpr bool contains(Iter first, Iter last, const std::iter_value_t<Iter>& val)
template<std::input_iterator Iter, typename T>
constexpr bool contains(Iter first, Iter last, const T& val)
{
return std::find(first, last, val) != last;
}

template<std::forward_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred>
std::vector<Iter> find_all(Iter first, Iter last, Pred&& pred)
{
GAPP_ASSERT(std::distance(first, last) >= 0);

std::vector<Iter> result;
result.reserve(last - first);

for (; first != last; ++first)
{
if (std::invoke(pred, *first)) result.push_back(first);
}

return result;
}

template<std::forward_iterator Iter, std::predicate<std::iter_value_t<Iter>> Pred>
auto find_all_v(Iter first, Iter last, Pred&& pred)
template<std::forward_iterator Iter, typename Pred>
requires std::predicate<Pred&, std::iter_reference_t<Iter>>
auto find_all(Iter first, Iter last, Pred&& pred)
{
GAPP_ASSERT(std::distance(first, last) >= 0);

Expand All @@ -244,8 +230,26 @@ namespace gapp::detail
return result;
}

template<detail::IndexableContainer Container, typename T>
constexpr std::optional<size_t> index_of(const Container& container, const T& val)
{
const auto found = std::find(container.begin(), container.end(), val);
const size_t idx = std::distance(container.begin(), found);

return (found != container.end()) ? idx : std::optional<size_t>{};
}

template<detail::IndexableContainer Container, std::predicate<typename Container::value_type> Pred>
constexpr std::optional<size_t> find_index(const Container& container, Pred&& pred)
{
const auto found = std::find_if(container.begin(), container.end(), std::forward<Pred>(pred));
const size_t idx = std::distance(container.begin(), found);

return (found != container.end()) ? idx : std::optional<size_t>{};
}

template<detail::IndexableContainer Container, typename Pred>
requires std::predicate<Pred&, typename Container::reference>
std::vector<size_t> find_indices(const Container& container, Pred&& pred)
{
std::vector<size_t> indices;
Expand All @@ -259,24 +263,6 @@ namespace gapp::detail
return indices;
}

template<detail::IndexableContainer Container>
constexpr std::optional<size_t> index_of(const Container& container, const typename Container::value_type& val)
{
const auto found = std::find(container.begin(), container.end(), val);
const size_t idx = std::distance(container.begin(), found);

return (found == container.end()) ? std::optional<size_t>{} : idx;
}

template<detail::IndexableContainer Container, std::predicate<typename Container::value_type> Pred>
constexpr std::optional<size_t> find_index(const Container& container, Pred&& pred)
{
const auto found = std::find_if(container.begin(), container.end(), std::forward<Pred>(pred));
const size_t idx = std::distance(container.begin(), found);

return (found == container.end()) ? std::optional<size_t>{} : idx;
}

template<typename T>
std::vector<T> elementwise_min(std::vector<T> left, std::span<const std::type_identity_t<T>> right)
{
Expand Down Expand Up @@ -305,11 +291,11 @@ namespace gapp::detail
return left;
}

template<detail::Container T>
constexpr bool erase_first_stable(T& container, const typename T::value_type& value)
template<detail::Container Container, typename T>
constexpr bool erase_first_stable(Container& container, const T& value)
{
const auto found = std::find(container.cbegin(), container.cend(), value);
if (found != container.cend())
const auto found = std::find(container.begin(), container.end(), value);
if (found != container.end())
{
container.erase(found);
return true;
Expand All @@ -321,8 +307,6 @@ namespace gapp::detail
requires detail::IndexableContainer<std::remove_cvref_t<Container>>
auto select(Container&& container, const std::vector<size_t>& indices)
{
GAPP_ASSERT(std::all_of(indices.begin(), indices.end(), [&](size_t idx) { return idx < container.size(); }));

using ValueType = typename std::remove_cvref_t<Container>::value_type;

std::vector<ValueType> selected;
Expand Down
8 changes: 4 additions & 4 deletions src/utility/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace gapp::detail
template<typename ValueType, std::invocable<ValueType&> F>
auto map(const std::vector<ValueType>& cont, F&& f)
{
using MappedType = std::invoke_result_t<std::remove_reference_t<F>, ValueType>;
using MappedType = std::invoke_result_t<F&, ValueType>;
using ResultType = std::vector<std::decay_t<MappedType>>;

ResultType result;
Expand All @@ -44,7 +44,7 @@ namespace gapp::detail
template<typename ValueType, std::invocable<ValueType&> F>
auto map(const std::vector<ValueType>& cont, F&& f) requires std::is_scalar_v<ValueType>
{
using MappedType = std::invoke_result_t<std::remove_reference_t<F>, ValueType>;
using MappedType = std::invoke_result_t<F&, ValueType>;
using ResultType = std::vector<std::decay_t<MappedType>>;

ResultType result(cont.size());
Expand All @@ -57,10 +57,10 @@ namespace gapp::detail
}

template<typename ValueType, size_t N, typename F>
requires std::invocable<std::remove_reference_t<F>, ValueType&>
requires std::invocable<F&, ValueType&>
constexpr auto map(const std::array<ValueType, N>& cont, F&& f)
{
using MappedType = std::invoke_result_t<std::remove_reference_t<F>, ValueType>;
using MappedType = std::invoke_result_t<F&, ValueType>;
using ResultType = std::array<std::decay_t<MappedType>, N>;

ResultType result;
Expand Down
44 changes: 27 additions & 17 deletions test/unit/algorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,38 @@ TEST_CASE("partial_argsort", "[algorithm]")
}
}

TEST_CASE("max_element", "[algorithm]")
{
const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 };

REQUIRE(*detail::max_element(nums.begin(), nums.end()) == 5.0);
REQUIRE(*detail::max_element(nums.rbegin(), nums.rend()) == 5.0);

REQUIRE(detail::max_element(nums.begin(), nums.begin()) == nums.begin());

REQUIRE(*detail::max_element(nums.begin(), nums.end(), std::negate{}) == 0.0);
}

TEST_CASE("min_element", "[algorithm]")
{
const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 };

REQUIRE(*detail::min_element(nums.begin(), nums.end()) == 0.0);
REQUIRE(*detail::min_element(nums.rbegin(), nums.rend()) == 0.0);

REQUIRE(detail::min_element(nums.begin(), nums.begin()) == nums.begin());

REQUIRE(*detail::min_element(nums.begin(), nums.end(), std::negate{}) == 5.0);
}

TEST_CASE("argmax", "[algorithm]")
{
const std::vector nums = { 4.0, 0.0, 2.0, 5.0, 1.0 };

REQUIRE(detail::argmax(nums.begin(), nums.end()) == 3);
REQUIRE(detail::argmax(nums.rbegin(), nums.rend()) == 3);

REQUIRE(detail::argmax(nums.begin(), nums.end(), std::greater<>{}) == 1);
REQUIRE(detail::argmax(nums.begin(), nums.end(), std::negate{}) == 1);

REQUIRE(detail::argmax(nums.begin(), nums.begin() + 3) == 0);
REQUIRE(detail::argmax(nums.begin() + 1, nums.end()) == 2);
Expand All @@ -122,7 +146,7 @@ TEST_CASE("argmin", "[algorithm]")
REQUIRE(detail::argmin(nums.begin(), nums.end()) == 1);
REQUIRE(detail::argmin(nums.rbegin(), nums.rend()) == 1);

REQUIRE(detail::argmin(nums.begin(), nums.end(), std::greater<>{}) == 3);
REQUIRE(detail::argmin(nums.begin(), nums.end(), std::negate{}) == 3);

REQUIRE(detail::argmin(nums.begin() + 2, nums.end()) == 2);
}
Expand Down Expand Up @@ -166,7 +190,7 @@ TEST_CASE("find_all", "[algorithm]")
const std::vector nums = { 4, 0, 2, 5, 1 };

const auto odd_nums = detail::find_all(nums.begin(), nums.end(), is_odd);
REQUIRE(odd_nums == std::vector{ nums.end() - 2, nums.end() - 1 });
REQUIRE(odd_nums == std::vector{ 5, 1 });

const auto big_nums = detail::find_all(nums.begin(), nums.end(), is_big);
REQUIRE(big_nums.empty());
Expand All @@ -175,20 +199,6 @@ TEST_CASE("find_all", "[algorithm]")
REQUIRE(detail::find_all(nums.begin(), nums.end(), always_false).size() == 0);
}

TEST_CASE("find_all_v", "[algorithm]")
{
const std::vector nums = { 4, 0, 2, 5, 1 };

const auto odd_nums = detail::find_all_v(nums.begin(), nums.end(), is_odd);
REQUIRE(odd_nums == std::vector{ 5, 1 });

const auto big_nums = detail::find_all_v(nums.begin(), nums.end(), is_big);
REQUIRE(big_nums.empty());

REQUIRE(detail::find_all_v(nums.begin(), nums.end(), always_true).size() == nums.size());
REQUIRE(detail::find_all_v(nums.begin(), nums.end(), always_false).size() == 0);
}

TEST_CASE("find_indices", "[algorithm]")
{
const std::vector nums = { 4, 0, 2, 5, 1 };
Expand Down

0 comments on commit 59e648a

Please sign in to comment.