Skip to content

Commit

Permalink
Merge pull request #190 from elbeno/bitset-transform-reduce
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevalenty authored Feb 11, 2025
2 parents 62b9868 + 7fc489b commit e6b6404
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
9 changes: 9 additions & 0 deletions docs/bitset.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,12 @@ objects, `lowest_unset` is also provided:
auto bs = stdx::bitset<8>{0b11'0111ul};
auto i = bs.lowest_unset(); // i == 3
----

`transform_reduce` is also provided for bitsets:
[source,cpp]
----
auto bs = stdx::bitset<8>{0b1010'1010ul};
auto result = transform_reduce([](auto i) { return i * 2 },
std::plus{}, std::size_t{}, bs);
// result is 1*2 + 3*2 + 5*2 + 7*2
----
31 changes: 31 additions & 0 deletions include/stdx/bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,24 @@ class bitset {
template <typename F, auto M, typename... S>
friend constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F;

template <typename T, typename F, typename R>
constexpr auto transform_reduce(F &&f, R &&r, T init) const -> T {
std::size_t i = 0;
for (auto e : storage) {
while (e != 0) {
auto const offset = static_cast<std::size_t>(countr_zero(e));
e &= static_cast<elem_t>(~(bit << offset));
init = r(std::move(init), f(i + offset));
}
i += std::numeric_limits<elem_t>::digits;
}
return init;
}

template <typename T, typename F, typename R, auto M, typename... S>
friend constexpr auto transform_reduce(F &&f, R &&r, T init,
bitset<M, S> const &...bs) -> T;

public:
constexpr bitset() = default;
constexpr explicit bitset(std::uint64_t value) {
Expand Down Expand Up @@ -421,6 +439,19 @@ constexpr auto for_each(F &&f, bitset<M, S> const &...bs) -> F {
}
}

template <typename T, typename F, typename R, auto M, typename... S>
constexpr auto transform_reduce(F &&f, R &&r, T init,
bitset<M, S> const &...bs) -> T {
if constexpr (sizeof...(bs) == 1) {
return (bs.transform_reduce(std::forward<F>(f), std::forward<R>(r),
std::move(init)),
...);
} else {
static_assert(stdx::always_false_v<F>, "unimplemented");
return init;
}
}

#if __cplusplus >= 202002L
template <std::size_t N> bitset(ct_string<N>) -> bitset<N - 1>;
#endif
Expand Down
14 changes: 14 additions & 0 deletions test/bitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,20 @@ TEMPLATE_TEST_CASE("for_each iterates in order lsb to msb", "[bitset]",
CHECK(result == bs);
}

TEMPLATE_TEST_CASE("transform_reduce", "[bitset]", std::uint8_t, std::uint16_t,
std::uint32_t, std::uint64_t) {
constexpr auto bs = stdx::bitset<8, TestType>{0b10101010ul};
int calls{};
auto const result = transform_reduce(
[&](auto i) {
++calls;
return i == 3 and calls == 2;
},
std::logical_or{}, false, bs);
CHECK(result);
CHECK(calls == bs.count());
}

TEMPLATE_TEST_CASE("set range of bits (lsb, length)", "[bitset]", std::uint8_t,
std::uint16_t, std::uint32_t, std::uint64_t) {
using namespace stdx::literals;
Expand Down

0 comments on commit e6b6404

Please sign in to comment.